squirrels 0.3.3__py3-none-any.whl → 0.4.1__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 (56) hide show
  1. squirrels/__init__.py +7 -3
  2. squirrels/_api_response_models.py +96 -72
  3. squirrels/_api_server.py +375 -201
  4. squirrels/_authenticator.py +23 -22
  5. squirrels/_command_line.py +70 -46
  6. squirrels/_connection_set.py +23 -25
  7. squirrels/_constants.py +29 -78
  8. squirrels/_dashboards_io.py +61 -0
  9. squirrels/_environcfg.py +53 -50
  10. squirrels/_initializer.py +184 -141
  11. squirrels/_manifest.py +168 -195
  12. squirrels/_models.py +159 -292
  13. squirrels/_package_loader.py +7 -8
  14. squirrels/_parameter_configs.py +173 -141
  15. squirrels/_parameter_sets.py +49 -38
  16. squirrels/_py_module.py +7 -7
  17. squirrels/_seeds.py +13 -12
  18. squirrels/_utils.py +114 -54
  19. squirrels/_version.py +1 -1
  20. squirrels/arguments/init_time_args.py +16 -10
  21. squirrels/arguments/run_time_args.py +89 -24
  22. squirrels/dashboards.py +82 -0
  23. squirrels/data_sources.py +212 -232
  24. squirrels/dateutils.py +29 -26
  25. squirrels/package_data/assets/index.css +1 -1
  26. squirrels/package_data/assets/index.js +27 -18
  27. squirrels/package_data/base_project/.gitignore +2 -2
  28. squirrels/package_data/base_project/connections.yml +1 -1
  29. squirrels/package_data/base_project/dashboards/dashboard_example.py +32 -0
  30. squirrels/package_data/base_project/dashboards.yml +10 -0
  31. squirrels/package_data/base_project/docker/.dockerignore +9 -4
  32. squirrels/package_data/base_project/docker/Dockerfile +7 -6
  33. squirrels/package_data/base_project/docker/compose.yml +1 -1
  34. squirrels/package_data/base_project/env.yml +2 -2
  35. squirrels/package_data/base_project/models/dbviews/{database_view1.py → dbview_example.py} +2 -1
  36. squirrels/package_data/base_project/models/dbviews/{database_view1.sql → dbview_example.sql} +3 -2
  37. squirrels/package_data/base_project/models/federates/{dataset_example.py → federate_example.py} +6 -6
  38. squirrels/package_data/base_project/models/federates/{dataset_example.sql → federate_example.sql} +1 -1
  39. squirrels/package_data/base_project/parameters.yml +6 -4
  40. squirrels/package_data/base_project/pyconfigs/auth.py +1 -1
  41. squirrels/package_data/base_project/pyconfigs/connections.py +1 -1
  42. squirrels/package_data/base_project/pyconfigs/context.py +38 -10
  43. squirrels/package_data/base_project/pyconfigs/parameters.py +15 -7
  44. squirrels/package_data/base_project/squirrels.yml.j2 +14 -7
  45. squirrels/package_data/templates/index.html +3 -3
  46. squirrels/parameter_options.py +103 -106
  47. squirrels/parameters.py +347 -195
  48. squirrels/project.py +378 -0
  49. squirrels/user_base.py +14 -6
  50. {squirrels-0.3.3.dist-info → squirrels-0.4.1.dist-info}/METADATA +9 -21
  51. squirrels-0.4.1.dist-info/RECORD +60 -0
  52. squirrels/_timer.py +0 -23
  53. squirrels-0.3.3.dist-info/RECORD +0 -56
  54. {squirrels-0.3.3.dist-info → squirrels-0.4.1.dist-info}/LICENSE +0 -0
  55. {squirrels-0.3.3.dist-info → squirrels-0.4.1.dist-info}/WHEEL +0 -0
  56. {squirrels-0.3.3.dist-info → squirrels-0.4.1.dist-info}/entry_points.txt +0 -0
squirrels/data_sources.py CHANGED
@@ -1,41 +1,35 @@
1
- from __future__ import annotations
2
- from typing import Type, Sequence, Iterable, Optional, Any
3
- from dataclasses import dataclass, field
4
- from abc import ABCMeta, abstractmethod
5
- import pandas as pd
1
+ from __future__ import annotations as _a
2
+ import pandas as _pd, typing as _t, dataclasses as _d, abc as _abc
6
3
 
7
- from . import _parameter_configs as pc, parameter_options as po, _utils as u, _constants as c
8
- from ._manifest import ManifestIO
4
+ from . import _parameter_configs as _pc, parameter_options as _po, _utils as _u
9
5
 
10
6
 
11
- @dataclass
12
- class DataSource(metaclass=ABCMeta):
7
+ @_d.dataclass
8
+ class DataSource(metaclass=_abc.ABCMeta):
13
9
  """
14
10
  Abstract class for lookup tables coming from a database
15
11
  """
16
12
  _table_or_query: str
17
- _id_col: Optional[str]
18
- _from_seeds: bool
19
- _user_group_col: Optional[str] # = field(default=None, kw_only=True)
20
- _parent_id_col: Optional[str] # = field(default=None, kw_only=True)
21
- _connection_name: Optional[str] # = field(default=None, kw_only=True)
13
+ _id_col: str | None
14
+ _is_from_seeds: bool
15
+ _user_group_col: str | None
16
+ _parent_id_col: str | None
17
+ _connection_name: str | None
22
18
 
