squirrels 0.5.0b3__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 (93) hide show
  1. squirrels/__init__.py +4 -0
  2. squirrels/_api_routes/__init__.py +5 -0
  3. squirrels/_api_routes/auth.py +337 -0
  4. squirrels/_api_routes/base.py +196 -0
  5. squirrels/_api_routes/dashboards.py +156 -0
  6. squirrels/_api_routes/data_management.py +148 -0
  7. squirrels/_api_routes/datasets.py +220 -0
  8. squirrels/_api_routes/project.py +289 -0
  9. squirrels/_api_server.py +440 -792
  10. squirrels/_arguments/__init__.py +0 -0
  11. squirrels/_arguments/{_init_time_args.py → init_time_args.py} +23 -43
  12. squirrels/_arguments/{_run_time_args.py → run_time_args.py} +32 -68
  13. squirrels/_auth.py +590 -264
  14. squirrels/_command_line.py +130 -58
  15. squirrels/_compile_prompts.py +147 -0
  16. squirrels/_connection_set.py +16 -15
  17. squirrels/_constants.py +36 -11
  18. squirrels/_dashboards.py +179 -0
  19. squirrels/_data_sources.py +40 -34
  20. squirrels/_dataset_types.py +16 -11
  21. squirrels/_env_vars.py +209 -0
  22. squirrels/_exceptions.py +9 -37
  23. squirrels/_http_error_responses.py +52 -0
  24. squirrels/_initializer.py +7 -6
  25. squirrels/_logging.py +121 -0
  26. squirrels/_manifest.py +155 -77
  27. squirrels/_mcp_server.py +578 -0
  28. squirrels/_model_builder.py +11 -55
  29. squirrels/_model_configs.py +5 -5
  30. squirrels/_model_queries.py +1 -1
  31. squirrels/_models.py +276 -143
  32. squirrels/_package_data/base_project/.env +1 -24
  33. squirrels/_package_data/base_project/.env.example +31 -17
  34. squirrels/_package_data/base_project/connections.yml +4 -3
  35. squirrels/_package_data/base_project/dashboards/dashboard_example.py +13 -7
  36. squirrels/_package_data/base_project/dashboards/dashboard_example.yml +6 -6
  37. squirrels/_package_data/base_project/docker/Dockerfile +2 -2
  38. squirrels/_package_data/base_project/docker/compose.yml +1 -1
  39. squirrels/_package_data/base_project/duckdb_init.sql +1 -0
  40. squirrels/_package_data/base_project/models/builds/build_example.py +2 -2
  41. squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +7 -2
  42. squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +16 -10
  43. squirrels/_package_data/base_project/models/federates/federate_example.py +27 -17
  44. squirrels/_package_data/base_project/models/federates/federate_example.sql +3 -7
  45. squirrels/_package_data/base_project/models/federates/federate_example.yml +7 -7
  46. squirrels/_package_data/base_project/models/sources.yml +5 -6
  47. squirrels/_package_data/base_project/parameters.yml +24 -38
  48. squirrels/_package_data/base_project/pyconfigs/connections.py +8 -3
  49. squirrels/_package_data/base_project/pyconfigs/context.py +26 -14
  50. squirrels/_package_data/base_project/pyconfigs/parameters.py +124 -81
  51. squirrels/_package_data/base_project/pyconfigs/user.py +48 -15
  52. squirrels/_package_data/base_project/resources/public/.gitkeep +0 -0
  53. squirrels/_package_data/base_project/seeds/seed_categories.yml +1 -1
  54. squirrels/_package_data/base_project/seeds/seed_subcategories.yml +1 -1
  55. squirrels/_package_data/base_project/squirrels.yml.j2 +21 -31
  56. squirrels/_package_data/templates/login_successful.html +53 -0
  57. squirrels/_package_data/templates/squirrels_studio.html +22 -0
  58. squirrels/_parameter_configs.py +43 -22
  59. squirrels/_parameter_options.py +1 -1
  60. squirrels/_parameter_sets.py +41 -30
  61. squirrels/_parameters.py +560 -123
  62. squirrels/_project.py +487 -277
  63. squirrels/_py_module.py +71 -10
  64. squirrels/_request_context.py +33 -0
  65. squirrels/_schemas/__init__.py +0 -0
  66. squirrels/_schemas/auth_models.py +83 -0
  67. squirrels/_schemas/query_param_models.py +70 -0
  68. squirrels/_schemas/request_models.py +26 -0
  69. squirrels/_schemas/response_models.py +286 -0
  70. squirrels/_seeds.py +52 -13
  71. squirrels/_sources.py +29 -23
  72. squirrels/_utils.py +221 -42
  73. squirrels/_version.py +1 -3
  74. squirrels/arguments.py +7 -2
  75. squirrels/auth.py +4 -0
  76. squirrels/connections.py +2 -0
  77. squirrels/dashboards.py +3 -1
  78. squirrels/data_sources.py +6 -0
  79. squirrels/parameter_options.py +5 -0
  80. squirrels/parameters.py +5 -0
  81. squirrels/types.py +10 -3
  82. squirrels-0.6.0.post0.dist-info/METADATA +148 -0
  83. squirrels-0.6.0.post0.dist-info/RECORD +101 -0
  84. {squirrels-0.5.0b3.dist-info → squirrels-0.6.0.post0.dist-info}/WHEEL +1 -1
  85. squirrels/_api_response_models.py +0 -190
  86. squirrels/_dashboard_types.py +0 -82
  87. squirrels/_dashboards_io.py +0 -79
  88. squirrels-0.5.0b3.dist-info/METADATA +0 -110
  89. squirrels-0.5.0b3.dist-info/RECORD +0 -80
  90. /squirrels/_package_data/base_project/{assets → resources}/expenses.db +0 -0
  91. /squirrels/_package_data/base_project/{assets → resources}/weather.db +0 -0
  92. {squirrels-0.5.0b3.dist-info → squirrels-0.6.0.post0.dist-info}/entry_points.txt +0 -0
  93. {squirrels-0.5.0b3.dist-info → squirrels-0.6.0.post0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,179 @@
1
+ from typing import Type, TypeVar, Callable, Coroutine, Any
2
+ from typing_extensions import Self
3
+ from enum import Enum
4
+ from dataclasses import dataclass
5
+ from pydantic import BaseModel, Field, model_validator, ValidationError
6
+ import matplotlib.figure as figure
7
+ import os, time, io, abc, typing
8
+
9
+ from ._arguments.run_time_args import DashboardArgs
10
+ from ._py_module import PyModule
11
+ from ._manifest import AnalyticsOutputConfig, AuthType, PermissionScope, ConfigurableOverride
12
+ from ._exceptions import InvalidInputError, ConfigurationError, FileExecutionError
13
+ from . import _constants as c, _utils as u
14
+
15
+
16
+ class Dashboard(metaclass=abc.ABCMeta):
17
+ """
18
+ Abstract parent class for all Dashboard classes.
19
+ """
20
+
21
+ @property
22
+ @abc.abstractmethod
23
+ def _content(self) -> bytes | str:
24
+ pass
25
+
26
+ @property
27
+ @abc.abstractmethod
28
+ def _format(self) -> str:
29
+ pass
30
+
31
+
32
+ class PngDashboard(Dashboard):
33
+ """
34
+ Instantiate a Dashboard in PNG format from a matplotlib figure or bytes
35
+ """
36
+
37
+ def __init__(self, content: figure.Figure | io.BytesIO | bytes) -> None:
38
+ """
39
+ Constructor for PngDashboard
40
+
41
+ Arguments:
42
+ content: The content of the dashboard as a matplotlib.figure.Figure or bytes
43
+ """
44
+ if isinstance(content, figure.Figure):
45
+ buffer = io.BytesIO()
46
+ content.savefig(buffer, format=c.PNG)
47
+ content = buffer.getvalue()
48
+
49
+ if isinstance(content, io.BytesIO):
50
+ content = content.getvalue()
51
+
52
+ self.__content = content
53
+
54
+ @property
55
+ def _content(self) -> bytes:
56
+ return self.__content
57
+
58
+ @property
59
+ def _format(self) -> typing.Literal['png']:
60
+ return c.PNG
61
+
62
+ def _repr_png_(self):
63
+ return self._content
64
+
65
+
66
+ class HtmlDashboard(Dashboard):
67
+ """
68
+ Instantiate a Dashboard from an HTML string
69
+ """
70
+
71
+ def __init__(self, content: io.StringIO | str) -> None:
72
+ """
73
+ Constructor for HtmlDashboard
74
+
75
+ Arguments:
76
+ content: The content of the dashboard as HTML string
77
+ """
78
+ if isinstance(content, io.StringIO):
79
+ content = content.getvalue()
80
+
81
+ self.__content = content
82
+
83
+ @property
84
+ def _content(self) -> str:
85
+ return self.__content
86
+
87
+ @property
88
+ def _format(self) -> typing.Literal['html']:
89
+ return c.HTML
90
+
91
+ def _repr_html_(self):
92
+ return self._content
93
+
94
+
95
+ T = TypeVar('T', bound=Dashboard)
96
+
97
+
98
+ class DashboardFormat(Enum):
99
+ PNG = "png"
100
+ HTML = "html"
101
+
102
+ class DashboardDependencies(BaseModel):
103
+ name: str
104
+ dataset: str
105
+ fixed_parameters: dict[str, str] = Field(default_factory=dict)
106
+
107
+ class DashboardConfig(AnalyticsOutputConfig):
108
+ format: DashboardFormat = Field(default=DashboardFormat.PNG)
109
+ depends_on: list[DashboardDependencies] = Field(default_factory=list)
110
+
111
+
112
+ @dataclass
113
+ class DashboardDefinition:
114
+ dashboard_name: str
115
+ filepath: str
116
+ config: DashboardConfig
117
+ project_path: str
118
+
119
+ @property
120
+ def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, Dashboard]]:
121
+ if not hasattr(self, '_dashboard_func'):
122
+ module = PyModule(self.filepath, self.project_path)
123
+ self._dashboard_func = module.get_func_or_class(c.MAIN_FUNC)
124
+ return self._dashboard_func
125
+
126
+ def get_dashboard_format(self) -> str:
127
+ return self.config.format.value
128
+
129
+ async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = Dashboard) -> T:
130
+ try:
131
+ dashboard = await self.dashboard_func(args)
132
+ assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
133
+ except (InvalidInputError, ConfigurationError, FileExecutionError) as e:
134
+ raise e
135
+ except Exception as e:
136
+ raise FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
137
+
138
+ return dashboard
139
+
140
+
141
+ class DashboardsIO:
142
+
143
+ @classmethod
144
+ def load_files(
145
+ cls, logger: u.Logger, project_path: str, auth_type: AuthType = AuthType.OPTIONAL, project_configurables: dict[str, Any] | None = None
146
+ ) -> dict[str, DashboardDefinition]:
147
+ start = time.time()
148
+
149
+ if project_configurables is None:
150
+ project_configurables = {}
151
+
152
+ default_scope = PermissionScope.PROTECTED if auth_type == AuthType.REQUIRED else PermissionScope.PUBLIC
153
+
154
+ dashboards_by_name = {}
155
+ for dp, _, filenames in os.walk(u.Path(project_path, c.DASHBOARDS_FOLDER)):
156
+ for file in filenames:
157
+ filepath = os.path.join(dp, file)
158
+ file_stem, extension = os.path.splitext(file)
159
+ if not extension == '.py':
160
+ continue
161
+
162
+ # Check for corresponding .yml file
163
+ yml_path = os.path.join(dp, file_stem + '.yml')
164
+ config_dict = u.load_yaml_config(yml_path) if os.path.exists(yml_path) else {}
165
+ try:
166
+ config = DashboardConfig(name=file_stem, project_configurables=project_configurables, **config_dict)
167
+ except ValidationError as e:
168
+ raise ConfigurationError(f'Failed to process dashboard config "{file_stem}". ' + str(e)) from e
169
+
170
+ if config.scope is None:
171
+ config.scope = default_scope
172
+
173
+ if auth_type == AuthType.REQUIRED and config.scope == PermissionScope.PUBLIC:
174
+ raise ConfigurationError(f'Authentication is required, so dashboard "{file_stem}" cannot be public. Update the scope in "{yml_path}"')
175
+
176
+ dashboards_by_name[file_stem] = DashboardDefinition(file_stem, filepath, config, project_path)
177
+
178
+ logger.log_activity_time("loading files for dashboards", start)
179
+ return dashboards_by_name
@@ -1,10 +1,16 @@
1
1
  from __future__ import annotations
