squirrels 0.2.0rc1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of squirrels might be problematic. Click here for more details.

Files changed (56) hide show
  1. {squirrels-0.2.0rc1 → squirrels-0.2.2}/PKG-INFO +13 -11
  2. {squirrels-0.2.0rc1 → squirrels-0.2.2}/README.md +1 -1
  3. {squirrels-0.2.0rc1 → squirrels-0.2.2}/pyproject.toml +16 -14
  4. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/__init__.py +2 -2
  5. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_api_server.py +66 -49
  6. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_authenticator.py +2 -3
  7. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_command_line.py +1 -1
  8. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_constants.py +7 -5
  9. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_environcfg.py +1 -1
  10. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_initializer.py +1 -2
  11. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_manifest.py +8 -12
  12. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_models.py +43 -21
  13. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_parameter_configs.py +4 -4
  14. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_parameter_sets.py +1 -3
  15. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_py_module.py +4 -2
  16. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_utils.py +7 -0
  17. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/arguments/run_time_args.py +15 -4
  18. squirrels-0.2.2/squirrels/package_data/assets/favicon.ico +0 -0
  19. squirrels-0.2.2/squirrels/package_data/assets/index.js +49 -0
  20. {squirrels-0.2.0rc1/squirrels/package_data/base_project/ignores → squirrels-0.2.2/squirrels/package_data/base_project}/.gitignore +4 -0
  21. {squirrels-0.2.0rc1/squirrels/package_data/base_project → squirrels-0.2.2/squirrels/package_data/base_project/docker}/Dockerfile +2 -2
  22. squirrels-0.2.2/squirrels/package_data/base_project/docker/compose.yml +7 -0
  23. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/environcfg.yml +1 -1
  24. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/parameters.yml +18 -18
  25. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/pyconfigs/auth.py +10 -14
  26. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/pyconfigs/context.py +12 -2
  27. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/squirrels.yml.j2 +18 -6
  28. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/parameter_options.py +24 -24
  29. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/parameters.py +3 -3
  30. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/user_base.py +10 -11
  31. squirrels-0.2.0rc1/squirrels/package_data/assets/favicon.ico +0 -0
  32. squirrels-0.2.0rc1/squirrels/package_data/assets/index.js +0 -49
  33. {squirrels-0.2.0rc1 → squirrels-0.2.2}/LICENSE +0 -0
  34. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_connection_set.py +0 -0
  35. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_package_loader.py +0 -0
  36. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_timer.py +0 -0
  37. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/_version.py +0 -0
  38. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/arguments/init_time_args.py +0 -0
  39. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/data_sources.py +0 -0
  40. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/dateutils.py +0 -0
  41. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/assets/index.css +0 -0
  42. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/connections.yml +0 -0
  43. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/database/expenses.db +0 -0
  44. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/database/weather.db +0 -0
  45. {squirrels-0.2.0rc1/squirrels/package_data/base_project/ignores → squirrels-0.2.2/squirrels/package_data/base_project/docker}/.dockerignore +0 -0
  46. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/models/dbviews/database_view1.py +0 -0
  47. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/models/dbviews/database_view1.sql +0 -0
  48. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/models/federates/dataset_example.py +0 -0
  49. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/models/federates/dataset_example.sql +0 -0
  50. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/pyconfigs/connections.py +0 -0
  51. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/pyconfigs/parameters.py +0 -0
  52. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/seeds/mocks/category.csv +0 -0
  53. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/seeds/mocks/max_filter.csv +0 -0
  54. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/seeds/mocks/subcategory.csv +0 -0
  55. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/base_project/tmp/.gitignore +0 -0
  56. {squirrels-0.2.0rc1 → squirrels-0.2.2}/squirrels/package_data/templates/index.html +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: squirrels
3
- Version: 0.2.0rc1
3
+ Version: 0.2.2
4
4
  Summary: Squirrels - API Framework for Data Analytics
5
- Home-page: https://squirrels-nest.github.io/squirrels-docs/
5
+ Home-page: https://squirrels-nest.github.io
6
6
  License: MIT