23
- @abstractmethod
19
+ @_abc.abstractmethod
24
20
  def __init__(
25
- self, table_or_query: str, *, id_col: Optional[str] = None, from_seeds: bool = False, user_group_col: Optional[str] = None,
26
- parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
21
+ self, table_or_query: str, *, id_col: str | None = None, from_seeds: bool = False, user_group_col: str | None = None,
22
+ parent_id_col: str | None = None, connection_name: str | None = None, **kwargs
27
23
  ) -> None:
28
24
  self._table_or_query = table_or_query
29
25
  self._id_col = id_col
30
- self._from_seeds = from_seeds
26
+ self._is_from_seeds = from_seeds
31
27
  self._user_group_col = user_group_col
32
28
  self._parent_id_col = parent_id_col
33
29
  self._connection_name = connection_name
34
30
 
35
- def _get_connection_name(self) -> str:
36
- if self._connection_name is None:
37
- return ManifestIO.obj.settings.get(c.DB_CONN_DEFAULT_USED_SETTING, c.DEFAULT_DB_CONN)
38
- return self._connection_name
31
+ def _get_connection_name(self, default_conn_name: str) -> str:
32
+ return self._connection_name if self._connection_name is not None else default_conn_name
39
33
 
40
34
  def _get_query(self) -> str:
41
35
  """
@@ -50,20 +44,20 @@ class DataSource(metaclass=ABCMeta):
50
44
  query = f'SELECT * FROM {self._table_or_query}'
51
45
  return query
52
46
 
53
- @abstractmethod
54
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.ParameterConfig:
47
+ @_abc.abstractmethod
48
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.ParameterConfig:
55
49
  """
56
50
  An abstract method for converting itself into a parameter
57
51
  """
58
52
  pass
59
53
 
60
- def _validate_parameter_type(self, ds_param: pc.DataSourceParameterConfig, target_parameter_type: Type[pc.ParameterConfig]) -> None:
54
+ def _validate_parameter_type(self, ds_param: _pc.DataSourceParameterConfig, target_parameter_type: _t.Type[_pc.ParameterConfig]) -> None:
61
55
  if ds_param.parameter_type != target_parameter_type:
62
56
  parameter_type_name = ds_param.parameter_type.__name__
63
57
  datasource_type_name = self.__class__.__name__
64
- raise u.ConfigurationError(f'Invalid widget type "{parameter_type_name}" for {datasource_type_name}')
58
+ raise _u.ConfigurationError(f'Invalid widget type "{parameter_type_name}" for {datasource_type_name}')
65
59
 
66
- def _get_aggregated_df(self, df: pd.DataFrame, columns_to_include: Iterable[str]) -> pd.DataFrame:
60
+ def _get_aggregated_df(self, df: _pd.DataFrame, columns_to_include: _t.Iterable[str]) -> _pd.DataFrame:
67
61
  agg_rules = {}
68
62
  for column in columns_to_include:
69
63
  if column is not None:
@@ -77,36 +71,33 @@ class DataSource(metaclass=ABCMeta):
77
71
  try:
78
72
  df_agg = df.groupby(groupby_dim).agg(agg_rules)
79
73
  except KeyError as e:
80
- raise u.ConfigurationError(e)
74
+ raise _u.ConfigurationError(e)
81
75
 
82
76
  return df_agg
83
77
 
84
- def _get_key_from_record(self, key: Optional[str], record: dict[str, Any], default: Any) -> Any:
78
+ def _get_key_from_record(self, key: str | None, record: dict[str, _t.Any], default: _t.Any) -> _t.Any:
85
79
  return record[key] if key is not None else default
86
80
 
87
- def _get_key_from_record_as_list(self, key: Optional[str], record: dict[str, Any]) -> Iterable[str]:
81
+ def _get_key_from_record_as_list(self, key: str | None, record: dict[str, _t.Any]) -> _t.Iterable[str]:
88
82
  value = self._get_key_from_record(key, record, list())
89
83
  return [str(x) for x in value]
90
-
91
- def is_from_seed(self) -> bool:
92
- return self._from_seeds
93
84
 
94
85
 
95
- @dataclass
86
+ @_d.dataclass
96
87
  class _SelectionDataSource(DataSource):
97
88
  """
98
89
  Abstract class for selection parameter data sources
