squirrels 0.1.1.post1__py3-none-any.whl → 0.2.0.dev0__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.

Files changed (74) hide show
  1. squirrels/__init__.py +10 -16
  2. squirrels/_api_server.py +234 -80
  3. squirrels/_authenticator.py +84 -0
  4. squirrels/_command_line.py +60 -72
  5. squirrels/_connection_set.py +96 -0
  6. squirrels/_constants.py +114 -33
  7. squirrels/_environcfg.py +77 -0
  8. squirrels/_initializer.py +126 -67
  9. squirrels/_manifest.py +195 -168
  10. squirrels/_models.py +495 -0
  11. squirrels/_package_loader.py +26 -0
  12. squirrels/_parameter_configs.py +401 -0
  13. squirrels/_parameter_sets.py +188 -0
  14. squirrels/_py_module.py +60 -0
  15. squirrels/_timer.py +36 -0
  16. squirrels/_utils.py +81 -49
  17. squirrels/_version.py +2 -2
  18. squirrels/arguments/init_time_args.py +32 -0
  19. squirrels/arguments/run_time_args.py +82 -0
  20. squirrels/data_sources.py +380 -155
  21. squirrels/dateutils.py +86 -57
  22. squirrels/package_data/base_project/Dockerfile +15 -0
  23. squirrels/package_data/base_project/connections.yml +7 -0
  24. squirrels/package_data/base_project/database/{sample_database.db → expenses.db} +0 -0
  25. squirrels/package_data/base_project/environcfg.yml +29 -0
  26. squirrels/package_data/base_project/ignores/.dockerignore +8 -0
  27. squirrels/package_data/base_project/ignores/.gitignore +7 -0
  28. squirrels/package_data/base_project/models/dbviews/database_view1.py +36 -0
  29. squirrels/package_data/base_project/models/dbviews/database_view1.sql +15 -0
  30. squirrels/package_data/base_project/models/federates/dataset_example.py +20 -0
  31. squirrels/package_data/base_project/models/federates/dataset_example.sql +3 -0
  32. squirrels/package_data/base_project/parameters.yml +109 -0
  33. squirrels/package_data/base_project/pyconfigs/auth.py +47 -0
  34. squirrels/package_data/base_project/pyconfigs/connections.py +28 -0
  35. squirrels/package_data/base_project/pyconfigs/context.py +45 -0
  36. squirrels/package_data/base_project/pyconfigs/parameters.py +55 -0
  37. squirrels/package_data/base_project/seeds/mocks/category.csv +3 -0
  38. squirrels/package_data/base_project/seeds/mocks/max_filter.csv +2 -0
  39. squirrels/package_data/base_project/seeds/mocks/subcategory.csv +6 -0
  40. squirrels/package_data/base_project/squirrels.yml.j2 +57 -0
  41. squirrels/package_data/base_project/tmp/.gitignore +2 -0
  42. squirrels/package_data/static/script.js +159 -63
  43. squirrels/package_data/static/style.css +79 -15
  44. squirrels/package_data/static/widgets.js +133 -0
  45. squirrels/package_data/templates/index.html +65 -23
  46. squirrels/package_data/templates/index2.html +22 -0
  47. squirrels/parameter_options.py +216 -119
  48. squirrels/parameters.py +407 -478
  49. squirrels/user_base.py +58 -0
  50. squirrels-0.2.0.dev0.dist-info/METADATA +126 -0
  51. squirrels-0.2.0.dev0.dist-info/RECORD +56 -0
  52. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/WHEEL +1 -2
  53. squirrels-0.2.0.dev0.dist-info/entry_points.txt +3 -0
  54. squirrels/_credentials_manager.py +0 -87
  55. squirrels/_module_loader.py +0 -37
  56. squirrels/_parameter_set.py +0 -151
  57. squirrels/_renderer.py +0 -286
  58. squirrels/_timed_imports.py +0 -37
  59. squirrels/connection_set.py +0 -126
  60. squirrels/package_data/base_project/.gitignore +0 -4
  61. squirrels/package_data/base_project/connections.py +0 -20
  62. squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -22
  63. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -29
  64. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -12
  65. squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -11
  66. squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -3
  67. squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -47
  68. squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -9
  69. squirrels/package_data/base_project/squirrels.yaml +0 -22
  70. squirrels-0.1.1.post1.dist-info/METADATA +0 -67
  71. squirrels-0.1.1.post1.dist-info/RECORD +0 -40
  72. squirrels-0.1.1.post1.dist-info/entry_points.txt +0 -2
  73. squirrels-0.1.1.post1.dist-info/top_level.txt +0 -1
  74. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/LICENSE +0 -0