7
7
  Author: Tim Huang
8
8
  Author-email: tim.yuting@hotmail.com
@@ -17,19 +17,21 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
18
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Classifier: Typing :: Typed
20
+ Provides-Extra: duckdb
20
21
  Requires-Dist: cachetools (>=5.3.2,<6.0.0)
21
22
  Requires-Dist: cryptography (>=41.0.7,<42.0.0)
22
- Requires-Dist: fastapi (>=0.104.1,<0.105.0)
23
- Requires-Dist: gitpython (>=3.1.40,<4.0.0)
24
- Requires-Dist: inquirer (>=3.1.4,<4.0.0)
25
- Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
23
+ Requires-Dist: duckdb (>=0.10.0) ; extra == "duckdb"
24
+ Requires-Dist: fastapi (>=0.109.2,<0.110.0)
25
+ Requires-Dist: gitpython (>=3.1.41,<4.0.0)
26
+ Requires-Dist: inquirer (>=3.2.1,<4.0.0)
27
+ Requires-Dist: jinja2 (>=3.1.3,<4.0.0)
26
28
  Requires-Dist: pandas (>=2.1.4,<3.0.0)
27
29
  Requires-Dist: python-jose (>=3.3.0,<4.0.0)
28
- Requires-Dist: python-multipart (>=0.0.6,<0.0.7)
30
+ Requires-Dist: python-multipart (>=0.0.9,<0.0.10)
29
31
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
30
- Requires-Dist: sqlalchemy (>=2.0.23,<3.0.0)
31
- Requires-Dist: uvicorn (>=0.24.0.post1,<0.25.0)
32
- Project-URL: Documentation, https://squirrels-nest.github.io/squirrels-docs/
32
+ Requires-Dist: sqlalchemy (>=2.0.25,<3.0.0)
33
+ Requires-Dist: uvicorn (>=0.27.1,<0.28.0)
34
+ Project-URL: Documentation, https://squirrels-nest.github.io
33
35
  Project-URL: Repository, https://github.com/squirrels-nest/squirrels
34
36
  Description-Content-Type: text/markdown
35
37
 
@@ -37,7 +39,7 @@ Description-Content-Type: text/markdown
37
39
 
38
40
  Squirrels is an API framework that lets you create REST APIs for dynamic data analytics!
39
41
 
40
- **Documentation**: <a href="https://squirrels-nest.github.io/squirrels-docs" target="_blank">https://squirrels-nest.github.io/squirrels-docs</a>
42
+ **Documentation**: <a href="https://squirrels-nest.github.io/" target="_blank">https://squirrels-nest.github.io/</a>
41
43
 
42
44
  **Source Code**: <a href="https://github.com/squirrels-nest/squirrels" target="_blank">https://github.com/squirrels-nest/squirrels</a>
43
45
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Squirrels is an API framework that lets you create REST APIs for dynamic data analytics!
4
4
 
5
- **Documentation**: <a href="https://squirrels-nest.github.io/squirrels-docs" target="_blank">https://squirrels-nest.github.io/squirrels-docs</a>
5
+ **Documentation**: <a href="https://squirrels-nest.github.io/" target="_blank">https://squirrels-nest.github.io/</a>
6
6
 
7
7
  **Source Code**: <a href="https://github.com/squirrels-nest/squirrels" target="_blank">https://github.com/squirrels-nest/squirrels</a>
8
8
 
@@ -1,13 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "squirrels"
3
- version = "0.2.0rc1"
3
+ version = "0.2.2"
4
4
  description = "Squirrels - API Framework for Data Analytics"
5
5
  license = "MIT"
6
6
  authors = ["Tim Huang <tim.yuting@hotmail.com>"]
7
7
  readme = "README.md"
8
- homepage = "https://squirrels-nest.github.io/squirrels-docs/"
8
+ homepage = "https://squirrels-nest.github.io"
9
9
  repository = "https://github.com/squirrels-nest/squirrels"