99
90
  """
100
91
  _options_col: str
101
- _order_by_col: Optional[str] # = field(default=None, kw_only=True)
102
- _is_default_col: Optional[str] # = field(default=None, kw_only=True)
103
- _custom_cols: dict[str, str] # = field(default_factory=dict, kw_only=True)
92
+ _order_by_col: str | None
93
+ _is_default_col: str | None
94
+ _custom_cols: dict[str, str]
104
95
 
105
- @abstractmethod
96
+ @_abc.abstractmethod
106
97
  def __init__(
107
- self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
108
- is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, from_seeds: bool = False,
109
- user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None,
98
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: str | None = None,
99
+ is_default_col: str | None = None, custom_cols: dict[str, str] = {}, from_seeds: bool = False,
100
+ user_group_col: str | None = None, parent_id_col: str | None = None, connection_name: str | None = None,
110
101
  **kwargs
111
102
  ) -> None:
112
103
  super().__init__(
@@ -118,7 +109,7 @@ class _SelectionDataSource(DataSource):
118
109
  self._is_default_col = is_default_col
119
110
  self._custom_cols = custom_cols
120
111
 
121
- def _get_all_options(self, df: pd.DataFrame) -> Sequence[po.SelectParameterOption]:
112
+ def _get_all_options(self, df: _pd.DataFrame) -> _t.Sequence[_po.SelectParameterOption]:
122
113
  columns = [self._options_col, self._order_by_col, self._is_default_col, *self._custom_cols.values()]
123
114
  df_agg = self._get_aggregated_df(df, columns)
124
115
 
@@ -127,18 +118,18 @@ class _SelectionDataSource(DataSource):
127
118
  else:
128
119
  df_agg.sort_values(self._order_by_col, inplace=True)
129
120
 
130
- def get_is_default(record: dict[str, Any]) -> bool:
121
+ def get_is_default(record: dict[str, _t.Any]) -> bool:
131
122
  return int(record[self._is_default_col]) == 1 if self._is_default_col is not None else False
132
123
 
133
- def get_custom_fields(record: dict[str, Any]) -> dict[str, Any]:
124
+ def get_custom_fields(record: dict[str, _t.Any]) -> dict[str, _t.Any]:
134
125
  result = {}
135
126
  for key, val in self._custom_cols.items():
136
127
  result[key] = record[val]
137
128
  return result
138
129
 
139
- records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
130
+ records = df_agg.to_dict("index")
140
131
  return tuple(
141
- po.SelectParameterOption(str(id), str(record[self._options_col]),
132
+ _po.SelectParameterOption(str(id), str(record[self._options_col]),
142
133
  is_default=get_is_default(record), custom_fields=get_custom_fields(record),
143
134
  user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
144
135
  parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
@@ -146,46 +137,43 @@ class _SelectionDataSource(DataSource):
146
137
  )
147
138
 
148
139
 
149
- @dataclass
140
+ @_d.dataclass
150
141
  class SelectDataSource(_SelectionDataSource):
151
142
  """
152
143
  Lookup table for select parameter options
153
-
154
- Attributes:
155
- table_or_query: Either the name of the table to use, or a query to run
156
- id_col: The column name of the id
157
- options_col: The column name of the options
158
- order_by_col: The column name to order the options by. Orders by the id_col instead if this is None
159
- is_default_col: The column name that indicates which options are the default
160
- custom_cols: Dictionary of attribute to column name for custom fields for the SelectParameterOption
161
- from_seeds: Boolean for whether this datasource is created from seeds
162
- user_group_col: The column name of the user group that the user is in for this option to be valid
163
- parent_id_col: The column name of the parent option id that must be selected for this option to be valid
164
- connection_name: Name of the connection to use defined in connections.py
165
144
  """
166
145
 
167
146
  def __init__(
168
- self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
169
- is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, from_seeds: bool = False,
170
- user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None,
147
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: str | None = None,
148
+ is_default_col: str | None = None, custom_cols: dict[str, str] = {}, from_seeds: bool = False,
149
+ user_group_col: str | None = None, parent_id_col: str | None = None, connection_name: str | None = None,
171
150
  **kwargs
172
151
  ) -> None:
173
152
  """
174
153
  Constructor for SelectDataSource
175
154
 
176
- Parameters:
177
- ...see Attributes of SelectDataSource
155
+ Arguments:
156
+ table_or_query: Either the name of the table to use, or a query to run
157
+ id_col: The column name of the id
158
+ options_col: The column name of the options
159
+ order_by_col: The column name to order the options by. Orders by the id_col instead if this is None
160
+ is_default_col: The column name that indicates which options are the default
161
+ custom_cols: Dictionary of attribute to column name for custom fields for the SelectParameterOption
162
+ from_seeds: Boolean for whether this datasource is created from seeds
163
+ user_group_col: The column name of the user group that the user is in for this option to be valid
164
+ parent_id_col: The column name of the parent option id that must be selected for this option to be valid
165
+ connection_name: Name of the connection to use defined in connections.py
178
166
  """
179
167
  super().__init__(
180
168
  table_or_query, id_col, options_col, order_by_col=order_by_col, is_default_col=is_default_col, custom_cols=custom_cols,
181
169
  from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col, connection_name=connection_name
182
170
  )
183
171
 
184
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.SelectionParameterConfig:
172
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.SelectionParameterConfig:
185
173
  """
186
174
  Method to convert the associated DataSourceParameter into a SingleSelectParameterConfig or MultiSelectParameterConfig
187
175
 
188
- Parameters:
176
+ Arguments:
189
177
  ds_param: The parameter to convert
190
178
  df: The dataframe containing the parameter options data
191
179
 
@@ -193,77 +181,71 @@ class SelectDataSource(_SelectionDataSource):
193
181
  The converted parameter
194
182
  """
195
183
  all_options = self._get_all_options(df)
196
- if ds_param.parameter_type == pc.SingleSelectParameterConfig:
197
- return pc.SingleSelectParameterConfig(
184
+ if ds_param.parameter_type == _pc.SingleSelectParameterConfig:
185
+ return _pc.SingleSelectParameterConfig(
198
186
  ds_param.name, ds_param.label, all_options, description=ds_param.description,
199
187
  user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name, **ds_param.extra_args
200
188
  )
201
- elif ds_param.parameter_type == pc.MultiSelectParameterConfig:
202
- return pc.MultiSelectParameterConfig(
189
+ elif ds_param.parameter_type == _pc.MultiSelectParameterConfig:
190
+ return _pc.MultiSelectParameterConfig(
203
191
  ds_param.name, ds_param.label, all_options, description=ds_param.description,
204
192
  user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name, **ds_param.extra_args
205
193
  )
206
194
  else:
207
- raise u.ConfigurationError(f'Invalid widget type "{ds_param.parameter_type}" for SelectDataSource')
195
+ raise _u.ConfigurationError(f'Invalid widget type "{ds_param.parameter_type}" for SelectDataSource')
208
196
 
209
197
 
210
- @dataclass
198
+ @_d.dataclass
211
199
  class SingleSelectDataSource(_SelectionDataSource):
212
200
  """
