squirrels 0.1.0__py3-none-any.whl → 0.6.0.post0__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.
Files changed (127) hide show
  1. dateutils/__init__.py +6 -0
  2. dateutils/_enums.py +25 -0
  3. squirrels/dateutils.py → dateutils/_implementation.py +409 -380
  4. dateutils/types.py +6 -0
  5. squirrels/__init__.py +21 -18
  6. squirrels/_api_routes/__init__.py +5 -0
  7. squirrels/_api_routes/auth.py +337 -0
  8. squirrels/_api_routes/base.py +196 -0
  9. squirrels/_api_routes/dashboards.py +156 -0
  10. squirrels/_api_routes/data_management.py +148 -0
  11. squirrels/_api_routes/datasets.py +220 -0
  12. squirrels/_api_routes/project.py +289 -0
  13. squirrels/_api_server.py +552 -134
  14. squirrels/_arguments/__init__.py +0 -0
  15. squirrels/_arguments/init_time_args.py +83 -0
  16. squirrels/_arguments/run_time_args.py +111 -0
  17. squirrels/_auth.py +777 -0
  18. squirrels/_command_line.py +239 -107
  19. squirrels/_compile_prompts.py +147 -0
  20. squirrels/_connection_set.py +94 -0
  21. squirrels/_constants.py +141 -64
  22. squirrels/_dashboards.py +179 -0
  23. squirrels/_data_sources.py +570 -0
  24. squirrels/_dataset_types.py +91 -0
  25. squirrels/_env_vars.py +209 -0
  26. squirrels/_exceptions.py +29 -0
  27. squirrels/_http_error_responses.py +52 -0
  28. squirrels/_initializer.py +319 -110
  29. squirrels/_logging.py +121 -0
  30. squirrels/_manifest.py +357 -187
  31. squirrels/_mcp_server.py +578 -0
  32. squirrels/_model_builder.py +69 -0
  33. squirrels/_model_configs.py +74 -0
  34. squirrels/_model_queries.py +52 -0
  35. squirrels/_models.py +1201 -0
  36. squirrels/_package_data/base_project/.env +7 -0
  37. squirrels/_package_data/base_project/.env.example +44 -0
  38. squirrels/_package_data/base_project/connections.yml +16 -0
  39. squirrels/_package_data/base_project/dashboards/dashboard_example.py +40 -0
  40. squirrels/_package_data/base_project/dashboards/dashboard_example.yml +22 -0
  41. squirrels/_package_data/base_project/docker/.dockerignore +16 -0
  42. squirrels/_package_data/base_project/docker/Dockerfile +16 -0
  43. squirrels/_package_data/base_project/docker/compose.yml +7 -0
  44. squirrels/_package_data/base_project/duckdb_init.sql +10 -0
  45. squirrels/_package_data/base_project/gitignore +13 -0
  46. squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  47. squirrels/_package_data/base_project/models/builds/build_example.py +26 -0
  48. squirrels/_package_data/base_project/models/builds/build_example.sql +16 -0
  49. squirrels/_package_data/base_project/models/builds/build_example.yml +57 -0
  50. squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +17 -0
  51. squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +32 -0
  52. squirrels/_package_data/base_project/models/federates/federate_example.py +51 -0
  53. squirrels/_package_data/base_project/models/federates/federate_example.sql +21 -0
  54. squirrels/_package_data/base_project/models/federates/federate_example.yml +65 -0
  55. squirrels/_package_data/base_project/models/sources.yml +38 -0
  56. squirrels/_package_data/base_project/parameters.yml +142 -0
  57. squirrels/_package_data/base_project/pyconfigs/connections.py +19 -0
  58. squirrels/_package_data/base_project/pyconfigs/context.py +96 -0
  59. squirrels/_package_data/base_project/pyconfigs/parameters.py +141 -0
  60. squirrels/_package_data/base_project/pyconfigs/user.py +56 -0
  61. squirrels/_package_data/base_project/resources/expenses.db +0 -0
  62. squirrels/_package_data/base_project/resources/public/.gitkeep +0 -0
  63. squirrels/_package_data/base_project/resources/weather.db +0 -0
  64. squirrels/_package_data/base_project/seeds/seed_categories.csv +6 -0
  65. squirrels/_package_data/base_project/seeds/seed_categories.yml +15 -0
  66. squirrels/_package_data/base_project/seeds/seed_subcategories.csv +15 -0
  67. squirrels/_package_data/base_project/seeds/seed_subcategories.yml +21 -0
  68. squirrels/_package_data/base_project/squirrels.yml.j2 +61 -0
  69. squirrels/_package_data/base_project/tmp/.gitignore +2 -0
  70. squirrels/_package_data/templates/login_successful.html +53 -0
  71. squirrels/_package_data/templates/squirrels_studio.html +22 -0
  72. squirrels/_package_loader.py +29 -0
  73. squirrels/_parameter_configs.py +592 -0
  74. squirrels/_parameter_options.py +348 -0
  75. squirrels/_parameter_sets.py +207 -0
  76. squirrels/_parameters.py +1703 -0
  77. squirrels/_project.py +796 -0
  78. squirrels/_py_module.py +122 -0
  79. squirrels/_request_context.py +33 -0
  80. squirrels/_schemas/__init__.py +0 -0
  81. squirrels/_schemas/auth_models.py +83 -0
  82. squirrels/_schemas/query_param_models.py +70 -0
  83. squirrels/_schemas/request_models.py +26 -0
  84. squirrels/_schemas/response_models.py +286 -0
  85. squirrels/_seeds.py +97 -0
  86. squirrels/_sources.py +112 -0
  87. squirrels/_utils.py +540 -149
  88. squirrels/_version.py +1 -3
  89. squirrels/arguments.py +7 -0
  90. squirrels/auth.py +4 -0
  91. squirrels/connections.py +3 -0
  92. squirrels/dashboards.py +3 -0
  93. squirrels/data_sources.py +14 -282
  94. squirrels/parameter_options.py +13 -189
  95. squirrels/parameters.py +14 -801
  96. squirrels/types.py +18 -0
  97. squirrels-0.6.0.post0.dist-info/METADATA +148 -0
  98. squirrels-0.6.0.post0.dist-info/RECORD +101 -0
  99. {squirrels-0.1.0.dist-info → squirrels-0.6.0.post0.dist-info}/WHEEL +1 -2
  100. {squirrels-0.1.0.dist-info → squirrels-0.6.0.post0.dist-info}/entry_points.txt +1 -0
  101. squirrels-0.6.0.post0.dist-info/licenses/LICENSE +201 -0
  102. squirrels/_credentials_manager.py +0 -87
  103. squirrels/_module_loader.py +0 -37
  104. squirrels/_parameter_set.py +0 -151
  105. squirrels/_renderer.py +0 -286
  106. squirrels/_timed_imports.py +0 -37
  107. squirrels/connection_set.py +0 -126
  108. squirrels/package_data/base_project/.gitignore +0 -4
  109. squirrels/package_data/base_project/connections.py +0 -21
  110. squirrels/package_data/base_project/database/sample_database.db +0 -0
  111. squirrels/package_data/base_project/database/seattle_weather.db +0 -0
  112. squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -8
  113. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -23
  114. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -7
  115. squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -10
  116. squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -2
  117. squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -30
  118. squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -6
  119. squirrels/package_data/base_project/squirrels.yaml +0 -26
  120. squirrels/package_data/static/favicon.ico +0 -0
  121. squirrels/package_data/static/script.js +0 -234
  122. squirrels/package_data/static/style.css +0 -110
  123. squirrels/package_data/templates/index.html +0 -32
  124. squirrels-0.1.0.dist-info/LICENSE +0 -22
  125. squirrels-0.1.0.dist-info/METADATA +0 -67
  126. squirrels-0.1.0.dist-info/RECORD +0 -40
  127. squirrels-0.1.0.dist-info/top_level.txt +0 -1