10
- documentation = "https://squirrels-nest.github.io/squirrels-docs/"
10
+ documentation = "https://squirrels-nest.github.io"
11
11
  classifiers = [
12
12
  "Intended Audience :: Developers",
13
13
  "Topic :: Software Development :: Libraries :: Application Frameworks",
@@ -17,28 +17,30 @@ classifiers = [
17
17
 
18
18
  [tool.poetry.scripts]
19
19
  squirrels = "squirrels._command_line:main"
20
+ sqrl = "squirrels._command_line:main"
20
21
 
21
22
  [tool.poetry.dependencies]
22
23
  python = "^3.9"
23
24
  cachetools = "^5.3.2"
24
25
  cryptography = "^41.0.7"
25
- fastapi = "^0.104.1"
26
- gitpython = "^3.1.40"
27
- inquirer = "^3.1.4"
28
- jinja2 = "^3.1.2"
26
+ fastapi = "^0.109.2"
27
+ gitpython = "^3.1.41"
28
+ inquirer = "^3.2.1"
29
+ jinja2 = "^3.1.3"
29
30
  pandas = "^2.1.4"
30
31
  python-jose = "^3.3.0"
31
- python-multipart = "^0.0.6"
32
+ python-multipart = "^0.0.9"
32
33
  pyyaml = "^6.0.1"
33
- sqlalchemy = "^2.0.23"
34
- uvicorn = "^0.24.0.post1"
34
+ sqlalchemy = "^2.0.25"
35
+ uvicorn = "^0.27.1"
36
+ duckdb = { version = ">=0.10.0", optional = true }
35
37
 
36
38
 
37
- [tool.poetry.group.test.dependencies]
38
- pytest = "*"
39
+ [tool.poetry.extras]
40
+ duckdb = ["duckdb"]
39
41
 
40
- [tool.poetry.group.dev.dependencies]
41
- ipykernel = "*"
42
+ [tool.poetry.group.test.dependencies]
43
+ pytest = "^7.4.4"
42
44
 
43
45
  [build-system]
44
46
  requires = ["poetry-core"]
@@ -1,8 +1,8 @@
1
- __version__ = '0.2.0'
1
+ __version__ = '0.2.2'
2
2
 
3
3
  from .arguments.init_time_args import ConnectionsArgs, ParametersArgs
4
4
  from .arguments.run_time_args import AuthArgs, ContextArgs, ModelDepsArgs, ModelArgs
5
5
  from .parameter_options import SelectParameterOption, DateParameterOption, DateRangeParameterOption, NumberParameterOption, NumberRangeParameterOption
6
- from .parameters import Parameter, SingleSelectParameter, MultiSelectParameter, DateParameter, DateRangeParameter, NumberParameter, NumberRangeParameter
6
+ from .parameters import SingleSelectParameter, MultiSelectParameter, DateParameter, DateRangeParameter, NumberParameter, NumberRangeParameter
7
7
  from .data_sources import SingleSelectDataSource, MultiSelectDataSource, DateDataSource, DateRangeDataSource, NumberDataSource, NumberRangeDataSource
8
8
  from .user_base import User, WrongPassword
@@ -1,5 +1,5 @@
1
- from typing import Iterable, Optional, Mapping, Callable, Coroutine, TypeVar, Any
2
- from fastapi import Depends, FastAPI, Request, HTTPException, status
1
+ from typing import List, Iterable, Optional, Mapping, Callable, Coroutine, TypeVar, Any
2
+ from fastapi import Depends, FastAPI, Request, HTTPException, Response, status
3
3
  from fastapi.responses import HTMLResponse, JSONResponse
4
4
  from fastapi.templating import Jinja2Templates
5
5
  from fastapi.staticfiles import StaticFiles
@@ -45,36 +45,35 @@ class ApiServer:
45
45
  start = time.time()
46
46
  app = FastAPI()
47
47
 
48
- app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
48
+ @app.middleware("http")
49
+ async def catch_exceptions_middleware(request: Request, call_next):
50
+ try:
51
+ return await call_next(request)
52
+ except u.InvalidInputError as exc:
53
+ traceback.print_exc()
54
+ return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST,
55
+ content={"message": f"Invalid user input: {str(exc)}"})
56
+ except u.ConfigurationError as exc:
57
+ traceback.print_exc()
58
+ return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
59
+ content={"message": f"Squirrels configuration error: {str(exc)}"})
60
+ except NotImplementedError as exc:
61
+ traceback.print_exc()
62
+ return JSONResponse(status_code=status.HTTP_501_NOT_IMPLEMENTED,
63
+ content={"message": f"Not implemented error: {str(exc)}"})
64
+ except Exception as exc:
65
+ traceback.print_exc()
66
+ return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
67
+ content={"message": f"Server error: {str(exc)}"})
68
+
69
+ app.add_middleware(
70
+ CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
71
+ expose_headers=["Applied-Username"]
72
+ )
49
73
 
