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
@@ -0,0 +1,1703 @@
1
+ from __future__ import annotations
2
+ from typing import Callable, Type, TypeVar, Sequence, Generic, Any
3
+ from dataclasses import dataclass
4
+ from datetime import datetime, date
5
+ from decimal import Decimal
6
+ from abc import ABCMeta, abstractmethod
7
+
8
+ from ._arguments.init_time_args import ParametersArgs
9
+ from ._schemas import response_models as rm
10
+ from . import _data_sources as d, _parameter_configs as pc, _parameter_options as po, _parameter_sets as ps
11
+ from . import _utils as u
12
+
13
+ IntOrFloat = TypeVar("IntOrFloat", int, float)
14
+
15
+ PC = TypeVar("PC", bound=pc.ParameterConfig)
16
+ PO = TypeVar("PO", bound=po.ParameterOption)
17
+ DS = TypeVar("DS", bound=d.DataSource)
18
+
19
+ @dataclass
20
+ class Parameter(Generic[PC, PO, DS], metaclass=ABCMeta):
21
+ """
22
+ Abstract class for all parameter widgets
23
+ """
24
+ _config: PC
25
+
26
+ @abstractmethod
27
+ def is_enabled(self) -> bool:
28
+ return True
29
+
30
+ @staticmethod
31
+ @abstractmethod
32
+ def _ParameterConfigType() -> Type[PC]: # Gets the actual type of the ParameterConfig TypeVar at runtime
33
+ pass
34
+
35
+ @staticmethod
36
+ @abstractmethod
37
+ def _ParameterOptionType() -> Type[PO]: # Gets the actual type of the ParameterOption TypeVar at runtime
38
+ pass
39
+
40
+ @staticmethod
41
+ @abstractmethod
42
+ def _DataSourceType() -> Type[DS]: # Gets the actual type of the DataSource TypeVar at runtime
43
+ pass
44
+
45
+ @classmethod
46
+ def CreateWithOptions(
47
+ cls, name: str, label: str, all_options: Sequence[PO | dict], *, description: str = "",
48
+ user_attribute: str | None = None, parent_name: str | None = None, **kwargs
49
+ ) -> PC:
50
+ """
51
+ Method for creating the configurations for a Parameter that may include user attribute or parent
52
+
53
+ .. deprecated::
54
+ Use the lowercase decorator form `create_with_options` instead.
55
+
56
+ Arguments:
57
+ name: The name of the parameter
58
+ label: The display label for the parameter
59
+ all_options: All options associated to this parameter regardless of the user group or parent parameter option they depend on
60
+ description: Explains the meaning of the parameter
61
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
62
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
63
+ """
64
+ param_option_type = cls._ParameterOptionType()
65
+ if not isinstance(all_options, Sequence) or not all(isinstance(x, (param_option_type, dict)) for x in all_options):
66
+ raise u.ConfigurationError(f"The parameter must take a sequence of {param_option_type.__name__} objects")
67
+
68
+ param_config_type = cls._ParameterConfigType()
69
+ param_config = param_config_type(
70
+ name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name, **kwargs
71
+ )
72
+ return param_config
73
+
74
+ @classmethod
75
+ def create_with_options(
76
+ cls, name: str, label: str, *, description: str = "", user_attribute: str | None = None, parent_name: str | None = None
77
+ ):
78
+ """
79
+ Python decorator for creating the configurations for a Parameter that may include user attribute or parent.
80
+
81
+ The decorated function must return a list of ParameterOption objects.
82
+
83
+ Arguments:
84
+ name: The name of the parameter
85
+ label: The display label for the parameter
86
+ description: Explains the meaning of the parameter
87
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
88
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
89
+ """
90
+ def decorator(func: Callable[..., Sequence[PO]]):
91
+ def wrapper(sqrl: ParametersArgs):
92
+ options = u.call_func(func, sqrl=sqrl)
93
+ return cls.CreateWithOptions(
94
+ name, label, options, description=description,
95
+ user_attribute=user_attribute, parent_name=parent_name
96
+ )
97
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
98
+ return wrapper
99
+ return decorator
100
+
101
+ @classmethod
102
+ @abstractmethod
103
+ def CreateSimple(cls, name: str, label: str, *args, description: str = "", **kwargs) -> None:
104
+ pass
105
+
106
+ @classmethod
107
+ @abstractmethod
108
+ def create_simple(cls, name: str, label: str, *args, description: str = "", **kwargs) -> None:
109
+ pass
110
+
111
+ @classmethod
112
+ def _CreateFromSourceHelper(
113
+ cls, name: str, label: str, data_source: DS | dict, *, extra_args: dict = {}, description: str = "",
114
+ user_attribute: str | None = None, parent_name: str | None = None
115
+ ):
116
+ data_source_type = cls._DataSourceType()
117
+ if not isinstance(data_source, (data_source_type, dict)):
118
+ raise u.ConfigurationError(f"The data source must be a {data_source_type.__name__} object")
119
+
120
+ param_config = pc.DataSourceParameterConfig(
121
+ cls._ParameterConfigType(), name, label, data_source, description=description, user_attribute=user_attribute,
122
+ parent_name=parent_name, extra_args=extra_args
123
+ )
124
+ return param_config
125
+
126
+ @classmethod
127
+ def CreateFromSource(
128
+ cls, name: str, label: str, data_source: DS | dict, *, description: str = "",
129
+ user_attribute: str | None = None, parent_name: str | None = None, **kwargs
130
+ ):
131
+ """
132
+ Method for creating the configurations for any Parameter that uses a DataSource to receive the options
133
+
134
+ .. deprecated::
135
+ Use the lowercase decorator form `create_from_source` instead.
136
+
137
+ Arguments:
138
+ name: The name of the parameter
139
+ label: The display label for the parameter
140
+ data_source: The lookup table to use for this parameter
141
+ description: Explains the meaning of the parameter
142
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
143
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
144
+ """
145
+ return cls._CreateFromSourceHelper(name, label, data_source, description=description, user_attribute=user_attribute, parent_name=parent_name)
146
+
147
+ @classmethod
148
+ def create_from_source(
149
+ cls, name: str, label: str, *, description: str = "", user_attribute: str | None = None, parent_name: str | None = None
150
+ ):
151
+ """
152
+ Python decorator for creating the configurations for a Parameter that uses a DataSource to receive the options from a lookup table
153
+
154
+ The decorated function must return a DataSource object.
155
+
156
+ Arguments:
157
+ name: The name of the parameter
158
+ label: The display label for the parameter
159
+ description: Explains the meaning of the parameter
160
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
161
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
162
+ """
163
+ def decorator(func: Callable[..., DS]):
164
+ def wrapper(sqrl: ParametersArgs):
165
+ data_source = u.call_func(func, sqrl=sqrl)
166
+ return cls.CreateFromSource(
167
+ name, label, data_source, description=description,
168
+ user_attribute=user_attribute, parent_name=parent_name
169
+ )
170
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
171
+ return wrapper
172
+ return decorator
173
+
174
+ def _enquote(self, value: str) -> str:
175
+ return "'" + value.replace("'", "''") + "'"
176
+
177
+ def _validate_input_date(self, input_date: date | str, curr_option: po._DateTypeParameterOption) -> date:
178
+ if isinstance(input_date, str):
179
+ try:
180
+ input_date = datetime.strptime(input_date.strip(), "%Y-%m-%d").date()
181
+ except ValueError:
182
+ raise self._config._invalid_input_error(str(input_date), "Must be a date in YYYY-MM-DD format.")
183
+
184
+ try:
185
+ return curr_option._validate_date(input_date)
186
+ except u.ConfigurationError as e:
187
+ raise self._config._invalid_input_error(str(input_date), str(e))
188
+
189
+ def _validate_number(self, input_number: po.Number, curr_option: po._NumericParameterOption) -> Decimal:
190
+ try:
191
+ return curr_option._validate_value(input_number)
192
+ except u.ConfigurationError as e:
193
+ raise self._config._invalid_input_error(str(input_number), str(e))
194
+
195
+ @abstractmethod
196
+ def _to_json_dict0(self) -> dict:
197
+ """
198
+ Helper method to convert the derived Parameter class into a JSON dictionary
199
+ """
200
+ output = {
201
+ "widget_type": self._config.widget_type(), "name": self._config.name,
202
+ "label": self._config.label, "description": self._config.description
203
+ }
204
+ if not self.is_enabled():
205
+ output["widget_type"] = "disabled"
206
+ return output
207
+
208
+ @abstractmethod
209
+ def _get_response_model0(self) -> type[rm.ParameterModelBase]:
210
+ pass
211
+
212
+ def _to_api_response_model0(self) -> rm.ParameterModelBase:
213
+ return self._get_response_model0().model_validate(self._to_json_dict0())
214
+
215
+
216
+ SelectionPC = TypeVar("SelectionPC", bound=pc.SelectionParameterConfig)
217
+
218
+ @dataclass
219
+ class _SelectionParameter(Parameter[SelectionPC, po.SelectParameterOption, d.SelectDataSource], Generic[SelectionPC]):
220
+ _options: Sequence[po.SelectParameterOption]
221
+
222
+ def __post_init__(self):
223
+ self._options = tuple(self._options)
224
+
225
+ def is_enabled(self) -> bool:
226
+ return len(self._options) > 0
227
+
228
+ @abstractmethod
229
+ def _get_selected_ids_as_list(self) -> Sequence[str]:
230
+ pass
231
+
232
+ def _validate_selected_id_in_options(self, selected_id):
233
+ if selected_id not in (x._identifier for x in self._options):
234
+ raise self._config._invalid_input_error(selected_id, f"The selected id {selected_id} does not exist in available options.")
235
+
236
+ @abstractmethod
237
+ def _to_json_dict0(self) -> dict:
238
+ """
239
+ Helper method to convert the derived selection parameter class into a JSON object
240
+ """
241
+ output = super()._to_json_dict0()
242
+ output['trigger_refresh'] = self._config.trigger_refresh
243
+ output['options'] = [x._to_json_dict() for x in self._options]
244
+ return output
245
+
246
+
247
+ @dataclass
248
+ class SingleSelectParameter(_SelectionParameter[pc.SingleSelectParameterConfig]):
249
+ """
250
+ Class for single-select parameter widgets.
251
+
252
+ Attributes:
253
+ config: The config for this widget parameter (for immutable attributes like name, label, all possible options, etc)
254
+ options: The parameter options that are currently selectable
255
+ selected_id: The ID of the selected option
256
+ """
257
+ _selected_id: str | None
258
+
259
+ def __post_init__(self):
260
+ super().__post_init__()
261
+ if len(self._options) > 0:
262
+ assert self._selected_id != None
263
+ self._validate_selected_id_in_options(self._selected_id)
264
+ else:
265
+ self._selected_id = None
266
+
267
+ @staticmethod
268
+ def _ParameterConfigType():
269
+ return pc.SingleSelectParameterConfig
270
+
271
+ @staticmethod
272
+ def _ParameterOptionType():
273
+ return po.SelectParameterOption
274
+
275
+ @staticmethod
276
+ def _DataSourceType():
277
+ return d.SelectDataSource
278
+
279
+ @classmethod
280
+ def CreateSimple(
281
+ cls, name: str, label: str, all_options: Sequence[po.SelectParameterOption | dict], *, description: str = "", **kwargs
282
+ ):
283
+ """
284
+ Method for creating the configurations for a SingleSelectParameter that doesn't involve user attributes or parent parameters
285
+
286
+ .. deprecated::
287
+ Use the lowercase decorator form `create_simple` instead.
288
+
289
+ Arguments:
290
+ name: The name of the parameter
291
+ label: The display label for the parameter
292
+ all_options: All options associated to this parameter regardless of the user group or parent parameter option they depend on
293
+ description: Explains the meaning of the parameter
294
+ """
295
+ return cls.CreateWithOptions(name, label, all_options, description=description)
296
+
297
+ @classmethod
298
+ def create_simple(cls, name: str, label: str, *, description: str = ""):
299
+ """
300
+ Python decorator for creating the configurations for a SingleSelectParameter that doesn't involve user attributes or parent parameters
301
+
302
+ The decorated function must return a list of SelectParameterOption objects.
303
+
304
+ Arguments:
305
+ name: The name of the parameter
306
+ label: The display label for the parameter
307
+ description: Explains the meaning of the parameter
308
+ """
309
+ def decorator(func: Callable[..., Sequence[po.SelectParameterOption]]):
310
+ def wrapper(sqrl: ParametersArgs):
311
+ options = u.call_func(func, sqrl=sqrl)
312
+ return cls.CreateSimple(name, label, options, description=description)
313
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
314
+ return wrapper
315
+ return decorator
316
+
317
+ def get_selected(
318
+ self, field: str | None = None, *, default_field: str | None = None, default: Any = None, **kwargs
319
+ ) -> po.SelectParameterOption | Any | None:
320
+ """
321
+ Gets the selected single-select option or selected custom field
322
+
323
+ Arguments:
324
+ field: If field is not None, the method gets this field from the "custom_fields" attribute of the selected option.
325
+ Otherwise, returns the class object of the selected option
326
+ default_field: If field does not exist for a parameter option and default_field is not None, the default_field is used
327
+ as the "field" instead. Does nothing if field is None
328
+ default: If field does not exist for a parameter option, default_field is None, but default is not None, then the default
329
+ is returned as the selected field. Does nothing if field is None or default_field is not None
330
+
331
+ Returns:
332
+ A SelectParameterOption class object if no field is provided, or the type of the custom field
333
+ """
334
+ def get_selected_from_id(identifier: str):
335
+ selected = next(x for x in self._options if x._identifier == identifier)
336
+ if field is not None:
337
+ selected = selected.get_custom_field(field, default_field=default_field, default=default)
338
+ return selected
339
+ return u.process_if_not_none(self._selected_id, get_selected_from_id)
340
+
341
+ def get_selected_quoted(self, field: str, *, default_field: str | None = None, default: str | None = None, **kwargs) -> str | None:
342
+ """
343
+ Gets the selected single-select option surrounded by single quotes
344
+
345
+ Arguments:
346
+ field: The "custom_fields" attribute of the selected option.
347
+ default_field: If field does not exist for a parameter option and default_field is not None, the default_field is used
348
+ as the "field" instead.
349
+ default: If field does not exist for a parameter option, default_field is None, but default is not None, then the default
350
+ is returned as the selected field. Does nothing if default_field is not None
351
+
352
+ Returns:
353
+ A string surrounded by single quotes
354
+ """
355
+ selected_value = self.get_selected(field, default_field=default_field, default=default)
356
+
357
+ def _enquote(x: Any) -> str:
358
+ if not isinstance(selected_value, str):
359
+ raise u.ConfigurationError(
360
+ f"Method 'get_selected_quoted' can only be used on fields with only string values"
361
+ )
362
+ return self._enquote(x)
363
+
364
+ return u.process_if_not_none(selected_value, _enquote)
365
+
366
+ def get_selected_id(self, **kwargs) -> str | None:
367
+ """
368
+ Gets the ID of the selected option
369
+
370
+ Returns:
371
+ A string ID or None if there are no selectable options
372
+ """
373
+ def get_id(x: po.SelectParameterOption):
374
+ return x._identifier
375
+ return u.process_if_not_none(self.get_selected(), get_id)
376
+
377
+ def get_selected_id_quoted(self, **kwargs) -> str | None:
378
+ """
379
+ Gets the ID of the selected option surrounded by single quotes
380
+
381
+ Returns:
382
+ A string or None if there are no selectable options
383
+ """
384
+ return u.process_if_not_none(self.get_selected_id(), self._enquote)
385
+
386
+ def get_selected_label(self, **kwargs) -> str | None:
387
+ """
388
+ Gets the label of the selected option
389
+
390
+ Returns:
391
+ A string or None if there are no selectable options
392
+ """
393
+ def get_label(x: po.SelectParameterOption): return x._label
394
+ return u.process_if_not_none(self.get_selected(), get_label)
395
+
396
+ def get_selected_label_quoted(self, **kwargs) -> str | None:
397
+ """
398
+ Gets the label of the selected option surrounded by single quotes
399
+
400
+ Returns:
401
+ A string or None if there are no selectable options
402
+ """
403
+ return u.process_if_not_none(self.get_selected_label(), self._enquote)
404
+
405
+ def _get_selected_ids_as_list(self) -> Sequence[str]:
406
+ selected_id = self.get_selected_id()
407
+ if selected_id is not None:
408
+ return (selected_id,)
409
+ else:
410
+ return tuple()
411
+
412
+ def _to_json_dict0(self) -> dict:
413
+ """
414
+ Converts this parameter as a JSON object for the parameters API response
415
+
416
+ Returns:
417
+ A dictionary for the JSON object
418
+ """
419
+ output = super()._to_json_dict0()
420
+ output['selected_id'] = self._selected_id
421
+ return output
422
+
423
+ def _get_response_model0(self):
424
+ return rm.SingleSelectParameterModel if self.is_enabled() else rm.NoneParameterModel
425
+
426
+
427
+ @dataclass
428
+ class MultiSelectParameter(_SelectionParameter[pc.MultiSelectParameterConfig]):
429
+ """
430
+ Class for multi-select parameter widgets.
431
+
432
+ Attributes:
433
+ config: The config for this widget parameter (for immutable attributes like name, label, all possible options, etc)
434
+ options: The parameter options that are currently selectable
435
+ selected_ids: A sequence of IDs of the selected options
436
+ """
437
+ _selected_ids: Sequence[str]
438
+
439
+ def __post_init__(self):
440
+ super().__post_init__()
441
+ self._selected_ids = tuple(self._selected_ids)
442
+ for selected_id in self._selected_ids:
443
+ self._validate_selected_id_in_options(selected_id)
444
+
445
+ @staticmethod
446
+ def _ParameterConfigType():
447
+ return pc.MultiSelectParameterConfig
448
+
449
+ @staticmethod
450
+ def _ParameterOptionType():
451
+ return po.SelectParameterOption
452
+
453
+ @staticmethod
454
+ def _DataSourceType():
455
+ return d.SelectDataSource
456
+
457
+ @classmethod
458
+ def CreateWithOptions(
459
+ cls, name: str, label: str, all_options: Sequence[po.SelectParameterOption | dict], *, description: str = "",
460
+ show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True,
461
+ user_attribute: str | None = None, parent_name: str | None = None, **kwargs
462
+ ):
463
+ """
464
+ Method for creating the configurations for a MultiSelectParameter that may include user attribute or parent
465
+
466
+ .. deprecated::
467
+ Use the lowercase decorator form `create_with_options` instead.
468
+
469
+ Arguments:
470
+ name: The name of the parameter
471
+ label: The display label for the parameter
472
+ all_options: All options associated to this parameter regardless of the user group or parent parameter option they depend on
473
+ description: Explains the meaning of the parameter
474
+ show_select_all: Communicate to front-end whether to include a "select all" option
475
+ order_matters: Communicate to front-end whether the order of the selections made matter
476
+ none_is_all: Whether having no options selected is equivalent to all selectable options selected
477
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
478
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
479
+ """
480
+ return super().CreateWithOptions(
481
+ name, label, all_options, description=description, user_attribute=user_attribute, parent_name=parent_name,
482
+ show_select_all=show_select_all, order_matters=order_matters, none_is_all=none_is_all
483
+ )
484
+
485
+ @classmethod
486
+ def create_with_options(
487
+ cls, name: str, label: str, *, description: str = "", show_select_all: bool = True, order_matters: bool = False,
488
+ none_is_all: bool = True, user_attribute: str | None = None, parent_name: str | None = None
489
+ ):
490
+ """
491
+ Python decorator for creating the configurations for a MultiSelectParameter that may include user attribute or parent
492
+
493
+ The decorated function must return a list of SelectParameterOption objects.
494
+
495
+ Arguments:
496
+ name: The name of the parameter
497
+ label: The display label for the parameter
498
+ description: Explains the meaning of the parameter
499
+ show_select_all: Communicate to front-end whether to include a "select all" option
500
+ order_matters: Communicate to front-end whether the order of the selections made matter
501
+ none_is_all: Whether having no options selected is equivalent to all selectable options selected
502
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
503
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
504
+ """
505
+ def decorator(func: Callable[..., Sequence[po.SelectParameterOption]]):
506
+ def wrapper(sqrl: ParametersArgs):
507
+ options = u.call_func(func, sqrl=sqrl)
508
+ return cls.CreateWithOptions(
509
+ name, label, options, description=description, user_attribute=user_attribute, parent_name=parent_name,
510
+ show_select_all=show_select_all, order_matters=order_matters, none_is_all=none_is_all
511
+ )
512
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
513
+ return wrapper
514
+ return decorator
515
+
516
+ @classmethod
517
+ def CreateSimple(
518
+ cls, name: str, label: str, all_options: Sequence[po.SelectParameterOption], *, description: str = "",
519
+ show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True, **kwargs
520
+ ):
521
+ """
522
+ Method for creating the configurations for a MultiSelectParameter that doesn't involve user attributes or parent parameters
523
+
524
+ .. deprecated::
525
+ Use the lowercase decorator form `create_simple` instead.
526
+
527
+ Arguments:
528
+ name: The name of the parameter
529
+ label: The display label for the parameter
530
+ all_options: All options associated to this parameter regardless of the user group or parent parameter option they depend on
531
+ description: Explains the meaning of the parameter
532
+ show_select_all: Communicate to front-end whether to include a "select all" option
533
+ order_matters: Communicate to front-end whether the order of the selections made matter
534
+ none_is_all: Whether having no options selected is equivalent to all selectable options selected
535
+ """
536
+ return cls.CreateWithOptions(
537
+ name, label, all_options, description=description,
538
+ show_select_all=show_select_all, order_matters=order_matters, none_is_all=none_is_all
539
+ )
540
+
541
+ @classmethod
542
+ def create_simple(
543
+ cls, name: str, label: str, *, description: str = "",
544
+ show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True
545
+ ):
546
+ """
547
+ Python decorator for creating the configurations for a MultiSelectParameter that doesn't involve user attributes or parent parameters
548
+
549
+ The decorated function must return a list of SelectParameterOption objects.
550
+
551
+ Arguments:
552
+ name: The name of the parameter
553
+ label: The display label for the parameter
554
+ description: Explains the meaning of the parameter
555
+ show_select_all: Communicate to front-end whether to include a "select all" option
556
+ order_matters: Communicate to front-end whether the order of the selections made matter
557
+ none_is_all: Whether having no options selected is equivalent to all selectable options selected
558
+ """
559
+ def decorator(func: Callable[..., Sequence[po.SelectParameterOption]]):
560
+ def wrapper(sqrl: ParametersArgs):
561
+ options = u.call_func(func, sqrl=sqrl)
562
+ return cls.CreateSimple(
563
+ name, label, options, description=description,
564
+ show_select_all=show_select_all, order_matters=order_matters, none_is_all=none_is_all
565
+ )
566
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
567
+ return wrapper
568
+ return decorator
569
+
570
+ @classmethod
571
+ def CreateFromSource(
572
+ cls, name: str, label: str, data_source: d.SelectDataSource | dict, *, description: str = "",
573
+ show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True,
574
+ user_attribute: str | None = None, parent_name: str | None = None, **kwargs
575
+ ):
576
+ """
577
+ Method for creating the configurations for a MultiSelectParameter that uses a SelectDataSource to receive the options
578
+
579
+ .. deprecated::
580
+ Use the lowercase decorator form `create_from_source` instead.
581
+
582
+ Arguments:
583
+ name: The name of the parameter
584
+ label: The display label for the parameter
585
+ data_source: The lookup table to use for this parameter
586
+ description: Explains the meaning of the parameter
587
+ show_select_all: Communicate to front-end whether to include a "select all" option
588
+ order_matters: Communicate to front-end whether the order of the selections made matter
589
+ none_is_all: Whether having no options selected is equivalent to all selectable options selected
590
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
591
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
592
+ """
593
+ extra_args = {
594
+ "show_select_all": show_select_all, "order_matters": order_matters, "none_is_all": none_is_all
595
+ }
596
+ return cls._CreateFromSourceHelper(
597
+ name, label, data_source, extra_args=extra_args, description=description,
598
+ user_attribute=user_attribute, parent_name=parent_name
599
+ )
600
+
601
+ @classmethod
602
+ def create_from_source(
603
+ cls, name: str, label: str, *, description: str = "",
604
+ show_select_all: bool = True, order_matters: bool = False, none_is_all: bool = True,
605
+ user_attribute: str | None = None, parent_name: str | None = None
606
+ ):
607
+ """
608
+ Python decorator for creating the configurations for a MultiSelectParameter that uses a SelectDataSource to receive the options from a lookup table
609
+
610
+ The decorated function must return a SelectDataSource object.
611
+
612
+ Arguments:
613
+ name: The name of the parameter
614
+ label: The display label for the parameter
615
+ data_source: The lookup table to use for this parameter
616
+ description: Explains the meaning of the parameter
617
+ show_select_all: Communicate to front-end whether to include a "select all" option
618
+ order_matters: Communicate to front-end whether the order of the selections made matter
619
+ none_is_all: Whether having no options selected is equivalent to all selectable options selected
620
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
621
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
622
+ """
623
+ def decorator(func: Callable[..., d.SelectDataSource]):
624
+ def wrapper(sqrl: ParametersArgs):
625
+ data_source = u.call_func(func, sqrl=sqrl)
626
+ return cls.CreateFromSource(
627
+ name, label, data_source, description=description,
628
+ show_select_all=show_select_all, order_matters=order_matters, none_is_all=none_is_all,
629
+ user_attribute=user_attribute, parent_name=parent_name
630
+ )
631
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
632
+ return wrapper
633
+ return decorator
634
+
635
+ def has_non_empty_selection(self) -> bool:
636
+ """
637
+ Returns True if more than zero options were selected. False otherwise.
638
+
639
+ Note that even when this returns False, all "get_selected" functions would
640
+ return the full list of options if "include_all" is set to True
641
+
642
+ Returns:
643
+ A boolean
644
+ """
645
+ return len(self._selected_ids) > 0
646
+
647
+ def get_selected_list(
648
+ self, field: str | None = None, *, default_field: str | None = None, default: Any = None, **kwargs
649
+ ) -> Sequence[po.SelectParameterOption | Any]:
650
+ """
651
+ Gets the sequence of the selected option(s) or a sequence of selected custom fields
652
+
653
+ Arguments:
654
+ field: If field is not None, the method gets this field from the "custom_fields" attribute of the selected options.
655
+ Otherwise, returns the class objects of the selected options
656
+ default_field: If field does not exist for a parameter option and default_field is not None, the default_field is used
657
+ as the "field" instead. Does nothing if field is None
658
+ default: If field does not exist for a parameter option, default_field is None, but default is not None, the default
659
+ is returned as the selected field. Does nothing if field is None or default_field is not None
660
+
661
+ Returns:
662
+ A sequence of SelectParameterOption class objects or sequence of type of custom field
663
+ """
664
+ if not self.has_non_empty_selection() and self._config.none_is_all:
665
+ selected_list = self._options
666
+ else:
667
+ selected_list = (x for x in self._options if x._identifier in self._selected_ids)
668
+
669
+ if field is not None:
670
+ selected_list = [selected.get_custom_field(field, default_field=default_field, default=default) for selected in selected_list]
671
+
672
+ return tuple(selected_list)
673
+
674
+ def _get_selected_list_of_strings(
675
+ self, method: str, field: str, default_field: str | None, default: str | None, **kwargs
676
+ ) -> list[str]:
677
+ selected_list = self.get_selected_list(field, default_field=default_field, default=default)
678
+ list_of_strings: list[str] = []
679
+ for selected in selected_list:
680
+ if not isinstance(selected, str):
681
+ raise u.ConfigurationError(
682
+ f"Method '{method}' can only be used on fields with only string values"
683
+ )
684
+ list_of_strings.append(selected)
685
+ return list_of_strings
686
+
687
+ def get_selected_list_joined(self, field: str, *, default_field: str | None = None, default: str | None = None, **kwargs) -> str:
688
+ """
689
+ Gets the selected custom fields joined by comma
690
+
691
+ Arguments:
692
+ field: The "custom_fields" attribute of the selected options.
693
+ default_field: If field does not exist for a parameter option and default_field is not None, the default_field is used
694
+ as the "field" instead.
695
+ default: If field does not exist for a parameter option, default_field is None, but default is not None, the default
696
+ is returned as the selected field. Does nothing if default_field is not None
697
+
698
+ Returns:
699
+ A string
700
+ """
701
+ list_of_strings = self._get_selected_list_of_strings("get_selected_list_joined", field, default_field, default)
702
+ return ','.join(list_of_strings)
703
+
704
+ def get_selected_list_quoted(self, field: str, *, default_field: str | None = None, default: str | None = None, **kwargs) -> tuple[str, ...]:
705
+ """
706
+ Gets the selected custom fields surrounded by single quotes
707
+
708
+ Arguments:
709
+ field: The "custom_fields" attribute of the selected options.
710
+ default_field: If field does not exist for a parameter option and default_field is not None, the default_field is used
711
+ as the "field" instead.
712
+ default: If field does not exist for a parameter option, default_field is None, but default is not None, the default
713
+ is returned as the selected field. Does nothing if default_field is not None
714
+
715
+ Returns:
716
+ A tuple of strings
717
+ """
718
+ list_of_strings = self._get_selected_list_of_strings("get_selected_list_quoted", field, default_field, default)
719
+ return tuple(self._enquote(x) for x in list_of_strings)
720
+
721
+ def get_selected_list_quoted_joined(self, field: str, *, default_field: str | None = None, default: str | None = None, **kwargs) -> str:
722
+ """
723
+ Gets the selected custom fields surrounded by single quotes and joined by comma
724
+
725
+ Arguments:
726
+ field: The "custom_fields" attribute of the selected options.
727
+ default_field: If field does not exist for a parameter option and default_field is not None, the default_field is used
728
+ as the "field" instead.
729
+ default: If field does not exist for a parameter option, default_field is None, but default is not None, the default
730
+ is returned as the selected field. Does nothing if default_field is not None
731
+
732
+ Returns:
733
+ A string
734
+ """
735
+ list_of_strings = self._get_selected_list_of_strings("get_selected_list_quoted_joined", field, default_field, default)
736
+ return ','.join(self._enquote(x) for x in list_of_strings)
737
+
738
+ def get_selected_ids_as_list(self, **kwargs) -> Sequence[str]:
739
+ """
740
+ Gets the sequence of ID(s) of the selected option(s)
741
+
742
+ Returns:
743
+ A sequence of strings
744
+ """
745
+ return tuple(x._identifier for x in self.get_selected_list())
746
+
747
+ def get_selected_ids_joined(self, **kwargs) -> str:
748
+ """
749
+ Gets the ID(s) of the selected option(s) joined by comma
750
+
751
+ Returns:
752
+ A string
753
+ """
754
+ return ','.join(self.get_selected_ids_as_list())
755
+
756
+ def get_selected_ids_quoted_as_list(self, **kwargs) -> Sequence[str]:
757
+ """
758
+ Gets the sequence of ID(s) of the selected option(s) surrounded by single quotes
759
+
760
+ Returns:
761
+ A sequence of strings
762
+ """
763
+ return tuple(self._enquote(x) for x in self.get_selected_ids_as_list())
764
+
765
+ def get_selected_ids_quoted_joined(self, **kwargs) -> str:
766
+ """
767
+ Gets the ID(s) of the selected option(s) surrounded by single quotes and joined by comma
768
+
769
+ Returns:
770
+ A string
771
+ """
772
+ return ','.join(self.get_selected_ids_quoted_as_list())
773
+
774
+ def get_selected_labels_as_list(self, **kwargs) -> Sequence[str]:
775
+ """
776
+ Gets the sequence of label(s) of the selected option(s)
777
+
778
+ Returns:
779
+ A sequence of strings
780
+ """
781
+ return tuple(x._label for x in self.get_selected_list())
782
+
783
+ def get_selected_labels_joined(self, **kwargs) -> str:
784
+ """
785
+ Gets the label(s) of the selected option(s) joined by comma
786
+
787
+ Returns:
788
+ A string
789
+ """
790
+ return ','.join(self.get_selected_labels_as_list())
791
+
792
+ def get_selected_labels_quoted_as_list(self, **kwargs) -> Sequence[str]:
793
+ """
794
+ Gets the sequence of label(s) of the selected option(s) surrounded by single quotes
795
+
796
+ Returns:
797
+ A sequence of strings
798
+ """
799
+ return tuple(self._enquote(x) for x in self.get_selected_labels_as_list())
800
+
801
+ def get_selected_labels_quoted_joined(self, **kwargs) -> str:
802
+ """
803
+ Gets the label(s) of the selected option(s) surrounded by single quotes and joined by comma
804
+
805
+ Returns:
806
+ A string
807
+ """
808
+ return ','.join(self.get_selected_labels_quoted_as_list())
809
+
810
+ def _get_selected_ids_as_list(self, **kwargs) -> Sequence[str]:
811
+ return self.get_selected_ids_as_list()
812
+
813
+ def _to_json_dict0(self):
814
+ """
815
+ Converts this parameter as a JSON object for the parameters API response
816
+
817
+ Returns:
818
+ A dictionary for the JSON object
819
+ """
820
+ output = super()._to_json_dict0()
821
+ output['show_select_all'] = self._config.show_select_all
822
+ output['order_matters'] = self._config.order_matters
823
+ output['selected_ids'] = list(self._selected_ids)
824
+ return output
825
+
826
+ def _get_response_model0(self):
827
+ return rm.MultiSelectParameterModel if self.is_enabled() else rm.NoneParameterModel
828
+
829
+
830
+ DatePO = TypeVar("DatePO", bound=po._DateTypeParameterOption)
831
+
832
+ @dataclass
833
+ class _DateTypeParameter(Parameter[PC, DatePO, DS], Generic[PC, DatePO, DS]):
834
+ _curr_option: DatePO | None
835
+
836
+ def is_enabled(self) -> bool:
837
+ return self._curr_option is not None
838
+
839
+ def _cast_optional_date_to_str(self, date: date | None) -> str | None:
840
+ return None if date is None else date.strftime("%Y-%m-%d")
841
+
842
+ def _to_json_dict0(self):
843
+ output = super()._to_json_dict0()
844
+ if self._curr_option is not None:
845
+ output["min_date"] = self._cast_optional_date_to_str(self._curr_option._min_date)
846
+ output["max_date"] = self._cast_optional_date_to_str(self._curr_option._max_date)
847
+ return output
848
+
849
+
850
+ @dataclass
851
+ class DateParameter(_DateTypeParameter[pc.DateParameterConfig, po.DateParameterOption, d.DateDataSource]):
852
+ """
853
+ Class for date parameter widgets.
854
+
855
+ Attributes:
856
+ config: The config for this widget parameter (for immutable attributes like name, label, all possible options, etc)
857
+ curr_option: The current option showing for defaults based on user attribute and selection of parent
858
+ selected_date: The selected date
859
+ """
860
+ _selected_date: date | str | None
861
+
862
+ def __post_init__(self):
863
+ if self._curr_option is not None and self._selected_date is not None:
864
+ self._selected_date = self._validate_input_date(self._selected_date, self._curr_option)
865
+
866
+ def is_enabled(self) -> bool:
867
+ return self._curr_option is not None
868
+
869
+ @staticmethod
870
+ def _ParameterConfigType():
871
+ return pc.DateParameterConfig
872
+
873
+ @staticmethod
874
+ def _ParameterOptionType():
875
+ return po.DateParameterOption
876
+
877
+ @staticmethod
878
+ def _DataSourceType():
879
+ return d.DateDataSource
880
+
881
+ @classmethod
882
+ def CreateSimple(
883
+ cls, name: str, label: str, default_date: str | date, *, description: str = "",
884
+ min_date: str | date | None = None, max_date: str | date | None = None, date_format: str = '%Y-%m-%d', **kwargs
885
+ ):
886
+ """
887
+ Method for creating the configurations for a DateParameter that doesn't involve user attributes or parent parameters
888
+
889
+ .. deprecated::
890
+ Use the lowercase decorator form `create_simple` instead.
891
+
892
+ Arguments:
893
+ name: The name of the parameter
894
+ label: The display label for the parameter
895
+ default_date: Default date for this option
896
+ description: Explains the meaning of the parameter
897
+ min_date: Minimum selectable date
898
+ max_date: Maximum selectable date
899
+ date_format: Format of the default date, default is '%Y-%m-%d'
900
+ """
901
+ single_param_option = po.DateParameterOption(default_date, min_date=min_date, max_date=max_date, date_format=date_format)
902
+ return cls.CreateWithOptions(name, label, (single_param_option,), description=description)
903
+
904
+ @classmethod
905
+ def create_simple(
906
+ cls, name: str, label: str, default_date: str | date, *, description: str = "",
907
+ min_date: str | date | None = None, max_date: str | date | None = None, date_format: str = '%Y-%m-%d'
908
+ ):
909
+ """
910
+ Python decorator for creating the configurations for a DateParameter that doesn't involve user attributes or parent parameters
911
+
912
+ Arguments:
913
+ name: The name of the parameter
914
+ label: The display label for the parameter
915
+ default_date: Default date for this option
916
+ description: Explains the meaning of the parameter
917
+ min_date: Minimum selectable date
918
+ max_date: Maximum selectable date
919
+ date_format: Format of the default date, default is '%Y-%m-%d'
920
+ """
921
+ def decorator(func: Callable[..., Any]):
922
+ def wrapper(sqrl: ParametersArgs):
923
+ return cls.CreateSimple(
924
+ name, label, default_date, description=description,
925
+ min_date=min_date, max_date=max_date, date_format=date_format
926
+ )
927
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
928
+ return wrapper
929
+ return decorator
930
+
931
+ def get_selected_date(self, *, date_format: str | None = None, **kwargs) -> str:
932
+ """
933
+ Gets selected date as string
934
+
935
+ Arguments:
936
+ date_format: The date format (see Python's datetime formats). If not specified, self.date_format is used
937
+
938
+ Returns:
939
+ A string
940
+ """
941
+ assert self._curr_option is not None and isinstance(self._selected_date, date), "Parameter is not enabled"
942
+ date_format = self._curr_option._date_format if date_format is None else date_format
943
+ return self._selected_date.strftime(date_format)
944
+
945
+ def get_selected_date_quoted(self, *, date_format: str | None = None, **kwargs) -> str:
946
+ """
947
+ Gets selected date as string surrounded by single quotes
948
+
949
+ Arguments:
950
+ date_format: The date format (see Python's datetime formats). If not specified, self.date_format is used
951
+
952
+ Returns:
953
+ A string
954
+ """
955
+ return self._enquote(self.get_selected_date(date_format=date_format))
956
+
957
+ def _to_json_dict0(self):
958
+ """
959
+ Converts this parameter as a JSON object for the parameters API response
960
+
961
+ The "selected_date" field will always be in yyyy-mm-dd format
962
+
963
+ Returns:
964
+ A dictionary for the JSON object
965
+ """
966
+ output = super()._to_json_dict0()
967
+ if self.is_enabled():
968
+ output["selected_date"] = self.get_selected_date(date_format="%Y-%m-%d")
969
+ else:
970
+ output["selected_date"] = ""
971
+ return output
972
+
973
+ def _get_response_model0(self):
974
+ return rm.DateParameterModel if self.is_enabled() else rm.NoneParameterModel
975
+
976
+
977
+ @dataclass
978
+ class DateRangeParameter(_DateTypeParameter[pc.DateRangeParameterConfig, po.DateRangeParameterOption, d.DateRangeDataSource]):
979
+ """
980
+ Class for date range parameter widgets.
981
+
982
+ Attributes:
983
+ config: The config for this widget parameter (for immutable attributes like name, label, all possible options, etc)
984
+ curr_option: The current option showing for defaults based on user attribute and selection of parent
985
+ selected_start_date: The selected start date
986
+ selected_end_date: The selected end date
987
+ """
988
+ _selected_start_date: date | str | None
989
+ _selected_end_date: date | str | None
990
+
991
+ def __post_init__(self):
992
+ if self._curr_option is not None:
993
+ if self._selected_start_date is not None:
994
+ self._selected_start_date = self._validate_input_date(self._selected_start_date, self._curr_option)
995
+ if self._selected_end_date is not None:
996
+ self._selected_end_date = self._validate_input_date(self._selected_end_date, self._curr_option)
997
+
998
+ def is_enabled(self) -> bool:
999
+ return self._curr_option is not None
1000
+
1001
+ @staticmethod
1002
+ def _ParameterConfigType():
1003
+ return pc.DateRangeParameterConfig
1004
+
1005
+ @staticmethod
1006
+ def _ParameterOptionType():
1007
+ return po.DateRangeParameterOption
1008
+
1009
+ @staticmethod
1010
+ def _DataSourceType():
1011
+ return d.DateRangeDataSource
1012
+
1013
+ @classmethod
1014
+ def CreateSimple(
1015
+ cls, name: str, label: str, default_start_date: str | date, default_end_date: str | date, *, description: str = "",
1016
+ min_date: str | date | None = None, max_date: str | date | None = None, date_format: str = '%Y-%m-%d', **kwargs
1017
+ ):
1018
+ """
1019
+ Method for creating the configurations for a DateRangeParameter that doesn't involve user attributes or parent parameters
1020
+
1021
+ .. deprecated::
1022
+ Use the lowercase decorator form `create_simple` instead.
1023
+
1024
+ Arguments:
1025
+ name: The name of the parameter
1026
+ label: The display label for the parameter
1027
+ default_start_date: Default start date for this option
1028
+ default_end_date: Default end date for this option
1029
+ description: Explains the meaning of the parameter
1030
+ min_date: Minimum selectable date
1031
+ max_date: Maximum selectable date
1032
+ date_format: Format of the default date, default is '%Y-%m-%d'
1033
+ """
1034
+ single_param_option = po.DateRangeParameterOption(
1035
+ default_start_date, default_end_date, min_date=min_date, max_date=max_date, date_format=date_format
1036
+ )
1037
+ return cls.CreateWithOptions(name, label, (single_param_option,), description=description)
1038
+
1039
+ @classmethod
1040
+ def create_simple(
1041
+ cls, name: str, label: str, default_start_date: str | date, default_end_date: str | date, *, description: str = "",
1042
+ min_date: str | date | None = None, max_date: str | date | None = None, date_format: str = '%Y-%m-%d'
1043
+ ):
1044
+ """
1045
+ Python decorator for creating the configurations for a DateRangeParameter that doesn't involve user attributes or parent parameters
1046
+
1047
+ Arguments:
1048
+ name: The name of the parameter
1049
+ label: The display label for the parameter
1050
+ default_start_date: Default start date for this option
1051
+ default_end_date: Default end date for this option
1052
+ description: Explains the meaning of the parameter
1053
+ min_date: Minimum selectable date
1054
+ max_date: Maximum selectable date
1055
+ date_format: Format of the default date, default is '%Y-%m-%d'
1056
+ """
1057
+ def decorator(func: Callable[..., Any]):
1058
+ def wrapper(sqrl: ParametersArgs):
1059
+ return cls.CreateSimple(
1060
+ name, label, default_start_date, default_end_date, description=description,
1061
+ min_date=min_date, max_date=max_date, date_format=date_format
1062
+ )
1063
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
1064
+ return wrapper
1065
+ return decorator
1066
+
1067
+ def get_selected_start_date(self, *, date_format: str | None = None, **kwargs) -> str:
1068
+ """
1069
+ Gets selected start date as string
1070
+
1071
+ Arguments:
1072
+ date_format: The date format (see Python's datetime formats). If not specified, self.date_format is used
1073
+
1074
+ Returns:
1075
+ A string
1076
+ """
1077
+ assert self._curr_option is not None and isinstance(self._selected_start_date, date), "Parameter is not enabled"
1078
+ date_format = self._curr_option._date_format if date_format is None else date_format
1079
+ return self._selected_start_date.strftime(date_format)
1080
+
1081
+ def get_selected_start_date_quoted(self, *, date_format: str | None = None, **kwargs) -> str:
1082
+ """
1083
+ Gets selected start date as string surrounded by single quotes
1084
+
1085
+ Arguments:
1086
+ date_format: The date format (see Python's datetime formats). If not specified, self.date_format is used
1087
+
1088
+ Returns:
1089
+ A string
1090
+ """
1091
+ return self._enquote(self.get_selected_start_date(date_format=date_format))
1092
+
1093
+ def get_selected_end_date(self, *, date_format: str | None = None, **kwargs) -> str:
1094
+ """
1095
+ Gets selected end date as string
1096
+
1097
+ Arguments:
1098
+ date_format: The date format (see Python's datetime formats). If not specified, self.date_format is used
1099
+
1100
+ Returns:
1101
+ A string
1102
+ """
1103
+ assert self._curr_option is not None and isinstance(self._selected_end_date, date), "Parameter is not enabled"
1104
+ date_format = self._curr_option._date_format if date_format is None else date_format
1105
+ return self._selected_end_date.strftime(date_format)
1106
+
1107
+ def get_selected_end_date_quoted(self, *, date_format: str | None = None, **kwargs) -> str:
1108
+ """
1109
+ Gets selected end date as string surrounded by single quotes
1110
+
1111
+ Arguments:
1112
+ date_format: The date format (see Python's datetime formats). If not specified, self.date_format is used
1113
+
1114
+ Returns:
1115
+ A string
1116
+ """
1117
+ return self._enquote(self.get_selected_end_date(date_format=date_format))
1118
+
1119
+ def _to_json_dict0(self):
1120
+ """
1121
+ Converts this parameter as a JSON object for the parameters API response
1122
+
1123
+ The "selected_date" field will always be in yyyy-mm-dd format
1124
+
1125
+ Returns:
1126
+ A dictionary for the JSON object
1127
+ """
1128
+ output = super()._to_json_dict0()
1129
+ if self.is_enabled():
1130
+ output["selected_start_date"] = self.get_selected_start_date(date_format="%Y-%m-%d")
1131
+ output["selected_end_date"] = self.get_selected_end_date(date_format="%Y-%m-%d")
1132
+ return output
1133
+
1134
+ def _get_response_model0(self):
1135
+ return rm.DateRangeParameterModel if self.is_enabled() else rm.NoneParameterModel
1136
+
1137
+
1138
+ NumericPO = TypeVar("NumericPO", bound=po._NumericParameterOption)
1139
+
1140
+ @dataclass
1141
+ class _NumberTypeParameter(Parameter[PC, NumericPO, DS], Generic[PC, NumericPO, DS]):
1142
+ _curr_option: NumericPO | None
1143
+
1144
+ def is_enabled(self) -> bool:
1145
+ return self._curr_option is not None
1146
+
1147
+ def _to_json_dict0(self):
1148
+ output = super()._to_json_dict0()
1149
+ if self._curr_option is not None:
1150
+ output["min_value"] = float(self._curr_option._min_value)
1151
+ output["max_value"] = float(self._curr_option._max_value)
1152
+ output["increment"] = float(self._curr_option._increment)
1153
+ return output
1154
+
1155
+
1156
+ @dataclass
1157
+ class NumberParameter(_NumberTypeParameter[pc.NumberParameterConfig, po.NumberParameterOption, d.NumberDataSource]):
1158
+ """
1159
+ Class for number parameter widgets.
1160
+
1161
+ Attributes:
1162
+ config: The config for this widget parameter (for immutable attributes like name, label, all possible options, etc)
1163
+ curr_option: The current option showing for defaults based on user attribute and selection of parent
1164
+ selected_value: The selected integer or decimal number
1165
+ """
1166
+ _selected_value: po.Number | None
1167
+
1168
+ def __post_init__(self):
1169
+ if self._curr_option is not None and self._selected_value is not None:
1170
+ self._selected_value = self._validate_number(self._selected_value, self._curr_option)
1171
+
1172
+ @staticmethod
1173
+ def _ParameterConfigType():
1174
+ return pc.NumberParameterConfig
1175
+
1176
+ @staticmethod
1177
+ def _ParameterOptionType():
1178
+ return po.NumberParameterOption
1179
+
1180
+ @staticmethod
1181
+ def _DataSourceType():
1182
+ return d.NumberDataSource
1183
+
1184
+ @classmethod
1185
+ def CreateSimple(
1186
+ cls, name: str, label: str, min_value: po.Number, max_value: po.Number, *, description: str = "",
1187
+ increment: po.Number = 1, default_value: po.Number | None = None, **kwargs
1188
+ ):
1189
+ """
1190
+ Method for creating the configurations for a NumberParameter that doesn't involve user attributes or parent parameters
1191
+
1192
+ .. deprecated::
1193
+ Use the lowercase decorator form `create_simple` instead.
1194
+
1195
+ * Note that the "Number" type denotes an int, a Decimal (from decimal module), or a string that can be parsed to Decimal
1196
+
1197
+ Arguments:
1198
+ name: The name of the parameter
1199
+ label: The display label for the parameter
1200
+ min_value: Minimum selectable value
1201
+ max_value: Maximum selectable value
1202
+ description: Explains the meaning of the parameter
1203
+ increment: Increment of selectable values, and must fit evenly between min_value and max_value
1204
+ default_value: Default value for this option, and must be selectable based on min_value, max_value, and increment
1205
+ """
1206
+ single_param_option = po.NumberParameterOption(min_value, max_value, increment=increment, default_value=default_value)
1207
+ return cls.CreateWithOptions(name, label, (single_param_option,), description=description)
1208
+
1209
+ @classmethod
1210
+ def create_simple(
1211
+ cls, name: str, label: str, min_value: po.Number, max_value: po.Number, *, description: str = "",
1212
+ increment: po.Number = 1, default_value: po.Number | None = None
1213
+ ):
1214
+ """
1215
+ Python decorator for creating the configurations for a NumberParameter that doesn't involve user attributes or parent parameters
1216
+
1217
+ * Note that the "Number" type denotes an int, a Decimal (from decimal module), or a string that can be parsed to Decimal
1218
+
1219
+ Arguments:
1220
+ name: The name of the parameter
1221
+ label: The display label for the parameter
1222
+ min_value: Minimum selectable value
1223
+ max_value: Maximum selectable value
1224
+ description: Explains the meaning of the parameter
1225
+ increment: Increment of selectable values, and must fit evenly between min_value and max_value
1226
+ default_value: Default value for the parameter
1227
+ """
1228
+ def decorator(func: Callable[..., Any]):
1229
+ def wrapper(sqrl: ParametersArgs):
1230
+ return cls.CreateSimple(
1231
+ name, label, min_value, max_value, description=description, increment=increment, default_value=default_value
1232
+ )
1233
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
1234
+ return wrapper
1235
+ return decorator
1236
+
1237
+ def get_selected_value(self, **kwargs) -> float:
1238
+ """
1239
+ Get the selected number (converted from Decimal to float)
1240
+
1241
+ Returns:
1242
+ float
1243
+ """
1244
+ assert self._selected_value is not None, "Parameter is not enabled"
1245
+ return float(self._selected_value)
1246
+
1247
+ def _to_json_dict0(self):
1248
+ """
1249
+ Converts this parameter as a JSON object for the parameters API response
1250
+
1251
+ Returns:
1252
+ A dictionary for the JSON object
1253
+ """
1254
+ output = super()._to_json_dict0()
1255
+ if self.is_enabled():
1256
+ output["selected_value"] = self.get_selected_value()
1257
+ return output
1258
+
1259
+ def _get_response_model0(self):
1260
+ return rm.NumberParameterModel if self.is_enabled() else rm.NoneParameterModel
1261
+
1262
+
1263
+ @dataclass
1264
+ class NumberRangeParameter(_NumberTypeParameter[pc.NumberRangeParameterConfig, po.NumberRangeParameterOption, d.NumberRangeDataSource]):
1265
+ """
1266
+ Class for number range parameter widgets.
1267
+
1268
+ Attributes:
1269
+ config: The config for this widget parameter (for immutable attributes like name, label, all possible options, etc)
1270
+ curr_option: The current option showing for defaults based on user attribute and selection of parent
1271
+ selected_lower_value: The selected lower integer or decimal number
1272
+ selected_upper_value: The selected upper integer or decimal number
1273
+ """
1274
+ _selected_lower_value: po.Number | None
1275
+ _selected_upper_value: po.Number | None
1276
+
1277
+ def __post_init__(self):
1278
+ if self._curr_option is not None:
1279
+ if self._selected_lower_value is not None:
1280
+ self._selected_lower_value = self._validate_number(self._selected_lower_value, self._curr_option)
1281
+ if self._selected_upper_value is not None:
1282
+ self._selected_upper_value = self._validate_number(self._selected_upper_value, self._curr_option)
1283
+
1284
+ @staticmethod
1285
+ def _ParameterConfigType():
1286
+ return pc.NumberRangeParameterConfig
1287
+
1288
+ @staticmethod
1289
+ def _ParameterOptionType():
1290
+ return po.NumberRangeParameterOption
1291
+
1292
+ @staticmethod
1293
+ def _DataSourceType():
1294
+ return d.NumberRangeDataSource
1295
+
1296
+ @classmethod
1297
+ def CreateSimple(
1298
+ cls, name: str, label: str, min_value: po.Number, max_value: po.Number, *, description: str = "",
1299
+ increment: po.Number = 1, default_lower_value: po.Number | None = None, default_upper_value: po.Number | None = None,**kwargs
1300
+ ):
1301
+ """
1302
+ Method for creating the configurations for a NumberRangeParameter that doesn't involve user attributes or parent parameters
1303
+
1304
+ .. deprecated::
1305
+ Use the lowercase decorator form `create_simple` instead.
1306
+
1307
+ * Note that the "Number" type denotes an int, a Decimal (from decimal module), or a string that can be parsed to Decimal
1308
+
1309
+ Arguments:
1310
+ name: The name of the parameter
1311
+ label: The display label for the parameter
1312
+ min_value: Minimum selectable value
1313
+ max_value: Maximum selectable value
1314
+ description: Explains the meaning of the parameter
1315
+ increment: Increment of selectable values, and must fit evenly between min_value and max_value
1316
+ default_lower_value: Default lower value for this option, and must be selectable based on min_value, max_value, and increment
1317
+ default_upper_value: Default upper value for this option, and must be selectable based on min_value, max_value, and increment.
1318
+ Must also be greater than default_lower_value
1319
+ """
1320
+ single_param_option = po.NumberRangeParameterOption(
1321
+ min_value, max_value, increment=increment, default_lower_value=default_lower_value, default_upper_value=default_upper_value
1322
+ )
1323
+ return cls.CreateWithOptions(name, label, (single_param_option,), description=description)
1324
+
1325
+ @classmethod
1326
+ def create_simple(
1327
+ cls, name: str, label: str, min_value: po.Number, max_value: po.Number, *, description: str = "",
1328
+ increment: po.Number = 1, default_lower_value: po.Number | None = None, default_upper_value: po.Number | None = None
1329
+ ):
1330
+ """
1331
+ Python decorator for creating the configurations for a NumberRangeParameter that doesn't involve user attributes or parent parameters
1332
+
1333
+ * Note that the "Number" type denotes an int, a Decimal (from decimal module), or a string that can be parsed to Decimal
1334
+
1335
+ Arguments:
1336
+ name: The name of the parameter
1337
+ label: The display label for the parameter
1338
+ min_value: Minimum selectable value
1339
+ max_value: Maximum selectable value
1340
+ description: Explains the meaning of the parameter
1341
+ increment: Increment of selectable values, and must fit evenly between min_value and max_value
1342
+ default_lower_value: Default lower value for this option, and must be selectable based on min_value, max_value, and increment
1343
+ default_upper_value: Default upper value for this option, and must be selectable based on min_value, max_value, and increment.
1344
+ Must also be greater than default_lower_value
1345
+ """
1346
+ def decorator(func: Callable[..., Any]):
1347
+ def wrapper(sqrl: ParametersArgs):
1348
+ return cls.CreateSimple(
1349
+ name, label, min_value, max_value, description=description, increment=increment,
1350
+ default_lower_value=default_lower_value, default_upper_value=default_upper_value
1351
+ )
1352
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
1353
+ return wrapper
1354
+ return decorator
1355
+
1356
+ def get_selected_lower_value(self, **kwargs) -> float:
1357
+ """
1358
+ Get the selected lower value number (converted from Decimal to float)
1359
+
1360
+ Returns:
1361
+ float
1362
+ """
1363
+ assert self._selected_lower_value is not None, "Parameter is not enabled"
1364
+ return float(self._selected_lower_value)
1365
+
1366
+ def get_selected_upper_value(self, **kwargs) -> float:
1367
+ """
1368
+ Get the selected upper value number (converted from Decimal to float)
1369
+
1370
+ Returns:
1371
+ float
1372
+ """
1373
+ assert self._selected_upper_value is not None, "Parameter is not enabled"
1374
+ return float(self._selected_upper_value)
1375
+
1376
+ def _to_json_dict0(self):
1377
+ """
1378
+ Converts this parameter as a JSON object for the parameters API response
1379
+
1380
+ Returns:
1381
+ A dictionary for the JSON object
1382
+ """
1383
+ output = super()._to_json_dict0()
1384
+ if self.is_enabled():
1385
+ output['selected_lower_value'] = self.get_selected_lower_value()
1386
+ output['selected_upper_value'] = self.get_selected_upper_value()
1387
+ return output
1388
+
1389
+ def _get_response_model0(self):
1390
+ return rm.NumberRangeParameterModel if self.is_enabled() else rm.NoneParameterModel
1391
+
1392
+
1393
+ @dataclass
1394
+ class TextValue:
1395
+ _value_do_not_touch: str
1396
+
1397
+ def __repr__(self):
1398
+ raise u.ConfigurationError(
1399
+ "Cannot convert TextValue directly to string (to avoid SQL injection). Try using it through placeholders instead"
1400
+ )
1401
+
1402
+ def apply(self, str_to_str_function: Callable[[str], str]) -> TextValue:
1403
+ """
1404
+ Transforms the entered text with a function that takes a string and returns a string.
1405
+
1406
+ This method returns a new object and leaves the original the same.
1407
+
1408
+ Arguments:
1409
+ str_to_str_function: A function that accepts a string and returns a string
1410
+
1411
+ Returns:
1412
+ A new TextValue with the transformed entered text
1413
+ """
1414
+ new_value = str_to_str_function(self._value_do_not_touch)
1415
+ if not isinstance(new_value, str):
1416
+ raise u.ConfigurationError("Function provided must return string")
1417
+ return TextValue(new_value)
1418
+
1419
+ def apply_percent_wrap(self) -> TextValue:
1420
+ """
1421
+ Adds percent signs before and after the entered text, and returns a new object, leaving the original the same.
1422
+
1423
+ Returns:
1424
+ A new TextValue with the transformed entered text
1425
+ """
1426
+ return self.apply(lambda x: "%"+x+"%")
1427
+
1428
+ def apply_as_bool(self, str_to_bool_function: Callable[[str], bool]) -> bool:
1429
+ """
1430
+ Transforms the entered text with a function that takes a string and returns a boolean.
1431
+
1432
+ Arguments:
1433
+ str_to_bool_function: A function that accepts a string and returns a boolean.
1434
+
1435
+ Returns:
1436
+ A boolean for the transformed value
1437
+ """
1438
+ new_value = str_to_bool_function(self._value_do_not_touch)
1439
+ if not isinstance(new_value, bool):
1440
+ raise u.ConfigurationError("Function provided must return bool")
1441
+ return new_value
1442
+
1443
+ def apply_as_number(self, str_to_num_function: Callable[[str], IntOrFloat]) -> IntOrFloat:
1444
+ """
1445
+ Transforms the entered text with a function that takes a string and returns an int or float.
1446
+
1447
+ Arguments:
1448
+ str_to_num_function: A function that accepts a string and returns an int or float.
1449
+
1450
+ Returns:
1451
+ An int or float for the transformed value
1452
+ """
1453
+ new_value = str_to_num_function(self._value_do_not_touch)
1454
+ if not isinstance(new_value, (int, float)):
1455
+ raise u.ConfigurationError("Function provided must return a number")
1456
+ return new_value
1457
+
1458
+ def apply_as_datetime(self, str_to_datetime_function: Callable[[str], datetime]) -> datetime:
1459
+ """
1460
+ Transforms the entered text with a function that takes a string and returns a datetime object.
1461
+
1462
+ Arguments:
1463
+ str_to_datetime_function: A function that accepts a string and returns a datetime object.
1464
+
1465
+ Returns:
1466
+ A datetime object for the transformed value
1467
+ """
1468
+ new_value = str_to_datetime_function(self._value_do_not_touch)
1469
+ if not isinstance(new_value, datetime):
1470
+ raise u.ConfigurationError("Function provided must return datetime")
1471
+ return new_value
1472
+
1473
+
1474
+ @dataclass
1475
+ class TextParameter(Parameter[pc.TextParameterConfig, po.TextParameterOption, d.TextDataSource]):
1476
+ """
1477
+ Class for text parameter widgets.
1478
+ """
1479
+ _curr_option: po.TextParameterOption | None
1480
+ _entered_text: str | None
1481
+
1482
+ def __post_init__(self):
1483
+ if self.is_enabled() and isinstance(self._entered_text, str):
1484
+ try:
1485
+ self._entered_text = self._config.validate_entered_text(self._entered_text)
1486
+ except u.ConfigurationError as e:
1487
+ raise self._config._invalid_input_error(self._entered_text, str(e))
1488
+
1489
+ def is_enabled(self) -> bool:
1490
+ return self._curr_option is not None
1491
+
1492
+ @staticmethod
1493
+ def _ParameterConfigType():
1494
+ return pc.TextParameterConfig
1495
+
1496
+ @staticmethod
1497
+ def _ParameterOptionType():
1498
+ return po.TextParameterOption
1499
+
1500
+ @staticmethod
1501
+ def _DataSourceType():
1502
+ return d.TextDataSource
1503
+
1504
+ @classmethod
1505
+ def CreateWithOptions(
1506
+ cls, name: str, label: str, all_options: Sequence[po.TextParameterOption | dict], *, description: str = "",
1507
+ input_type: str = "text", user_attribute: str | None = None, parent_name: str | None = None, **kwargs
1508
+ ):
1509
+ """
1510
+ Method for creating the configurations for a TextParameter that doesn't involve user attribute or parent
1511
+
1512
+ .. deprecated::
1513
+ Use the lowercase decorator form `create_with_options` instead.
1514
+
1515
+ Arguments:
1516
+ name: The name of the parameter
1517
+ label: The display label for the parameter
1518
+ all_options: All options associated to this parameter regardless of the user group or parent parameter option they depend on
1519
+ description: Explains the meaning of the parameter
1520
+ input_type: The type of input field to use. Must be one of "text", "textarea", "number", "color", "date", "datetime-local", "month", "time", and "password". Optional, default is "text". More information on input types other than "textarea" can be found at https://www.w3schools.com/html/html_form_input_types.asp. More information on "textarea" can be found at https://www.w3schools.com/tags/tag_textarea.asp
1521
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
1522
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
1523
+ """
1524
+ return super().CreateWithOptions(
1525
+ name, label, all_options, description=description, input_type=input_type,
1526
+ user_attribute=user_attribute, parent_name=parent_name
1527
+ )
1528
+
1529
+ @classmethod
1530
+ def create_with_options(
1531
+ cls, name: str, label: str, *, description: str = "",
1532
+ input_type: str = "text", user_attribute: str | None = None, parent_name: str | None = None
1533
+ ):
1534
+ """
1535
+ Python decorator for creating the configurations for a TextParameter that may include user attribute or parent
1536
+
1537
+ The decorated function must return a list of TextParameterOption objects.
1538
+
1539
+ Arguments:
1540
+ name: The name of the parameter
1541
+ label: The display label for the parameter
1542
+ description: Explains the meaning of the parameter
1543
+ input_type: The type of input field to use. Must be one of "text", "textarea", "number", "color", "date", "datetime-local", "month", "time", and "password". Optional, default is "text". More information on input types other than "textarea" can be found at https://www.w3schools.com/html/html_form_input_types.asp. More information on "textarea" can be found at https://www.w3schools.com/tags/tag_textarea.asp
1544
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
1545
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
1546
+ """
1547
+ def decorator(func: Callable[..., Sequence[po.TextParameterOption]]):
1548
+ def wrapper(sqrl: ParametersArgs):
1549
+ options = u.call_func(func, sqrl=sqrl)
1550
+ return cls.CreateWithOptions(
1551
+ name, label, options, description=description, input_type=input_type,
1552
+ user_attribute=user_attribute, parent_name=parent_name
1553
+ )
1554
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
1555
+ return wrapper
1556
+ return decorator
1557
+
1558
+ @classmethod
1559
+ def CreateSimple(
1560
+ cls, name: str, label: str, *, description: str = "", default_text: str = "", input_type: str = "text", **kwargs
1561
+ ):
1562
+ """
1563
+ Method for creating the configurations for a TextParameter that doesn't involve user attributes or parent parameters
1564
+
1565
+ .. deprecated::
1566
+ Use the lowercase decorator form `create_simple` instead.
1567
+
1568
+ Arguments:
1569
+ name: The name of the parameter
1570
+ label: The display label for the parameter
1571
+ description: Explains the meaning of the parameter
1572
+ default_text: Default input text for this option. Optional, default is empty string.
1573
+ input_type: The type of input field to use. Must be one of "text", "textarea", "number", "color", "date", "datetime-local", "month", "time", and "password". Optional, default is "text". More information on input types other than "textarea" can be found at https://www.w3schools.com/html/html_form_input_types.asp. More information on "textarea" can be found at https://www.w3schools.com/tags/tag_textarea.asp
1574
+ """
1575
+ single_param_option = po.TextParameterOption(default_text=default_text)
1576
+ return cls.CreateWithOptions(name, label, (single_param_option,), description=description, input_type=input_type)
1577
+
1578
+ @classmethod
1579
+ def create_simple(cls, name: str, label: str, *, description: str = "", default_text: str = "", input_type: str = "text"):
1580
+ """
1581
+ Python decorator for creating the configurations for a TextParameter that doesn't involve user attributes or parent parameters
1582
+
1583
+ Arguments:
1584
+ name: The name of the parameter
1585
+ label: The display label for the parameter
1586
+ description: Explains the meaning of the parameter
1587
+ default_text: Default input text for this option. Optional, default is empty string.
1588
+ input_type: The type of input field to use. Must be one of "text", "textarea", "number", "color", "date", "datetime-local", "month", "time", and "password". Optional, default is "text". More information on input types other than "textarea" can be found at https://www.w3schools.com/html/html_form_input_types.asp. More information on "textarea" can be found at https://www.w3schools.com/tags/tag_textarea.asp
1589
+ """
1590
+ def decorator(func: Callable[..., Any]):
1591
+ def wrapper(sqrl: ParametersArgs):
1592
+ return cls.CreateSimple(name, label, description=description, default_text=default_text, input_type=input_type)
1593
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
1594
+ return wrapper
1595
+ return decorator
1596
+
1597
+ @classmethod
1598
+ def CreateFromSource(
1599
+ cls, name: str, label: str, data_source: d.TextDataSource | dict, *, description: str = "",
1600
+ input_type: str = "text", user_attribute: str | None = None, parent_name: str | None = None, **kwargs
1601
+ ):
1602
+ """
1603
+ Method for creating the configurations for a TextParameter that uses a TextDataSource to receive the options
1604
+
1605
+ .. deprecated::
1606
+ Use the lowercase decorator form `create_from_source` instead.
1607
+
1608
+ Arguments:
1609
+ name: The name of the parameter
1610
+ label: The display label for the parameter
1611
+ data_source: The lookup table to use for this parameter
1612
+ description: Explains the meaning of the parameter
1613
+ input_type: The type of input field to use. Options are one of "text", "textarea", "number", "color", "date", "datetime-local", "month", "time", and "password". Optional, default is "text". More information on input types other than "textarea" can be found at https://www.w3schools.com/html/html_form_input_types.asp. More information on "textarea" can be found at https://www.w3schools.com/tags/tag_textarea.asp
1614
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
1615
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
1616
+ """
1617
+ extra_args = {
1618
+ "input_type": input_type
1619
+ }
1620
+ return cls._CreateFromSourceHelper(
1621
+ name, label, data_source, extra_args=extra_args, description=description, user_attribute=user_attribute, parent_name=parent_name
1622
+ )
1623
+
1624
+ @classmethod
1625
+ def create_from_source(
1626
+ cls, name: str, label: str, data_source: d.TextDataSource | dict, *, description: str = "",
1627
+ input_type: str = "text", user_attribute: str | None = None, parent_name: str | None = None
1628
+ ):
1629
+ """
1630
+ Python decorator for creating the configurations for a TextParameter that uses a TextDataSource to receive the options from a lookup table
1631
+
1632
+ The decorated function must return a TextDataSource object.
1633
+
1634
+ Arguments:
1635
+ name: The name of the parameter
1636
+ label: The display label for the parameter
1637
+ data_source: The lookup table to use for this parameter
1638
+ description: Explains the meaning of the parameter
1639
+ input_type: The type of input field to use. Options are one of "text", "textarea", "number", "color", "date", "datetime-local", "month", "time", and "password". Optional, default is "text". More information on input types other than "textarea" can be found at https://www.w3schools.com/html/html_form_input_types.asp. More information on "textarea" can be found at https://www.w3schools.com/tags/tag_textarea.asp
1640
+ user_attribute: The user attribute that may cascade the options for this parameter. Default is None
1641
+ parent_name: Name of parent parameter that may cascade the options for this parameter. Default is None (no parent)
1642
+ """
1643
+ def decorator(func: Callable[..., d.TextDataSource]):
1644
+ def wrapper(sqrl: ParametersArgs):
1645
+ data_source = u.call_func(func, sqrl=sqrl)
1646
+ return cls.CreateFromSource(
1647
+ name, label, data_source, description=description,
1648
+ input_type=input_type, user_attribute=user_attribute, parent_name=parent_name
1649
+ )
1650
+ ps.ParameterConfigsSetIO.param_factories.append(wrapper)
1651
+ return wrapper
1652
+ return decorator
1653
+
1654
+ def get_entered_text(self, **kwargs) -> TextValue:
1655
+ """
1656
+ Get the entered text. Returns a TextValue object that cannot be converted to string except through placeholders.
1657
+
1658
+ Returns:
1659
+ A TextValue object
1660
+ """
1661
+ assert isinstance(self._entered_text, str), "Parameter is not enabled"
1662
+ return TextValue(self._entered_text)
1663
+
1664
+ def get_entered_int(self, **kwargs) -> int:
1665
+ """
1666
+ Get the entered integer. The TextParameter must be a "number" input type
1667
+
1668
+ Returns: int
1669
+ """
1670
+ if self._config.input_type != "number":
1671
+ raise u.ConfigurationError("Method 'get_entered_int' requires TextParameter to have input type 'number'")
1672
+ text = self.get_entered_text()
1673
+ return text.apply_as_number(int)
1674
+
1675
+ def get_entered_datetime(self, **kwargs) -> datetime:
1676
+ """
1677
+ Get the entered datetime. The TextParameter input type must be one of ["date", "datetime-local", "month", "time"]
1678
+
1679
+ Returns: datetime
1680
+ """
1681
+ applicable_input_types = ["date", "datetime-local", "month", "time"]
1682
+ if self._config.input_type not in applicable_input_types:
1683
+ raise u.ConfigurationError(f"Method 'get_entered_datetime' requires TextParameter to have one of these input types: {applicable_input_types}")
1684
+ text = self.get_entered_text()
1685
+
1686
+ date_formats = { "date": "%Y-%m-%d", "datetime-local": "%Y-%m-%dT%H:%M", "month": "%Y-%m", "time": "%H:%M" }
1687
+ return text.apply_as_datetime(lambda x: datetime.strptime(x, date_formats[self._config.input_type]))
1688
+
1689
+ def _to_json_dict0(self):
1690
+ """
1691
+ Converts this parameter as a JSON object for the parameters API response
1692
+
1693
+ Returns:
1694
+ A dictionary for the JSON object
1695
+ """
1696
+ output = super()._to_json_dict0()
1697
+ output['input_type'] = self._config.input_type
1698
+ if self.is_enabled():
1699
+ output['entered_text'] = self._entered_text
1700
+ return output
1701
+
1702
+ def _get_response_model0(self):
1703
+ return rm.TextParameterModel if self.is_enabled() else rm.NoneParameterModel