213
201
  DEPRECATED. Use "SelectDataSource" instead.
214
202
  """
215
203
 
216
204
  def __init__(
217
- self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
218
- is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, user_group_col: Optional[str] = None,
219
- parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
205
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: str | None = None,
206
+ is_default_col: str | None = None, custom_cols: dict[str, str] = {}, user_group_col: str | None = None,
207
+ parent_id_col: str | None = None, connection_name: str | None = None, **kwargs
220
208
  ) -> None:
221
209
  """
222
- Constructor for SelectDataSource
223
-
224
- Parameters:
225
- ...see Attributes of SelectDataSource
210
+ DEPRECATED. Use "SelectDataSource" instead.
226
211
  """
227
212
  super().__init__(table_or_query, id_col, options_col, order_by_col=order_by_col, is_default_col=is_default_col,
228
213
  custom_cols=custom_cols, user_group_col=user_group_col, parent_id_col=parent_id_col,
229
214
  connection_name=connection_name)
230
215
 
231
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.SingleSelectParameterConfig:
216
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.SingleSelectParameterConfig:
232
217
  """
233
218
  Method to convert the associated DataSourceParameter into a SingleSelectParameterConfig
234
219
 
235
- Parameters:
220
+ Arguments:
236
221
  ds_param: The parameter to convert
237
222
  df: The dataframe containing the parameter options data
238
223
 
239
224
  Returns:
240
225
  The converted parameter
241
226
  """
242
- self._validate_parameter_type(ds_param, pc.SingleSelectParameterConfig)
227
+ self._validate_parameter_type(ds_param, _pc.SingleSelectParameterConfig)
243
228
  all_options = self._get_all_options(df)
244
- return pc.SingleSelectParameterConfig(ds_param.name, ds_param.label, all_options, description=ds_param.description,
229
+ return _pc.SingleSelectParameterConfig(ds_param.name, ds_param.label, all_options, description=ds_param.description,
245
230
  user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name)
246
231
 
247
- @dataclass
232
+ @_d.dataclass
248
233
  class MultiSelectDataSource(_SelectionDataSource):
249
234
  """
250
235
  DEPRECATED. Use "SelectDataSource" instead.
251
236
  """
252
- _show_select_all: bool # = field(default=True, kw_only=True)
253
- _order_matters: bool # = field(default=False, kw_only=True)
254
- _none_is_all: bool # = field(default=True, kw_only=True)
237
+ _show_select_all: bool
238
+ _order_matters: bool
239
+ _none_is_all: bool
255
240
 
256
241
  def __init__(
257
- self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: Optional[str] = None,
258
- is_default_col: Optional[str] = None, custom_cols: dict[str, str] = {}, show_select_all: bool = True,
259
- order_matters: bool = False, none_is_all: bool = True, user_group_col: Optional[str] = None,
260
- parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
242
+ self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: str | None = None,
243
+ is_default_col: str | None = None, custom_cols: dict[str, str] = {}, show_select_all: bool = True,
244
+ order_matters: bool = False, none_is_all: bool = True, user_group_col: str | None = None,
245
+ parent_id_col: str | None = None, connection_name: str | None = None, **kwargs
261
246
  ) -> None:
262
247
  """
263
- Constructor for SingleSelectDataSource
264
-
265
- Parameters:
266
- ...see Attributes of SingleSelectDataSource
248
+ DEPRECATED. Use "SelectDataSource" instead.
267
249
  """
268
250
  super().__init__(table_or_query, id_col, options_col, order_by_col=order_by_col, is_default_col=is_default_col,
269
251
  custom_cols=custom_cols, user_group_col=user_group_col, parent_id_col=parent_id_col,
@@ -272,121 +254,122 @@ class MultiSelectDataSource(_SelectionDataSource):
272
254
  self._order_matters = order_matters
273
255
  self._none_is_all = none_is_all
274
256
 
275
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.MultiSelectParameterConfig:
257
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.MultiSelectParameterConfig:
276
258
  """
277
259
  Method to convert the associated DataSourceParameter into a MultiSelectParameterConfig
278
260
 
279
- Parameters:
261
+ Arguments:
280
262
  ds_param: The parameter to convert
281
263
  df: The dataframe containing the parameter options data
282
264
 
283
265
  Returns:
284
266
  The converted parameter
285
267
  """
286
- self._validate_parameter_type(ds_param, pc.MultiSelectParameterConfig)
268
+ self._validate_parameter_type(ds_param, _pc.MultiSelectParameterConfig)
287
269
  all_options = self._get_all_options(df)
288
- return pc.MultiSelectParameterConfig(
270
+ return _pc.MultiSelectParameterConfig(
289
271
  ds_param.name, ds_param.label, all_options, show_select_all=self._show_select_all,
290
272
  order_matters=self._order_matters, none_is_all=self._none_is_all, description=ds_param.description,
291
273
  user_attribute=ds_param.user_attribute, parent_name=ds_param.parent_name
292
274
  )
293
275
 
294
276
 
295
- @dataclass
277
+ @_d.dataclass
296
278
  class DateDataSource(DataSource):
297
279
  """
298
280
  Lookup table for date parameter default options