2
2
  from dataclasses import dataclass
3
- import polars as pl, typing as t, abc
3
+ from enum import Enum
4
+ import polars as pl, typing as t, abc, re
4
5
 
5
6
  from . import _parameter_configs as pc, _parameter_options as po
6
7
  from ._exceptions import ConfigurationError
7
8
 
9
+ class SourceEnum(Enum):
10
+ CONNECTION = "connection"
11
+ SEEDS = "seeds"
12
+ VDL = "vdl"
13
+
8
14
 
9
15
  @dataclass
10
16
  class DataSource(metaclass=abc.ABCMeta):
@@ -13,19 +19,19 @@ class DataSource(metaclass=abc.ABCMeta):
13
19
  """
14
20
  _table_or_query: str
15
21
  _id_col: str | None
16
- _is_from_seeds: bool
22
+ _source: SourceEnum
17
23
  _user_group_col: str | None
18
24
  _parent_id_col: str | None
19
25
  _connection: str | None
20
26
 
21
27
  @abc.abstractmethod
22
28
  def __init__(
23
- self, table_or_query: str, *, id_col: str | None = None, from_seeds: bool = False, user_group_col: str | None = None,
24
- parent_id_col: str | None = None, connection: str | None = None, **kwargs
29
+ self, table_or_query: str, *, id_col: str | None = None, source: SourceEnum = SourceEnum.CONNECTION,
30
+ user_group_col: str | None = None, parent_id_col: str | None = None, connection: str | None = None, **kwargs
25
31
  ) -> None:
26
32
  self._table_or_query = table_or_query
27
33
  self._id_col = id_col
28
- self._is_from_seeds = from_seeds
34
+ self._source = source
29
35
  self._user_group_col = user_group_col
30
36
  self._parent_id_col = parent_id_col
31
37
  self._connection = connection
@@ -40,7 +46,7 @@ class DataSource(metaclass=abc.ABCMeta):
40
46
  Returns:
41
47
  str: The converted select query
42
48
  """