50
74
  squirrels_version_path = f'/squirrels-v{sq_major_version}'
51
75
  partial_base_path = f'/{ManifestIO.obj.project_variables.get_name()}/v{ManifestIO.obj.project_variables.get_major_version()}'
52
76
  base_path = squirrels_version_path + u.normalize_name_for_api(partial_base_path)
53
-
54
- static_dir = u.join_paths(os.path.dirname(__file__), c.PACKAGE_DATA_FOLDER, c.ASSETS_FOLDER)
55
- app.mount('/'+c.ASSETS_FOLDER, StaticFiles(directory=static_dir), name=c.ASSETS_FOLDER)
56
-
57
- templates_dir = u.join_paths(os.path.dirname(__file__), c.PACKAGE_DATA_FOLDER, c.TEMPLATES_FOLDER)
58
- templates = Jinja2Templates(directory=templates_dir)
59
-
60
- # Exception handlers
61
- @app.exception_handler(u.InvalidInputError)
62
- async def invalid_input_error_handler(request: Request, exc: u.InvalidInputError):
63
- traceback.print_exc()
64
- return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST,
65
- content={"message": f"Invalid user input: {str(exc)}"})
66
-
67
- @app.exception_handler(u.ConfigurationError)
68
- async def configuration_error_handler(request: Request, exc: u.InvalidInputError):
69
- traceback.print_exc()
70
- return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
71
- content={"message": f"Squirrels configuration error: {str(exc)}"})
72
-
73
- @app.exception_handler(NotImplementedError)
74
- async def not_implemented_error_handler(request: Request, exc: u.InvalidInputError):
75
- traceback.print_exc()
76
- return JSONResponse(status_code=status.HTTP_501_NOT_IMPLEMENTED,
77
- content={"message": f"Not implemented error: {str(exc)}"})
78
77
 
79
78
  # Helpers
80
79
  T = TypeVar('T')
@@ -127,13 +126,21 @@ class ApiServer:
127
126
  # Changing selections into a cachable "frozenset" that will later be converted to dictionary
128
127
  selections = set()
129
128
  for key, val in params.items():
130
- if not isinstance(val, str):
129
+ if isinstance(val, List):
131
130
  val = tuple(val)
132
131
  selections.add((u.normalize_name(key), val))
133
132
  selections = frozenset(selections)
134
133
 
135
134
  return await api_function(user, dataset_normalized, selections, request_version)
136
135
 
136
+ async def do_cachable_action(cache: TTLCache, action: Callable[..., Coroutine[Any, Any, T]], *args) -> T:
137
+ cache_key = tuple(args)
138
+ result = cache.get(cache_key)
139
+ if result is None:
140
+ result = await action(*args)
141
+ cache[cache_key] = result
142
+ return result
143
+
137
144
  # Login
138
145
  token_path = base_path + '/token'
139
146
 
@@ -154,20 +161,14 @@ class ApiServer:
154
161
  "expiry_time": expiry
155
162
  }
156
163
 