299
-
300
- Attributes:
301
- table_or_query: Either the name of the table to use, or a query to run
302
- default_date_col: The column name of the default date
303
- date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
304
- id_col: The column name of the id
305
- from_seeds: Boolean for whether this datasource is created from seeds
306
- user_group_col: The column name of the user group that the user is in for this option to be valid
307
- parent_id_col: The column name of the parent option id that the default date belongs to
308
- connection_name: Name of the connection to use defined in connections.py
309
281
  """
310
282
  _default_date_col: str
311
- _date_format: str # = field(default="%Y-%m-%d", kw_only=True)
283
+ _date_format: str
312
284
 
313
285
  def __init__(
314
- self, table_or_query: str, default_date_col: str, *, date_format: str = '%Y-%m-%d', id_col: Optional[str] = None,
315
- from_seeds: bool = False, user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None,
316
- connection_name: Optional[str] = None, **kwargs
286
+ self, table_or_query: str, default_date_col: str, *, min_date_col: str | None = None,
287
+ max_date_col: str | None = None, date_format: str = '%Y-%m-%d', id_col: str | None = None,
288
+ from_seeds: bool = False, user_group_col: str | None = None, parent_id_col: str | None = None,
289
+ connection_name: str | None = None, **kwargs
317
290
  ) -> None:
318
291
  """
319
292
  Constructor for DateDataSource
320
293
 
321
- Parameters:
322
- ...see Attributes of DateDataSource
294
+ Arguments:
295
+ table_or_query: Either the name of the table to use, or a query to run
296
+ default_date_col: The column name of the default date
297
+ date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
298
+ id_col: The column name of the id
299
+ from_seeds: Boolean for whether this datasource is created from seeds
300
+ user_group_col: The column name of the user group that the user is in for this option to be valid
301
+ parent_id_col: The column name of the parent option id that the default date belongs to
302
+ connection_name: Name of the connection to use defined in connections.py
323
303
  """
324
304
  super().__init__(
325
305
  table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
326
306
  connection_name=connection_name
327
307
  )
328
308
  self._default_date_col = default_date_col
309
+ self._min_date_col = min_date_col
310
+ self._max_date_col = max_date_col
329
311
  self._date_format = date_format
330
312
 
331
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.DateParameterConfig:
313
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.DateParameterConfig:
332
314
  """
333
315
  Method to convert the associated DataSourceParameter into a DateParameterConfig
334
316
 
335
- Parameters:
317
+ Arguments:
336
318
  ds_param: The parameter to convert
337
319
  df: The dataframe containing the parameter options data
338
320
 
339
321
  Returns:
340
322
  The converted parameter
341
323
  """
342
- self._validate_parameter_type(ds_param, pc.DateParameterConfig)
324
+ self._validate_parameter_type(ds_param, _pc.DateParameterConfig)
343
325
 
344
- columns = [self._default_date_col]
326
+ columns = [self._default_date_col, self._min_date_col, self._max_date_col]
345
327
  df_agg = self._get_aggregated_df(df, columns)
346
328
 
347
- records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
329
+ records = df_agg.to_dict("index")
348
330
  options = tuple(
349
- po.DateParameterOption(str(record[self._default_date_col]), date_format=self._date_format,
350
- user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
351
- parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
331
+ _po.DateParameterOption(
332
+ str(record[self._default_date_col]), date_format=self._date_format,
333
+ min_date = str(record[self._min_date_col]) if self._min_date_col else None,
334
+ max_date = str(record[self._max_date_col]) if self._max_date_col else None,
335
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
336
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record)
337
+ )
352
338
  for _, record in records.items()
353
339
  )
354
- return pc.DateParameterConfig(
340
+ return _pc.DateParameterConfig(
355
341
  ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
356
342
  parent_name=ds_param.parent_name, **ds_param.extra_args
357
343
  )
358
344
 
359
345
 
360
- @dataclass
346
+ @_d.dataclass
361
347
  class DateRangeDataSource(DataSource):
362
348
  """
363
349
  Lookup table for date parameter default options
364
-
365
- Attributes:
366
- table_or_query: Either the name of the table to use, or a query to run
367
- default_start_date_col: The column name of the default start date
368
- default_end_date_col: The column name of the default end date
369
- date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
370
- id_col: The column name of the id
371
- from_seeds: Boolean for whether this datasource is created from seeds
372
- user_group_col: The column name of the user group that the user is in for this option to be valid
373
- parent_id_col: The column name of the parent option id that the default date belongs to
374
- connection_name: Name of the connection to use defined in connections.py
375
350
  """
376
351
  _default_start_date_col: str
377
352
  _default_end_date_col: str
378
- _date_format: str # = field(default="%Y-%m-%d", kw_only=True)
353
+ _date_format: str
379
354
 
380
355
  def __init__(
381
356
  self, table_or_query: str, default_start_date_col: str, default_end_date_col: str, *, date_format: str = '%Y-%m-%d',
382
- id_col: Optional[str] = None, from_seeds: bool = False, user_group_col: Optional[str] = None,
383
- parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
357
+ min_date_col: str | None = None, max_date_col: str | None = None, id_col: str | None = None, from_seeds: bool = False,
358
+ user_group_col: str | None = None, parent_id_col: str | None = None, connection_name: str | None = None, **kwargs
384
359
  ) -> None:
385
360
  """
386
361
  Constructor for DateRangeDataSource
387
362
 