43
- if self._table_or_query.strip().lower().startswith('select '):
49
+ if re.match(r'select\s', self._table_or_query.strip(), re.IGNORECASE):
44
50
  query = self._table_or_query
45
51
  else:
46
52
  query = f'SELECT * FROM {self._table_or_query}'
@@ -100,12 +106,12 @@ class _SelectionDataSource(DataSource):
100
106
  @abc.abstractmethod
101
107
  def __init__(
102
108
  self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: str | None = None,
103
- is_default_col: str | None = None, custom_cols: dict[str, str] = {}, from_seeds: bool = False,
109
+ is_default_col: str | None = None, custom_cols: dict[str, str] = {}, source: SourceEnum = SourceEnum.CONNECTION,
104
110
  user_group_col: str | None = None, parent_id_col: str | None = None, connection: str | None = None,
105
111
  **kwargs
106
112
  ) -> None:
107
113
  super().__init__(
108
- table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
114
+ table_or_query, id_col=id_col, source=source, user_group_col=user_group_col, parent_id_col=parent_id_col,
109
115
  connection=connection
110
116
  )
111
117
  self._options_col = options_col
@@ -151,7 +157,7 @@ class SelectDataSource(_SelectionDataSource):
151
157
 
152
158
  def __init__(
153
159
  self, table_or_query: str, id_col: str, options_col: str, *, order_by_col: str | None = None,
154
- is_default_col: str | None = None, custom_cols: dict[str, str] = {}, from_seeds: bool = False,
160
+ is_default_col: str | None = None, custom_cols: dict[str, str] = {}, source: SourceEnum = SourceEnum.CONNECTION,
155
161
  user_group_col: str | None = None, parent_id_col: str | None = None, connection: str | None = None,
156
162
  **kwargs
157
163
  ) -> None:
@@ -165,19 +171,19 @@ class SelectDataSource(_SelectionDataSource):
165
171
  order_by_col: The column name to order the options by. Orders by the id_col instead if this is None
166
172
  is_default_col: The column name that indicates which options are the default
167
173
  custom_cols: Dictionary of attribute to column name for custom fields for the SelectParameterOption
168
- from_seeds: Boolean for whether this datasource is created from seeds
174
+ source: The source to fetch data from. Must be "connection", "seeds", or "vdl". Defaults to "connection"
169
175
  user_group_col: The column name of the user group that the user is in for this option to be valid
170
176
  parent_id_col: The column name of the parent option id that must be selected for this option to be valid
171
177
  connection: Name of the connection to use defined in connections.py
172
178
  """
173
179
  super().__init__(
174
180
  table_or_query, id_col, options_col, order_by_col=order_by_col, is_default_col=is_default_col, custom_cols=custom_cols,
175
- from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col, connection=connection
181
+ source=source, user_group_col=user_group_col, parent_id_col=parent_id_col, connection=connection
176
182
  )
177
183
 
178
184
  def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pl.DataFrame) -> pc.SelectionParameterConfig:
179
185
  """
180
- Method to convert the associated DataSourceParameter into a SingleSelectParameterConfig or MultiSelectParameterConfig
186
+ Method to convert the associated DataSourceParameterConfig into a SingleSelectParameterConfig or MultiSelectParameterConfig
181
187
 