157
- async def get_current_user(token: str = Depends(oauth2_scheme)) -> Optional[User]:
164
+ async def get_current_user(response: Response, token: str = Depends(oauth2_scheme)) -> Optional[User]:
158
165
  user = self.authenticator.get_user_from_token(token)
166
+ username = "" if user is None else user.username
167
+ response.headers["Applied-Username"] = username
159
168
  return user
160
169
 
161
- async def do_cachable_action(cache: TTLCache, action: Callable[..., Coroutine[Any, Any, T]], *args) -> T:
162
- cache_key = tuple(args)
163
- result = cache.get(cache_key)
164
- if result is None:
165
- result = await action(*args)
166
- cache[cache_key] = result
167
- return result
168
-
169
170
  # Parameters API
170
- parameters_path = base_path + '/{dataset}/parameters'
171
+ parameters_path = base_path + '/dataset/{dataset}/parameters'
171
172
 
172
173
  parameters_cache_size = ManifestIO.obj.settings.get(c.PARAMETERS_CACHE_SIZE_SETTING, 1024)
173
174
  parameters_cache_ttl = ManifestIO.obj.settings.get(c.PARAMETERS_CACHE_TTL_SETTING, 0)
@@ -209,7 +210,7 @@ class ApiServer:
209
210
  return result
210
211
 
211
212
  # Results API
212
- results_path = base_path + '/{dataset}'
213
+ results_path = base_path + '/dataset/{dataset}'
213
214
 
214
215
  results_cache_size = ManifestIO.obj.settings.get(c.RESULTS_CACHE_SIZE_SETTING, 128)
215
216
  results_cache_ttl = ManifestIO.obj.settings.get(c.RESULTS_CACHE_TTL_SETTING, 0)
@@ -248,8 +249,10 @@ class ApiServer:
248
249
  timer.add_activity_time("POST REQUEST total time for DATASET", start)
249
250
  return result
250
251
 
251
- # Catalog API
252
- def get_catalog0(user: Optional[User]):
252
+ # Datasets Catalog API
253
+ datasets_path = base_path + '/datasets'
254
+
255
+ def get_datasets0(user: Optional[User]):
253
256
  datasets_info = []
254
257
  for dataset_name, dataset_config in self.dataset_configs.items():
255
258
  if can_user_access_dataset(user, dataset_name):
@@ -258,30 +261,44 @@ class ApiServer:
258
261
  'name': dataset_name,
259
262
  'label': dataset_config.label,
260
263
  'parameters_path': parameters_path.format(dataset=dataset_normalized),
261
- 'result_path': results_path.format(dataset=dataset_normalized),
262
- 'first_minor_version': 0
264
+ 'result_path': results_path.format(dataset=dataset_normalized)
263
265
  })
264
-
266
+ return {"datasets": datasets_info}
267
+
268
+ @app.get(datasets_path)
269
+ def get_datasets(request: Request, user: Optional[User] = Depends(get_current_user)):
270
+ return process_based_on_response_version_header(request.headers, {
271
+ 0: lambda: get_datasets0(user)
272
+ })
273
+
274
+ # Projects Catalog API
275
+ def get_catalog0():
265
276
  return {
266
277
  'projects': [{
267
278
  'name': ManifestIO.obj.project_variables.get_name(),
268
279
  'label': ManifestIO.obj.project_variables.get_label(),
269
280
  'versions': [{
270
281
  'major_version': ManifestIO.obj.project_variables.get_major_version(),
271
- 'latest_minor_version': ManifestIO.obj.project_variables.get_minor_version(),
282
+ 'minor_versions': [0],
272
283
  'token_path': token_path,
273
- 'datasets': datasets_info
284
+ 'datasets_path': datasets_path
274
285
  }]
275
286
  }]
276
287
  }
277
288
 
278
289
  @app.get(squirrels_version_path, response_class=JSONResponse)
279
- async def get_catalog(request: Request, user: Optional[User] = Depends(get_current_user)):
290
+ async def get_catalog(request: Request):
280
291
  return process_based_on_response_version_header(request.headers, {
281
- 0: lambda: get_catalog0(user)
292
+ 0: lambda: get_catalog0()
282
293
  })