388
- Parameters:
389
- ...see Attributes of DateRangeDataSource
363
+ Arguments:
364
+ table_or_query: Either the name of the table to use, or a query to run
365
+ default_start_date_col: The column name of the default start date
366
+ default_end_date_col: The column name of the default end date
367
+ date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
368
+ id_col: The column name of the id
369
+ from_seeds: Boolean for whether this datasource is created from seeds
370
+ user_group_col: The column name of the user group that the user is in for this option to be valid
371
+ parent_id_col: The column name of the parent option id that the default date belongs to
372
+ connection_name: Name of the connection to use defined in connections.py
390
373
  """
391
374
  super().__init__(
392
375
  table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
@@ -394,52 +377,58 @@ class DateRangeDataSource(DataSource):
394
377
  )
395
378
  self._default_start_date_col = default_start_date_col
396
379
  self._default_end_date_col = default_end_date_col
380
+ self._min_date_col = min_date_col
381
+ self._max_date_col = max_date_col
397
382
  self._date_format = date_format
398
383
 
399
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.DateRangeParameterConfig:
384
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.DateRangeParameterConfig:
400
385
  """
401
386
  Method to convert the associated DataSourceParameter into a DateRangeParameterConfig
402
387
 
403
- Parameters:
388
+ Arguments:
404
389
  ds_param: The parameter to convert
405
390
  df: The dataframe containing the parameter options data
406
391
 
407
392
  Returns:
408
393
  The converted parameter
409
394
  """
410
- self._validate_parameter_type(ds_param, pc.DateRangeParameterConfig)
395
+ self._validate_parameter_type(ds_param, _pc.DateRangeParameterConfig)
411
396
 
412
- columns = [self._default_start_date_col, self._default_end_date_col]
397
+ columns = [self._default_start_date_col, self._default_end_date_col, self._min_date_col, self._max_date_col]
413
398
  df_agg = self._get_aggregated_df(df, columns)
414
399
 
415
- records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
400
+ records = df_agg.to_dict("index")
416
401
  options = tuple(
417
- po.DateRangeParameterOption(str(record[self._default_start_date_col]), str(record[self._default_end_date_col]),
418
- date_format=self._date_format,
419
- user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
420
- parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
402
+ _po.DateRangeParameterOption(
403
+ str(record[self._default_start_date_col]), str(record[self._default_end_date_col]),
404
+ min_date=str(record[self._min_date_col]) if self._min_date_col else None,
405
+ max_date=str(record[self._max_date_col]) if self._max_date_col else None,
406
+ date_format=self._date_format,
407
+ user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
408
+ parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record)
409
+ )
421
410
  for _, record in records.items()
422
411
  )
423
- return pc.DateRangeParameterConfig(
412
+ return _pc.DateRangeParameterConfig(
424
413
  ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
425
414
  parent_name=ds_param.parent_name, **ds_param.extra_args
426
415
  )
427
416
 
428
417
 
429
- @dataclass
418
+ @_d.dataclass
430
419
  class _NumericDataSource(DataSource):
431
420
  """
432
421
  Abstract class for number or number range data sources
433
422
  """
434
423
  _min_value_col: str
435
424
  _max_value_col: str
436
- _increment_col: Optional[str] # = field(default=None, kw_only=True)
425
+ _increment_col: str | None
437
426
 
438
- @abstractmethod
427
+ @_abc.abstractmethod
439
428
  def __init__(
440
- self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: Optional[str] = None,
441
- id_col: Optional[str] = None, from_seeds: bool = False, user_group_col: Optional[str] = None,
442
- parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
429
+ self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: str | None = None,
430
+ id_col: str | None = None, from_seeds: bool = False, user_group_col: str | None = None,
431
+ parent_id_col: str | None = None, connection_name: str | None = None, **kwargs
443
432
  ) -> None:
444
433
  super().__init__(
445
434
  table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
@@ -450,35 +439,32 @@ class _NumericDataSource(DataSource):
450
439
  self._increment_col = increment_col
451
440
 
452
441
 
453
- @dataclass
442
+ @_d.dataclass
454
443
  class NumberDataSource(_NumericDataSource):
455
444
  """
456
445
  Lookup table for number parameter default options
457
-
458
- Attributes:
459
- table_or_query: Either the name of the table to use, or a query to run
460
- min_value_col: The column name of the minimum value
461
- max_value_col: The column name of the maximum value
462
- increment_col: The column name of the increment value. Defaults to column of 1's if None
463
- default_value_col: The column name of the default value. Defaults to min_value_col if None
464
- id_col: The column name of the id
465
- from_seeds: Boolean for whether this datasource is created from seeds
466
- user_group_col: The column name of the user group that the user is in for this option to be valid
467
- parent_id_col: The column name of the parent option id that the default value belongs to
468
- connection_name: Name of the connection to use defined in connections.py
469
446
  """
470
- _default_value_col: Optional[str] # = field(default=None, kw_only=True)
447
+ _default_value_col: str | None
471
448
 
472
449
  def __init__(
473
- self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: Optional[str] = None,
474
- default_value_col: Optional[str] = None, id_col: Optional[str] = None, from_seeds: bool = False,
475
- user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None, **kwargs
450
+ self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: str | None = None,
451
+ default_value_col: str | None = None, id_col: str | None = None, from_seeds: bool = False,
452
+ user_group_col: str | None = None, parent_id_col: str | None = None, connection_name: str | None = None, **kwargs
476
453
  ) -> None:
477
454
  """
478
455
  Constructor for NumberDataSource
479
456
 