182
188
  Arguments:
183
189
  ds_param: The parameter to convert
@@ -212,7 +218,7 @@ class DateDataSource(DataSource):
212
218
  def __init__(
213
219
  self, table_or_query: str, default_date_col: str, *, min_date_col: str | None = None,
214
220
  max_date_col: str | None = None, date_format: str = '%Y-%m-%d', id_col: str | None = None,
215
- from_seeds: bool = False, user_group_col: str | None = None, parent_id_col: str | None = None,
221
+ source: SourceEnum = SourceEnum.CONNECTION, user_group_col: str | None = None, parent_id_col: str | None = None,
216
222
  connection: str | None = None, **kwargs
217
223
  ) -> None:
218
224
  """
@@ -223,13 +229,13 @@ class DateDataSource(DataSource):
223
229
  default_date_col: The column name of the default date
224
230
  date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
225
231
  id_col: The column name of the id
226
- from_seeds: Boolean for whether this datasource is created from seeds
232
+ source: The source to fetch data from. Must be "connection", "seeds", or "vdl". Defaults to "connection"
227
233
  user_group_col: The column name of the user group that the user is in for this option to be valid
228
234
  parent_id_col: The column name of the parent option id that the default date belongs to
229
235
  connection: Name of the connection to use defined in connections.py
230
236
  """
231
237
  super().__init__(
232
- table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
238
+ table_or_query, id_col=id_col, source=source, user_group_col=user_group_col, parent_id_col=parent_id_col,
233
239
  connection=connection
234
240
  )
235
241
  self._default_date_col = default_date_col