283
294
 
284
295
  # Squirrels UI
296
+ static_dir = u.join_paths(os.path.dirname(__file__), c.PACKAGE_DATA_FOLDER, c.ASSETS_FOLDER)
297
+ app.mount('/'+c.ASSETS_FOLDER, StaticFiles(directory=static_dir), name=c.ASSETS_FOLDER)
298
+
299
+ templates_dir = u.join_paths(os.path.dirname(__file__), c.PACKAGE_DATA_FOLDER, c.TEMPLATES_FOLDER)
300
+ templates = Jinja2Templates(directory=templates_dir)
301
+
285
302
  @app.get('/', response_class=HTMLResponse)
286
303
  async def get_ui(request: Request):
287
304
  return templates.TemplateResponse('index.html', {
@@ -34,7 +34,7 @@ class Authenticator:
34
34
  return AuthArgs(conn_args.proj_vars, conn_args.env_vars, conn_args._get_credential, connections, username, password)
35
35
 
36
36
  def authenticate_user(self, username: str, password: str) -> Optional[User]:
37
- user_cls = self.auth_helper.get_func_or_class("User", default_attr=User)
37
+ user_cls: type[User] = self.auth_helper.get_func_or_class("User", default_attr=User)
38
38
  get_user = self.auth_helper.get_func_or_class(c.GET_USER_FUNC, is_required=False)
39
39
  try:
40
40
  real_user = get_user(self._get_auth_args(username, password)) if get_user is not None else None
@@ -48,9 +48,8 @@ class Authenticator:
48
48
  fake_users = EnvironConfigIO.obj.get_users()
49
49
  if username in fake_users and secrets.compare_digest(fake_users[username][c.USER_PWD_KEY], password):
50
50
  is_internal = fake_users[username].get("is_internal", False)
51
- user: User = user_cls(username, is_internal=is_internal)
52
51
  try:
53
- return user.with_attributes(fake_users[username])
52
+ return user_cls.Create(username, fake_users[username], is_internal=is_internal)
54
53
  except Exception as e:
55
54
  raise u.FileExecutionError(f'Failed to create user from User model in {c.AUTH_FILE}', e)
56
55
 
@@ -33,7 +33,7 @@ def main():
33
33
 
34
34
  compile_parser = subparsers.add_parser(c.COMPILE_CMD, help='Create files for rendered sql queries in the "target/compile" folder', add_help=False)
35
35
  compile_parser.add_argument('-h', '--help', action="help", help="Show this help message and exit")
36
- compile_parser.add_argument('-d', '--dataset', type=str, help="Select dataset to use for dataset args. If not specified, all models for all datasets are compiled")
36
+ compile_parser.add_argument('-d', '--dataset', type=str, help="Select dataset to use for dataset traits. If not specified, all models for all datasets are compiled")
37
37
  compile_parser.add_argument('-a', '--all-test-sets', action="store_true", help="Compile models for all selection test sets")
38
38
  compile_parser.add_argument('-t', '--test-set', type=str, help="The selection test set to use. Default selections are used if not specified. Ignored if using --all-test-sets")
39
39
  compile_parser.add_argument('-s', '--select', type=str, help="Select single model to compile. If not specified, all models for the dataset are compiled. Also, ignored if --dataset is not specified")
@@ -9,7 +9,6 @@ PROJ_VARS_KEY = 'project_variables'
9
9
  PROJECT_NAME_KEY = 'name'
10
10
  PROJECT_LABEL_KEY = 'label'
11
11
  MAJOR_VERSION_KEY = 'major_version'
12
- MINOR_VERSION_KEY = 'minor_version'
13
12
 
14
13
  PACKAGES_KEY = 'packages'
15
14
  PACKAGE_GIT_KEY = 'git'
@@ -48,7 +47,7 @@ DATASET_NAME_KEY = 'name'
48
47
  DATASET_LABEL_KEY = 'label'
49
48
  DATASET_MODEL_KEY = 'model'
50
49
  DATASET_PARAMETERS_KEY = 'parameters'
51
- DATASET_ARGS_KEY = 'args'
50
+ DATASET_TRAITS_KEY = 'traits'
52
51
 
53
52
  DATASET_SCOPE_KEY = 'scope'
54
53
  PUBLIC_SCOPE = 'public'
@@ -109,14 +108,17 @@ PARAMETERS_OUTPUT = 'parameters.json'
109
108
  FINAL_VIEW_OUT_STEM = 'final_view'
110
109
 
111
110
  # Dataset setting names
112
- AUTH_TOKEN_EXPIRE_SETTING = 'auth.token.expire.minutes'
111
+ AUTH_TOKEN_EXPIRE_SETTING = 'auth.token.expire_minutes'
113
112
  PARAMETERS_CACHE_SIZE_SETTING = 'parameters.cache.size'
114
- PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl.minutes'
113
+ PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl_minutes'
115
114
  RESULTS_CACHE_SIZE_SETTING = 'results.cache.size'
116
- RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl.minutes'
115
+ RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl_minutes'
117
116
  TEST_SET_DEFAULT_USED_SETTING = 'selection_test_sets.default_name_used'
118
117
  DB_CONN_DEFAULT_USED_SETTING = 'connections.default_name_used'
119
118
  DEFAULT_MATERIALIZE_SETTING = 'defaults.federates.materialized'
119
+ IN_MEMORY_DB_SETTING = 'in_memory_database'
120
+ SQLITE = 'sqlite'
121
+ DUCKDB = 'duckdb'
120
122
 
121
123
  # Selection cfg sections
122
124
  USER_ATTRIBUTES_SECTION = 'user_attributes'
@@ -43,7 +43,7 @@ class _EnvironConfig:
43
43
  return credential[c.USERNAME_KEY], credential[c.PASSWORD_KEY]
44
44
 
45
45
  def get_secret(self, key: str, *, default_factory: Optional[Callable[[],str]] = None) -> str:
46
- if key not in self._secrets and default_factory is not None:
46
+ if not self._secrets.get(key) and default_factory is not None:
47
47
  self._secrets[key] = default_factory()
48
48
  return self._secrets.get(key)
49
49
 
@@ -40,7 +40,6 @@ class Initializer:
40
40
  options = ["core", "connections", "parameters", "dbview", "federate", "auth", "sample_db"]
41
41
  CORE, CONNECTIONS, PARAMETERS, DBVIEW, FEDERATE, AUTH, SAMPLE_DB = options
42
42
  TMP_FOLDER = "tmp"
43
- IGNORES_FOLDER = "ignores"
44
43
 
45
44
  answers = { x: getattr(args, x) for x in options }
46
45
  if not any(answers.values()):
@@ -136,7 +135,7 @@ class Initializer:
136
135
 
137
136
  create_manifest_file()
138
137
 
139
- self._copy_file(".gitignore", src_folder=IGNORES_FOLDER)
138
+ self._copy_file(".gitignore")
140
139
  self._copy_file(c.MANIFEST_FILE, src_folder=TMP_FOLDER)
141
140
 
142
141
  if connections_use_py:
@@ -26,10 +26,10 @@ class ProjectVarsConfig(ManifestComponentConfig):
26
26
  data: dict
27
27
 
28
28
  def __post_init__(self):
29
- required_keys = [c.PROJECT_NAME_KEY, c.MAJOR_VERSION_KEY, c.MINOR_VERSION_KEY]
29
+ required_keys = [c.PROJECT_NAME_KEY, c.MAJOR_VERSION_KEY]
30
30
  self._validate_required(self.data, required_keys, c.PROJ_VARS_KEY)
31
31
 
32
- integer_keys = [c.MAJOR_VERSION_KEY, c.MINOR_VERSION_KEY]
32
+ integer_keys = [c.MAJOR_VERSION_KEY]
33
33
  for key in integer_keys:
34
34
  if key in self.data and not isinstance(self.data[key], int):
35
35
  raise u.ConfigurationError(f'Project variable "{key}" must be an integer')
@@ -46,9 +46,6 @@ class ProjectVarsConfig(ManifestComponentConfig):
46
46
 
47
47
  def get_major_version(self) -> int:
48
48
  return self.data[c.MAJOR_VERSION_KEY]
49
-
50
- def get_minor_version(self) -> int:
51
- return self.data[c.MINOR_VERSION_KEY]
52
49
 
53
50
 
54
51
  @dataclass
@@ -84,14 +81,13 @@ class DbConnConfig(ManifestComponentConfig):
84
81
 
85
82
  @dataclass
86
83
  class ParametersConfig(ManifestComponentConfig):
87
- name: str
88
84
  type: str
89
85
  factory: str
90
86
  arguments: dict
91
87
 
92
88
  @classmethod
93
89
  def from_dict(cls, kwargs: dict):
94
- all_keys = [c.PARAMETER_NAME_KEY, c.PARAMETER_TYPE_KEY, c.PARAMETER_FACTORY_KEY, c.PARAMETER_ARGS_KEY]
90
+ all_keys = [c.PARAMETER_TYPE_KEY, c.PARAMETER_FACTORY_KEY, c.PARAMETER_ARGS_KEY]
95
91
  cls._validate_required(kwargs, all_keys, c.PARAMETERS_KEY)
96
92
  args = {key: kwargs[key] for key in all_keys}
97
93
  return cls(**args)
@@ -150,7 +146,7 @@ class DatasetsConfig(ManifestComponentConfig):
150
146
  model: str
151
147
  scope: DatasetScope
152
148
  parameters: Optional[list[str]]
153
- args: dict
149
+ traits: dict
154
150
 
155
151
  @classmethod
156
152
  def from_dict(cls, kwargs: dict):
@@ -166,8 +162,8 @@ class DatasetsConfig(ManifestComponentConfig):
166
162
  raise u.ConfigurationError(f'Scope not found for dataset "{name}". Scope must be one of {scope_list}') from e
167
163
 
168
164
  parameters = kwargs.get(c.DATASET_PARAMETERS_KEY)
169
- args = kwargs.get(c.DATASET_ARGS_KEY, {})
170
- return cls(name, label, model, scope, parameters, args)
165
+ traits = kwargs.get(c.DATASET_TRAITS_KEY, {})
166
+ return cls(name, label, model, scope, parameters, traits)
171
167
 
172
168
 
173
169
  @dataclass
@@ -175,7 +171,7 @@ class _ManifestConfig:
175
171
  project_variables: ProjectVarsConfig
176
172
  packages: list[PackageConfig]
177
173
  connections: dict[str, DbConnConfig]
178
- parameters: dict[str, ParametersConfig]
174
+ parameters: list[ParametersConfig]
179
175
  selection_test_sets: dict[str, TestSetsConfig]
180
176
  dbviews: dict[str, DbviewConfig]
181
177
  federates: dict[str, FederateConfig]
@@ -209,7 +205,7 @@ class _ManifestConfig:
209
205
  all_package_dirs.add(package.directory)
210
206
 
211
207
  db_conns = cls._create_configs_as_dict(DbConnConfig, kwargs, c.DB_CONNECTIONS_KEY, c.DB_CONN_NAME_KEY)
212
- params = cls._create_configs_as_dict(ParametersConfig, kwargs, c.PARAMETERS_KEY, c.PARAMETER_NAME_KEY)
208
+ params = [ParametersConfig.from_dict(x) for x in kwargs.get(c.PARAMETERS_KEY, [])]
213
209
 
214
210
  test_sets = cls._create_configs_as_dict(TestSetsConfig, kwargs, c.TEST_SETS_KEY, c.TEST_SET_NAME_KEY)
215
211
  default_test_set: str = settings.get(c.TEST_SET_DEFAULT_USED_SETTING, c.DEFAULT_TEST_SET_NAME)