480
- Parameters:
481
- ...see Attributes of NumberDataSource
457
+ Arguments:
458
+ table_or_query: Either the name of the table to use, or a query to run
459
+ min_value_col: The column name of the minimum value
460
+ max_value_col: The column name of the maximum value
461
+ increment_col: The column name of the increment value. Defaults to column of 1's if None
462
+ default_value_col: The column name of the default value. Defaults to min_value_col if None
463
+ id_col: The column name of the id
464
+ from_seeds: Boolean for whether this datasource is created from seeds
465
+ user_group_col: The column name of the user group that the user is in for this option to be valid
466
+ parent_id_col: The column name of the parent option id that the default value belongs to
467
+ connection_name: Name of the connection to use defined in connections.py
482
468
  """
483
469
  super().__init__(
484
470
  table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col, from_seeds=from_seeds,
@@ -486,69 +472,66 @@ class NumberDataSource(_NumericDataSource):
486
472
  )
487
473
  self._default_value_col = default_value_col
488
474
 
489
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.NumberParameterConfig:
475
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.NumberParameterConfig:
490
476
  """
491
477
  Method to convert the associated DataSourceParameter into a NumberParameterConfig
492
478
 
493
- Parameters:
479
+ Arguments:
494
480
  ds_param: The parameter to convert
495
481
  df: The dataframe containing the parameter options data
496
482
 
497
483
  Returns:
498
484
  The converted parameter
499
485
  """
500
- self._validate_parameter_type(ds_param, pc.NumberParameterConfig)
486
+ self._validate_parameter_type(ds_param, _pc.NumberParameterConfig)
501
487
 
502
488
  columns = [self._min_value_col, self._max_value_col, self._increment_col, self._default_value_col]
503
489
  df_agg = self._get_aggregated_df(df, columns)
504
490
 
505
- records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
491
+ records = df_agg.to_dict("index")
506
492
  options = tuple(
507
- po.NumberParameterOption(record[self._min_value_col], record[self._max_value_col],
493
+ _po.NumberParameterOption(record[self._min_value_col], record[self._max_value_col],
508
494
  increment=self._get_key_from_record(self._increment_col, record, 1),
509
495
  default_value=self._get_key_from_record(self._default_value_col, record, None),
510
496
  user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
511
497
  parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
512
498
  for _, record in records.items()
513
499
  )
514
- return pc.NumberParameterConfig(
500
+ return _pc.NumberParameterConfig(
515
501
  ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
516
502
  parent_name=ds_param.parent_name, **ds_param.extra_args
517
503
  )
518
504
 
519
505
 
520
- @dataclass
506
+ @_d.dataclass
521
507
  class NumberRangeDataSource(_NumericDataSource):
522
508
  """
523
509
  Lookup table for number range parameter default options
524
-
525
- Attributes:
526
- table_or_query: Either the name of the table to use, or a query to
527
- min_value_col: The column name of the minimum value
528
- max_value_col: The column name of the maximum value
529
- increment_col: The column name of the increment value. Defaults to column of 1's if None
530
- default_lower_value_col: The column name of the default lower value. Defaults to min_value_col if None
531
- default_upper_value_col: The column name of the default upper value. Defaults to max_value_col if None
532
- id_col: The column name of the id
533
- from_seeds: Boolean for whether this datasource is created from seeds
534
- user_group_col: The column name of the user group that the user is in for this option to be valid
535
- parent_id_col: The column name of the parent option id that the default value belongs to
536
- connection_name: Name of the connection to use defined in connections.py
537
510
  """
538
- _default_lower_value_col: Optional[str] # = field(default=None, kw_only=True)
539
- _default_upper_value_col: Optional[str] # = field(default=None, kw_only=True)
511
+ _default_lower_value_col: str | None
512
+ _default_upper_value_col: str | None
540
513
 
541
514
  def __init__(
542
- self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: Optional[str] = None,
543
- default_lower_value_col: Optional[str] = None, default_upper_value_col: Optional[str] = None, id_col: Optional[str] = None,
544
- from_seeds: bool = False, user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None,
545
- connection_name: Optional[str] = None, **kwargs
515
+ self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: str | None = None,
516
+ default_lower_value_col: str | None = None, default_upper_value_col: str | None = None, id_col: str | None = None,
517
+ from_seeds: bool = False, user_group_col: str | None = None, parent_id_col: str | None = None,
518
+ connection_name: str | None = None, **kwargs
546
519
  ) -> None:
547
520
  """
548
521
  Constructor for NumRangeDataSource
549
522
 
550
- Parameters:
551
- ...see Attributes of NumRangeDataSource
523
+ Arguments:
524
+ table_or_query: Either the name of the table to use, or a query to
525
+ min_value_col: The column name of the minimum value
526
+ max_value_col: The column name of the maximum value
527
+ increment_col: The column name of the increment value. Defaults to column of 1's if None
528
+ default_lower_value_col: The column name of the default lower value. Defaults to min_value_col if None
529
+ default_upper_value_col: The column name of the default upper value. Defaults to max_value_col if None
530
+ id_col: The column name of the id
531
+ from_seeds: Boolean for whether this datasource is created from seeds
532
+ user_group_col: The column name of the user group that the user is in for this option to be valid
533
+ parent_id_col: The column name of the parent option id that the default value belongs to
534
+ connection_name: Name of the connection to use defined in connections.py
552
535
  """
553
536
  super().__init__(
554
537
  table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col, from_seeds=from_seeds,
@@ -557,25 +540,25 @@ class NumberRangeDataSource(_NumericDataSource):
557
540
  self._default_lower_value_col = default_lower_value_col
558
541
  self._default_upper_value_col = default_upper_value_col
559
542
 
560
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.NumberRangeParameterConfig:
543
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.NumberRangeParameterConfig:
561
544
  """
562
545
  Method to convert the associated DataSourceParameter into a NumberRangeParameterConfig