squirrels/data_sources.py CHANGED
@@ -1,65 +1,196 @@
1
1
  from __future__ import annotations
2
- from typing import Type, Dict, Tuple, List, Optional
2
+ from typing import Type, Sequence, Iterable, Optional, Any
3
3
  from dataclasses import dataclass, field
4
+ from abc import ABCMeta, abstractmethod
5
+ import pandas as pd
4
6
 
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
7
+ from . import _parameter_configs as pc, parameter_options as po, _utils as u, _constants as c
8
+ from ._manifest import ManifestIO
8
9
 
9
10
 
10
11
  @dataclass
11
- class DataSource:
12
+ class DataSource(metaclass=ABCMeta):
12
13
  """
13
14
  Abstract class for lookup tables coming from a database
14
15
  """
15
- table_or_query: str
16
+ _table_or_query: str
17
+ _id_col: Optional[str]
18
+ _user_group_col: Optional[str] # = field(default=None, kw_only=True)
19
+ _parent_id_col: Optional[str] # = field(default=None, kw_only=True)
20
+ _connection_name: Optional[str] # = field(default=None, kw_only=True)
21
+
22
+ @abstractmethod
23
+ def __init__(
24
+ self, table_or_query: str, *, id_col: Optional[str] = None, user_group_col: Optional[str] = None,
25
+ parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
26
+ ) -> None:
27
+ self._table_or_query = table_or_query
28
+ self._id_col = id_col
29
+ self._user_group_col = user_group_col
30
+ self._parent_id_col = parent_id_col
31
+ self._connection_name = connection_name
16
32
 
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
33
+ def _get_connection_name(self) -> str:
34
+ if self._connection_name is None:
35
+ return ManifestIO.obj.settings.get(c.DB_CONN_DEFAULT_USED_SETTING, c.DEFAULT_DB_CONN)
36
+ return self._connection_name
22
37
 
23
- def get_query(self) -> str:
38
+ def _get_query(self) -> str:
24
39
  """
25
40
  Get the "table_or_query" attribute as a select query
26
41
 
27
42
  Returns:
28
43
  str: The converted select query
29
44
  """
30
- if self.table_or_query.strip().lower().startswith('select '):
31
- query = self.table_or_query
45
+ if self._table_or_query.strip().lower().startswith('select '):
46
+ query = self._table_or_query
32
47
  else:
33
- query = f'SELECT * FROM {self.table_or_query}'
48
+ query = f'SELECT * FROM {self._table_or_query}'
34
49
  return query
35
50
 
36
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.Parameter:
51
+ @abstractmethod
52
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.ParameterConfig:
37
53
  """
38
54
  An abstract method for converting itself into a parameter
55
+ """
56
+ pass
57
+
58
+ def _validate_parameter_type(self, ds_param: pc.DataSourceParameterConfig, target_parameter_type: Type[pc.ParameterConfig]) -> None:
59
+ if ds_param.parameter_type != target_parameter_type:
60
+ parameter_type_name = ds_param.parameter_type.__name__
61
+ datasource_type_name = self.__class__.__name__
62
+ raise u.ConfigurationError(f'Invalid widget type "{parameter_type_name}" for {datasource_type_name}')
63
+
64
+ def _get_aggregated_df(self, df: pd.DataFrame, columns_to_include: Iterable[str]) -> pd.DataFrame:
65
+ agg_rules = {}
66
+ for column in columns_to_include:
67
+ if column is not None:
68
+ agg_rules[column] = "first"
69
+ if self._user_group_col is not None:
70
+ agg_rules[self._user_group_col] = list
71
+ if self._parent_id_col is not None:
72
+ agg_rules[self._parent_id_col] = list
73
+
74
+ groupby_dim = self._id_col if self._id_col is not None else df.index
75
+ try:
76
+ df_agg = df.groupby(groupby_dim).agg(agg_rules)
77
+ except KeyError as e:
78
+ raise u.ConfigurationError(e)
79
+
80
+ return df_agg
81
+
82
+ def _get_key_from_record(self, key: Optional[str], record: dict[str, Any], default: Any) -> Any:
83
+ return record[key] if key is not None else default
84
+
85
+ def _get_key_from_record_as_list(self, key: Optional[str], record: dict[str, Any]) -> Iterable[str]:
86
+ value = self._get_key_from_record(key, record, list())
87
+ return [str(x) for x in value]
88
+
39
89
 
