squirrels 0.4.1__py3-none-any.whl → 0.5.0__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.
- dateutils/__init__.py +6 -0
- dateutils/_enums.py +25 -0
- squirrels/dateutils.py → dateutils/_implementation.py +58 -111
- dateutils/types.py +6 -0
- squirrels/__init__.py +13 -11
- squirrels/_api_routes/__init__.py +5 -0
- squirrels/_api_routes/auth.py +271 -0
- squirrels/_api_routes/base.py +165 -0
- squirrels/_api_routes/dashboards.py +150 -0
- squirrels/_api_routes/data_management.py +145 -0
- squirrels/_api_routes/datasets.py +257 -0
- squirrels/_api_routes/oauth2.py +298 -0
- squirrels/_api_routes/project.py +252 -0
- squirrels/_api_server.py +256 -450
- squirrels/_arguments/__init__.py +0 -0
- squirrels/_arguments/init_time_args.py +108 -0
- squirrels/_arguments/run_time_args.py +147 -0
- squirrels/_auth.py +960 -0
- squirrels/_command_line.py +126 -45
- squirrels/_compile_prompts.py +147 -0
- squirrels/_connection_set.py +48 -26
- squirrels/_constants.py +68 -38
- squirrels/_dashboards.py +160 -0
- squirrels/_data_sources.py +570 -0
- squirrels/_dataset_types.py +84 -0
- squirrels/_exceptions.py +29 -0
- squirrels/_initializer.py +177 -80
- squirrels/_logging.py +115 -0
- squirrels/_manifest.py +208 -79
- squirrels/_model_builder.py +69 -0
- squirrels/_model_configs.py +74 -0
- squirrels/_model_queries.py +52 -0
- squirrels/_models.py +926 -367
- squirrels/_package_data/base_project/.env +42 -0
- squirrels/_package_data/base_project/.env.example +42 -0
- squirrels/_package_data/base_project/assets/expenses.db +0 -0
- squirrels/_package_data/base_project/connections.yml +16 -0
- squirrels/_package_data/base_project/dashboards/dashboard_example.py +34 -0
- squirrels/_package_data/base_project/dashboards/dashboard_example.yml +22 -0
- squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +5 -2
- squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +3 -3
- squirrels/{package_data → _package_data}/base_project/docker/compose.yml +1 -1
- squirrels/_package_data/base_project/duckdb_init.sql +10 -0
- squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +3 -2
- squirrels/_package_data/base_project/macros/macros_example.sql +17 -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 +57 -0
- squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +12 -0
- squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +26 -0
- squirrels/_package_data/base_project/models/federates/federate_example.py +37 -0
- squirrels/_package_data/base_project/models/federates/federate_example.sql +19 -0
- squirrels/_package_data/base_project/models/federates/federate_example.yml +65 -0
- squirrels/_package_data/base_project/models/sources.yml +38 -0
- squirrels/{package_data → _package_data}/base_project/parameters.yml +56 -40
- squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
- squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +21 -40
- squirrels/_package_data/base_project/pyconfigs/parameters.py +141 -0
- squirrels/_package_data/base_project/pyconfigs/user.py +44 -0
- squirrels/_package_data/base_project/seeds/seed_categories.yml +15 -0
- squirrels/_package_data/base_project/seeds/seed_subcategories.csv +15 -0
- squirrels/_package_data/base_project/seeds/seed_subcategories.yml +21 -0
- squirrels/_package_data/base_project/squirrels.yml.j2 +61 -0
- squirrels/_package_data/templates/dataset_results.html +112 -0
- squirrels/_package_data/templates/oauth_login.html +271 -0
- squirrels/_package_data/templates/squirrels_studio.html +20 -0
- squirrels/_package_loader.py +8 -4
- squirrels/_parameter_configs.py +104 -103
- squirrels/_parameter_options.py +348 -0
- squirrels/_parameter_sets.py +57 -47
- squirrels/_parameters.py +1664 -0
- squirrels/_project.py +721 -0
- squirrels/_py_module.py +7 -5
- squirrels/_schemas/__init__.py +0 -0
- squirrels/_schemas/auth_models.py +167 -0
- squirrels/_schemas/query_param_models.py +75 -0
- squirrels/{_api_response_models.py → _schemas/response_models.py} +126 -47
- squirrels/_seeds.py +35 -16
- squirrels/_sources.py +110 -0
- squirrels/_utils.py +248 -73
- squirrels/_version.py +1 -1
- squirrels/arguments.py +7 -0
- squirrels/auth.py +4 -0
- squirrels/connections.py +3 -0
- squirrels/dashboards.py +2 -81
- squirrels/data_sources.py +14 -631
- squirrels/parameter_options.py +13 -348
- squirrels/parameters.py +14 -1266
- squirrels/types.py +16 -0
- squirrels-0.5.0.dist-info/METADATA +113 -0
- squirrels-0.5.0.dist-info/RECORD +97 -0
- {squirrels-0.4.1.dist-info → squirrels-0.5.0.dist-info}/WHEEL +1 -1
- squirrels-0.5.0.dist-info/entry_points.txt +3 -0
- {squirrels-0.4.1.dist-info → squirrels-0.5.0.dist-info/licenses}/LICENSE +1 -1
- squirrels/_authenticator.py +0 -85
- squirrels/_dashboards_io.py +0 -61
- squirrels/_environcfg.py +0 -84
- squirrels/arguments/init_time_args.py +0 -40
- squirrels/arguments/run_time_args.py +0 -208
- 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/assets/expenses.db +0 -0
- squirrels/package_data/base_project/connections.yml +0 -7
- squirrels/package_data/base_project/dashboards/dashboard_example.py +0 -32
- 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/models/dbviews/dbview_example.sql +0 -22
- squirrels/package_data/base_project/models/federates/federate_example.py +0 -21
- squirrels/package_data/base_project/models/federates/federate_example.sql +0 -3
- squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
- squirrels/package_data/base_project/pyconfigs/connections.py +0 -19
- squirrels/package_data/base_project/pyconfigs/parameters.py +0 -95
- squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -15
- squirrels/package_data/base_project/squirrels.yml.j2 +0 -94
- squirrels/package_data/templates/index.html +0 -18
- squirrels/project.py +0 -378
- squirrels/user_base.py +0 -55
- squirrels-0.4.1.dist-info/METADATA +0 -117
- squirrels-0.4.1.dist-info/RECORD +0 -60
- squirrels-0.4.1.dist-info/entry_points.txt +0 -4
- /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
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,14 +7,15 @@ 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
|
|
13
|
-
from .
|
|
12
|
+
from . import _data_sources as d, _parameter_options as po, _parameters as p, _utils as u, _constants as c
|
|
13
|
+
from ._exceptions import InvalidInputError
|
|
14
|
+
from ._schemas.auth_models import AbstractUser
|
|
14
15
|
from ._connection_set import ConnectionSet
|
|
15
16
|
from ._seeds import Seeds
|
|
16
17
|
|
|
17
|
-
ParamOptionType = TypeVar("ParamOptionType", bound=
|
|
18
|
+
ParamOptionType = TypeVar("ParamOptionType", bound=po.ParameterOption)
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
@dataclass
|
|
@@ -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,28 +52,24 @@ 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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if user is None:
|
|
72
|
-
raise _u.ConfigurationError(f"Non-authenticated users (only allowed for public datasets) cannot use parameter " +
|
|
73
|
-
f"'{self.name}' because 'user_attribute' is defined on this parameter.")
|
|
74
|
-
return getattr(user, self.user_attribute)
|
|
59
|
+
def _get_user_group(self, user: AbstractUser) -> Any:
|
|
60
|
+
if self.user_attribute is None:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
final_object = user
|
|
64
|
+
attribute = self.user_attribute
|
|
65
|
+
try:
|
|
66
|
+
if "." in attribute:
|
|
67
|
+
parts = attribute.split(".", 1)
|
|
68
|
+
final_object = getattr(final_object, parts[0])
|
|
69
|
+
attribute = parts[1]
|
|
70
|
+
return getattr(final_object, attribute)
|
|
71
|
+
except AttributeError:
|
|
72
|
+
raise u.ConfigurationError(f"User attribute '{self.user_attribute}' is not valid")
|
|
75
73
|
|
|
76
74
|
def copy(self):
|
|
77
75
|
"""
|
|
@@ -92,7 +90,7 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
|
|
|
92
90
|
self, name: str, label: str, all_options: Sequence[ParamOptionType | dict], *, description: str = "",
|
|
93
91
|
user_attribute: str | None = None, parent_name: str | None = None
|
|
94
92
|
) -> None:
|
|
95
|
-
super().__init__(
|
|
93
|
+
super().__init__(name, label, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
96
94
|
self._all_options = tuple(self._to_param_option(x) for x in all_options)
|
|
97
95
|
|
|
98
96
|
def _to_param_option(self, option: ParamOptionType | dict) -> ParamOptionType:
|
|
@@ -117,18 +115,17 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
|
|
|
117
115
|
def DataSource(*args, **kwargs) -> d.DataSource:
|
|
118
116
|
pass
|
|
119
117
|
|
|
120
|
-
def _invalid_input_error(self, selection: str, more_details: str = '') ->
|
|
121
|
-
return
|
|
118
|
+
def _invalid_input_error(self, selection: str, more_details: str = '') -> InvalidInputError:
|
|
119
|
+
return InvalidInputError(400, "invalid_parameter_selection", f'Selected value "{selection}" is not valid for parameter "{self.name}". ' + more_details)
|
|
122
120
|
|
|
123
121
|
@abstractmethod
|
|
124
122
|
def with_selection(
|
|
125
|
-
self, selection: str | None, user:
|
|
126
|
-
*, request_version: int | None = None
|
|
123
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
127
124
|
) -> p.Parameter:
|
|
128
125
|
pass
|
|
129
126
|
|
|
130
127
|
def _get_options_iterator(
|
|
131
|
-
self, all_options: Sequence[ParamOptionType], user:
|
|
128
|
+
self, all_options: Sequence[ParamOptionType], user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
132
129
|
) -> Iterator[ParamOptionType]:
|
|
133
130
|
user_group = self._get_user_group(user)
|
|
134
131
|
selected_parent_option_ids = frozenset(parent_param._get_selected_ids_as_list()) if parent_param else None
|
|
@@ -140,7 +137,7 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
|
|
|
140
137
|
|
|
141
138
|
|
|
142
139
|
@dataclass
|
|
143
|
-
class SelectionParameterConfig(ParameterConfig[
|
|
140
|
+
class SelectionParameterConfig(ParameterConfig[po.SelectParameterOption]):
|
|
144
141
|
"""
|
|
145
142
|
Abstract class for select parameter classes (single-select, multi-select, etc)
|
|
146
143
|
"""
|
|
@@ -149,7 +146,7 @@ class SelectionParameterConfig(ParameterConfig[_po.SelectParameterOption]):
|
|
|
149
146
|
|
|
150
147
|
@abstractmethod
|
|
151
148
|
def __init__(
|
|
152
|
-
self, name: str, label: str, all_options: Sequence[
|
|
149
|
+
self, name: str, label: str, all_options: Sequence[po.SelectParameterOption | dict], *,
|
|
153
150
|
description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
154
151
|
) -> None:
|
|
155
152
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
@@ -158,16 +155,16 @@ class SelectionParameterConfig(ParameterConfig[_po.SelectParameterOption]):
|
|
|
158
155
|
|
|
159
156
|
@staticmethod
|
|
160
157
|
def ParameterOption(*args, **kwargs):
|
|
161
|
-
return
|
|
158
|
+
return po.SelectParameterOption(*args, **kwargs)
|
|
162
159
|
|
|
163
160
|
def _add_child_mutate(self, child: ParameterConfigBase):
|
|
164
161
|
self.children[child.name] = child
|
|
165
162
|
self.trigger_refresh = True
|
|
166
163
|
|
|
167
|
-
def _get_options(self, user:
|
|
164
|
+
def _get_options(self, user: AbstractUser, parent_param: p._SelectionParameter | None) -> Sequence[po.SelectParameterOption]:
|
|
168
165
|
return tuple(self._get_options_iterator(self.all_options, user, parent_param))
|
|
169
166
|
|
|
170
|
-
def _get_default_ids_iterator(self, options: Sequence[
|
|
167
|
+
def _get_default_ids_iterator(self, options: Sequence[po.SelectParameterOption]) -> Iterator[str]:
|
|
171
168
|
return (x._identifier for x in options if x._is_default)
|
|
172
169
|
|
|
173
170
|
def copy(self) -> Self:
|
|
@@ -186,7 +183,7 @@ class SingleSelectParameterConfig(SelectionParameterConfig):
|
|
|
186
183
|
"""
|
|
187
184
|
|
|
188
185
|
def __init__(
|
|
189
|
-
self, name: str, label: str, all_options: Sequence[
|
|
186
|
+
self, name: str, label: str, all_options: Sequence[po.SelectParameterOption | dict], *, description: str = "",
|
|
190
187
|
user_attribute: str | None = None, parent_name: str | None = None
|
|
191
188
|
) -> None:
|
|
192
189
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
@@ -200,8 +197,7 @@ class SingleSelectParameterConfig(SelectionParameterConfig):
|
|
|
200
197
|
return d.SelectDataSource(*args, **kwargs)
|
|
201
198
|
|
|
202
199
|
def with_selection(
|
|
203
|
-
self, selection: str | None, user:
|
|
204
|
-
*, request_version: int | None = None
|
|
200
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
205
201
|
) -> p.SingleSelectParameter:
|
|
206
202
|
options = self._get_options(user, parent_param)
|
|
207
203
|
if selection is None:
|
|
@@ -224,12 +220,12 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
|
|
|
224
220
|
"""
|
|
225
221
|
Class to define configurations for multi-select parameter widgets.
|
|
226
222
|
"""
|
|
227
|
-
show_select_all: bool
|
|
228
|
-
order_matters: bool
|
|
229
|
-
none_is_all: bool
|
|
223
|
+
show_select_all: bool = field(default=True, kw_only=True)
|
|
224
|
+
order_matters: bool = field(default=False, kw_only=True)
|
|
225
|
+
none_is_all: bool = field(default=True, kw_only=True)
|
|
230
226
|
|
|
231
227
|
def __init__(
|
|
232
|
-
self, name: str, label: str, all_options: Sequence[
|
|
228
|
+
self, name: str, label: str, all_options: Sequence[po.SelectParameterOption | dict], *, description: str = "",
|
|
233
229
|
show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True,
|
|
234
230
|
user_attribute: str | None = None, parent_name: str | None = None
|
|
235
231
|
) -> None:
|
|
@@ -249,14 +245,13 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
|
|
|
249
245
|
return d.SelectDataSource(*args, **kwargs)
|
|
250
246
|
|
|
251
247
|
def with_selection(
|
|
252
|
-
self, selection: str | None, user:
|
|
253
|
-
*, request_version: int | None = None
|
|
248
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
254
249
|
) -> p.MultiSelectParameter:
|
|
255
250
|
options = self._get_options(user, parent_param)
|
|
256
251
|
if selection is None:
|
|
257
252
|
selected_ids = tuple(self._get_default_ids_iterator(options))
|
|
258
253
|
else:
|
|
259
|
-
selected_ids =
|
|
254
|
+
selected_ids = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
260
255
|
return p.MultiSelectParameter(self, options, selected_ids)
|
|
261
256
|
|
|
262
257
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
@@ -281,14 +276,14 @@ class _DateTypeParameterConfig(ParameterConfig[ParamOptionType]):
|
|
|
281
276
|
|
|
282
277
|
|
|
283
278
|
@dataclass
|
|
284
|
-
class DateParameterConfig(_DateTypeParameterConfig[
|
|
279
|
+
class DateParameterConfig(_DateTypeParameterConfig[po.DateParameterOption]):
|
|
285
280
|
"""
|
|
286
281
|
Class to define configurations for date parameter widgets.
|
|
287
282
|
"""
|
|
288
|
-
_all_options: Sequence[
|
|
283
|
+
_all_options: Sequence[po.DateParameterOption] = field(repr=False)
|
|
289
284
|
|
|
290
285
|
def __init__(
|
|
291
|
-
self, name: str, label: str, all_options: Sequence[
|
|
286
|
+
self, name: str, label: str, all_options: Sequence[po.DateParameterOption | dict], *,
|
|
292
287
|
description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
293
288
|
) -> None:
|
|
294
289
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
@@ -299,36 +294,35 @@ class DateParameterConfig(_DateTypeParameterConfig[_po.DateParameterOption]):
|
|
|
299
294
|
|
|
300
295
|
@staticmethod
|
|
301
296
|
def ParameterOption(*args, **kwargs):
|
|
302
|
-
return
|
|
297
|
+
return po.DateParameterOption(*args, **kwargs)
|
|
303
298
|
|
|
304
299
|
@staticmethod
|
|
305
300
|
def DataSource(*args, **kwargs):
|
|
306
301
|
return d.DateDataSource(*args, **kwargs)
|
|
307
302
|
|
|
308
303
|
def with_selection(
|
|
309
|
-
self, selection: str | None, user:
|
|
310
|
-
*, request_version: int | None = None
|
|
304
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
311
305
|
) -> p.DateParameter:
|
|
312
|
-
curr_option:
|
|
306
|
+
curr_option: po.DateParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
313
307
|
selected_date = curr_option._default_date if selection is None and curr_option is not None else selection
|
|
314
308
|
return p.DateParameter(self, curr_option, selected_date)
|
|
315
309
|
|
|
316
310
|
def get_api_field_info(self) -> APIParamFieldInfo:
|
|
317
311
|
examples = [str(x._default_date) for x in self.all_options]
|
|
318
312
|
return APIParamFieldInfo(
|
|
319
|
-
self.name, str, title=self.label, description=self.description, examples=examples, pattern=c.
|
|
313
|
+
self.name, str, title=self.label, description=self.description, examples=examples, pattern=c.DATE_REGEX
|
|
320
314
|
)
|
|
321
315
|
|
|
322
316
|
|
|
323
317
|
@dataclass
|
|
324
|
-
class DateRangeParameterConfig(_DateTypeParameterConfig[
|
|
318
|
+
class DateRangeParameterConfig(_DateTypeParameterConfig[po.DateRangeParameterOption]):
|
|
325
319
|
"""
|
|
326
320
|
Class to define configurations for date range parameter widgets.
|
|
327
321
|
"""
|
|
328
|
-
_all_options: Sequence[
|
|
322
|
+
_all_options: Sequence[po.DateRangeParameterOption] = field(repr=False)
|
|
329
323
|
|
|
330
324
|
def __init__(
|
|
331
|
-
self, name: str, label: str, all_options: Sequence[
|
|
325
|
+
self, name: str, label: str, all_options: Sequence[po.DateRangeParameterOption | dict], *,
|
|
332
326
|
description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
333
327
|
) -> None:
|
|
334
328
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
@@ -339,17 +333,16 @@ class DateRangeParameterConfig(_DateTypeParameterConfig[_po.DateRangeParameterOp
|
|
|
339
333
|
|
|
340
334
|
@staticmethod
|
|
341
335
|
def ParameterOption(*args, **kwargs):
|
|
342
|
-
return
|
|
336
|
+
return po.DateRangeParameterOption(*args, **kwargs)
|
|
343
337
|
|
|
344
338
|
@staticmethod
|
|
345
339
|
def DataSource(*args, **kwargs):
|
|
346
340
|
return d.DateRangeDataSource(*args, **kwargs)
|
|
347
341
|
|
|
348
342
|
def with_selection(
|
|
349
|
-
self, selection: str | None, user:
|
|
350
|
-
*, request_version: int | None = None
|
|
343
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
351
344
|
) -> p.DateRangeParameter:
|
|
352
|
-
curr_option:
|
|
345
|
+
curr_option: po.DateRangeParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
353
346
|
if selection is None:
|
|
354
347
|
if curr_option is not None:
|
|
355
348
|
selected_start_date = curr_option._default_start_date
|
|
@@ -358,7 +351,7 @@ class DateRangeParameterConfig(_DateTypeParameterConfig[_po.DateRangeParameterOp
|
|
|
358
351
|
selected_start_date, selected_end_date = None, None
|
|
359
352
|
else:
|
|
360
353
|
try:
|
|
361
|
-
selected_start_date, selected_end_date =
|
|
354
|
+
selected_start_date, selected_end_date = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
362
355
|
except ValueError:
|
|
363
356
|
raise self._invalid_input_error(selection, "Date range parameter selection must be two dates.")
|
|
364
357
|
return p.DateRangeParameter(self, curr_option, selected_start_date, selected_end_date)
|
|
@@ -385,14 +378,14 @@ class _NumericParameterConfig(ParameterConfig[ParamOptionType]):
|
|
|
385
378
|
|
|
386
379
|
|
|
387
380
|
@dataclass
|
|
388
|
-
class NumberParameterConfig(_NumericParameterConfig[
|
|
381
|
+
class NumberParameterConfig(_NumericParameterConfig[po.NumberParameterOption]):
|
|
389
382
|
"""
|
|
390
383
|
Class to define configurations for number parameter widgets.
|
|
391
384
|
"""
|
|
392
|
-
_all_options: Sequence[
|
|
385
|
+
_all_options: Sequence[po.NumberParameterOption] = field(repr=False)
|
|
393
386
|
|
|
394
387
|
def __init__(
|
|
395
|
-
self, name: str, label: str, all_options: Sequence[
|
|
388
|
+
self, name: str, label: str, all_options: Sequence[po.NumberParameterOption | dict], *,
|
|
396
389
|
description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
397
390
|
) -> None:
|
|
398
391
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
@@ -403,17 +396,16 @@ class NumberParameterConfig(_NumericParameterConfig[_po.NumberParameterOption]):
|
|
|
403
396
|
|
|
404
397
|
@staticmethod
|
|
405
398
|
def ParameterOption(*args, **kwargs):
|
|
406
|
-
return
|
|
399
|
+
return po.NumberParameterOption(*args, **kwargs)
|
|
407
400
|
|
|
408
401
|
@staticmethod
|
|
409
402
|
def DataSource(*args, **kwargs):
|
|
410
403
|
return d.NumberDataSource(*args, **kwargs)
|
|
411
404
|
|
|
412
405
|
def with_selection(
|
|
413
|
-
self, selection: str | None, user:
|
|
414
|
-
*, request_version: int | None = None
|
|
406
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
415
407
|
) -> p.NumberParameter:
|
|
416
|
-
curr_option:
|
|
408
|
+
curr_option: po.NumberParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
417
409
|
selected_value = curr_option._default_value if selection is None and curr_option is not None else selection
|
|
418
410
|
return p.NumberParameter(self, curr_option, selected_value)
|
|
419
411
|
|
|
@@ -425,14 +417,14 @@ class NumberParameterConfig(_NumericParameterConfig[_po.NumberParameterOption]):
|
|
|
425
417
|
|
|
426
418
|
|
|
427
419
|
@dataclass
|
|
428
|
-
class NumberRangeParameterConfig(_NumericParameterConfig[
|
|
420
|
+
class NumberRangeParameterConfig(_NumericParameterConfig[po.NumberRangeParameterOption]):
|
|
429
421
|
"""
|
|
430
422
|
Class to define configurations for number range parameter widgets.
|
|
431
423
|
"""
|
|
432
|
-
_all_options: Sequence[
|
|
424
|
+
_all_options: Sequence[po.NumberRangeParameterOption] = field(repr=False)
|
|
433
425
|
|
|
434
426
|
def __init__(
|
|
435
|
-
self, name: str, label: str, all_options: Sequence[
|
|
427
|
+
self, name: str, label: str, all_options: Sequence[po.NumberRangeParameterOption | dict], *,
|
|
436
428
|
description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
437
429
|
) -> None:
|
|
438
430
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
@@ -443,17 +435,16 @@ class NumberRangeParameterConfig(_NumericParameterConfig[_po.NumberRangeParamete
|
|
|
443
435
|
|
|
444
436
|
@staticmethod
|
|
445
437
|
def ParameterOption(*args, **kwargs):
|
|
446
|
-
return
|
|
438
|
+
return po.NumberRangeParameterOption(*args, **kwargs)
|
|
447
439
|
|
|
448
440
|
@staticmethod
|
|
449
441
|
def DataSource(*args, **kwargs):
|
|
450
442
|
return d.NumberRangeDataSource(*args, **kwargs)
|
|
451
443
|
|
|
452
444
|
def with_selection(
|
|
453
|
-
self, selection: str | None, user:
|
|
454
|
-
*, request_version: int | None = None
|
|
445
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
455
446
|
) -> p.NumberRangeParameter:
|
|
456
|
-
curr_option:
|
|
447
|
+
curr_option: po.NumberRangeParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
457
448
|
if selection is None:
|
|
458
449
|
if curr_option is not None:
|
|
459
450
|
selected_lower_value = curr_option._default_lower_value
|
|
@@ -462,7 +453,7 @@ class NumberRangeParameterConfig(_NumericParameterConfig[_po.NumberRangeParamete
|
|
|
462
453
|
selected_lower_value, selected_upper_value = None, None
|
|
463
454
|
else:
|
|
464
455
|
try:
|
|
465
|
-
selected_lower_value, selected_upper_value =
|
|
456
|
+
selected_lower_value, selected_upper_value = u.load_json_or_comma_delimited_str_as_list(selection)
|
|
466
457
|
except ValueError:
|
|
467
458
|
raise self._invalid_input_error(selection, "Number range parameter selection must be two numbers.")
|
|
468
459
|
return p.NumberRangeParameter(self, curr_option, selected_lower_value, selected_upper_value)
|
|
@@ -475,22 +466,22 @@ class NumberRangeParameterConfig(_NumericParameterConfig[_po.NumberRangeParamete
|
|
|
475
466
|
|
|
476
467
|
|
|
477
468
|
@dataclass
|
|
478
|
-
class TextParameterConfig(ParameterConfig[
|
|
469
|
+
class TextParameterConfig(ParameterConfig[po.TextParameterOption]):
|
|
479
470
|
"""
|
|
480
471
|
Class to define configurations for text parameter widgets.
|
|
481
472
|
"""
|
|
482
|
-
_all_options: Sequence[
|
|
473
|
+
_all_options: Sequence[po.TextParameterOption] = field(repr=False)
|
|
483
474
|
input_type: str
|
|
484
475
|
|
|
485
476
|
def __init__(
|
|
486
|
-
self, name: str, label: str, all_options: Sequence[
|
|
477
|
+
self, name: str, label: str, all_options: Sequence[po.TextParameterOption | dict], *, description: str = "",
|
|
487
478
|
input_type: str = "text", user_attribute: str | None = None, parent_name: str | None = None
|
|
488
479
|
) -> None:
|
|
489
480
|
super().__init__(name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
490
481
|
|
|
491
482
|
allowed_input_types = ["text", "textarea", "number", "date", "datetime-local", "month", "time", "color", "password"]
|
|
492
483
|
if input_type not in allowed_input_types:
|
|
493
|
-
raise
|
|
484
|
+
raise u.ConfigurationError(f"Invalid input type '{input_type}' for text parameter '{name}'. Must be one of {allowed_input_types}.")
|
|
494
485
|
|
|
495
486
|
self.input_type = input_type
|
|
496
487
|
for option in self._all_options:
|
|
@@ -499,9 +490,9 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
499
490
|
def validate_entered_text(self, entered_text: str) -> str:
|
|
500
491
|
if self.input_type == "number":
|
|
501
492
|
try:
|
|
502
|
-
|
|
493
|
+
float(entered_text)
|
|
503
494
|
except ValueError:
|
|
504
|
-
raise self._invalid_input_error(entered_text, "Must be
|
|
495
|
+
raise self._invalid_input_error(entered_text, "Must be a number")
|
|
505
496
|
elif self.input_type == "date":
|
|
506
497
|
try:
|
|
507
498
|
datetime.strptime(entered_text, "%Y-%m-%d")
|
|
@@ -523,7 +514,7 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
523
514
|
except ValueError:
|
|
524
515
|
raise self._invalid_input_error(entered_text, "Must be a time in hh:mm format.")
|
|
525
516
|
elif self.input_type == "color":
|
|
526
|
-
if not re.match(c.
|
|
517
|
+
if not re.match(c.COLOR_REGEX, entered_text):
|
|
527
518
|
raise self._invalid_input_error(entered_text, "Must be a valid color hex code (e.g. #000000).")
|
|
528
519
|
|
|
529
520
|
return entered_text
|
|
@@ -534,17 +525,16 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
534
525
|
|
|
535
526
|
@staticmethod
|
|
536
527
|
def ParameterOption(*args, **kwargs):
|
|
537
|
-
return
|
|
528
|
+
return po.TextParameterOption(*args, **kwargs)
|
|
538
529
|
|
|
539
530
|
@staticmethod
|
|
540
531
|
def DataSource(*args, **kwargs):
|
|
541
532
|
return d.TextDataSource(*args, **kwargs)
|
|
542
533
|
|
|
543
534
|
def with_selection(
|
|
544
|
-
self, selection: str | None, user:
|
|
545
|
-
*, request_version: int | None = None
|
|
535
|
+
self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
|
|
546
536
|
) -> p.TextParameter:
|
|
547
|
-
curr_option:
|
|
537
|
+
curr_option: po.TextParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
|
|
548
538
|
entered_text = curr_option._default_text if selection is None and curr_option is not None else selection
|
|
549
539
|
return p.TextParameter(self, curr_option, entered_text)
|
|
550
540
|
|
|
@@ -555,37 +545,48 @@ class TextParameterConfig(ParameterConfig[_po.TextParameterOption]):
|
|
|
555
545
|
)
|
|
556
546
|
|
|
557
547
|
|
|
558
|
-
|
|
559
|
-
|
|
548
|
+
ParamConfigType = TypeVar("ParamConfigType", bound=ParameterConfig)
|
|
549
|
+
|
|
550
|
+
class DataSourceParameterConfig(Generic[ParamConfigType], ParameterConfigBase):
|
|
560
551
|
"""
|
|
561
552
|
Class to define configurations for parameter widgets whose options come from lookup tables
|
|
562
553
|
"""
|
|
563
|
-
parameter_type: Type[ParameterConfig]
|
|
564
|
-
data_source: d.DataSource
|
|
565
|
-
|
|
566
554
|
def __init__(
|
|
567
|
-
self, parameter_type:
|
|
555
|
+
self, parameter_type: type[ParamConfigType], name: str, label: str, data_source: d.DataSource | dict, *,
|
|
568
556
|
extra_args: dict = {}, description: str = "", user_attribute: str | None = None, parent_name: str | None = None
|
|
569
557
|
) -> None:
|
|
570
|
-
super().__init__(
|
|
558
|
+
super().__init__(name, label, description=description, user_attribute=user_attribute, parent_name=parent_name)
|
|
571
559
|
self.parameter_type = parameter_type
|
|
572
560
|
if isinstance(data_source, dict):
|
|
561
|
+
if "source" in data_source:
|
|
562
|
+
data_source["source"] = d.SourceEnum(data_source["source"])
|
|
573
563
|
data_source = parameter_type.DataSource(**data_source)
|
|
574
564
|
self.data_source = data_source
|
|
575
565
|
self.extra_args = extra_args
|
|
576
566
|
|
|
577
|
-
def convert(self, df:
|
|
567
|
+
def convert(self, df: pl.DataFrame) -> ParamConfigType:
|
|
578
568
|
return self.data_source._convert(self, df)
|
|
579
569
|
|
|
580
|
-
def get_dataframe(self, default_conn_name: str, conn_set: ConnectionSet, seeds: Seeds) ->
|
|
570
|
+
def get_dataframe(self, default_conn_name: str, conn_set: ConnectionSet, seeds: Seeds, datalake_db_path: str = "") -> pl.DataFrame:
|
|
581
571
|
datasource = self.data_source
|
|
582
572
|
query = datasource._get_query()
|
|
583
|
-
if datasource.
|
|
573
|
+
if datasource._source == d.SourceEnum.SEEDS:
|
|
584
574
|
df = seeds.run_query(query)
|
|
585
|
-
|
|
575
|
+
elif datasource._source == d.SourceEnum.VDL:
|
|
576
|
+
vdl_conn = u.create_duckdb_connection(datalake_db_path)
|
|
577
|
+
try:
|
|
578
|
+
# Query the VDL database
|
|
579
|
+
df = vdl_conn.sql(query).pl()
|
|
580
|
+
except Exception as e:
|
|
581
|
+
raise u.ConfigurationError(f'Error executing query for datasource parameter "{self.name}" from VDL') from e
|
|
582
|
+
finally:
|
|
583
|
+
vdl_conn.close()
|
|
584
|
+
else: # source == "connection"
|
|
585
|
+
conn_name = None
|
|
586
586
|
try:
|
|
587
587
|
conn_name = datasource._get_connection_name(default_conn_name)
|
|
588
588
|
df = conn_set.run_sql_query_from_conn_name(query, conn_name)
|
|
589
589
|
except RuntimeError as e:
|
|
590
|
-
|
|
590
|
+
ending = f' "{conn_name}"' if conn_name is not None else ""
|
|
591
|
+
raise u.ConfigurationError(f'Error executing query for datasource parameter "{self.name}" from connection{ending}') from e
|
|
591
592
|
return df
|