563
546
 
564
- Parameters:
547
+ Arguments:
565
548
  ds_param: The parameter to convert
566
549
  df: The dataframe containing the parameter options data
567
550
 
568
551
  Returns:
569
552
  The converted parameter
570
553
  """
571
- self._validate_parameter_type(ds_param, pc.NumberRangeParameterConfig)
554
+ self._validate_parameter_type(ds_param, _pc.NumberRangeParameterConfig)
572
555
 
573
556
  columns = [self._min_value_col, self._max_value_col, self._increment_col, self._default_lower_value_col, self._default_upper_value_col]
574
557
  df_agg = self._get_aggregated_df(df, columns)
575
558
 
576
- records: dict[str, Any] = df_agg.to_dict("index")
559
+ records = df_agg.to_dict("index")
577
560
  options = tuple(
578
- po.NumberRangeParameterOption(record[self._min_value_col], record[self._max_value_col],
561
+ _po.NumberRangeParameterOption(record[self._min_value_col], record[self._max_value_col],
579
562
  increment=self._get_key_from_record(self._increment_col, record, 1),
580
563
  default_lower_value=self._get_key_from_record(self._default_lower_value_col, record, None),
581
564
  default_upper_value=self._get_key_from_record(self._default_upper_value_col, record, None),
@@ -583,38 +566,35 @@ class NumberRangeDataSource(_NumericDataSource):
583
566
  parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
584
567
  for _, record in records.items()
585
568
  )
586
- return pc.NumberRangeParameterConfig(
569
+ return _pc.NumberRangeParameterConfig(
587
570
  ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
588
571
  parent_name=ds_param.parent_name, **ds_param.extra_args
589
572
  )
590
573
 
591
574
 
592
- @dataclass
575
+ @_d.dataclass
593
576
  class TextDataSource(DataSource):
594
577
  """
595
578
  Lookup table for text parameter default options
596
-
597
- Attributes:
598
- table_or_query: Either the name of the table to use, or a query to run
599
- default_text_col: The column name of the default text
600
- id_col: The column name of the id
601
- from_seeds: Boolean for whether this datasource is created from seeds
602
- user_group_col: The column name of the user group that the user is in for this option to be valid
603
- parent_id_col: The column name of the parent option id that the default date belongs to
604
- connection_name: Name of the connection to use defined in connections.py
605
579
  """
606
580
  _default_text_col: str
607
581
 
608
582
  def __init__(
609
- self, table_or_query: str, default_text_col: str, *, id_col: Optional[str] = None, from_seeds: bool = False,
610
- user_group_col: Optional[str] = None, parent_id_col: Optional[str] = None, connection_name: Optional[str] = None,
583
+ self, table_or_query: str, default_text_col: str, *, id_col: str | None = None, from_seeds: bool = False,
584
+ user_group_col: str | None = None, parent_id_col: str | None = None, connection_name: str | None = None,
611
585
  **kwargs
612
586
  ) -> None:
613
587
  """
614
- Constructor for DateDataSource
588
+ Constructor for TextDataSource
615
589
 
616
- Parameters:
617
- ...see Attributes of DateDataSource
590
+ Arguments:
591
+ table_or_query: Either the name of the table to use, or a query to run
592
+ default_text_col: The column name of the default text
593
+ id_col: The column name of the id
594
+ from_seeds: Boolean for whether this datasource is created from seeds
595
+ user_group_col: The column name of the user group that the user is in for this option to be valid
596
+ parent_id_col: The column name of the parent option id that the default date belongs to
597
+ connection_name: Name of the connection to use defined in connections.py
618
598
  """
619
599
  super().__init__(
620
600
  table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
@@ -622,30 +602,30 @@ class TextDataSource(DataSource):
622
602
  )
623
603
  self._default_text_col = default_text_col
624
604
 
625
- def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pd.DataFrame) -> pc.TextParameterConfig:
605
+ def _convert(self, ds_param: _pc.DataSourceParameterConfig, df: _pd.DataFrame) -> _pc.TextParameterConfig:
626
606
  """
627
607
  Method to convert the associated DataSourceParameter into a TextParameterConfig
628
608
 
629
- Parameters:
609
+ Arguments:
630
610
  ds_param: The parameter to convert
631
611
  df: The dataframe containing the parameter options data
632
612
 
633
613
  Returns:
634
614
  The converted parameter
635
615
  """
636
- self._validate_parameter_type(ds_param, pc.TextParameterConfig)
616
+ self._validate_parameter_type(ds_param, _pc.TextParameterConfig)
637
617
 
638
618
  columns = [self._default_text_col]
639
619
  df_agg = self._get_aggregated_df(df, columns)
640
620
 
641
- records: dict[str, dict[str, Any]] = df_agg.to_dict("index")
621
+ records = df_agg.to_dict("index")
642
622
  options = tuple(
643
- po.TextParameterOption(default_text=str(record[self._default_text_col]),
623
+ _po.TextParameterOption(default_text=str(record[self._default_text_col]),
644
624
  user_groups=self._get_key_from_record_as_list(self._user_group_col, record),
645
625
  parent_option_ids=self._get_key_from_record_as_list(self._parent_id_col, record))
646
626
  for _, record in records.items()
647
627
  )
648
- return pc.TextParameterConfig(
628
+ return _pc.TextParameterConfig(
649
629
  ds_param.name, ds_param.label, options, description=ds_param.description, user_attribute=ds_param.user_attribute,
650
630
  parent_name=ds_param.parent_name, **ds_param.extra_args
651
631
  )