40
- Args:
90
+ @dataclass
91
+ class _SelectionDataSource(DataSource):
92
+ """
93
+ Abstract class for selection parameter data sources
94
+ """
95
+ _options_col: str
96
+ _order_by_col: Optional[str] # = field(default=None, kw_only=True)
97
+ _is_default_col: Optional[str] # = field(default=None, kw_only=True)
98
+ _custom_cols: dict[str, str] # = field(default_factory=dict, kw_only=True)
99
+
100
+ @abstractmethod
101
+ def __init__(
102
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
103
+ is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, user_group_col: Optional[str] = None,
104
+ parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
105
+ ) -> None:
106
+ super().__init__(table_or_query, id_col=id_col, user_group_col=user_group_col, parent_id_col=parent_id_col,
107
+ connection_name=connection_name)
108
+ self._options_col = options_col
109
+ self._order_by_col = order_by_col
110
+ self._is_default_col = is_default_col
111
+ self._custom_cols = custom_cols
112
+
113
+ def _get_all_options(self, df: pd.DataFrame) -> Sequence[po.SelectParameterOption]:
114
+ columns = [self._options_col, self._order_by_col, self._is_default_col, *self._custom_cols.values()]
115
+ df_agg = self._get_aggregated_df(df, columns)
116
+
117
+ if self._order_by_col is None:
118
+ df_agg.sort_index(inplace=True)
119
+ else:
120
+ df_agg.sort_values(self._order_by_col, inplace=True)
121
+
122
+ def get_is_default(record: dict[str, Any]) -> bool:
123
+ return int(record[self._is_default_col]) == 1 if self._is_default_col is not None else False
124
+
125
+ def get_custom_fields(record: dict[str, Any]) -> dict[str, Any]:
126
+ result = {}
127
+ for key, val in self._custom_cols.items():
128
+ result[key] = record[val]
129
+ return result
130
+
131
+ records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
132
+ return tuple(
133
+ po.SelectParameterOption(str(id), str(record[self._options_col]),
134
+ is_default=get_is_default(record), custom_fields=get_custom_fields(record),
135
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
136
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
137
+ for id, record in records.items()
138
+ )
139
+
140
+
141
+ @dataclass
142
+ class SingleSelectDataSource(_SelectionDataSource):
143
+ """
144
+ Lookup table for single select parameter options
145
+
146
+ Attributes:
147
+ table_or_query: Either the name of the table to use, or a query to run
148
+ id_col: The column name of the id
149
+ options_col: The column name of the options
150
+ order_by_col: The column name to order the options by. Orders by the id_col instead if this is None
151
+ is_default_col: The column name that indicates which options are the default
152
+ custom_cols: Dictionary of attribute to column name for custom fields for the SelectParameterOption
153
+ user_group_col: The column name of the user group that the user is in for this option to be valid
154
+ parent_id_col: The column name of the parent option id that must be selected for this option to be valid
155
+ connection_name: Name of the connection to use defined in connections.py
156
+ """
157
+
158
+ def __init__(
159
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
160
+ is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, user_group_col: Optional[str] = None,
161
+ parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
162
+ ) -> None:
163
+ """
164
+ Constructor for SingleSelectDataSource
165
+
166
+ Parameters:
167
+ ...see Attributes of SingleSelectDataSource
168
+ """
169
+ super().__init__(table_or_query, id_col, options_col, order_by_col=order_by_col, is_default_col=is_default_col,
170
+ custom_cols=custom_cols, user_group_col=user_group_col, parent_id_col=parent_id_col,
171
+ connection_name=connection_name)
172
+
173
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.SingleSelectParameterConfig:
174
+ """
175
+ Method to convert the associated DataSourceParameter into a SingleSelectParameterConfig
176
+
177
+ Parameters:
41
178
  ds_param: The parameter to convert
42
179
  df: The dataframe containing the parameter options data
43
180
 
44
181
  Returns:
45
182
  The converted parameter
46
183
  """
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}')
184
+ self._validate_parameter_type(ds_param, pc.SingleSelectParameterConfig)
185
+ all_options = self._get_all_options(df)
186
+ return pc.SingleSelectParameterConfig(ds_param.name, ds_param.label, all_options, is_hidden=ds_param.is_hidden,
187
+ user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
57
188
 
58
189
 
59
190
  @dataclass
60
- class SelectionDataSource(DataSource):
191
+ class MultiSelectDataSource(_SelectionDataSource):
61
192
  """
62
- Lookup table for selection parameters (single and multi)
193
+ Lookup table for single select parameter options
63
194
 
64
195
  Attributes:
65
196
  table_or_query: Either the name of the table to use, or a query to run
@@ -67,24 +198,36 @@ class SelectionDataSource(DataSource):
67
198
  options_col: The column name of the options
68
199
  order_by_col: The column name to order the options by. Orders by the id_col instead if this is None
69
200
  is_default_col: The column name that indicates which options are the default
70
- parent_id_col: The column name of the parent option id that this option belongs to
71
201
  custom_cols: Dictionary of attribute to column name for custom fields for the SelectParameterOption
202
+ include_all: Whether applying no selection is equivalent to selecting all. Default is True
203
+ order_matters: Whether the ordering of the selection matters. Default is False
204
+ user_group_col: The column name of the user group that the user is in for this option to be valid
205
+ parent_id_col: The column name of the parent option id that must be selected for this option to be valid
72
206
  connection_name: Name of the connection to use defined in connections.py
73
207
  """
74
- id_col: str
75
- options_col: str
76
- order_by_col: Optional[str] = None
77
- is_default_col: Optional[str] = None
78
- parent_id_col: Optional[str] = None
79
- custom_cols: Dict[str, str] = field(default_factory=dict)
80
- connection_name: str = c.DEFAULT_DB_CONN
81
-
82
- def __post_init__(self):
83
- self.order_by_col = self.order_by_col if self.order_by_col is not None else self.id_col
84
-
85
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.Parameter:
208
+ _include_all: bool # = field(default=True, kw_only=True)
209
+ _order_matters: bool # = field(default=False, kw_only=True)
210
+
211
+ def __init__(
212
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
213
+ is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, include_all: bool = True, order_matters: bool = False,
214
+ user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
215
+ ) -> None:
216
+ """
217
+ Constructor for SingleSelectDataSource
218
+
219
+ Parameters:
220
+ ...see Attributes of SingleSelectDataSource
221
+ """
222
+ super().__init__(table_or_query, id_col, options_col, order_by_col=order_by_col, is_default_col=is_default_col,
223
+ custom_cols=custom_cols, user_group_col=user_group_col, parent_id_col=parent_id_col,
224
+ connection_name=connection_name)
225
+ self._include_all = include_all
226
+ self._order_matters = order_matters
227
+
228
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.MultiSelectParameterConfig:
86
229
  """
87
- Method to convert the associated DataSourceParameter into a SingleSelect or MultiSelect Parameter
230
+ Method to convert the associated DataSourceParameter into a MultiSelectParameterConfig
88
231
 
89
232
  Parameters:
90
233
  ds_param: The parameter to convert
@@ -93,29 +236,11 @@ class SelectionDataSource(DataSource):
93
236
  Returns:
94
237
  The converted parameter
95
238
  """
96
- self._validate_parameter_class(ds_param, [p.SingleSelectParameter, p.MultiSelectParameter])
97
-
98
- def is_default(row):
99
- return int(_utils.get_row_value(row, self.is_default_col)) == 1 if self.is_default_col is not None else False
100
-
101
- def get_custom_fields(row):
102
- result = {}
103
- for key, val in self.custom_cols.items():
104
- result[key] = _utils.get_row_value(row, val)
105
- return result
106
-
107
- try:
108
- df.sort_values(self.order_by_col, inplace=True)
109
- except KeyError as e:
110
- raise _utils.ConfigurationError(f'Could not sort on column name "{self.order_by_col}" as it does not exist')
111
-
112
- options = tuple(
113
- po.SelectParameterOption(str(_utils.get_row_value(row, self.id_col)), str(_utils.get_row_value(row, self.options_col)),
114
- is_default=is_default(row), parent_option_id=self._get_parent(row), custom_fields=get_custom_fields(row))
115
- for _, row in df.iterrows()
116
- )
117
-
118
- return ds_param.parameter_class(ds_param.name, ds_param.label, options, is_hidden=ds_param.is_hidden, parent=ds_param.parent)
239
+ self._validate_parameter_type(ds_param, pc.MultiSelectParameterConfig)
240
+ all_options = self._get_all_options(df)
241
+ return pc.MultiSelectParameterConfig(ds_param.name, ds_param.label, all_options, include_all=self._include_all,
242
+ order_matters=self._order_matters, is_hidden=ds_param.is_hidden,
243
+ user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
119
244
 
120
245
 
121
246
  @dataclass
@@ -125,19 +250,97 @@ class DateDataSource(DataSource):
125
250
 
126
251
  Attributes:
127
252
  table_or_query: Either the name of the table to use, or a query to run
253
+ id_col: The column name of the id
128
254
  default_date_col: The column name of the default date
255
+ date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
256
+ user_group_col: The column name of the user group that the user is in for this option to be valid
129
257
  parent_id_col: The column name of the parent option id that the default date belongs to
258
+ connection_name: Name of the connection to use defined in connections.py
259
+ """
260
+ _default_date_col: str
261
+ _date_format: str # = field(default="%Y-%m-%d", kw_only=True)
262
+
263
+ def __init__(
264
+ self, table_or_query: str, default_date_col: str, *, date_format: str = '%Y-%m-%d', id_col: Optional[str] = None,
265
+ user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
266
+ ) -> None:
267
+ """
268
+ Constructor for DateDataSource
269
+
270
+ Parameters:
271
+ ...see Attributes of DateDataSource
272
+ """
273
+ super().__init__(table_or_query, id_col=id_col, user_group_col=user_group_col, parent_id_col=parent_id_col,
274
+ connection_name=connection_name)
275
+ self._default_date_col = default_date_col
276
+ self._date_format = date_format
277
+
278
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.DateParameterConfig:
279
+ """
280
+ Method to convert the associated DataSourceParameter into a DateParameterConfig
281
+
282
+ Parameters:
283
+ ds_param: The parameter to convert
284
+ df: The dataframe containing the parameter options data
285
+
286
+ Returns:
287
+ The converted parameter
288
+ """
289
+ self._validate_parameter_type(ds_param, pc.DateParameterConfig)
290
+
291
+ columns = [self._default_date_col]
292
+ df_agg = self._get_aggregated_df(df, columns)
293
+
294
+ records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
295
+ options = tuple(
296
+ po.DateParameterOption(str(record[self._default_date_col]), date_format=self._date_format,
297
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
298
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
299
+ for _, record in records.items()
300
+ )
301
+ return pc.DateParameterConfig(ds_param.name, ds_param.label, options, is_hidden=ds_param.is_hidden,
302
+ user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
303
+
304
+
305
+ @dataclass
306
+ class DateRangeDataSource(DataSource):
307
+ """
308
+ Lookup table for date parameter default options
309
+
310
+ Attributes:
311
+ table_or_query: Either the name of the table to use, or a query to run
312
+ id_col: The column name of the id
313
+ default_start_date_col: The column name of the default start date
314
+ default_end_date_col: The column name of the default end date
130
315
  date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
316
+ user_group_col: The column name of the user group that the user is in for this option to be valid
317
+ parent_id_col: The column name of the parent option id that the default date belongs to
131
318
  connection_name: Name of the connection to use defined in connections.py
132
319
  """
133
- default_date_col: str
134
- parent_id_col: Optional[str] = None
135
- date_format: Optional[str] = '%Y-%m-%d'
136
- connection_name: str = c.DEFAULT_DB_CONN
320
+ _default_start_date_col: str
321
+ _default_end_date_col: str
322
+ _date_format: str # = field(default="%Y-%m-%d", kw_only=True)
323
+
324
+ def __init__(
325
+ self, table_or_query: str, default_start_date_col: str, default_end_date_col: str, *, date_format: str = '%Y-%m-%d',
326
+ id_col: Optional[str] = None, user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None,
327
+ connection_name: Optional[str] = None, **kwargs
328
+ ) -> None:
329
+ """
330
+ Constructor for DateRangeDataSource
331
+
332
+ Parameters:
333
+ ...see Attributes of DateRangeDataSource
334
+ """
335
+ super().__init__(table_or_query, id_col=id_col, user_group_col=user_group_col, parent_id_col=parent_id_col,
336
+ connection_name=connection_name)
337
+ self._default_start_date_col = default_start_date_col
338
+ self._default_end_date_col = default_end_date_col
339
+ self._date_format = date_format
137
340
 
138
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.DateParameter:
341
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.DateRangeParameterConfig:
139
342
  """
140
- Method to convert the associated DataSourceParameter into a DateParameter
343
+ Method to convert the associated DataSourceParameter into a DateRangeParameterConfig
141
344
 
142
345
  Parameters:
143
346
  ds_param: The parameter to convert
@@ -146,22 +349,21 @@ class DateDataSource(DataSource):
146
349
  Returns:
147
350
  The converted parameter
148
351
  """
149
- self._validate_parameter_class(ds_param, [p.DateParameter])
150
-
151
- def get_date(row: pd.Series) -> str:
152
- return str(_utils.get_row_value(row, self.default_date_col))
153
-
154
- def create_date_param_option(row: pd.Series) -> po.DateParameterOption:
155
- return po.DateParameterOption(get_date(row), self.date_format, self._get_parent(row))
156
-
157
- if ds_param.parent is None:
158
- row = df.iloc[0]
159
- return p.DateParameter(ds_param.name, ds_param.label, get_date(row), self.date_format,
160
- is_hidden=ds_param.is_hidden)
161
- else:
162
- all_options = tuple(create_date_param_option(row) for _, row in df.iterrows())
163
- return p.DateParameter.WithParent(ds_param.name, ds_param.label, all_options, ds_param.parent,
164
- is_hidden=ds_param.is_hidden)
352
+ self._validate_parameter_type(ds_param, pc.DateRangeParameterConfig)
353
+
354
+ columns = [self._default_start_date_col, self._default_end_date_col]
355
+ df_agg = self._get_aggregated_df(df, columns)
356
+
357
+ records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
358
+ options = tuple(
359
+ po.DateRangeParameterOption(str(record[self._default_start_date_col]), str(record[self._default_end_date_col]),
360
+ date_format=self._date_format,
361
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
362
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
363
+ for _, record in records.items()
364
+ )
365
+ return pc.DateRangeParameterConfig(ds_param.name, ds_param.label, options, is_hidden=ds_param.is_hidden,
366
+ user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
165
367
 
166
368
 
167
369
  @dataclass
@@ -169,15 +371,21 @@ class _NumericDataSource(DataSource):
169
371
  """
170
372
  Abstract class for number or number range data sources
171
373
  """
172
- min_value_col: str
173
- max_value_col: str
174
- increment_col: Optional[str] = None
374
+ _min_value_col: str
375
+ _max_value_col: str
376
+ _increment_col: Optional[str] # = field(default=None, kw_only=True)
377
+
378
+ @abstractmethod
379
+ def __init__(
380
+ self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: Optional[str] = None,
381
+ id_col: Optional[str] = None, user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None,
382
+ connection_name: Optional[str] = None, **kwargs
383
+ ) -> None:
384
+ super().__init__(table_or_query, id_col=id_col, user_group_col=user_group_col, parent_id_col=parent_id_col, connection_name=connection_name)
385
+ self._min_value_col = min_value_col
386
+ self._max_value_col = max_value_col
387
+ self._increment_col = increment_col
175
388
 
176
- def _convert_helper(self, row: pd.Series) -> Tuple[str, str, str]:
177
- min_val = str(_utils.get_row_value(row, self.min_value_col))
178
- max_val = str(_utils.get_row_value(row, self.max_value_col))
179
- incr_val = str(_utils.get_row_value(row, self.increment_col)) if self.increment_col is not None else '1'
180
- return min_val, max_val, incr_val
181
389
 
182
390
  @dataclass
183
391
  class NumberDataSource(_NumericDataSource):
@@ -186,20 +394,35 @@ class NumberDataSource(_NumericDataSource):
186
394
 
187
395
  Attributes:
188
396
  table_or_query: Either the name of the table to use, or a query to run
397
+ id_col: The column name of the id
189
398
  min_value_col: The column name of the minimum value
190
399
  max_value_col: The column name of the maximum value
191
400
  increment_col: The column name of the increment value. Defaults to column of 1's if None
192
401
  default_value_col: The column name of the default value. Defaults to min_value_col if None
402
+ user_group_col: The column name of the user group that the user is in for this option to be valid
193
403
  parent_id_col: The column name of the parent option id that the default value belongs to
194
404
  connection_name: Name of the connection to use defined in connections.py
195
405
  """
196
- default_value_col: Optional[str] = None
197
- parent_id_col: Optional[str] = None
198
- connection_name: str = c.DEFAULT_DB_CONN
406
+ _default_value_col: Optional[str] # = field(default=None, kw_only=True)
407
+
408
+ def __init__(
409
+ self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: Optional[str] = None,
410
+ default_value_col: Optional[str] = None, id_col: Optional[str] = None, user_group_col: Optional[str] = None,
411
+ parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
412
+ ) -> None:
413
+ """
414
+ Constructor for NumberDataSource
199
415
 
200
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.NumberParameter:
416
+ Parameters:
417
+ ...see Attributes of NumberDataSource
418
+ """
419
+ super().__init__(table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col,
420
+ user_group_col=user_group_col, parent_id_col=parent_id_col, connection_name=connection_name)
421
+ self._default_value_col = default_value_col
422
+
423
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.NumberParameterConfig:
201
424
  """
202
- Method to convert the associated DataSourceParameter into a NumberParameter
425
+ Method to convert the associated DataSourceParameter into a NumberParameterConfig
203
426
 
204
427
  Parameters:
205
428
  ds_param: The parameter to convert
@@ -208,51 +431,63 @@ class NumberDataSource(_NumericDataSource):
208
431
  Returns:
209
432
  The converted parameter
210
433
  """
211
- self._validate_parameter_class(ds_param, [p.NumberParameter])
434
+ self._validate_parameter_type(ds_param, pc.NumberParameterConfig)
212
435
 
213
- def _get_default_value(row: pd.Series) -> str:
214
- return str(_utils.get_row_value(row, self.default_value_col)) if self.default_value_col is not None \
215
- else str(_utils.get_row_value(row, self.min_value_col))
216
-
217
- def _create_num_param_option(row: pd.Series) -> po.NumberParameterOption:
218
- min_value, max_value, increment = self._convert_helper(row)
219
- return po.NumberParameterOption(min_value, max_value, increment, _get_default_value(row),
220
- self._get_parent(row))
221
-
222
- if ds_param.parent is None:
223
- row = df.iloc[0]
224
- min_value, max_value, increment = self._convert_helper(row)
225
- return p.NumberParameter(ds_param.name, ds_param.label, min_value, max_value, increment,
226
- _get_default_value(row), is_hidden=ds_param.is_hidden)
227
- else:
228
- all_options = tuple(_create_num_param_option(row) for _, row in df.iterrows())
229
- return p.NumberParameter.WithParent(ds_param.name, ds_param.label, all_options, ds_param.parent,
230
- is_hidden=ds_param.is_hidden)
436
+ columns = [self._min_value_col, self._max_value_col, self._increment_col, self._default_value_col]
437
+ df_agg = self._get_aggregated_df(df, columns)
438
+
439
+ records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
440
+ options = tuple(
441
+ po.NumberParameterOption(record[self._min_value_col], record[self._max_value_col],
442
+ increment=self._get_key_from_record(self._increment_col, record, 1),
443
+ default_value=self._get_key_from_record(self._default_value_col, record, None),
444
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
445
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
446
+ for _, record in records.items()
447
+ )
448
+ return pc.NumberParameterConfig(ds_param.name, ds_param.label, options, is_hidden=ds_param.is_hidden,
449
+ user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
231
450
 
232
451
 
233
452
  @dataclass
234
- class NumRangeDataSource(_NumericDataSource):
453
+ class NumberRangeDataSource(_NumericDataSource):
235
454
  """
236
455
  Lookup table for number range parameter default options
237
456
 
238
457
  Attributes:
239
- table_or_query: Either the name of the table to use, or a query to run
458
+ table_or_query: Either the name of the table to use, or a query to
459
+ id_col: The column name of the id
240
460
  min_value_col: The column name of the minimum value
241
461
  max_value_col: The column name of the maximum value
242
462
  increment_col: The column name of the increment value. Defaults to column of 1's if None
243
463
  default_lower_value_col: The column name of the default lower value. Defaults to min_value_col if None
244
464
  default_upper_value_col: The column name of the default upper value. Defaults to max_value_col if None
465
+ user_group_col: The column name of the user group that the user is in for this option to be valid
245
466
  parent_id_col: The column name of the parent option id that the default value belongs to
246
467
  connection_name: Name of the connection to use defined in connections.py
247
468
  """
248
- default_lower_value_col: Optional[str] = None
249
- default_upper_value_col: Optional[str] = None
250
- parent_id_col: Optional[str] = None
251
- connection_name: str = c.DEFAULT_DB_CONN
469
+ _default_lower_value_col: Optional[str] # = field(default=None, kw_only=True)
470
+ _default_upper_value_col: Optional[str] # = field(default=None, kw_only=True)
471
+
472
+ def __init__(
473
+ self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: Optional[str] = None,
474
+ default_lower_value_col: Optional[str] = None, default_upper_value_col: Optional[str] = None, id_col: Optional[str] = None,
475
+ user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
476
+ ) -> None:
477
+ """
478
+ Constructor for NumRangeDataSource
479
+
480
+ Parameters:
481
+ ...see Attributes of NumRangeDataSource
482
+ """
483
+ super().__init__(table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col,
484
+ user_group_col=user_group_col, parent_id_col=parent_id_col, connection_name=connection_name)
485
+ self._default_lower_value_col = default_lower_value_col
486
+ self._default_upper_value_col = default_upper_value_col
252
487
 
253
- def convert(self, ds_param: p.DataSourceParameter, df: pd.DataFrame) -> p.NumRangeParameter:
488
+ def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.NumberRangeParameterConfig:
254
489
  """
255
- Method to convert the associated DataSourceParameter into a NumRangeParameter
490
+ Method to convert the associated DataSourceParameter into a NumberRangeParameterConfig
256
491
 
257
492
  Parameters:
258
493
  ds_param: The parameter to convert
@@ -261,30 +496,20 @@ class NumRangeDataSource(_NumericDataSource):
261
496
  Returns:
262
497
  The converted parameter
263
498
  """
264
- self._validate_parameter_class(ds_param, [p.NumRangeParameter])
265
-
266
- def _get_default_lower_upper_values(row: pd.Series) -> Tuple[str, str]:
267
- lower_value_col = self.default_lower_value_col if self.default_lower_value_col is not None \
268
- else self.min_value_col
269
- upper_value_col = self.default_upper_value_col if self.default_upper_value_col is not None \
270
- else self.max_value_col
271
- lower_value = str(_utils.get_row_value(row, lower_value_col))
272
- upper_value = str(_utils.get_row_value(row, upper_value_col))
273
- return lower_value, upper_value
274
-
275
- def _create_range_param_option(row: pd.Series) -> po.NumRangeParameterOption:
276
- min_value, max_value, increment = self._convert_helper(row)
277
- lower_value, upper_value = _get_default_lower_upper_values(row)
278
- return po.NumRangeParameterOption(min_value, max_value, increment, lower_value, upper_value,
279
- self._get_parent(row))
280
-
281
- if ds_param.parent is None:
282
- row = df.iloc[0]
283
- min_value, max_value, increment = self._convert_helper(row)
284
- lower_value, upper_value = _get_default_lower_upper_values(row)
285
- return p.NumRangeParameter(ds_param.name, ds_param.label, min_value, max_value, increment,
286
- lower_value, upper_value, is_hidden=ds_param.is_hidden)
287
- else:
288
- all_options = tuple(_create_range_param_option(row) for _, row in df.iterrows())
289
- return p.NumRangeParameter.WithParent(ds_param.name, ds_param.label, all_options, ds_param.parent,
290
- is_hidden=ds_param.is_hidden)
499
+ self._validate_parameter_type(ds_param, pc.NumberRangeParameterConfig)
500
+
501
+ columns = [self._min_value_col, self._max_value_col, self._increment_col, self._default_lower_value_col, self._default_upper_value_col]
502
+ df_agg = self._get_aggregated_df(df, columns)
503
+
504
+ records: dict[str, Any] = df_agg.to_dict("index")
505
+ options = tuple(
506
+ po.NumberRangeParameterOption(record[self._min_value_col], record[self._max_value_col],
507
+ increment=self._get_key_from_record(self._increment_col, record, 1),
508
+ default_lower_value=self._get_key_from_record(self._default_lower_value_col, record, None),
509
+ default_upper_value=self._get_key_from_record(self._default_upper_value_col, record, None),
510
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
511
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
512
+ for _, record in records.items()
513
+ )
514
+ return pc.NumberRangeParameterConfig(ds_param.name, ds_param.label, options, is_hidden=ds_param.is_hidden,
515
+ user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)