@@ -239,7 +245,7 @@ class DateDataSource(DataSource):
239
245
 
240
246
  def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pl.DataFrame) -> pc.DateParameterConfig:
241
247
  """
242
- Method to convert the associated DataSourceParameter into a DateParameterConfig
248
+ Method to convert the associated DataSourceParameterConfig into a DateParameterConfig
243
249
 
244
250
  Arguments:
245
251
  ds_param: The parameter to convert
@@ -281,7 +287,7 @@ class DateRangeDataSource(DataSource):
281
287
 
282
288
  def __init__(
283
289
  self, table_or_query: str, default_start_date_col: str, default_end_date_col: str, *, date_format: str = '%Y-%m-%d',
284
- min_date_col: str | None = None, max_date_col: str | None = None, id_col: str | None = None, from_seeds: bool = False,
290
+ min_date_col: str | None = None, max_date_col: str | None = None, id_col: str | None = None, source: SourceEnum = SourceEnum.CONNECTION,
285
291
  user_group_col: str | None = None, parent_id_col: str | None = None, connection: str | None = None, **kwargs
286
292
  ) -> None:
287
293
  """
@@ -293,13 +299,13 @@ class DateRangeDataSource(DataSource):
293
299
  default_end_date_col: The column name of the default end date
294
300
  date_format: The format of the default date(s). Defaults to '%Y-%m-%d'
295
301
  id_col: The column name of the id
296
- from_seeds: Boolean for whether this datasource is created from seeds
302
+ source: The source to fetch data from. Must be "connection", "seeds", or "vdl". Defaults to "connection"
297
303
  user_group_col: The column name of the user group that the user is in for this option to be valid
298
304
  parent_id_col: The column name of the parent option id that the default date belongs to
299
305
  connection: Name of the connection to use defined in connections.py
300
306
  """
301
307
  super().__init__(
302
- table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
308
+ table_or_query, id_col=id_col, source=source, user_group_col=user_group_col, parent_id_col=parent_id_col,
303
309
  connection=connection
304
310
  )
305
311
  self._default_start_date_col = default_start_date_col
@@ -310,7 +316,7 @@ class DateRangeDataSource(DataSource):
310
316
 
311
317
  def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pl.DataFrame) -> pc.DateRangeParameterConfig:
312
318
  """
313
- Method to convert the associated DataSourceParameter into a DateRangeParameterConfig
319
+ Method to convert the associated DataSourceParameterConfig into a DateRangeParameterConfig
314
320
 
315
321
  Arguments:
316
322
  ds_param: The parameter to convert
@@ -354,11 +360,11 @@ class _NumericDataSource(DataSource):
354
360
  @abc.abstractmethod
355
361
  def __init__(
356
362
  self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: str | None = None,
357
- id_col: str | None = None, from_seeds: bool = False, user_group_col: str | None = None,
363
+ id_col: str | None = None, source: SourceEnum = SourceEnum.CONNECTION, user_group_col: str | None = None,
358
364
  parent_id_col: str | None = None, connection: str | None = None, **kwargs
359
365
  ) -> None:
360
366
  super().__init__(
361
- table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
367
+ table_or_query, id_col=id_col, source=source, user_group_col=user_group_col, parent_id_col=parent_id_col,
362
368
  connection=connection
363
369
  )
364
370
  self._min_value_col = min_value_col
@@ -375,7 +381,7 @@ class NumberDataSource(_NumericDataSource):
375
381
 
376
382
  def __init__(
377
383
  self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: str | None = None,
378
- default_value_col: str | None = None, id_col: str | None = None, from_seeds: bool = False,
384
+ default_value_col: str | None = None, id_col: str | None = None, source: SourceEnum = SourceEnum.CONNECTION,
379
385
  user_group_col: str | None = None, parent_id_col: str | None = None, connection: str | None = None, **kwargs
380
386
  ) -> None:
381
387
  """
@@ -388,20 +394,20 @@ class NumberDataSource(_NumericDataSource):
388
394
  increment_col: The column name of the increment value. Defaults to column of 1's if None
389
395
  default_value_col: The column name of the default value. Defaults to min_value_col if None
390
396
  id_col: The column name of the id
391
- from_seeds: Boolean for whether this datasource is created from seeds
397
+ source: The source to fetch data from. Must be "connection", "seeds", or "vdl". Defaults to "connection"
392
398
  user_group_col: The column name of the user group that the user is in for this option to be valid
393
399
  parent_id_col: The column name of the parent option id that the default value belongs to
394
400
  connection: Name of the connection to use defined in connections.py
395
401
  """
396
402
  super().__init__(
397
- table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col, from_seeds=from_seeds,
403
+ table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col, source=source,
398
404
  user_group_col=user_group_col, parent_id_col=parent_id_col, connection=connection
399
405
  )
400
406
  self._default_value_col = default_value_col
401
407
 
402
408
  def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pl.DataFrame) -> pc.NumberParameterConfig:
403
409
  """
404
- Method to convert the associated DataSourceParameter into a NumberParameterConfig
410
+ Method to convert the associated DataSourceParameterConfig into a NumberParameterConfig
405
411
 
406
412
  Arguments:
407
413
  ds_param: The parameter to convert
@@ -443,7 +449,7 @@ class NumberRangeDataSource(_NumericDataSource):
443
449
  def __init__(
444
450
  self, table_or_query: str, min_value_col: str, max_value_col: str, *, increment_col: str | None = None,
445
451
  default_lower_value_col: str | None = None, default_upper_value_col: str | None = None, id_col: str | None = None,
446
- from_seeds: bool = False, user_group_col: str | None = None, parent_id_col: str | None = None,
452
+ source: SourceEnum = SourceEnum.CONNECTION, user_group_col: str | None = None, parent_id_col: str | None = None,
447
453
  connection: str | None = None, **kwargs
448
454
  ) -> None:
449
455
  """
@@ -457,13 +463,13 @@ class NumberRangeDataSource(_NumericDataSource):
457
463
  default_lower_value_col: The column name of the default lower value. Defaults to min_value_col if None
458
464
  default_upper_value_col: The column name of the default upper value. Defaults to max_value_col if None
459
465
  id_col: The column name of the id
460
- from_seeds: Boolean for whether this datasource is created from seeds
466
+ source: The source to fetch data from. Must be "connection", "seeds", or "vdl". Defaults to "connection"
461
467
  user_group_col: The column name of the user group that the user is in for this option to be valid
462
468
  parent_id_col: The column name of the parent option id that the default value belongs to
463
469
  connection: Name of the connection to use defined in connections.py
464
470
  """
465
471
  super().__init__(
466
- table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col, from_seeds=from_seeds,
472
+ table_or_query, min_value_col, max_value_col, increment_col=increment_col, id_col=id_col, source=source,
467
473
  user_group_col=user_group_col, parent_id_col=parent_id_col, connection=connection
468
474
  )
469
475
  self._default_lower_value_col = default_lower_value_col
@@ -471,7 +477,7 @@ class NumberRangeDataSource(_NumericDataSource):
471
477
 
472
478
  def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pl.DataFrame) -> pc.NumberRangeParameterConfig:
473
479
  """
474
- Method to convert the associated DataSourceParameter into a NumberRangeParameterConfig
480
+ Method to convert the associated DataSourceParameterConfig into a NumberRangeParameterConfig
475
481
 
476
482
  Arguments:
477
483
  ds_param: The parameter to convert
@@ -511,7 +517,7 @@ class TextDataSource(DataSource):
511
517
  _default_text_col: str
512
518
 
513
519
  def __init__(
514
- self, table_or_query: str, default_text_col: str, *, id_col: str | None = None, from_seeds: bool = False,
520
+ self, table_or_query: str, default_text_col: str, *, id_col: str | None = None, source: SourceEnum = SourceEnum.CONNECTION,
515
521
  user_group_col: str | None = None, parent_id_col: str | None = None, connection: str | None = None,
516
522
  **kwargs
517
523
  ) -> None:
