squirrels 0.4.0__py3-none-any.whl → 0.5.0rc0__py3-none-any.whl
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.
- squirrels/__init__.py +10 -6
- squirrels/_api_response_models.py +93 -44
- squirrels/_api_server.py +571 -219
- squirrels/_auth.py +451 -0
- squirrels/_command_line.py +61 -20
- squirrels/_connection_set.py +38 -25
- squirrels/_constants.py +44 -34
- squirrels/_dashboards_io.py +34 -16
- squirrels/_exceptions.py +57 -0
- squirrels/_initializer.py +117 -44
- squirrels/_manifest.py +124 -62
- squirrels/_model_builder.py +111 -0
- squirrels/_model_configs.py +74 -0
- squirrels/_model_queries.py +52 -0
- squirrels/_models.py +860 -354
- squirrels/_package_loader.py +8 -4
- squirrels/_parameter_configs.py +45 -65
- squirrels/_parameter_sets.py +15 -13
- squirrels/_project.py +561 -0
- squirrels/_py_module.py +4 -3
- squirrels/_seeds.py +35 -16
- squirrels/_sources.py +106 -0
- squirrels/_utils.py +166 -63
- squirrels/_version.py +1 -1
- squirrels/arguments/init_time_args.py +78 -15
- squirrels/arguments/run_time_args.py +62 -101
- squirrels/dashboards.py +4 -4
- squirrels/data_sources.py +94 -162
- squirrels/dataset_result.py +86 -0
- squirrels/dateutils.py +4 -4
- squirrels/package_data/base_project/.env +30 -0
- squirrels/package_data/base_project/.env.example +30 -0
- squirrels/package_data/base_project/.gitignore +3 -2
- squirrels/package_data/base_project/assets/expenses.db +0 -0
- squirrels/package_data/base_project/connections.yml +11 -3
- squirrels/package_data/base_project/dashboards/dashboard_example.py +15 -13
- squirrels/package_data/base_project/dashboards/dashboard_example.yml +22 -0
- squirrels/package_data/base_project/docker/.dockerignore +5 -2
- squirrels/package_data/base_project/docker/Dockerfile +3 -3
- squirrels/package_data/base_project/docker/compose.yml +1 -1
- squirrels/package_data/base_project/duckdb_init.sql +9 -0
- squirrels/package_data/base_project/macros/macros_example.sql +15 -0
- squirrels/package_data/base_project/models/builds/build_example.py +26 -0
- squirrels/package_data/base_project/models/builds/build_example.sql +16 -0
- squirrels/package_data/base_project/models/builds/build_example.yml +55 -0
- squirrels/package_data/base_project/models/dbviews/dbview_example.sql +12 -22
- squirrels/package_data/base_project/models/dbviews/dbview_example.yml +26 -0
- squirrels/package_data/base_project/models/federates/federate_example.py +38 -15
- squirrels/package_data/base_project/models/federates/federate_example.sql +16 -2
- squirrels/package_data/base_project/models/federates/federate_example.yml +65 -0
- squirrels/package_data/base_project/models/sources.yml +39 -0
- squirrels/package_data/base_project/parameters.yml +36 -21
- squirrels/package_data/base_project/pyconfigs/connections.py +6 -11
- squirrels/package_data/base_project/pyconfigs/context.py +20 -33
- squirrels/package_data/base_project/pyconfigs/parameters.py +19 -21
- squirrels/package_data/base_project/pyconfigs/user.py +23 -0
- squirrels/package_data/base_project/seeds/seed_categories.yml +15 -0
- squirrels/package_data/base_project/seeds/seed_subcategories.csv +15 -15
- squirrels/package_data/base_project/seeds/seed_subcategories.yml +21 -0
- squirrels/package_data/base_project/squirrels.yml.j2 +17 -40
- squirrels/parameters.py +20 -20
- {squirrels-0.4.0.dist-info → squirrels-0.5.0rc0.dist-info}/METADATA +31 -32
- squirrels-0.5.0rc0.dist-info/RECORD +70 -0
- {squirrels-0.4.0.dist-info → squirrels-0.5.0rc0.dist-info}/WHEEL +1 -1
- squirrels-0.5.0rc0.dist-info/entry_points.txt +3 -0
- {squirrels-0.4.0.dist-info → squirrels-0.5.0rc0.dist-info/licenses}/LICENSE +1 -1
- squirrels/_authenticator.py +0 -85
- squirrels/_environcfg.py +0 -84
- squirrels/package_data/assets/favicon.ico +0 -0
- squirrels/package_data/assets/index.css +0 -1
- squirrels/package_data/assets/index.js +0 -58
- squirrels/package_data/base_project/dashboards.yml +0 -10
- squirrels/package_data/base_project/env.yml +0 -29
- squirrels/package_data/base_project/models/dbviews/dbview_example.py +0 -47
- squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
- squirrels/package_data/templates/index.html +0 -18
- squirrels/project.py +0 -378
- squirrels/user_base.py +0 -55
- squirrels-0.4.0.dist-info/RECORD +0 -60
- squirrels-0.4.0.dist-info/entry_points.txt +0 -4
squirrels/_package_loader.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import shutil, os, time
|
|
2
2
|
|
|
3
|
-
from . import _constants as c, _utils as
|
|
3
|
+
from . import _constants as c, _utils as u
|
|
4
4
|
from ._manifest import ManifestConfig
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class PackageLoaderIO:
|
|
8
8
|
|
|
9
9
|
@classmethod
|
|
10
|
-
def load_packages(cls, logger:
|
|
10
|
+
def load_packages(cls, logger: u.Logger, manifest_cfg: ManifestConfig, *, reload: bool = False) -> None:
|
|
11
11
|
start = time.time()
|
|
12
|
+
|
|
13
|
+
# Importing git here avoids requirement of having git installed on system if not needed
|
|
14
|
+
import git
|
|
15
|
+
|
|
12
16
|
# If reload, delete the modules directory (if it exists). It will be recreated later
|
|
13
17
|
if reload and os.path.exists(c.PACKAGES_FOLDER):
|
|
14
18
|
shutil.rmtree(c.PACKAGES_FOLDER)
|
|
@@ -20,6 +24,6 @@ class PackageLoaderIO:
|
|
|
20
24
|
try:
|
|
21
25
|
git.Repo.clone_from(repo.git, target_dir, branch=repo.revision, depth=1)
|
|
22
26
|
except git.GitCommandError as e:
|
|
23
|
-
raise
|
|
27
|
+
raise u.ConfigurationError(f"Git clone of package failed for this repository: {repo.git}") from e
|
|
24
28
|
|
|
25
29
|
logger.log_activity_time("loading packages", start)
|
squirrels/_parameter_configs.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Generic, TypeVar, Annotated,
|
|
2
|
+
from typing import Generic, TypeVar, Annotated, Sequence, Iterator, Any
|
|
3
3
|
from typing_extensions import Self
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from dataclasses import dataclass, field
|
|
@@ -7,10 +7,11 @@ from abc import ABCMeta, abstractmethod
|
|
|
7
7
|
from copy import copy
|
|
8
8
|
from fastapi import Query
|
|
9
9
|
from pydantic.fields import Field
|
|
10
|
-
import
|
|
10
|
+
import polars as pl, re
|
|
11
11
|
|
|
12
|
-
from . import parameter_options as _po, parameters as p, data_sources as d, _utils as
|
|
13
|
-
from .
|
|
12
|
+
from . import parameter_options as _po, parameters as p, data_sources as d, _utils as u, _constants as c
|
|
13
|
+
from ._exceptions import InvalidInputError
|
|
14
|
+
from ._auth import BaseUser
|
|
14
15
|
from ._connection_set import ConnectionSet
|
|
15
16
|
from ._seeds import Seeds
|
|
16
17
|
|
|
@@ -27,16 +28,17 @@ class APIParamFieldInfo:
|
|
|
27
28
|
pattern: str | None = None
|
|
28
29
|
min_length: int | None = None
|
|
29
30
|
max_length: int | None = None
|
|
31
|
+
default: Any = None
|
|
30
32
|
|
|
31
33
|
def as_query_info(self):
|
|
32
34
|
query_info = Query(
|
|
33
35
|
title=self.title, description=self.description, examples=self.examples, pattern=self.pattern,
|
|
34
36
|
min_length=self.min_length, max_length=self.max_length
|
|
35
37
|
)
|
|
36
|
-
return (self.name, Annotated[self.type, query_info],
|
|
38
|
+
return (self.name, Annotated[self.type, query_info], self.default)
|
|
37
39
|
|
|
38
40
|
def as_body_info(self):
|
|
39
|
-
field_info = Field(
|
|
41
|
+
field_info = Field(self.default,
|
|
40
42
|
title=self.title, description=self.description, examples=self.examples, pattern=self.pattern,
|
|
41
43
|
min_length=self.min_length, max_length=self.max_length
|
|
42
44
|
)
|
|
@@ -50,26 +52,14 @@ class ParameterConfigBase(metaclass=ABCMeta):
|
|
|
50
52
|
"""
|
|
51
53
|
name: str
|
|
52
54
|
label: str
|
|
53
|
-
description: str
|
|
54
|
-
user_attribute: str | None
|
|
55
|
-
parent_name: str | None
|
|
55
|
+
description: str = field(default="", kw_only=True)
|
|
56
|
+
user_attribute: str | None = field(default=None, kw_only=True)
|
|
57
|
+
parent_name: str | None = field(default=None, kw_only=True)
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
def __init__(
|
|
59
|
-
self, widget_type: str, name: str, label: str, *, description: str = "", user_attribute: str | None = None,
|
|
60
|
-
parent_name: str | None = None
|
|
61
|
-
) -> None:
|
|
62
|
-
self.widget_type = widget_type
|
|
63
|
-
self.name = name
|
|
64
|
-
self.label = label
|
|
65
|
-
self.description = description
|
|
66
|
-
self.user_attribute = user_attribute
|
|
67
|
-
self.parent_name = parent_name
|
|
68
|
-
|
|
69
|
-
def _get_user_group(self, user: User | None) -> Any:
|
|
59
|
+
def _get_user_group(self, user: BaseUser | None) -> Any:
|
|
70
60
|
if self.user_attribute is not None:
|
|
71
61
|
if user is None:
|
|
72
|
-
raise
|
|
62
|
+
raise u.ConfigurationError(f"Public datasets (accessible without authentication) cannot use parameter " +
|
|
73
63
|
f"'{self.name}' because 'user_attribute' is defined on this parameter.")
|
|
74
64
|
return getattr(user, self.user_attribute)
|
|
75
65
|
|
|
@@ -92,7 +82,7 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
|
|
|
92
82
|
self, name: str, label: str, all_options: Sequence[ParamOptionType | dict], *, description: str = "",
|
|
93
83
|
user_attribute: str | None = None, parent_name: str | None = None
|
|
94
84
|
) -> None:
|
|
95
|
-
super().__init__(
|
|
85
|
+
super().__init__(name, label, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
96
86
|
self._all_options = tuple(self._to_param_option(x) for x in all_options)
|
|
97
87
|
|
|
98
88
|
def _to_param_option(self, option: ParamOptionType | dict) -> ParamOptionType:
|
|
@@ -117,18 +107,17 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
|
|
|
117
107
|
def DataSource(*args, **kwargs) -> d.DataSource:
|
|
118
108
|
pass
|
|
119
109
|
|
|
120
|
-
def _invalid_input_error(self, selection: str, more_details: str = '') ->
|
|
121
|
-
return
|
|
110
|
+
def _invalid_input_error(self, selection: str, more_details: str = '') -> InvalidInputError:
|
|
111
|
+
return InvalidInputError(200, f'Selected value "{selection}" is not valid for parameter "{self.name}". ' + more_details)
|
|
122
112
|
|
|
123
113
|
@abstractmethod
|
|
124
114
|
def with_selection(
|
|
125
|
-
self, selection: str | None, user:
|
|
126
|
-
*, request_version: int | None = None
|
|
115
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
127
116
|
) -> p.Parameter:
|
|
128
117
|
pass
|
|
129
118
|
|
|
130
119
|
def _get_options_iterator(
|
|
131
|
-
self, all_options: Sequence[ParamOptionType], user:
|
|
120
|
+
self, all_options: Sequence[ParamOptionType], user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
132
121
|
) -> Iterator[ParamOptionType]:
|
|
133
122
|
user_group = self._get_user_group(user)
|
|
134
123
|
selected_parent_option_ids = frozenset(parent_param._get_selected_ids_as_list()) if parent_param else None
|
|
@@ -164,7 +153,7 @@ class SelectionParameterConfig(ParameterConfig[_po.SelectParameterOption]):
|
|
|
164
153
|
self.children[child.name] = child
|
|
165
154
|
self.trigger_refresh = True
|
|
166
155
|
|
|
167
|
-
def _get_options(self, user:
|
|
156
|
+
def _get_options(self, user: BaseUser | None, parent_param: p._SelectionParameter | None) -> Sequence[_po.SelectParameterOption]:
|
|
168
157
|
return tuple(self._get_options_iterator(self.all_options, user, parent_param))
|
|
169
158
|
|
|
170
159
|
def _get_default_ids_iterator(self, options: Sequence[_po.SelectParameterOption]) -> Iterator[str]:
|
|
@@ -200,8 +189,7 @@ class SingleSelectParameterConfig(SelectionParameterConfig):
|
|
|
200
189
|
return d.SelectDataSource(*args, **kwargs)
|
|
201
190
|
|
|
202
191
|
def with_selection(
|
|
203
|
-
self, selection: str | None, user:
|
|
204
|
-
*, request_version: int | None = None
|
|
192
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
205
193
|
) -> p.SingleSelectParameter:
|
|
206
194
|
options = self._get_options(user, parent_param)
|
|
207
195
|
if selection is None:
|
|
@@ -224,9 +212,9 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
|
|
|
224
212
|
"""
|
|
225
213
|
Class to define configurations for multi-select parameter widgets.
|
|
226
214
|
"""
|
|
227
|
-
show_select_all: bool
|
|
228
|
-
order_matters: bool
|
|
229
|
-
none_is_all: bool
|
|
215
|
+
show_select_all: bool = field(default=True, kw_only=True)
|
|
216
|
+
order_matters: bool = field(default=False, kw_only=True)
|
|
217
|
+
none_is_all: bool = field(default=True, kw_only=True)
|
|
230
218
|
|
|
231
219
|
def __init__(
|
|
232
220
|
self, name: str, label: str, all_options: Sequence[_po.SelectParameterOption | dict], *, description: str = "",
|
|
@@ -249,14 +237,13 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
|
|
|
249
237
|
return d.SelectDataSource(*args, **kwargs)
|
|
250
238
|
|
|
251
239
|
def with_selection(
|
|
252
|
-
self, selection: str | None, user:
|
|
253
|
-
*, request_version: int | None = None
|
|
240
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
254
241
|
) -> p.MultiSelectParameter:
|
|
255
242
|
options = self._get_options(user, parent_param)
|
|
256
243
|
if selection is None:
|
|
257
244
|
selected_ids = tuple(self._get_default_ids_iterator(options))
|
|
258
245
|
else:
|
|
259
|
-
selected_ids =
|
|
246
|
+
selected_ids = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
260
247
|
return p.MultiSelectParameter(self, options, selected_ids)
|
|
261
248
|
|
|
262
249
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
@@ -306,8 +293,7 @@ class DateParameterConfig(_DateTypeParameterConfig[_po.DateParameterOption]):
|
|
|
306
293
|
return d.DateDataSource(*args, **kwargs)
|
|
307
294
|
|
|
308
295
|
def with_selection(
|
|
309
|
-
self, selection: str | None, user:
|
|
310
|
-
*, request_version: int | None = None
|
|
296
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
311
297
|
) -> p.DateParameter:
|
|
312
298
|
curr_option: _po.DateParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
313
299
|
selected_date = curr_option._default_date if selection is None and curr_option is not None else selection
|
|
@@ -316,7 +302,7 @@ class DateParameterConfig(_DateTypeParameterConfig[_po.DateParameterOption]):
|
|
|
316
302
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
317
303
|
examples = [str(x._default_date) for x in self.all_options]
|
|
318
304
|
return APIParamFieldInfo(
|
|
319
|
-
self.name, str, title=self.label, description=self.description, examples=examples, pattern=c.
|
|
305
|
+
self.name, str, title=self.label, description=self.description, examples=examples, pattern=c.DATE_REGEX
|
|
320
306
|
)
|
|
321
307
|
|
|
322
308
|
|
|
@@ -346,8 +332,7 @@ class DateRangeParameterConfig(_DateTypeParameterConfig[_po.DateRangeParameterOp
|
|
|
346
332
|
return d.DateRangeDataSource(*args, **kwargs)
|
|
347
333
|
|
|
348
334
|
def with_selection(
|
|
349
|
-
self, selection: str | None, user:
|
|
350
|
-
*, request_version: int | None = None
|
|
335
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
351
336
|
) -> p.DateRangeParameter:
|
|
352
337
|
curr_option: _po.DateRangeParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
353
338
|
if selection is None:
|
|
@@ -358,7 +343,7 @@ class DateRangeParameterConfig(_DateTypeParameterConfig[_po.DateRangeParameterOp
|
|
|
358
343
|
selected_start_date, selected_end_date = None, None
|
|
359
344
|
else:
|
|
360
345
|
try:
|
|
361
|
-
selected_start_date, selected_end_date =
|
|
346
|
+
selected_start_date, selected_end_date = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
362
347
|
except ValueError:
|
|
363
348
|
raise self._invalid_input_error(selection, "Date range parameter selection must be two dates.")
|
|
364
349
|
return p.DateRangeParameter(self, curr_option, selected_start_date, selected_end_date)
|
|
@@ -410,8 +395,7 @@ class NumberParameterConfig(_NumericParameterConfig[_po.NumberParameterOption]):
|
|
|
410
395
|
return d.NumberDataSource(*args, **kwargs)
|
|
411
396
|
|
|
412
397
|
def with_selection(
|
|
413
|
-
self, selection: str | None, user:
|
|
414
|
-
*, request_version: int | None = None
|
|
398
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
415
399
|
) -> p.NumberParameter:
|
|
416
400
|
curr_option: _po.NumberParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
417
401
|
selected_value = curr_option._default_value if selection is None and curr_option is not None else selection
|
|
@@ -450,8 +434,7 @@ class NumberRangeParameterConfig(_NumericParameterConfig[_po.NumberRangeParamete
|
|
|
450
434
|
return d.NumberRangeDataSource(*args, **kwargs)
|
|
451
435
|
|
|
452
436
|
def with_selection(
|
|
453
|
-
self, selection: str | None, user:
|
|
454
|
-
*, request_version: int | None = None
|
|
437
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
455
438
|
) -> p.NumberRangeParameter:
|
|
456
439
|
curr_option: _po.NumberRangeParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
457
440
|
if selection is None:
|
|
@@ -462,7 +445,7 @@ class NumberRangeParameterConfig(_NumericParameterConfig[_po.NumberRangeParamete
|
|
|
462
445
|
selected_lower_value, selected_upper_value = None, None
|
|
463
446
|
else:
|
|
464
447
|
try:
|
|
465
|
-
selected_lower_value, selected_upper_value =
|
|
448
|
+
selected_lower_value, selected_upper_value = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
466
449
|
except ValueError:
|
|
467
450
|
raise self._invalid_input_error(selection, "Number range parameter selection must be two numbers.")
|
|
468
451
|
return p.NumberRangeParameter(self, curr_option, selected_lower_value, selected_upper_value)
|
|
@@ -490,7 +473,7 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
490
473
|
|
|
491
474
|
allowed_input_types = ["text", "textarea", "number", "date", "datetime-local", "month", "time", "color", "password"]
|
|
492
475
|
if input_type not in allowed_input_types:
|
|
493
|
-
raise
|
|
476
|
+
raise u.ConfigurationError(f"Invalid input type '{input_type}' for text parameter '{name}'. Must be one of {allowed_input_types}.")
|
|
494
477
|
|
|
495
478
|
self.input_type = input_type
|
|
496
479
|
for option in self._all_options:
|
|
@@ -499,9 +482,9 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
499
482
|
def validate_entered_text(self, entered_text: str) -> str:
|
|
500
483
|
if self.input_type == "number":
|
|
501
484
|
try:
|
|
502
|
-
|
|
485
|
+
float(entered_text)
|
|
503
486
|
except ValueError:
|
|
504
|
-
raise self._invalid_input_error(entered_text, "Must be
|
|
487
|
+
raise self._invalid_input_error(entered_text, "Must be a number")
|
|
505
488
|
elif self.input_type == "date":
|
|
506
489
|
try:
|
|
507
490
|
datetime.strptime(entered_text, "%Y-%m-%d")
|
|
@@ -523,7 +506,7 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
523
506
|
except ValueError:
|
|
524
507
|
raise self._invalid_input_error(entered_text, "Must be a time in hh:mm format.")
|
|
525
508
|
elif self.input_type == "color":
|
|
526
|
-
if not re.match(c.
|
|
509
|
+
if not re.match(c.COLOR_REGEX, entered_text):
|
|
527
510
|
raise self._invalid_input_error(entered_text, "Must be a valid color hex code (e.g. #000000).")
|
|
528
511
|
|
|
529
512
|
return entered_text
|
|
@@ -541,8 +524,7 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
541
524
|
return d.TextDataSource(*args, **kwargs)
|
|
542
525
|
|
|
543
526
|
def with_selection(
|
|
544
|
-
self, selection: str | None, user:
|
|
545
|
-
*, request_version: int | None = None
|
|
527
|
+
self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
|
|
546
528
|
) -> p.TextParameter:
|
|
547
529
|
curr_option: _po.TextParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
548
530
|
entered_text = curr_option._default_text if selection is None and curr_option is not None else selection
|
|
@@ -555,29 +537,27 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
555
537
|
)
|
|
556
538
|
|
|
557
539
|
|
|
558
|
-
|
|
559
|
-
|
|
540
|
+
ParamConfigType = TypeVar("ParamConfigType", bound=ParameterConfig)
|
|
541
|
+
|
|
542
|
+
class DataSourceParameterConfig(Generic[ParamConfigType], ParameterConfigBase):
|
|
560
543
|
"""
|
|
561
544
|
Class to define configurations for parameter widgets whose options come from lookup tables
|
|
562
545
|
"""
|
|
563
|
-
parameter_type: Type[ParameterConfig]
|
|
564
|
-
data_source: d.DataSource
|
|
565
|
-
|
|
566
546
|
def __init__(
|
|
567
|
-
self, parameter_type:
|
|
547
|
+
self, parameter_type: type[ParamConfigType], name: str, label: str, data_source: d.DataSource | dict, *,
|
|
568
548
|
extra_args: dict = {}, description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
569
549
|
) -> None:
|
|
570
|
-
super().__init__(
|
|
550
|
+
super().__init__(name, label, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
571
551
|
self.parameter_type = parameter_type
|
|
572
552
|
if isinstance(data_source, dict):
|
|
573
553
|
data_source = parameter_type.DataSource(**data_source)
|
|
574
554
|
self.data_source = data_source
|
|
575
555
|
self.extra_args = extra_args
|
|
576
556
|
|
|
577
|
-
def convert(self, df:
|
|
557
|
+
def convert(self, df: pl.DataFrame) -> ParamConfigType:
|
|
578
558
|
return self.data_source._convert(self, df)
|
|
579
559
|
|
|
580
|
-
def get_dataframe(self, default_conn_name: str, conn_set: ConnectionSet, seeds: Seeds) ->
|
|
560
|
+
def get_dataframe(self, default_conn_name: str, conn_set: ConnectionSet, seeds: Seeds) -> pl.DataFrame:
|
|
581
561
|
datasource = self.data_source
|
|
582
562
|
query = datasource._get_query()
|
|
583
563
|
if datasource._is_from_seeds:
|
|
@@ -587,5 +567,5 @@ class DataSourceParameterConfig(ParameterConfigBase):
|
|
|
587
567
|
conn_name = datasource._get_connection_name(default_conn_name)
|
|
588
568
|
df = conn_set.run_sql_query_from_conn_name(query, conn_name)
|
|
589
569
|
except RuntimeError as e:
|
|
590
|
-
raise
|
|
570
|
+
raise u.ConfigurationError(f'Error executing query for datasource parameter "{self.name}"') from e
|
|
591
571
|
return df
|
squirrels/_parameter_sets.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Optional, Sequence
|
|
2
|
+
from typing import Optional, Sequence, Any
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from collections import OrderedDict
|
|
5
|
-
import time, concurrent.futures,
|
|
5
|
+
import time, concurrent.futures, polars as pl
|
|
6
6
|
|
|
7
7
|
from . import _utils as u, _constants as c, parameters as p, _parameter_configs as _pc, _py_module as pm, _api_response_models as arm
|
|
8
8
|
from .arguments.init_time_args import ParametersArgs
|
|
9
9
|
from ._manifest import ParametersConfig, ManifestConfig
|
|
10
10
|
from ._connection_set import ConnectionSet, ConnectionsArgs
|
|
11
11
|
from ._seeds import Seeds
|
|
12
|
-
from .
|
|
12
|
+
from ._auth import BaseUser
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
@@ -51,7 +51,7 @@ class ParameterConfigsSet:
|
|
|
51
51
|
def _get_all_ds_param_configs(self) -> Sequence[_pc.DataSourceParameterConfig]:
|
|
52
52
|
return list(self._data_source_params.values())
|
|
53
53
|
|
|
54
|
-
def __convert_datasource_params(self, df_dict: dict[str,
|
|
54
|
+
def __convert_datasource_params(self, df_dict: dict[str, pl.DataFrame]) -> None:
|
|
55
55
|
done = set()
|
|
56
56
|
for curr_name in self._data_source_params:
|
|
57
57
|
stack = [curr_name] # Note: parents must be converted first before children
|
|
@@ -98,19 +98,21 @@ class ParameterConfigsSet:
|
|
|
98
98
|
|
|
99
99
|
parent._add_child_mutate(param_config)
|
|
100
100
|
|
|
101
|
-
def _post_process_params(self, df_dict: dict[str,
|
|
101
|
+
def _post_process_params(self, df_dict: dict[str, pl.DataFrame]) -> None:
|
|
102
102
|
self.__convert_datasource_params(df_dict)
|
|
103
103
|
self.__validate_param_relationships()
|
|
104
104
|
|
|
105
105
|
def apply_selections(
|
|
106
|
-
self, dataset_params: Optional[Sequence[str]], selections: dict[str,
|
|
107
|
-
*, updates_only: bool = False, request_version: Optional[int] = None
|
|
106
|
+
self, dataset_params: Optional[Sequence[str]], selections: dict[str, Any], user: BaseUser | None, *, parent_param: str | None = None
|
|
108
107
|
) -> ParameterSet:
|
|
109
108
|
if dataset_params is None:
|
|
110
|
-
|
|
109
|
+
if user is None:
|
|
110
|
+
dataset_params = [k for k, v in self._data.items() if v.user_attribute is None]
|
|
111
|
+
else:
|
|
112
|
+
dataset_params = list(self._data.keys())
|
|
111
113
|
|
|
112
114
|
parameters_by_name: dict[str, p.Parameter] = {}
|
|
113
|
-
params_to_process =
|
|
115
|
+
params_to_process = [parent_param] if parent_param else dataset_params
|
|
114
116
|
params_to_process_set = set(params_to_process)
|
|
115
117
|
for some_name in params_to_process:
|
|
116
118
|
stack = [some_name] # Note: process parent selections first (if applicable) before children
|
|
@@ -154,8 +156,8 @@ class ParameterConfigsSetIO:
|
|
|
154
156
|
obj: ParameterConfigsSet # this is static (set in load_from_file) to simplify development experience for pyconfigs/parameters.py
|
|
155
157
|
|
|
156
158
|
@classmethod
|
|
157
|
-
def _get_df_dict_from_data_sources(cls, default_conn_name: str, seeds: Seeds, conn_set: ConnectionSet) -> dict[str,
|
|
158
|
-
def get_dataframe(ds_param_config: _pc.DataSourceParameterConfig) -> tuple[str,
|
|
159
|
+
def _get_df_dict_from_data_sources(cls, default_conn_name: str, seeds: Seeds, conn_set: ConnectionSet) -> dict[str, pl.DataFrame]:
|
|
160
|
+
def get_dataframe(ds_param_config: _pc.DataSourceParameterConfig) -> tuple[str, pl.DataFrame]:
|
|
159
161
|
return ds_param_config.name, ds_param_config.get_dataframe(default_conn_name, conn_set, seeds)
|
|
160
162
|
|
|
161
163
|
ds_param_configs = cls.obj._get_all_ds_param_configs()
|
|
@@ -172,7 +174,7 @@ class ParameterConfigsSetIO:
|
|
|
172
174
|
|
|
173
175
|
@classmethod
|
|
174
176
|
def get_param_args(cls, conn_args: ConnectionsArgs) -> ParametersArgs:
|
|
175
|
-
return ParametersArgs(conn_args.proj_vars, conn_args.env_vars)
|
|
177
|
+
return ParametersArgs(conn_args.project_path, conn_args.proj_vars, conn_args.env_vars)
|
|
176
178
|
|
|
177
179
|
@classmethod
|
|
178
180
|
def load_from_file(
|
|
@@ -186,7 +188,7 @@ class ParameterConfigsSetIO:
|
|
|
186
188
|
|
|
187
189
|
pm.run_pyconfig_main(base_path, c.PARAMETERS_FILE, {"sqrl": param_args})
|
|
188
190
|
|
|
189
|
-
default_conn_name = manifest_cfg.
|
|
191
|
+
default_conn_name = manifest_cfg.env_vars.get(c.SQRL_CONNECTIONS_DEFAULT_NAME_USED, "default")
|
|
190
192
|
df_dict = cls._get_df_dict_from_data_sources(default_conn_name, seeds, conn_set)
|
|
191
193
|
cls.obj._post_process_params(df_dict)
|
|
192
194
|
|