squirrels 0.5.0b4__py3-none-any.whl → 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of squirrels might be problematic. Click here for more details.

Files changed (69) hide show
  1. squirrels/__init__.py +2 -0
  2. squirrels/_api_routes/auth.py +83 -74
  3. squirrels/_api_routes/base.py +58 -41
  4. squirrels/_api_routes/dashboards.py +37 -21
  5. squirrels/_api_routes/data_management.py +72 -27
  6. squirrels/_api_routes/datasets.py +107 -84
  7. squirrels/_api_routes/oauth2.py +11 -13
  8. squirrels/_api_routes/project.py +71 -33
  9. squirrels/_api_server.py +130 -63
  10. squirrels/_arguments/run_time_args.py +9 -9
  11. squirrels/_auth.py +117 -162
  12. squirrels/_command_line.py +68 -32
  13. squirrels/_compile_prompts.py +147 -0
  14. squirrels/_connection_set.py +11 -2
  15. squirrels/_constants.py +22 -8
  16. squirrels/_data_sources.py +38 -32
  17. squirrels/_dataset_types.py +2 -4
  18. squirrels/_initializer.py +1 -1
  19. squirrels/_logging.py +117 -0
  20. squirrels/_manifest.py +125 -58
  21. squirrels/_model_builder.py +10 -54
  22. squirrels/_models.py +224 -108
  23. squirrels/_package_data/base_project/.env +15 -4
  24. squirrels/_package_data/base_project/.env.example +14 -3
  25. squirrels/_package_data/base_project/connections.yml +4 -3
  26. squirrels/_package_data/base_project/dashboards/dashboard_example.py +2 -2
  27. squirrels/_package_data/base_project/dashboards/dashboard_example.yml +4 -4
  28. squirrels/_package_data/base_project/duckdb_init.sql +1 -0
  29. squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +7 -2
  30. squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +16 -10
  31. squirrels/_package_data/base_project/models/federates/federate_example.py +22 -15
  32. squirrels/_package_data/base_project/models/federates/federate_example.sql +3 -7
  33. squirrels/_package_data/base_project/models/federates/federate_example.yml +1 -1
  34. squirrels/_package_data/base_project/models/sources.yml +5 -6
  35. squirrels/_package_data/base_project/parameters.yml +24 -38
  36. squirrels/_package_data/base_project/pyconfigs/connections.py +5 -1
  37. squirrels/_package_data/base_project/pyconfigs/context.py +23 -12
  38. squirrels/_package_data/base_project/pyconfigs/parameters.py +68 -33
  39. squirrels/_package_data/base_project/pyconfigs/user.py +11 -18
  40. squirrels/_package_data/base_project/seeds/seed_categories.yml +1 -1
  41. squirrels/_package_data/base_project/seeds/seed_subcategories.yml +1 -1
  42. squirrels/_package_data/base_project/squirrels.yml.j2 +18 -28
  43. squirrels/_package_data/templates/squirrels_studio.html +20 -0
  44. squirrels/_parameter_configs.py +43 -22
  45. squirrels/_parameter_options.py +1 -1
  46. squirrels/_parameter_sets.py +8 -10
  47. squirrels/_project.py +351 -234
  48. squirrels/_request_context.py +33 -0
  49. squirrels/_schemas/auth_models.py +32 -9
  50. squirrels/_schemas/query_param_models.py +9 -1
  51. squirrels/_schemas/response_models.py +36 -10
  52. squirrels/_seeds.py +1 -1
  53. squirrels/_sources.py +23 -19
  54. squirrels/_utils.py +83 -35
  55. squirrels/_version.py +1 -1
  56. squirrels/arguments.py +5 -0
  57. squirrels/auth.py +4 -1
  58. squirrels/connections.py +2 -0
  59. squirrels/dashboards.py +3 -1
  60. squirrels/data_sources.py +6 -0
  61. squirrels/parameter_options.py +5 -0
  62. squirrels/parameters.py +5 -0
  63. squirrels/types.py +6 -1
  64. {squirrels-0.5.0b4.dist-info → squirrels-0.5.1.dist-info}/METADATA +28 -13
  65. squirrels-0.5.1.dist-info/RECORD +98 -0
  66. squirrels-0.5.0b4.dist-info/RECORD +0 -94
  67. {squirrels-0.5.0b4.dist-info → squirrels-0.5.1.dist-info}/WHEEL +0 -0
  68. {squirrels-0.5.0b4.dist-info → squirrels-0.5.1.dist-info}/entry_points.txt +0 -0
  69. {squirrels-0.5.0b4.dist-info → squirrels-0.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,55 +1,75 @@
1
- from squirrels import arguments as args, parameters as p, parameter_options as po, data_sources as ds
1
+ from squirrels import parameters as p, parameter_options as po, data_sources as ds
2
2
 
3
3
 
4
4
  ## Example of creating SingleSelectParameter and specifying each option by code
5
5
  @p.SingleSelectParameter.create_with_options(
6
- "group_by", "Group By", description="Dimension(s) to aggregate by", user_attribute="role"
6
+ name="group_by", label="Group By",
7
+ description="Dimension(s) to aggregate by",
8
+ user_attribute="access_level"
7
9
  )
8
10
  def group_by_options():
9
11
  return [
10
12
  po.SelectParameterOption(
11
- "trans", "Transaction",
13
+ id="trans", label="Transaction",
12
14
  columns=["id","date","category","subcategory","description"],
13
- aliases=["_id","date","category","subcategory","description"], # any alias starting with "_" will not be selected - see context.py for implementation
14
- user_groups=["manager"]
15
+ aliases=["_id","date","category","subcategory","description"], # in context.py, any alias starting with "_" will not be selected
16
+ user_groups=["admin"]
17
+ ),
18
+ po.SelectParameterOption(
19
+ id="day", label="Day",
20
+ columns=["date"],
21
+ aliases=["day"],
22
+ user_groups=["admin","member"]
23
+ ),
24
+ po.SelectParameterOption(
25
+ id="month", label="Month",
26
+ columns=["month"],
27
+ user_groups=["admin","member","guest"]
28
+ ),
29
+ po.SelectParameterOption(
30
+ id="cat", label="Category",
31
+ columns=["category"],
32
+ user_groups=["admin","member","guest"]
33
+ ),
34
+ po.SelectParameterOption(
35
+ id="subcat", label="Subcategory",
36
+ columns=["category","subcategory"],
37
+ user_groups=["admin","member","guest"]
15
38
  ),
16
- po.SelectParameterOption("day" , "Day" , columns=["date"], aliases=["day"] , user_groups=["manager","employee"]),
17
- po.SelectParameterOption("month" , "Month" , columns=["month"] , user_groups=["manager","employee"]),
18
- po.SelectParameterOption("cat" , "Category" , columns=["category"] , user_groups=["manager","employee"]),
19
- po.SelectParameterOption("subcat" , "Subcategory" , columns=["category","subcategory"] , user_groups=["manager","employee"]),
20
39
  ]
21
40
 
22
41
 
23
- ## Example of creating NumberParameter with options
24
- @p.NumberParameter.create_with_options(
25
- "limit", "Max Number of Rows", description="Maximum number of rows to return", parent_name="group_by"
26
- )
27
- def limit_options():
28
- return [po.NumberParameterOption(0, 1000, increment=10, default_value=1000, parent_option_ids="trans")]
29
-
30
-
31
42
  ## Example of creating DateParameter
32
43
  @p.DateParameter.create_from_source(
33
- "start_date", "Start Date", description="Start date to filter transactions by"
44
+ name="start_date", label="Start Date",
45
+ description="Start date to filter transactions by"
34
46
  )
35
47
  def start_date_source():
36
48
  return ds.DateDataSource(
37
- "SELECT min(date) AS min_date, max(date) AS max_date FROM expenses",
38
- default_date_col="min_date", min_date_col="min_date", max_date_col="max_date"
49
+ table_or_query="SELECT min(date) AS min_date, max(date) AS max_date FROM expenses",
50
+ default_date_col="min_date",
51
+ min_date_col="min_date", max_date_col="max_date",
39
52
  )
40
53
 
41
54
 
42
55
  ## Example of creating DateParameter from list of DateParameterOption's
43
56
  @p.DateParameter.create_with_options(
44
- "end_date", "End Date", description="End date to filter transactions by"
57
+ name="end_date", label="End Date",
58
+ description="End date to filter transactions by"
45
59
  )
46
60
  def end_date_options():
47
- return [po.DateParameterOption("2024-12-31", min_date="2024-01-01", max_date="2024-12-31")]
61
+ return [
62
+ po.DateParameterOption(
63
+ default_date="2024-12-31", min_date="2024-01-01", max_date="2024-12-31"
64
+ )
65
+ ]
48
66
 
49
67
 
50
68
  ## Example of creating DateRangeParameter
51
69
  @p.DateRangeParameter.create_simple(
52
- "date_range", "Date Range", "2024-01-01", "2024-12-31", min_date="2024-01-01", max_date="2024-12-31",
70
+ name="date_range", label="Date Range",
71
+ default_start_date="2024-01-01", default_end_date="2024-12-31",
72
+ min_date="2024-01-01", max_date="2024-12-31",
53
73
  description="Date range to filter transactions by"
54
74
  )
55
75
  def date_range_options():
@@ -58,27 +78,38 @@ def date_range_options():
58
78
 
59
79
  ## Example of creating MultiSelectParameter from lookup query/table
60
80
  @p.MultiSelectParameter.create_from_source(
61
- "category", "Category Filter",
81
+ name="category", label="Category Filter",
62
82
  description="The expense categories to filter transactions by"
63
83
  )
64
84
  def category_source():
65
- return ds.SelectDataSource("seed_categories", "category_id", "category", from_seeds=True)
85
+ return ds.SelectDataSource(
86
+ table_or_query="seed_categories",
87
+ id_col="category_id",
88
+ options_col="category",
89
+ source=ds.SourceEnum.SEEDS
90
+ )
66
91
 
67
92
 
68
93
  ## Example of creating MultiSelectParameter with parent from lookup query/table
69
94
  @p.MultiSelectParameter.create_from_source(
70
- "subcategory", "Subcategory Filter", parent_name="category",
71
- description="The expense subcategories to filter transactions by (available options are based on selected value(s) of 'Category Filter')"
95
+ name="subcategory", label="Subcategory Filter",
96
+ description="The expense subcategories to filter transactions by (available options are based on selected value(s) of 'Category Filter')",
97
+ parent_name="category"
72
98
  )
73
99
  def subcategory_source():
74
100
  return ds.SelectDataSource(
75
- "seed_subcategories", "subcategory_id", "subcategory", from_seeds=True, parent_id_col="category_id"
101
+ table_or_query="seed_subcategories",
102
+ id_col="subcategory_id",
103
+ options_col="subcategory",
104
+ source=ds.SourceEnum.SEEDS,
105
+ parent_id_col="category_id"
76
106
  )
77
107
 
78
108
 
79
109
  ## Example of creating NumberParameter
80
110
  @p.NumberParameter.create_simple(
81
- "min_filter", "Amounts Greater Than", min_value=0, max_value=300, increment=10,
111
+ name="min_filter", label="Amounts Greater Than",
112
+ min_value=0, max_value=300, increment=10,
82
113
  description="Number to filter on transactions with an amount greater than this value"
83
114
  )
84
115
  def min_filter_options():
@@ -87,19 +118,23 @@ def min_filter_options():
87
118
 
88
119
  ## Example of creating NumberParameter from lookup query/table
89
120
  @p.NumberParameter.create_from_source(
90
- "max_filter", "Amounts Less Than",
121
+ name="max_filter", label="Amounts Less Than",
91
122
  description="Number to filter on transactions with an amount less than this value"
92
123
  )
93
124
  def max_filter_source():
94
- query = "SELECT 0 as min_value, 300 as max_value, 10 as increment"
95
125
  return ds.NumberDataSource(
96
- query, "min_value", "max_value", increment_col="increment", default_value_col="max_value"
126
+ table_or_query="SELECT 0 as min_value, 300 as max_value, 10 as increment",
127
+ min_value_col="min_value", max_value_col="max_value",
128
+ increment_col="increment",
129
+ default_value_col="max_value"
97
130
  )
98
131
 
99
132
 
100
133
  ## Example of creating NumberRangeParameter
101
134
  @p.NumberRangeParameter.create_simple(
102
- "between_filter", "Amounts Between", min_value=0, max_value=300, default_lower_value=0, default_upper_value=300,
135
+ name="between_filter", label="Amounts Between",
136
+ min_value=0, max_value=300,
137
+ default_lower_value=0, default_upper_value=300,
103
138
  description="Number range to filter on transactions with an amount within this range"
104
139
  )
105
140
  def between_filter_options():
@@ -2,25 +2,17 @@ from typing import Literal
2
2
  from squirrels import auth, arguments as args
3
3
 
4
4
 
5
- class User(auth.BaseUser):
5
+ class CustomUserFields(auth.CustomUserFields):
6
6
  """
7
- Extend the BaseUser class with custom attributes. The attributes defined here will be added as columns to the users table.
7
+ Extend the CustomUserFields class to add custom user attributes.
8
8
  - Only the following types are supported: [str, int, float, bool, typing.Literal]
9
- - For str, int, and float types, add "| None" after the type to make it nullable.
10
- - Always set a default value for the column (use None if default is null).
9
+ - Add "| None" after the type to make it nullable.
10
+ - Always set a default value for the field (use None if default is null).
11
11
 
12
12
  Example:
13
13
  organization: str | None = None
14
14
  """
15
- role: Literal["manager", "employee"] = "employee"
16
-
17
- @classmethod
18
- def dropped_columns(cls) -> list[str]:
19
- """
20
- The fields defined above cannot be modified once added to the database.
21
- However, you can choose to drop columns by adding them to this list.
22
- """
23
- return []
15
+ role: Literal["manager", "staff", "customer"] = "staff"
24
16
 
25
17
 
26
18
  # @auth.provider(name="google", label="Google", icon="https://www.google.com/favicon.ico")
@@ -31,11 +23,12 @@ def google_auth_provider(sqrl: args.AuthProviderArgs) -> auth.ProviderConfigs:
31
23
  See the following page for setting up the CLIENT_ID and CLIENT_SECRET for Google specifically:
32
24
  https://support.google.com/googleapi/answer/6158849?hl=en
33
25
  """
34
- def get_sqrl_user(claims: dict) -> User:
35
- return User(
26
+ def get_sqrl_user(claims: dict) -> auth.RegisteredUser:
27
+ custom_fields = CustomUserFields(role="customer")
28
+ return auth.RegisteredUser(
36
29
  username=claims["email"],
37
- is_admin=False,
38
- role="employee"
30
+ access_level="member",
31
+ custom_fields=custom_fields
39
32
  )
40
33
 
41
34
  # TODO: Add GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to the .env file
@@ -43,7 +36,7 @@ def google_auth_provider(sqrl: args.AuthProviderArgs) -> auth.ProviderConfigs:
43
36
  provider_configs = auth.ProviderConfigs(
44
37
  client_id="", # sqrl.env_vars["GOOGLE_CLIENT_ID"],
45
38
  client_secret="", # sqrl.env_vars["GOOGLE_CLIENT_SECRET"],
46
- server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
39
+ server_url="https://accounts.google.com",
47
40
  client_kwargs={"scope": "openid email profile"},
48
41
  get_user=get_sqrl_user
49
42
  )
@@ -1,7 +1,7 @@
1
1
  description: |
2
2
  Lookup table for the category IDs and names of transactions.
3
3
 
4
- cast_column_types: true # optional, default is false - setting SQRL_SEEDS__INFER_SCHEMA is ignored for this seed if this is set to true
4
+ cast_column_types: true # optional, default is false - if set to true, then SQRL_SEEDS__INFER_SCHEMA is ignored for this seed
5
5
 
6
6
  columns:
7
7
  - name: category_id
@@ -1,7 +1,7 @@
1
1
  description: |
2
2
  Lookup table for the subcategory IDs and names of transactions.
3
3
 
4
- cast_column_types: true # optional, default is false - setting SQRL_SEEDS__INFER_SCHEMA is ignored for this seed if this is set to true
4
+ cast_column_types: true # optional, default is false - if set to true, then SQRL_SEEDS__INFER_SCHEMA is ignored for this seed
5
5
 
6
6
  columns:
7
7
  - name: subcategory_id
@@ -1,6 +1,6 @@
1
1
  project_variables:
2
- name: sample
3
- label: Sample Project - Expenses
2
+ name: sample_expenses
3
+ label: "Sample Expenses"
4
4
  description: This is a sample squirrels project for analyzing expense transactions
5
5
  major_version: 1
6
6
 
@@ -21,51 +21,41 @@ packages: []
21
21
 
22
22
 
23
23
  datasets:
24
- - name: dbview_dataset_example ## model name uses same name unless "model" field is specified
25
- label: Example Dataset from DBView Model
26
- description: Aggregated expense transactions by month using dbview_example model
24
+ - name: expense_transactions ## model name uses same name unless "model" field is specified
25
+ label: Expense Transactions - DBView Example
26
+ description: All expense transactions
27
27
  model: dbview_example
28
- scope: public ## optional - one of 'public' (default), 'protected', or 'private'
28
+ scope: private ## optional - one of 'public' (default), 'protected', or 'private'
29
29
  parameters: ## optional - if not specified, then all parameters are used
30
30
  - start_date
31
31
  - end_date
32
32
  - min_filter
33
33
  - max_filter
34
- traits: {} ## optional - defaults to empty object
35
34
 
36
- - name: federate_dataset_example
37
- label: Example Dataset from Federate Model
35
+ - name: grouped_expenses
36
+ label: Grouped Expenses - Federate Example
38
37
  description: Aggregated expense transactions by custom dimension using federate_example model
39
38
  model: federate_example
40
- scope: protected ## using an auth.py file is suggested for protected or private datasets
39
+ scope: public ## using an auth.py file is suggested for protected or private datasets
41
40
  parameters:
42
41
  - group_by
43
- - limit
44
42
  - date_range
45
43
  - category
46
44
  - subcategory
47
45
  - between_filter
48
- default_test_set: auth_test1 ## optional - if not specified, uses setting 'selection_test_sets.default_name_used'
49
46
 
50
47
 
51
48
  selection_test_sets:
52
- - name: no_auth_test1
53
- datasets: ## optional section - if not provided, then test set is applicable for any dataset
54
- - dbview_dataset_example
55
- parameters: ## optional section - if not provided, then assumes no parameters. For unspecified parameters, default value is used
56
- start_date: 2024-07-01 ## this parameter only exists for dataset 'dataset_example'
49
+ - name: set_start_date
50
+ parameters: ## optional section - if not provided, then default value is used for all parameters
51
+ start_date: 2024-07-01 ## this parameter is only used by model "dbview_example"
57
52
 
58
- - name: auth_test1
59
- datasets:
60
- - federate_dataset_example
61
- user_attributes: ## optional section - required if using test set on non-public datasets
62
- role: employee
53
+ - name: set_date_range
63
54
  parameters:
64
- date_range: [2024-02-01,2024-12-01] ## this parameter only exists for dataset 'protected_dataset_example'
55
+ date_range: [2024-02-01,2024-11-30] ## this parameter is only used by model "federate_example"
65
56
 
66
- - name: auth_test2
67
- user_attributes:
68
- is_internal: True ## optional - defaults to False
69
- role: manager
57
+ - name: use_admin_privileged_group_by
58
+ user:
59
+ access_level: admin
70
60
  parameters:
71
- group_by: g3 ## this parameter exists for all datasets. "g3" is the id for option "subcategory"
61
+ group_by: trans
@@ -0,0 +1,20 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link id="favicon" rel="icon" type="image/x-icon" href="{{ sqrl_studio_base_url }}/favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Squirrels Studio</title>
8
+ <script>
9
+ // Optional: Set global defaults for project parameters
10
+ window.DEFAULT_HOSTNAME = '';
11
+ window.DEFAULT_PROJECT_NAME = '{{ project_name }}';
12
+ window.DEFAULT_PROJECT_VERSION = '{{ project_version }}';
13
+ </script>
14
+ <script type="module" crossorigin src="{{ sqrl_studio_base_url }}/assets/index.js"></script>
15
+ <link rel="stylesheet" crossorigin href="{{ sqrl_studio_base_url }}/assets/index.css">
16
+ </head>
17
+ <body>
18
+ <div id="root"></div>
19
+ </body>
20
+ </html>
@@ -11,7 +11,7 @@ import polars as pl, re
11
11
 
12
12
  from . import _data_sources as d, _parameter_options as po, _parameters as p, _utils as u, _constants as c
13
13
  from ._exceptions import InvalidInputError
14
- from ._auth import BaseUser
14
+ from ._schemas.auth_models import AbstractUser
15
15
  from ._connection_set import ConnectionSet
16
16
  from ._seeds import Seeds
17
17
 
@@ -56,12 +56,20 @@ class ParameterConfigBase(metaclass=ABCMeta):
56
56
  user_attribute: str | None = field(default=None, kw_only=True)
57
57
  parent_name: str | None = field(default=None, kw_only=True)
58
58
 
59
- def _get_user_group(self, user: BaseUser | None) -> Any:
60
- if self.user_attribute is not None:
61
- if user is None:
62
- raise u.ConfigurationError(f"Public datasets (accessible without authentication) cannot use parameter " +
63
- f"'{self.name}' because 'user_attribute' is defined on this parameter.")
64
- return getattr(user, self.user_attribute)
59
+ def _get_user_group(self, user: AbstractUser) -> Any:
60
+ if self.user_attribute is None:
61
+ return None
62
+
63
+ final_object = user
64
+ attribute = self.user_attribute
65
+ try:
66
+ if "." in attribute:
67
+ parts = attribute.split(".", 1)
68
+ final_object = getattr(final_object, parts[0])
69
+ attribute = parts[1]
70
+ return getattr(final_object, attribute)
71
+ except AttributeError:
72
+ raise u.ConfigurationError(f"User attribute '{self.user_attribute}' is not valid")
65
73
 
66
74
  def copy(self):
67
75
  """
@@ -108,16 +116,16 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
108
116
  pass
109
117
 
110
118
  def _invalid_input_error(self, selection: str, more_details: str = '') -> InvalidInputError:
111
- return InvalidInputError(400, "Invalid parameter selection", f'Selected value "{selection}" is not valid for parameter "{self.name}". ' + more_details)
119
+ return InvalidInputError(400, "invalid_parameter_selection", f'Selected value "{selection}" is not valid for parameter "{self.name}". ' + more_details)
112
120
 
113
121
  @abstractmethod
114
122
  def with_selection(
115
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
123
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
116
124
  ) -> p.Parameter:
117
125
  pass
118
126
 
119
127
  def _get_options_iterator(
120
- self, all_options: Sequence[ParamOptionType], user: BaseUser | None, parent_param: p._SelectionParameter | None
128
+ self, all_options: Sequence[ParamOptionType], user: AbstractUser, parent_param: p._SelectionParameter | None
121
129
  ) -> Iterator[ParamOptionType]:
122
130
  user_group = self._get_user_group(user)
123
131
  selected_parent_option_ids = frozenset(parent_param._get_selected_ids_as_list()) if parent_param else None
@@ -153,7 +161,7 @@ class SelectionParameterConfig(ParameterConfig[po.SelectParameterOption]):
153
161
  self.children[child.name] = child
154
162
  self.trigger_refresh = True
155
163
 
156
- def _get_options(self, user: BaseUser | None, parent_param: p._SelectionParameter | None) -> Sequence[po.SelectParameterOption]:
164
+ def _get_options(self, user: AbstractUser, parent_param: p._SelectionParameter | None) -> Sequence[po.SelectParameterOption]:
157
165
  return tuple(self._get_options_iterator(self.all_options, user, parent_param))
158
166
 
159
167
  def _get_default_ids_iterator(self, options: Sequence[po.SelectParameterOption]) -> Iterator[str]:
@@ -189,7 +197,7 @@ class SingleSelectParameterConfig(SelectionParameterConfig):
189
197
  return d.SelectDataSource(*args, **kwargs)
190
198
 
191
199
  def with_selection(
192
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
200
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
193
201
  ) -> p.SingleSelectParameter:
194
202
  options = self._get_options(user, parent_param)
195
203
  if selection is None:
@@ -237,7 +245,7 @@ class MultiSelectParameterConfig(SelectionParameterConfig):
237
245
  return d.SelectDataSource(*args, **kwargs)
238
246
 
239
247
  def with_selection(
240
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
248
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
241
249
  ) -> p.MultiSelectParameter:
242
250
  options = self._get_options(user, parent_param)
243
251
  if selection is None:
@@ -293,7 +301,7 @@ class DateParameterConfig(_DateTypeParameterConfig[po.DateParameterOption]):
293
301
  return d.DateDataSource(*args, **kwargs)
294
302
 
295
303
  def with_selection(
296
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
304
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
297
305
  ) -> p.DateParameter:
298
306
  curr_option: po.DateParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
299
307
  selected_date = curr_option._default_date if selection is None and curr_option is not None else selection
@@ -332,7 +340,7 @@ class DateRangeParameterConfig(_DateTypeParameterConfig[po.DateRangeParameterOpt
332
340
  return d.DateRangeDataSource(*args, **kwargs)
333
341
 
334
342
  def with_selection(
335
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
343
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
336
344
  ) -> p.DateRangeParameter:
337
345
  curr_option: po.DateRangeParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
338
346
  if selection is None:
@@ -395,7 +403,7 @@ class NumberParameterConfig(_NumericParameterConfig[po.NumberParameterOption]):
395
403
  return d.NumberDataSource(*args, **kwargs)
396
404
 
397
405
  def with_selection(
398
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
406
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
399
407
  ) -> p.NumberParameter:
400
408
  curr_option: po.NumberParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
401
409
  selected_value = curr_option._default_value if selection is None and curr_option is not None else selection
@@ -434,7 +442,7 @@ class NumberRangeParameterConfig(_NumericParameterConfig[po.NumberRangeParameter
434
442
  return d.NumberRangeDataSource(*args, **kwargs)
435
443
 
436
444
  def with_selection(
437
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
445
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
438
446
  ) -> p.NumberRangeParameter:
439
447
  curr_option: po.NumberRangeParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
440
448
  if selection is None:
@@ -524,7 +532,7 @@ class TextParameterConfig(ParameterConfig[po.TextParameterOption]):
524
532
  return d.TextDataSource(*args, **kwargs)
525
533
 
526
534
  def with_selection(
527
- self, selection: str | None, user: BaseUser | None, parent_param: p._SelectionParameter | None
535
+ self, selection: str | None, user: AbstractUser, parent_param: p._SelectionParameter | None
528
536
  ) -> p.TextParameter:
529
537
  curr_option: po.TextParameterOption | None = next(self._get_options_iterator(self.all_options, user, parent_param), None)
530
538
  entered_text = curr_option._default_text if selection is None and curr_option is not None else selection
@@ -550,6 +558,8 @@ class DataSourceParameterConfig(Generic[ParamConfigType], ParameterConfigBase):
550
558
  super().__init__(name, label, description=description, user_attribute=user_attribute, parent_name=parent_name)
551
559
  self.parameter_type = parameter_type
552
560
  if isinstance(data_source, dict):
561
+ if "source" in data_source:
562
+ data_source["source"] = d.SourceEnum(data_source["source"])
553
563
  data_source = parameter_type.DataSource(**data_source)
554
564
  self.data_source = data_source
555
565
  self.extra_args = extra_args
@@ -557,15 +567,26 @@ class DataSourceParameterConfig(Generic[ParamConfigType], ParameterConfigBase):
557
567
  def convert(self, df: pl.DataFrame) -> ParamConfigType:
558
568
  return self.data_source._convert(self, df)
559
569
 
560
- def get_dataframe(self, default_conn_name: str, conn_set: ConnectionSet, seeds: Seeds) -> pl.DataFrame:
570
+ def get_dataframe(self, default_conn_name: str, conn_set: ConnectionSet, seeds: Seeds, datalake_db_path: str = "") -> pl.DataFrame:
561
571
  datasource = self.data_source
562
572
  query = datasource._get_query()
563
- if datasource._is_from_seeds:
573
+ if datasource._source == d.SourceEnum.SEEDS:
564
574
  df = seeds.run_query(query)
565
- else:
575
+ elif datasource._source == d.SourceEnum.VDL:
576
+ vdl_conn = u.create_duckdb_connection(datalake_db_path)
577
+ try:
578
+ # Query the VDL database
579
+ df = vdl_conn.sql(query).pl()
580
+ except Exception as e:
581
+ raise u.ConfigurationError(f'Error executing query for datasource parameter "{self.name}" from VDL') from e
582
+ finally:
583
+ vdl_conn.close()
584
+ else: # source == "connection"
585
+ conn_name = None
566
586
  try:
567
587
  conn_name = datasource._get_connection_name(default_conn_name)
568
588
  df = conn_set.run_sql_query_from_conn_name(query, conn_name)
569
589
  except RuntimeError as e:
570
- raise u.ConfigurationError(f'Error executing query for datasource parameter "{self.name}"') from e
590
+ ending = f' "{conn_name}"' if conn_name is not None else ""
591
+ raise u.ConfigurationError(f'Error executing query for datasource parameter "{self.name}" from connection{ending}') from e
571
592
  return df
@@ -35,7 +35,7 @@ class ParameterOption(metaclass=ABCMeta):
35
35
 
36
36
  Arguments:
37
37
  user_group: The value of the user's "user group attribute". Only None when "user_attribute" is not specified
38
- for the Parameter factory. Note that when user is None but "user_attribute" is specified, an error is thrown
38
+ for the Parameter factory.
39
39
  selected_parent_option_ids: List of selected option ids from the parent parameter. Only None when the Parameter
40
40
  object has no parent parameter.
41
41
 
@@ -10,7 +10,7 @@ from ._arguments.init_time_args import ParametersArgs
10
10
  from ._manifest import ParametersConfig, ManifestConfig
11
11
  from ._connection_set import ConnectionSet
12
12
  from ._seeds import Seeds
13
- from ._auth import BaseUser
13
+ from ._schemas.auth_models import AbstractUser
14
14
 
15
15
 
16
16
  @dataclass
@@ -104,13 +104,10 @@ class ParameterConfigsSet:
104
104
  self.__validate_param_relationships()
105
105
 
106
106
  def apply_selections(
107
- self, dataset_params: Optional[Sequence[str]], selections: dict[str, Any], user: BaseUser | None, *, parent_param: str | None = None
107
+ self, dataset_params: Optional[Sequence[str]], selections: dict[str, Any], user: AbstractUser, *, parent_param: str | None = None
108
108
  ) -> ParameterSet:
109
109
  if dataset_params is None:
110
- if user is None:
111
- dataset_params = [k for k, v in self._data.items() if v.user_attribute is None]
112
- else:
113
- dataset_params = list(self._data.keys())
110
+ dataset_params = list(self._data.keys())
114
111
 
115
112
  parameters_by_name: dict[str, p.Parameter] = {}
116
113
  params_to_process = [parent_param] if parent_param else dataset_params
@@ -158,11 +155,11 @@ class ParameterConfigsSetIO:
158
155
 
159
156
  @classmethod
160
157
  def _get_df_dict_from_data_sources(
161
- cls, param_configs_set: ParameterConfigsSet, default_conn_name: str, seeds: Seeds, conn_set: ConnectionSet
158
+ cls, param_configs_set: ParameterConfigsSet, default_conn_name: str, seeds: Seeds, conn_set: ConnectionSet, datalake_db_path: str
162
159
  ) -> dict[str, pl.DataFrame]:
163
160
 
164
161
  def get_dataframe(ds_param_config: pc.DataSourceParameterConfig) -> tuple[str, pl.DataFrame]:
165
- return ds_param_config.name, ds_param_config.get_dataframe(default_conn_name, conn_set, seeds)
162
+ return ds_param_config.name, ds_param_config.get_dataframe(default_conn_name, conn_set, seeds, datalake_db_path)
166
163
 
167
164
  ds_param_configs = param_configs_set._get_all_ds_param_configs()
168
165
  with concurrent.futures.ThreadPoolExecutor() as executor:
@@ -179,7 +176,8 @@ class ParameterConfigsSetIO:
179
176
 
180
177
  @classmethod
181
178
  def load_from_file(
182
- cls, logger: u.Logger, base_path: str, manifest_cfg: ManifestConfig, seeds: Seeds, conn_set: ConnectionSet, param_args: ParametersArgs
179
+ cls, logger: u.Logger, base_path: str, manifest_cfg: ManifestConfig, seeds: Seeds, conn_set: ConnectionSet, param_args: ParametersArgs,
180
+ datalake_db_path: str
183
181
  ) -> ParameterConfigsSet:
184
182
  start = time.time()
185
183
  param_configs_set = ParameterConfigsSet()
@@ -199,7 +197,7 @@ class ParameterConfigsSetIO:
199
197
  param_configs_set.add(param_config)
200
198
 
201
199
  default_conn_name = manifest_cfg.env_vars.get(c.SQRL_CONNECTIONS_DEFAULT_NAME_USED, "default")
202
- df_dict = cls._get_df_dict_from_data_sources(param_configs_set, default_conn_name, seeds, conn_set)
200
+ df_dict = cls._get_df_dict_from_data_sources(param_configs_set, default_conn_name, seeds, conn_set, datalake_db_path)
203
201
  param_configs_set._post_process_params(df_dict)
204
202
 
205
203
  logger.log_activity_time("loading parameters", start)