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,142 @@
1
+ parameters:
2
+ - type: SingleSelectParameter
3
+ factory: CreateWithOptions ## one of 'CreateSimple', 'CreateWithOptions', or 'CreateFromSource'
4
+ arguments: ## arguments to specify depend on values for 'type' and 'factory'
5
+ name: group_by
6
+ label: Group By
7
+ description: Dimension(s) to aggregate by ## optional, default is empty string
8
+ user_attribute: access_level ## optional, default is null
9
+ all_options:
10
+ - id: trans
11
+ label: Transaction
12
+ columns: ["id", "date", "category", "subcategory", "description"] ## custom field
13
+ aliases: ["_id", "date", "category", "subcategory", "description"] ## custom field (any alias starting with "_" will not be selected - see context.py for implementation)
14
+ is_default: false ## optional, shown is default - exists for SingleSelect or MultiSelect options only
15
+ user_groups: ["admin"] ## optional, default is empty list
16
+ parent_option_ids: [] ## optional, shown is default - exists for all parameter options
17
+ - id: day
18
+ label: Day
19
+ columns: [date]
20
+ aliases: [day]
21
+ user_groups: ["admin", "member"]
22
+ - id: month
23
+ label: Month
24
+ columns: [month]
25
+ user_groups: ["admin", "member", "guest"]
26
+ - id: cat
27
+ label: Category
28
+ columns: [category]
29
+ user_groups: ["admin", "member", "guest"]
30
+ - id: subcat
31
+ label: Subcategory
32
+ columns: [category, subcategory]
33
+ user_groups: ["admin", "member", "guest"]
34
+ parent_name: null ## optional, shown is default - exists for all parameter types
35
+
36
+ - type: DateParameter
37
+ factory: CreateFromSource
38
+ arguments:
39
+ name: start_date
40
+ label: Start Date
41
+ description: Start date to filter transactions by
42
+ data_source:
43
+ table_or_query: SELECT min(date) AS min_date, max(date) AS max_date FROM expenses
44
+ default_date_col: min_date
45
+ min_date_col: min_date
46
+ max_date_col: max_date
47
+
48
+ - type: DateParameter
49
+ factory: CreateWithOptions
50
+ arguments:
51
+ name: end_date
52
+ label: End Date
53
+ description: End date to filter transactions by
54
+ all_options:
55
+ - default_date: 2024-12-31
56
+ min_date: 2024-01-01
57
+ max_date: 2024-12-31
58
+
59
+ - type: DateRangeParameter
60
+ factory: CreateWithOptions
61
+ arguments:
62
+ name: date_range
63
+ label: Date Range
64
+ description: Date range to filter transactions by
65
+ all_options:
66
+ - default_start_date: 2024-01-01
67
+ default_end_date: 2024-12-31
68
+ min_date: 2024-01-01
69
+ max_date: 2024-12-31
70
+
71
+ - type: MultiSelectParameter
72
+ factory: CreateFromSource
73
+ arguments:
74
+ name: category
75
+ label: Category Filter
76
+ description: The expense categories to filter transactions by
77
+ data_source:
78
+ table_or_query: seed_categories
79
+ id_col: category_id
80
+ options_col: category
81
+ source: seeds ## optional, default is "connection" - must be one of "connection", "seeds", or "vdl" - exists for data_source of any parameters
82
+ order_by_col: null ## optional, shown is default - exists for data_source of SingleSelect and MultiSelect
83
+ is_default_col: null ## optional, shown is default - exists for data_source of SingleSelect and MultiSelect
84
+ custom_cols: {} ## optional, shown is default - exists for data_source of SingleSelect and MultiSelect
85
+ include_all: true ## optional, shown is default - exists for data_source of MultiSelect only
86
+ order_matters: false ## optional, shown is default - exists for data_source of MultiSelect only
87
+ user_group_col: null ## optional, shown is default - exists for data_source of any parameters
88
+ connection_name: default ## optional, shown is default - exists for data_source of any parameters
89
+
90
+ - type: MultiSelectParameter
91
+ factory: CreateFromSource
92
+ arguments:
93
+ name: subcategory
94
+ label: Subcategory Filter
95
+ description: The expense subcategories to filter transactions by (available options are based on selected value(s) of 'Category Filter')
96
+ parent_name: category
97
+ data_source:
98
+ table_or_query: seed_subcategories
99
+ id_col: subcategory_id
100
+ options_col: subcategory
101
+ source: seeds
102
+ parent_id_col: category_id ## optional, default is null - exists for all parameter types
103
+
104
+ - type: NumberParameter
105
+ factory: CreateWithOptions
106
+ arguments:
107
+ name: min_filter
108
+ label: Amounts Greater Than
109
+ description: Number to filter on transactions with an amount greater than this value
110
+ all_options:
111
+ - min_value: 0
112
+ max_value: 300
113
+ increment: 10 ## optional, default is 1 - exists for Number and NumberRange options
114
+ default_value: null ## optional, shown is default - exists for Number options only
115
+
116
+ - type: NumberParameter
117
+ factory: CreateFromSource
118
+ arguments:
119
+ name: max_filter
120
+ label: Amounts Less Than
121
+ description: Number to filter on transactions with an amount less than this value
122
+ data_source:
123
+ table_or_query: "SELECT 0 as min_value, 300 as max_value, 10 as increment"
124
+ min_value_col: min_value
125
+ max_value_col: max_value
126
+ increment_col: increment ## optional, default is null
127
+ default_value_col: max_value ## optional, default is null
128
+ id_col: null ## optional, shown is default - required for SingleSelect and MultiSelect, optional for others
129
+
130
+ - type: NumberRangeParameter
131
+ factory: CreateWithOptions
132
+ arguments:
133
+ name: between_filter
134
+ label: Amounts Between
135
+ description: Number range to filter on transactions with an amount within this range
136
+ all_options:
137
+ - min_value: 0
138
+ max_value: 300
139
+ default_lower_value: 0 ## optional, default is null (or min_value) - exists for NumberRange options only
140
+ default_upper_value: 300 ## optional, default is null (or max_value) - exists for NumberRange options only
141
+
142
+
@@ -0,0 +1,19 @@
1
+ from typing import Any
2
+ from squirrels.arguments import ConnectionsArgs
3
+ from squirrels.connections import ConnectionProperties, ConnectionTypeEnum
4
+
5
+
6
+ def main(connections: dict[str, ConnectionProperties | Any], sqrl: ConnectionsArgs) -> None:
7
+ """
8
+ Define sqlalchemy engines by adding them to the "connections" dictionary
9
+ """
10
+ ## SQLAlchemy URL for a connection engine
11
+ conn_str: str = sqrl.env_vars["SQLITE_URI"].format(project_path=sqrl.project_path)
12
+
13
+ ## Assigning names to connection engines
14
+ connections["default"] = ConnectionProperties(
15
+ label="SQLite Expenses Database",
16
+ type=ConnectionTypeEnum.SQLALCHEMY,
17
+ uri=conn_str
18
+ )
19
+
@@ -0,0 +1,96 @@
1
+ from typing import cast, Any
2
+ from squirrels.arguments import ContextArgs
3
+ from squirrels import parameters as p
4
+
5
+ from pyconfigs.user import CustomUserFields
6
+
7
+
8
+ def main(ctx: dict[str, Any], sqrl: ContextArgs) -> None:
9
+ """
10
+ Define context variables AFTER parameter selections are made by adding entries to the dictionary "ctx".
11
+ These context variables can then be used in the models.
12
+
13
+ Note that the code here is used by all datasets, regardless of the parameters they use. You can use
14
+ sqrl.param_exists to determine the conditions to execute certain blocks of code.
15
+ """
16
+ custom_fields = cast(CustomUserFields, sqrl.user.custom_fields)
17
+
18
+ if sqrl.param_exists("group_by"):
19
+ group_by_param = sqrl.prms["group_by"]
20
+ assert isinstance(group_by_param, p.SingleSelectParameter)
21
+
22
+ selected_id = group_by_param.get_selected_id()
23
+ columns = group_by_param.get_selected("columns")
24
+ aliases = group_by_param.get_selected("aliases", default_field="columns")
25
+ assert isinstance(columns, list) and isinstance(aliases, list) and len(columns) == len(aliases)
26
+
27
+ column_to_alias_mapping = {x: y for x, y in zip(columns, aliases) if not y.startswith("_")}
28
+ order_by_cols = list(column_to_alias_mapping.values())
29
+
30
+ ctx["column_to_alias_mapping"] = column_to_alias_mapping
31
+ ctx["group_by_cols"] = order_by_cols if selected_id != "trans" else None
32
+
33
+ # Only used if federate_example is a Python model
34
+ mask_column = lambda x: x if custom_fields.role == "manager" else "***MASKED***"
35
+ ctx["order_by_cols"] = order_by_cols
36
+ ctx["mask_column_function"] = mask_column
37
+
38
+ # Only used if federate_example is a SQL model
39
+ mask_column = lambda x: x if custom_fields.role == "manager" else "'***MASKED***'"
40
+ x_as_y = lambda x, y: (mask_column(x) if x in ["description"] else x)+" as "+y
41
+ ctx["select_dim_cols"] = list(x_as_y(x, y) for x, y in column_to_alias_mapping.items())
42
+ ctx["aggregator"] = "SUM" if selected_id != "trans" else ""
43
+ ctx["order_by_cols_desc"] = list(y+" DESC" for y in order_by_cols)
44
+
45
+ if sqrl.param_exists("start_date"):
46
+ start_date_param = sqrl.prms["start_date"]
47
+ assert isinstance(start_date_param, p.DateParameter)
48
+
49
+ ctx["start_date"] = start_date_param.get_selected_date()
50
+
51
+ if sqrl.param_exists("end_date"):
52
+ end_date_param = sqrl.prms["end_date"]
53
+ assert isinstance(end_date_param, p.DateParameter)
54
+
55
+ ctx["end_date"] = end_date_param.get_selected_date()
56
+
57
+ if sqrl.param_exists("date_range"):
58
+ date_range_param = sqrl.prms["date_range"]
59
+ assert isinstance(date_range_param, p.DateRangeParameter)
60
+
61
+ ctx["start_date_from_range"] = date_range_param.get_selected_start_date()
62
+ ctx["end_date_from_range"] = date_range_param.get_selected_end_date()
63
+
64
+ if sqrl.param_exists("category"):
65
+ category_param = sqrl.prms["category"]
66
+ assert isinstance(category_param, p.MultiSelectParameter)
67
+
68
+ ctx["has_categories"] = category_param.has_non_empty_selection()
69
+ ctx["categories"] = category_param.get_selected_ids_as_list()
70
+
71
+ if sqrl.param_exists("subcategory"):
72
+ subcategory_param = sqrl.prms["subcategory"]
73
+ assert isinstance(subcategory_param, p.MultiSelectParameter)
74
+
75
+ ctx["has_subcategories"] = subcategory_param.has_non_empty_selection()
76
+ ctx["subcategories"] = subcategory_param.get_selected_ids_as_list()
77
+
78
+ if sqrl.param_exists("min_filter"):
79
+ min_amount_filter = sqrl.prms["min_filter"]
80
+ assert isinstance(min_amount_filter, p.NumberParameter)
81
+
82
+ ctx["min_amount"] = min_amount_filter.get_selected_value()
83
+
84
+ if sqrl.param_exists("max_filter"):
85
+ max_amount_filter = sqrl.prms["max_filter"]
86
+ assert isinstance(max_amount_filter, p.NumberParameter)
87
+
88
+ ctx["max_amount"] = max_amount_filter.get_selected_value()
89
+
90
+ if sqrl.param_exists("between_filter"):
91
+ between_filter = sqrl.prms["between_filter"]
92
+ assert isinstance(between_filter, p.NumberRangeParameter)
93
+
94
+ ctx["min_amount_from_range"] = between_filter.get_selected_lower_value()
95
+ ctx["max_amount_from_range"] = between_filter.get_selected_upper_value()
96
+
@@ -0,0 +1,141 @@
1
+ from squirrels import parameters as p, parameter_options as po, data_sources as ds
2
+
3
+
4
+ ## Example of creating SingleSelectParameter and specifying each option by code
5
+ @p.SingleSelectParameter.create_with_options(
6
+ name="group_by", label="Group By",
7
+ description="Dimension(s) to aggregate by",
8
+ user_attribute="access_level"
9
+ )
10
+ def group_by_options():
11
+ return [
12
+ po.SelectParameterOption(
13
+ id="trans", label="Transaction",
14
+ columns=["id","date","category","subcategory","description"],
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"]
38
+ ),
39
+ ]
40
+
41
+
42
+ ## Example of creating DateParameter
43
+ @p.DateParameter.create_from_source(
44
+ name="start_date", label="Start Date",
45
+ description="Start date to filter transactions by"
46
+ )
47
+ def start_date_source():
48
+ return ds.DateDataSource(
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",
52
+ )
53
+
54
+
55
+ ## Example of creating DateParameter from list of DateParameterOption's
56
+ @p.DateParameter.create_with_options(
57
+ name="end_date", label="End Date",
58
+ description="End date to filter transactions by"
59
+ )
60
+ def end_date_options():
61
+ return [
62
+ po.DateParameterOption(
63
+ default_date="2024-12-31", min_date="2024-01-01", max_date="2024-12-31"
64
+ )
65
+ ]
66
+
67
+
68
+ ## Example of creating DateRangeParameter
69
+ @p.DateRangeParameter.create_simple(
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",
73
+ description="Date range to filter transactions by"
74
+ )
75
+ def date_range_options():
76
+ pass
77
+
78
+
79
+ ## Example of creating MultiSelectParameter from lookup query/table
80
+ @p.MultiSelectParameter.create_from_source(
81
+ name="category", label="Category Filter",
82
+ description="The expense categories to filter transactions by"
83
+ )
84
+ def category_source():
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
+ )
91
+
92
+
93
+ ## Example of creating MultiSelectParameter with parent from lookup query/table
94
+ @p.MultiSelectParameter.create_from_source(
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"
98
+ )
99
+ def subcategory_source():
100
+ return ds.SelectDataSource(
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"
106
+ )
107
+
108
+
109
+ ## Example of creating NumberParameter
110
+ @p.NumberParameter.create_simple(
111
+ name="min_filter", label="Amounts Greater Than",
112
+ min_value=0, max_value=300, increment=10,
113
+ description="Number to filter on transactions with an amount greater than this value"
114
+ )
115
+ def min_filter_options():
116
+ pass
117
+
118
+
119
+ ## Example of creating NumberParameter from lookup query/table
120
+ @p.NumberParameter.create_from_source(
121
+ name="max_filter", label="Amounts Less Than",
122
+ description="Number to filter on transactions with an amount less than this value"
123
+ )
124
+ def max_filter_source():
125
+ return ds.NumberDataSource(
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"
130
+ )
131
+
132
+
133
+ ## Example of creating NumberRangeParameter
134
+ @p.NumberRangeParameter.create_simple(
135
+ name="between_filter", label="Amounts Between",
136
+ min_value=0, max_value=300,
137
+ default_lower_value=0, default_upper_value=300,
138
+ description="Number range to filter on transactions with an amount within this range"
139
+ )
140
+ def between_filter_options():
141
+ pass
@@ -0,0 +1,56 @@
1
+ from typing import Literal, Any
2
+ from squirrels.auth import CustomUserFields as BaseCustomUserFields, ProviderConfigs, RegisteredUser, provider
3
+ from squirrels.arguments import AuthProviderArgs
4
+
5
+
6
+ class CustomUserFields(BaseCustomUserFields):
7
+ """
8
+ Extend the CustomUserFields class to add custom user attributes.
9
+ - Only the following types are supported: [str, int, float, bool, typing.Literal]
10
+ - Add "| None" after the type to make it nullable.
11
+ - Always set a default value for the field (use None if default is null).
12
+
13
+ Example:
14
+ organization: str | None = None
15
+ """
16
+ role: Literal["manager", "staff", "customer"] = "staff"
17
+
18
+
19
+ # @provider(
20
+ # name="google", label="Google",
21
+ # icon="https://www.google.com/favicon.ico"
22
+ # # ^ Note: You also can save the google favicon as an image file in "resources/public/logos/google.ico"
23
+ # # and use icon="/public/logos/google.ico" instead
24
+ # )
25
+ def google_auth_provider(sqrl: AuthProviderArgs) -> ProviderConfigs:
26
+ """
27
+ Example provider configs for authenticating a user using Google credentials.
28
+
29
+ See the following page for setting up the CLIENT_ID and CLIENT_SECRET for Google specifically:
30
+ - https://support.google.com/googleapi/answer/6158849?hl=en
31
+
32
+ IMPORTANT: Avoid using Google OAuth if you set auth_strategy to 'external'.
33
+ - If auth_strategy is 'external', MCP clients would require Dynamic Client Registration (DCR) to
34
+ authenticate with the associated OAuth provider used by the Squirrels MCP server.
35
+ - Unfortunately, Google OAuth (and many other OAuth providers) do not support DCR. If auth_strategy
36
+ is 'external', consider using an alternative that supports DCR instead (such as WorkOS or Keycloak).
37
+ """
38
+ def get_sqrl_user(claims: dict[str, Any]) -> RegisteredUser:
39
+ custom_fields = CustomUserFields(role="customer")
40
+ return RegisteredUser(
41
+ username=claims["email"],
42
+ access_level="member", # or "admin"
43
+ custom_fields=custom_fields
44
+ )
45
+
46
+ # TODO: Add GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to the .env file
47
+ # Then, uncomment the @provider decorator above and set the client_id and client_secret below
48
+ provider_configs = ProviderConfigs(
49
+ client_id="", # sqrl.env_vars["GOOGLE_CLIENT_ID"],
50
+ client_secret="", # sqrl.env_vars["GOOGLE_CLIENT_SECRET"],
51
+ server_url="https://accounts.google.com",
52
+ client_kwargs={"scope": "openid email profile"},
53
+ get_user=get_sqrl_user
54
+ )
55
+
56
+ return provider_configs
@@ -0,0 +1,6 @@
1
+ "category_id","category"
2
+ "0","Food"
3
+ "1","Bills"
4
+ "2","Shopping"
5
+ "3","Transportation"
6
+ "4","Entertainment"
@@ -0,0 +1,15 @@
1
+ description: |
2
+ Lookup table for the category IDs and names of transactions.
3
+
4
+ cast_column_types: true # optional, default is false - if set to true, then SQRL_SEEDS__INFER_SCHEMA is ignored for this seed
5
+
6
+ columns:
7
+ - name: category_id
8
+ type: string
9
+ description: The category ID
10
+ category: dimension
11
+
12
+ - name: category
13
+ type: string
14
+ description: The human-readable category name
15
+ category: dimension
@@ -0,0 +1,15 @@
1
+ "subcategory_id","subcategory","category_id"
2
+ "0","Dining Out","0"
3
+ "1","Groceries","0"
4
+ "2","Utilities","1"
5
+ "3","Electronics","2"
6
+ "4","Ride Sharing","3"
7
+ "5","Mobile","1"
8
+ "6","Home Decor","2"
9
+ "7","Internet","1"
10
+ "8","Theater","4"
11
+ "9","Movies","4"
12
+ "10","Sports","2"
13
+ "11","Public Transit","3"
14
+ "12","Clothing","2"
15
+ "13","Concerts","4"
@@ -0,0 +1,21 @@
1
+ description: |
2
+ Lookup table for the subcategory IDs and names of transactions.
3
+
4
+ cast_column_types: true # optional, default is false - if set to true, then SQRL_SEEDS__INFER_SCHEMA is ignored for this seed
5
+
6
+ columns:
7
+ - name: subcategory_id
8
+ type: string
9
+ description: The subcategory ID
10
+ category: dimension
11
+
12
+ - name: subcategory
13
+ type: string
14
+ description: The human-readable subcategory name
15
+ category: dimension
16
+
17
+ - name: category_id
18
+ type: string
19
+ description: The category ID that the subcategory belongs to
20
+ category: dimension
21
+
@@ -0,0 +1,61 @@
1
+ project_variables:
2
+ name: expenses
3
+ label: "Sample Expenses"
4
+ description: This is a sample Squirrels project for analyzing expense transactions
5
+ major_version: 1
6
+ auth_type: optional ## one of 'optional' (default) or 'required'
7
+
8
+
9
+ packages: []
10
+
11
+ ## Example for packages section:
12
+ # packages:
13
+ # - git: https://.../myrepo.git
14
+ # revision: v0.1.0
15
+ # directory: custom_name ## optional
16
+
17
+
18
+ {{ connections -}}
19
+
20
+
21
+ {{ parameters -}}
22
+
23
+
24
+ datasets:
25
+ - name: expense_transactions
26
+ label: Expense Transactions - DBView Example
27
+ description: All expense transactions
28
+ model: dbview_example ## optional - if not specified, model name uses the dataset name
29
+ scope: private ## optional - one of 'public', 'protected', or 'private'. Default is 'public' (if auth_type is 'optional') or 'protected' (if auth_type is 'required').
30
+ parameters: ## optional - if not specified, then all parameters are used
31
+ - start_date
32
+ - end_date
33
+ - min_filter
34
+ - max_filter
35
+
36
+ - name: grouped_expenses
37
+ label: Grouped Expenses - Federate Example
38
+ description: Aggregated expense transactions by custom dimension using federate_example model
39
+ model: federate_example
40
+ parameters:
41
+ - group_by
42
+ - date_range
43
+ - category
44
+ - subcategory
45
+ - between_filter
46
+
47
+
48
+ selection_test_sets:
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"
52
+
53
+ - name: set_date_range
54
+ parameters:
55
+ date_range: [2024-02-01,2024-11-30] ## this parameter is only used by model "federate_example"
56
+
57
+ - name: use_admin_privileged_group_by
58
+ user:
59
+ access_level: admin
60
+ parameters:
61
+ group_by: trans
@@ -0,0 +1,2 @@
1
+ *
2
+ !.gitignore
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Signed In</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, system-ui, sans-serif;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ height: 100vh;
14
+ margin: 0;
15
+ background-color: #f9fafb;
16
+ color: #111827;
17
+ }
18
+ .card {
19
+ text-align: center;
20
+ padding: 24px;
21
+ }
22
+ .icon {
23
+ color: #059669;
24
+ font-size: 48px;
25
+ margin-bottom: 16px;
26
+ }
27
+ h1 {
28
+ font-size: 24px;
29
+ font-weight: 600;
30
+ margin: 0 0 8px;
31
+ }
32
+ p {
33
+ font-size: 16px;
34
+ color: #6b7280;
35
+ margin: 0;
36
+ }
37
+ </style>
38
+ </head>
39
+ <body>
40
+ <div class="card">
41
+ <div class="icon">✓</div>
42
+ <h1>Signed in successfully</h1>
43
+ <p>You can close this window now.</p>
44
+ </div>
45
+
46
+ <script>
47
+ if (window.opener) {
48
+ window.opener.postMessage({ type: "squirrels:auth:login-successful" }, "*");
49
+ setTimeout(function() { window.close(); }, 1000);
50
+ }
51
+ </script>
52
+ </body>
53
+ </html>
@@ -0,0 +1,22 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link id="favicon" rel="icon" type="image/svg+xml" href="{{ sqrl_studio_base_url }}/favicon.svg" />
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_MOUNTPATH = '{{ mount_path }}';
11
+ </script>
12
+ <!--
13
+ GitHub Pages serves static files with a Cache-Control header that typically tells the browser to cache for 10 minutes.
14
+ The implementation of squirrels-studio-v2 should be compatible with all versions of Squirrels from v0.6.0 onwards.
15
+ -->
16
+ <script type="module" crossorigin src="{{ sqrl_studio_base_url }}/assets/index.js"></script>
17
+ <link rel="stylesheet" crossorigin href="{{ sqrl_studio_base_url }}/assets/index.css">
18
+ </head>
19
+ <body>
20
+ <div id="root"></div>
21
+ </body>
22
+ </html>