@@ -522,20 +528,20 @@ class TextDataSource(DataSource):
522
528
  table_or_query: Either the name of the table to use, or a query to run
523
529
  default_text_col: The column name of the default text
524
530
  id_col: The column name of the id
525
- from_seeds: Boolean for whether this datasource is created from seeds
531
+ source: The source to fetch data from. Must be "connection", "seeds", or "vdl". Defaults to "connection"
526
532
  user_group_col: The column name of the user group that the user is in for this option to be valid
527
533
  parent_id_col: The column name of the parent option id that the default date belongs to
528
534
  connection: Name of the connection to use defined in connections.py
529
535
  """
530
536
  super().__init__(
531
- table_or_query, id_col=id_col, from_seeds=from_seeds, user_group_col=user_group_col, parent_id_col=parent_id_col,
537
+ table_or_query, id_col=id_col, source=source, user_group_col=user_group_col, parent_id_col=parent_id_col,
532
538
  connection=connection
533
539
  )
534
540
  self._default_text_col = default_text_col
535
541
 
536
542
  def _convert(self, ds_param: pc.DataSourceParameterConfig, df: pl.DataFrame) -> pc.TextParameterConfig:
537
543
  """
538
- Method to convert the associated DataSourceParameter into a TextParameterConfig
544
+ Method to convert the associated DataSourceParameterConfig into a TextParameterConfig
539
545
 
540
546
  Arguments:
541
547
  ds_param: The parameter to convert
@@ -32,29 +32,34 @@ class DatasetMetadata:
32
32
  return self._json_repr
33
33
 
34
34
 
35
+ @dataclass(frozen=True)
36
+ class DatasetResultFormat:
37
+ orientation: Literal["records", "rows", "columns"]
38
+ offset: int
39
+ limit: int | None
40
+
41
+
35
42
  @dataclass
36
43
  class DatasetResult(DatasetMetadata):
37
44
  df: pl.DataFrame
38
- to_json: Callable[[str, tuple[str, ...], int, int], dict] = field(init=False)
45
+ to_json: Callable[[DatasetResultFormat], dict] = field(init=False)
39
46
 
40
47
  def __post_init__(self):
41
48
  self.to_json = lru_cache()(self._to_json)
42
49
 
43
- def _to_json(self, orientation: Literal["records", "rows", "columns"], select: tuple[str, ...], limit: int, offset: int) -> dict:
50
+ def _to_json(self, result_format: DatasetResultFormat) -> dict:
44
51
  df = self.df.lazy()
45
- if offset > 0:
46
- df = df.filter(pl.col("_row_num") > offset)
47
- if limit > 0:
48
- df = df.limit(limit)
49
- if select:
50
- df = df.select(select)
52
+ if result_format.offset > 0:
53
+ df = df.filter(pl.col("_row_num") > result_format.offset)
54
+ if result_format.limit is not None:
55
+ df = df.limit(result_format.limit)
51
56
  df = df.collect()
52
57
 
53
- if orientation == "columns":
58
+ if result_format.orientation == "columns":
54
59
  data = df.to_dict(as_series=False)
55
60
  else:
56
61
  data = df.to_dicts()
57
- if orientation == "rows":
62
+ if result_format.orientation == "rows":
58
63
  data = [[row[col] for col in df.columns] for row in data]
59
64
 
60
65
  column_details_by_name = {col.name: col for col in self.target_model_config.columns}
@@ -80,7 +85,7 @@ class DatasetResult(DatasetMetadata):
80
85
  "total_num_rows": self.df.select(pl.len()).item(),
81
86
  "data_details": {
82
87
  "num_rows": df.select(pl.len()).item(),
83
- "orientation": orientation
88
+ "orientation": result_format.orientation
84
89
  },
85
90
  "data": data
86
91
  }