squirrels/_version.py CHANGED
@@ -1,3 +1 @@
1
- __version__ = '0.1.0'
2
-
3
- major_version, minor_version, patch_version = __version__.split('.')
1
+ __version__ = '0.6.0'
squirrels/arguments.py ADDED
@@ -0,0 +1,7 @@
1
+ from ._arguments.init_time_args import ConnectionsArgs, AuthProviderArgs, ParametersArgs, BuildModelArgs
2
+ from ._arguments.run_time_args import ContextArgs, ModelArgs, DashboardArgs
3
+
4
+ __all__ = [
5
+ "ConnectionsArgs", "AuthProviderArgs", "ParametersArgs", "BuildModelArgs",
6
+ "ContextArgs", "ModelArgs", "DashboardArgs"
7
+ ]
squirrels/auth.py ADDED
@@ -0,0 +1,4 @@
1
+ from ._schemas.auth_models import CustomUserFields, RegisteredUser
2
+ from ._auth import ProviderConfigs, provider
3
+
4
+ __all__ = ["CustomUserFields", "RegisteredUser", "ProviderConfigs", "provider"]
@@ -0,0 +1,3 @@
1
+ from ._manifest import ConnectionProperties, ConnectionTypeEnum
2
+
3
+ __all__ = ["ConnectionProperties", "ConnectionTypeEnum"]
@@ -0,0 +1,3 @@
1
+ from ._dashboards import PngDashboard, HtmlDashboard
2
+
3
+ __all__ = ["PngDashboard", "HtmlDashboard"]
squirrels/data_sources.py CHANGED
@@ -1,282 +1,14 @@
1
- from __future__ import annotations
2
- from typing import Type, Tuple, List, Optional
3
- from dataclasses import dataclass
4
-
5
- from squirrels import parameters as p, parameter_options as po
6
- from squirrels._timed_imports import pandas as pd
7
- from squirrels import _constants as c, _utils
8
-
9
-
10
- @dataclass
11
- class DataSource:
12
- """
13
- Abstract class for lookup tables coming from a database
14
- """
15
- table_or_query: str
16
-
17
- def __post_init__(self) -> None:
18
- if not hasattr(self, 'parent_id_col'):
19
- self.parent_id_col = None
20
- if not hasattr(self, 'connection_name'):
21
- self.connection_name = c.DEFAULT_DB_CONN
22
-
23
- def get_query(self) -> str:
24
- """
25
- Get the "table_or_query" attribute as a select query
26
-
27
- Returns:
28
- str: The converted select query
29
- """
30
- if self.table_or_query.lower().startswith('select '):
31
- query = self.table_or_query
32
- else:
33
- query = f'SELECT * FROM {self.table_or_query}'
34
- return query
35
-
36
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.Parameter:
37
- """
38
- An abstract method for converting itself into a parameter
39
-
40
- Args:
41
- ds_param: The parameter to convert
42
- df: The dataframe containing the parameter options data
43
-
44
- Returns:
45
- The converted parameter
46
- """
47
- raise _utils.AbstractMethodCallError(self.__class__, "convert")
48
-
49
- def _get_parent(self, row):
50
- return str(_utils.get_row_value(row, self.parent_id_col)) if self.parent_id_col is not None else None
51
-
52
- def _validate_parameter_class(self, ds_param: p.DataSourceParameter, parameter_classes: List[Type[p.Parameter]]) -> None:
53
- if ds_param.parameter_class not in parameter_classes:
54
- parameter_class_name = ds_param.parameter_class.__name__
55
- datasource_class_name = self.__class__.__name__
56
- raise _utils.ConfigurationError(f'Invalid widget type "{parameter_class_name}" for {datasource_class_name}')
57
-
58
-
59
- @dataclass
60
- class SelectionDataSource(DataSource):
61
- """
62
- Lookup table for selection parameters (single and multi)
63
-
64
- Attributes:
65
- connection_name: Name of the connection to use defined in connections.py
66
- table_or_query: Either the name of the table to use, or a query to run
67
- id_col: The column name of the id
68
- options_col: The column name of the options
69
- order_by_col: The column name to order the options by. Orders by the id_col instead if this is None
70
- is_default_col: The column name that indicates which options are the default
71
- parent_id_col: The column name of the parent option id that this option belongs to
72
- """
73
- id_col: str
74
- options_col: str
75
- order_by_col: Optional[str] = None
76
- is_default_col: Optional[str] = None
77
- parent_id_col: Optional[str] = None
78
- connection_name: str = c.DEFAULT_DB_CONN
79
-
80
- def __post_init__(self):
81
- self.order_by_col = self.order_by_col if self.order_by_col is not None else self.id_col
82
-
83
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.Parameter:
84
- """
85
- Method to convert the associated DataSourceParameter into a SingleSelect or MultiSelect Parameter
86
-
87
- Parameters:
88
- ds_param: The parameter to convert
89
- df: The dataframe containing the parameter options data
90
-
91
- Returns:
92
- The converted parameter
93
- """
94
- self._validate_parameter_class(ds_param, [p.SingleSelectParameter, p.MultiSelectParameter])
95
-
96
- def is_default(row):
97
- return int(_utils.get_row_value(row, self.is_default_col)) == 1 if self.is_default_col is not None else False
98
-
99
- try:
100
- df.sort_values(self.order_by_col, inplace=True)
101
- except KeyError as e:
102
- raise _utils.ConfigurationError(f'Could not sort on column name "{self.order_by_col}" as it does not exist')
103
-
104
- options = tuple(
105
- po.SelectParameterOption(str(_utils.get_row_value(row, self.id_col)), str(_utils.get_row_value(row, self.options_col)), is_default(row),
106
- parent_option_id=self._get_parent(row))
107
- for _, row in df.iterrows()
108
- )
109
-
110
- return ds_param.parameter_class(ds_param.name, ds_param.label, options, is_hidden=ds_param.is_hidden, parent=ds_param.parent)
111
-
112
-
113
- @dataclass
114
- class DateDataSource(DataSource):
115
- """
116
- Lookup table for date parameter default options
117
-
118
- Attributes:
119
- connection_name: Name of the connection to use defined in connections.py
120
- table_or_query: Either the name of the table to use, or a query to run
121
- default_date_col: The column name of the default date
122
- date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
123
- parent_id_col: The column name of the parent option id that the default date belongs to
124
- """
125
- default_date_col: str
126
- parent_id_col: Optional[str] = None
127
- date_format: Optional[str] = '%Y-%m-%d'
128
- connection_name: str = c.DEFAULT_DB_CONN
129
-
130
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.DateParameter:
131
- """
132
- Method to convert the associated DataSourceParameter into a DateParameter
133
-
134
- Parameters:
135
- ds_param: The parameter to convert
136
- df: The dataframe containing the parameter options data
137
-
138
- Returns:
139
- The converted parameter
140
- """
141
- self._validate_parameter_class(ds_param, [p.DateParameter])
142
-
143
- def get_date(row: pd.Series) -> str:
144
- return str(_utils.get_row_value(row, self.default_date_col))
145
-
146
- def create_date_param_option(row: pd.Series) -> po.DateParameterOption:
147
- return po.DateParameterOption(get_date(row), self.date_format, self._get_parent(row))
148
-
149
- if ds_param.parent is None:
150
- row = df.iloc[0]
151
- return p.DateParameter(ds_param.name, ds_param.label, get_date(row), self.date_format,
152
- is_hidden=ds_param.is_hidden)
153
- else:
154
- all_options = tuple(create_date_param_option(row) for _, row in df.iterrows())
155
- return p.DateParameter.WithParent(ds_param.name, ds_param.label, all_options, ds_param.parent,
156
- is_hidden=ds_param.is_hidden)
157
-
158
-
159
- @dataclass
160
- class _NumericDataSource(DataSource):
161
- """
162
- Abstract class for number or number range data sources
163
- """
164
- min_value_col: str
165
- max_value_col: str
166
- increment_col: Optional[str] = None
167
-
168
- def _convert_helper(self, row: pd.Series) -> Tuple[str, str, str]:
169
- min_val = str(_utils.get_row_value(row, self.min_value_col))
170
- max_val = str(_utils.get_row_value(row, self.max_value_col))
171
- incr_val = str(_utils.get_row_value(row, self.increment_col)) if self.increment_col is not None else '1'
172
- return min_val, max_val, incr_val
173
-
174
- @dataclass
175
- class NumberDataSource(_NumericDataSource):
176
- """
177
- Lookup table for number parameter default options
178
-
179
- Attributes:
180
- connection_name: Name of the connection to use defined in connections.py
181
- table_or_query: Either the name of the table to use, or a query to run
182
- min_value_col: The column name of the minimum value
183
- max_value_col: The column name of the maximum value
184
- increment_col: The column name of the increment value. Defaults to column of 1's if None
185
- default_value_col: The column name of the default value. Defaults to min_value_col if None
186
- parent_id_col: The column name of the parent option id that the default value belongs to
187
- """
188
- default_value_col: Optional[str] = None
189
- parent_id_col: Optional[str] = None
190
- connection_name: str = c.DEFAULT_DB_CONN
191
-
192
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.NumberParameter:
193
- """
194
- Method to convert the associated DataSourceParameter into a NumberParameter
195
-
196
- Parameters:
197
- ds_param: The parameter to convert
198
- df: The dataframe containing the parameter options data
199
-
200
- Returns:
201
- The converted parameter
202
- """
203
- self._validate_parameter_class(ds_param, [p.NumberParameter])
204
-
205
- def _get_default_value(row: pd.Series) -> str:
206
- return str(_utils.get_row_value(row, self.default_value_col)) if self.default_value_col is not None \
207
- else str(_utils.get_row_value(row, self.min_value_col))
208
-
209
- def _create_num_param_option(row: pd.Series) -> po.NumberParameterOption:
210
- min_value, max_value, increment = self._convert_helper(row)
211
- return po.NumberParameterOption(min_value, max_value, increment, _get_default_value(row),
212
- self._get_parent(row))
213
-
214
- if ds_param.parent is None:
215
- row = df.iloc[0]
216
- min_value, max_value, increment = self._convert_helper(row)
217
- return p.NumberParameter(ds_param.name, ds_param.label, min_value, max_value, increment,
218
- _get_default_value(row), is_hidden=ds_param.is_hidden)
219
- else:
220
- all_options = tuple(_create_num_param_option(row) for _, row in df.iterrows())
221
- return p.NumberParameter.WithParent(ds_param.name, ds_param.label, all_options, ds_param.parent,
222
- is_hidden=ds_param.is_hidden)
223
-
224
-
225
- @dataclass
226
- class NumRangeDataSource(_NumericDataSource):
227
- """
228
- Lookup table for number range parameter default options
229
-
230
- Attributes:
231
- connection_name: Name of the connection to use defined in connections.py
232
- table_or_query: Either the name of the table to use, or a query to run
233
- min_value_col: The column name of the minimum value
234
- max_value_col: The column name of the maximum value
235
- increment_col: The column name of the increment value. Defaults to column of 1's if None
236
- default_lower_value_col: The column name of the default lower value. Defaults to min_value_col if None
237
- default_upper_value_col: The column name of the default upper value. Defaults to max_value_col if None
238
- parent_id_col: The column name of the parent option id that the default value belongs to
239
- """
240
- default_lower_value_col: Optional[str] = None
241
- default_upper_value_col: Optional[str] = None
242
- parent_id_col: Optional[str] = None
243
- connection_name: str = c.DEFAULT_DB_CONN
244
-
245
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.NumRangeParameter:
246
- """
247
- Method to convert the associated DataSourceParameter into a NumRangeParameter
248
-
249
- Parameters:
250
- ds_param: The parameter to convert
251
- df: The dataframe containing the parameter options data
252
-
253
- Returns:
254
- The converted parameter
255
- """
256
- self._validate_parameter_class(ds_param, [p.NumRangeParameter])
257
-
258
- def _get_default_lower_upper_values(row: pd.Series) -> Tuple[str, str]:
259
- lower_value_col = self.default_lower_value_col if self.default_lower_value_col is not None \
260
- else self.min_value_col
261
- upper_value_col = self.default_upper_value_col if self.default_upper_value_col is not None \
262
- else self.max_value_col
263
- lower_value = str(_utils.get_row_value(row, lower_value_col))
264
- upper_value = str(_utils.get_row_value(row, upper_value_col))
265
- return lower_value, upper_value
266
-
267
- def _create_range_param_option(row: pd.Series) -> po.NumRangeParameterOption:
268
- min_value, max_value, increment = self._convert_helper(row)
269
- lower_value, upper_value = _get_default_lower_upper_values(row)
270
- return po.NumRangeParameterOption(min_value, max_value, increment, lower_value, upper_value,
271
- self._get_parent(row))
272
-
273
- if ds_param.parent is None:
274
- row = df.iloc[0]
275
- min_value, max_value, increment = self._convert_helper(row)
276
- lower_value, upper_value = _get_default_lower_upper_values(row)
277
- return p.NumRangeParameter(ds_param.name, ds_param.label, min_value, max_value, increment,
278
- lower_value, upper_value, is_hidden=ds_param.is_hidden)
279
- else:
280
- all_options = tuple(_create_range_param_option(row) for _, row in df.iterrows())
281
- return p.NumRangeParameter.WithParent(ds_param.name, ds_param.label, all_options, ds_param.parent,
282
- is_hidden=ds_param.is_hidden)
1
+ from ._data_sources import (
2
+ SourceEnum,
3
+ SelectDataSource,
4
+ DateDataSource,
5
+ DateRangeDataSource,
6
+ NumberDataSource,
7
+ NumberRangeDataSource,
8
+ TextDataSource
9
+ )
10
+
11
+ __all__ = [
12
+ "SourceEnum", "SelectDataSource", "DateDataSource", "DateRangeDataSource",
13
+ "NumberDataSource", "NumberRangeDataSource", "TextDataSource"
14
+ ]
@@ -1,189 +1,13 @@
1
- from typing import Iterable, Optional, Union
2
- from dataclasses import dataclass, field
3
- from decimal import Decimal, InvalidOperation as InvalidDecimalConversion
4
- from datetime import datetime
5
-
6
- from squirrels._utils import ConfigurationError
7
-
8
- Number = Union[Decimal, int, str]
9
-
10
-
11
- @dataclass
12
- class ParameterOption:
13
- """
14
- Abstract class (or type) for parameter options
15
- """
16
- def __post_init__(self) -> None:
17
- if not hasattr(self, "parent_option_ids"):
18
- self.parent_option_ids = frozenset()
19
-
20
- self.parent_option_ids = frozenset(
21
- {self.parent_option_id} \
22
- if hasattr(self, "parent_option_id") and self.parent_option_id is not None \
23
- else self.parent_option_ids
24
- )
25
-
26
- def is_valid(self, selected_parent_option_ids: Optional[Iterable[str]] = None):
27
- """
28
- Checks if this option is valid given the selected parent options.
29
-
30
- Parameters:
31
- selected_parent_option_ids: List of selected option ids from the parent parameter
32
-
33
- Returns:
34
- True if valid, False otherwise
35
- """
36
- if selected_parent_option_ids is not None:
37
- return not self.parent_option_ids.isdisjoint(selected_parent_option_ids)
38
- else:
39
- return True
40
-
41
-
42
- @dataclass
43
- class SelectParameterOption(ParameterOption):
44
- """
45
- Parameter option for a select parameter
46
-
47
- Attributes:
48
- identifier: Unique identifier for this option that never changes over time
49
- label: Human readable label that gets shown as a dropdown option
50
- is_default: True if this is a default option, False otherwise
51
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
52
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None), or an empty set if this is a top-level option
53
- """
54
- identifier: str
55
- label: str
56
- is_default: bool = False
57
- parent_option_id: Optional[str] = field(default=None, repr=False)
58
- parent_option_ids: Iterable[str] = frozenset()
59
-
60
- def to_dict(self):
61
- return {'id': self.identifier, 'label': self.label}
62
-
63
-
64
- @dataclass
65
- class DateParameterOption(ParameterOption):
66
- """
67
- Parameter option for default dates if it varies based on selection of another parameter
68
-
69
- Attributes:
70
- default_date: Default date for this option
71
- date_format: Format of the default date, default is '%Y-%m-%d'
72
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
73
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None), or an empty set if this is a top-level option
74
- """
75
- default_date: Union[str, datetime]
76
- date_format: str = '%Y-%m-%d'
77
- parent_option_id: Optional[str] = field(default=None, repr=False)
78
- parent_option_ids: Iterable[str] = frozenset()
79
-
80
- def __post_init__(self) -> None:
81
- super().__post_init__()
82
- self.default_date = self._validate_date(self.default_date) \
83
- if isinstance(self.default_date, str) else self.default_date
84
-
85
- def _validate_date(self, date_str: str) -> datetime:
86
- try:
87
- return datetime.strptime(date_str, self.date_format)
88
- except ValueError as e:
89
- raise ConfigurationError(f'Invalid format for date "{date_str}".') from e
90
-
91
-
92
- @dataclass
93
- class _NumericParameterOption(ParameterOption):
94
- """
95
- Abstract class (or type) for numeric parameter options
96
- """
97
- min_value: Decimal
98
- max_value: Decimal
99
- increment: Decimal
100
-
101
- def __post_init__(self) -> None:
102
- super().__post_init__()
103
- try:
104
- self.min_value = Decimal(self.min_value)
105
- self.max_value = Decimal(self.max_value)
106
- self.increment = Decimal(self.increment)
107
- except InvalidDecimalConversion as e:
108
- raise ConfigurationError(f'Could not convert either min, max, or increment to number') from e
109
-
110
- if self.min_value > self.max_value:
111
- raise ConfigurationError(f'The min_value "{self.min_value}" must be less than or equal to ' +
112
- 'the max_value "{self.max_value}"')
113
- if (self.max_value - self.min_value) % self.increment != 0:
114
- raise ConfigurationError(f'The increment "{self.increment}" must fit evenly between ' +
115
- 'the min_value "{self.min_value}" and max_value "{self.max_value}"')
116
-
117
- def _value_in_range(self, value: Decimal, min_value: Decimal) -> bool:
118
- return min_value <= value <= self.max_value
119
-
120
- def _value_on_increment(self, value: Decimal, min_value: Decimal) -> bool:
121
- diff = (value - min_value)
122
- return diff >= 0 and diff % self.increment == 0
123
-
124
- def _validate_value(self, value: Number, min_value: Optional[Decimal] = None) -> Decimal:
125
- min_value = self.min_value if min_value is None else min_value
126
- try:
127
- value = Decimal(value)
128
- except InvalidDecimalConversion as e:
129
- raise ConfigurationError(f'Could not convert "{value}" to number', e)
130
-
131
- if not self._value_in_range(value, min_value):
132
- raise ConfigurationError(f'The selected value "{value}" is outside of bounds ' +
133
- '"{min_value}" and "{self.max_value}"')
134
- if not self._value_on_increment(value, min_value):
135
- raise ConfigurationError(f'The difference between selected value "{value}" and lower value ' +
136
- '"{min_value}" must be a multiple of increment "{self.increment}"')
137
- return value
138
-
139
-
140
- @dataclass
141
- class NumberParameterOption(_NumericParameterOption):
142
- """
143
- Parameter option for default numbers if it varies based on selection of another parameter
144
-
145
- Attributes:
146
- min_value: Minimum selectable value
147
- max_value: Maximum selectable value
148
- increment: Increment of selectable values, and must fit evenly between min_value and max_value
149
- default_value: Default value for this option, and must be selectable based on min_value, max_value, and increment
150
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
151
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None), or an empty set if this is a top-level option
152
- """
153
- default_value: Decimal
154
- parent_option_id: Optional[str] = field(default=None, repr=False)
155
- parent_option_ids: Iterable[str] = frozenset()
156
-
157
- def __post_init__(self) -> None:
158
- super().__post_init__()
159
- self.default_value = self._validate_value(self.default_value)
160
-
161
-
162
- @dataclass
163
- class NumRangeParameterOption(_NumericParameterOption):
164
- """
165
- Parameter option for default numeric ranges if it varies based on selection of another parameter
166
-
167
- Attributes:
168
- min_value: Minimum selectable value
169
- max_value: Maximum selectable value
170
- increment: Increment of selectable values, and must fit evenly between min_value and max_value
171
- default_lower_value: Default lower value for this option, and must be selectable based on min_value, max_value, and increment
172
- default_upper_value: Default upper value for this option, and must be selectable based on min_value, max_value, and increment.
173
- Must also be greater than default_lower_value
174
- parent_option_id: Identifier of the parent option, or None if this is a top-level option
175
- parent_option_ids: Set of parent option ids (only used if parent_option_id is None), or an empty set if this is a top-level option
176
- """
177
- default_lower_value: Decimal
178
- default_upper_value: Decimal
179
- parent_option_id: Optional[str] = field(default=None, repr=False)
180
- parent_option_ids: Iterable[str] = frozenset()
181
-
182
- def __post_init__(self) -> None:
183
- super().__post_init__()
184
- self.default_lower_value = self._validate_value(self.default_lower_value)
185
- self.default_upper_value = self._validate_value(self.default_upper_value, self.default_lower_value)
186
-
187
-
188
- # Types:
189
- NumericParameterOption = Union[NumberParameterOption, NumRangeParameterOption]
1
+ from ._parameter_options import (
2
+ SelectParameterOption,
3
+ DateParameterOption,
4
+ DateRangeParameterOption,
5
+ NumberParameterOption,
6
+ NumberRangeParameterOption,
7
+ TextParameterOption
8
+ )
9
+
10
+ __all__ = [
11
+ "SelectParameterOption", "DateParameterOption", "DateRangeParameterOption",
12
+ "NumberParameterOption", "NumberRangeParameterOption", "TextParameterOption"
13
+ ]