squirrels 0.2.1__py3-none-any.whl → 0.3.0__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.
- squirrels/__init__.py +11 -4
- squirrels/_api_response_models.py +118 -0
- squirrels/_api_server.py +140 -75
- squirrels/_authenticator.py +10 -8
- squirrels/_command_line.py +17 -11
- squirrels/_connection_set.py +2 -2
- squirrels/_constants.py +13 -5
- squirrels/_initializer.py +23 -13
- squirrels/_manifest.py +20 -10
- squirrels/_models.py +303 -148
- squirrels/_parameter_configs.py +195 -57
- squirrels/_parameter_sets.py +14 -17
- squirrels/_py_module.py +2 -4
- squirrels/_seeds.py +38 -0
- squirrels/_utils.py +41 -33
- squirrels/arguments/run_time_args.py +76 -34
- squirrels/data_sources.py +172 -51
- squirrels/dateutils.py +3 -3
- squirrels/package_data/assets/index.js +14 -14
- squirrels/package_data/base_project/connections.yml +1 -1
- squirrels/package_data/base_project/database/expenses.db +0 -0
- squirrels/package_data/base_project/docker/Dockerfile +1 -1
- squirrels/package_data/base_project/environcfg.yml +7 -7
- squirrels/package_data/base_project/models/dbviews/database_view1.py +25 -14
- squirrels/package_data/base_project/models/dbviews/database_view1.sql +21 -14
- squirrels/package_data/base_project/models/federates/dataset_example.py +6 -5
- squirrels/package_data/base_project/models/federates/dataset_example.sql +1 -1
- squirrels/package_data/base_project/parameters.yml +57 -28
- squirrels/package_data/base_project/pyconfigs/auth.py +11 -10
- squirrels/package_data/base_project/pyconfigs/connections.py +6 -8
- squirrels/package_data/base_project/pyconfigs/context.py +49 -33
- squirrels/package_data/base_project/pyconfigs/parameters.py +62 -30
- squirrels/package_data/base_project/seeds/seed_categories.csv +6 -0
- squirrels/package_data/base_project/seeds/seed_subcategories.csv +15 -0
- squirrels/package_data/base_project/squirrels.yml.j2 +37 -20
- squirrels/parameter_options.py +30 -10
- squirrels/parameters.py +300 -70
- squirrels/user_base.py +3 -13
- squirrels-0.3.0.dist-info/LICENSE +201 -0
- {squirrels-0.2.1.dist-info → squirrels-0.3.0.dist-info}/METADATA +15 -15
- squirrels-0.3.0.dist-info/RECORD +56 -0
- {squirrels-0.2.1.dist-info → squirrels-0.3.0.dist-info}/WHEEL +1 -1
- squirrels/package_data/base_project/seeds/mocks/category.csv +0 -3
- squirrels/package_data/base_project/seeds/mocks/max_filter.csv +0 -2
- squirrels/package_data/base_project/seeds/mocks/subcategory.csv +0 -6
- squirrels-0.2.1.dist-info/LICENSE +0 -22
- squirrels-0.2.1.dist-info/RECORD +0 -55
- {squirrels-0.2.1.dist-info → squirrels-0.3.0.dist-info}/entry_points.txt +0 -0
|
Binary file
|
|
@@ -3,7 +3,7 @@ FROM python:3.11-slim
|
|
|
3
3
|
WORKDIR /app
|
|
4
4
|
|
|
5
5
|
# Needed if any python dependencies are installed from git, or for
|
|
6
|
-
# "squirrels deps" if there are
|
|
6
|
+
# "squirrels deps" command if there are packages defined in "squirrels.yml"
|
|
7
7
|
RUN apt-get update && apt-get install -y git
|
|
8
8
|
|
|
9
9
|
COPY requirements-lock.txt .
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
## Note: You can copy this file to the .squirrels folder in your home directory to make
|
|
2
2
|
## the configurations global for all squirrels projects on the current machine
|
|
3
3
|
|
|
4
|
-
## Fake users for local development testing
|
|
4
|
+
## Fake users for local development testing. Must have an 'auth.py' file with a 'User' model to use custom attributes like 'role'
|
|
5
5
|
users:
|
|
6
|
-
|
|
6
|
+
alice:
|
|
7
7
|
is_internal: True
|
|
8
8
|
password: I<3Squirrels
|
|
9
|
-
full_name:
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
full_name: Alice Doe
|
|
10
|
+
role: employee
|
|
11
|
+
bob:
|
|
12
12
|
is_internal: False
|
|
13
13
|
password: abcd5678
|
|
14
|
-
full_name:
|
|
15
|
-
|
|
14
|
+
full_name: Bob Doe
|
|
15
|
+
role: customer
|
|
16
16
|
|
|
17
17
|
## Custom environment variables / secrets
|
|
18
18
|
env_vars:
|
|
@@ -1,35 +1,46 @@
|
|
|
1
1
|
from textwrap import dedent
|
|
2
|
-
|
|
2
|
+
from squirrels import ModelArgs
|
|
3
|
+
import pandas as pd
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
def main(sqrl:
|
|
6
|
+
def main(sqrl: ModelArgs) -> pd.DataFrame:
|
|
6
7
|
"""
|
|
7
8
|
Create a database view model in Python by sending an external query to a database or API, and return a
|
|
8
9
|
pandas DataFrame of the result in this function. Since the result is loaded into server memory, be mindful of
|
|
9
10
|
the size of the results coming from the external query.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
## If working with sqlalchemy ORMs, use 'sqrl.connections' to get a sqlalchemy engine
|
|
13
14
|
# from typing import Union
|
|
14
15
|
# engine1 = sqrl.connections[sqrl.connection_name] ## using the pre-assigned key
|
|
15
16
|
# engine2 = sqrl.connections["my_connection_name"] ## or use any defined key
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
## Example with building and running a sql query
|
|
19
|
+
masked_id = "id" if getattr(sqrl.user, "role", "") == "manager" else "'***'"
|
|
20
|
+
desc_cond = "description LIKE :desc_pattern" if sqrl.is_placeholder("desc_pattern") else "true"
|
|
21
|
+
category_cond = f"category IN ({sqrl.ctx['categories']})" if sqrl.ctx["has_categories"] else "true"
|
|
22
|
+
subcategory_cond = f"subcategory IN ({sqrl.ctx['subcategories']})" if sqrl.ctx["has_subcategories"] else "true"
|
|
20
23
|
query = dedent(f"""
|
|
21
|
-
|
|
24
|
+
WITH
|
|
25
|
+
transactions_with_masked_id AS (
|
|
26
|
+
SELECT *,
|
|
27
|
+
{masked_id} as masked_id
|
|
28
|
+
FROM transactions
|
|
29
|
+
)
|
|
30
|
+
SELECT {sqrl.ctx["select_dim_cols"]}
|
|
22
31
|
, sum(-amount) as total_amount
|
|
23
|
-
FROM
|
|
24
|
-
WHERE
|
|
25
|
-
|
|
26
|
-
AND
|
|
27
|
-
AND -amount
|
|
28
|
-
AND
|
|
32
|
+
FROM transactions_with_masked_id
|
|
33
|
+
WHERE date >= :start_date
|
|
34
|
+
AND date <= :end_date
|
|
35
|
+
AND -amount >= :min_amount
|
|
36
|
+
AND -amount <= :max_amount
|
|
37
|
+
AND {desc_cond}
|
|
38
|
+
AND {category_cond}
|
|
39
|
+
AND {subcategory_cond}
|
|
29
40
|
GROUP BY {sqrl.ctx["group_by_cols"]}
|
|
30
41
|
""").strip()
|
|
31
42
|
|
|
32
43
|
return sqrl.run_external_sql(query)
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
## A 'connection_name' argument is available to use a different connection key
|
|
35
46
|
# return sqrl.run_external_sql(query, connection_name="different_key")
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{%- if ctx["has_subcategories"] %}
|
|
9
|
-
AND subcategory IN ({{ ctx["subcategories"] }})
|
|
1
|
+
WITH
|
|
2
|
+
transactions_with_masked_id AS (
|
|
3
|
+
SELECT *,
|
|
4
|
+
{%- if user.role == "manager" %}
|
|
5
|
+
id as masked_id
|
|
6
|
+
{%- else %}
|
|
7
|
+
'***' as masked_id
|
|
10
8
|
{%- endif %}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
FROM transactions
|
|
10
|
+
)
|
|
11
|
+
SELECT {{ ctx.select_dim_cols }}
|
|
12
|
+
, sum(-amount) as total_amount
|
|
13
|
+
FROM transactions_with_masked_id
|
|
14
|
+
WHERE true
|
|
15
|
+
{% if is_placeholder("start_date") -%} AND date >= :start_date {%- endif %}
|
|
16
|
+
{% if is_placeholder("end_date") -%} AND date <= :end_date {%- endif %}
|
|
17
|
+
{% if is_placeholder("min_amount") -%} AND -amount >= :min_amount {%- endif %}
|
|
18
|
+
{% if is_placeholder("max_amount") -%} AND -amount <= :max_amount {%- endif %}
|
|
19
|
+
{% if is_placeholder("desc_pattern") -%} AND description LIKE :desc_pattern {%- endif %}
|
|
20
|
+
{% if ctx.has_categories -%} AND category IN ({{ ctx.categories }}) {%- endif %}
|
|
21
|
+
{% if ctx.has_subcategories -%} AND subcategory IN ({{ ctx.subcategories }}) {%- endif %}
|
|
22
|
+
GROUP BY {{ ctx.group_by_cols }}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from typing import Iterable
|
|
2
|
-
|
|
2
|
+
from squirrels import ModelDepsArgs, ModelArgs
|
|
3
|
+
import pandas as pd
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
def dependencies(sqrl:
|
|
6
|
+
def dependencies(sqrl: ModelDepsArgs) -> Iterable[str]:
|
|
6
7
|
"""
|
|
7
8
|
Define list of dependent models here. This will determine the dependencies first, at compile-time,
|
|
8
9
|
before running the model.
|
|
@@ -10,11 +11,11 @@ def dependencies(sqrl: sr.ModelDepsArgs) -> Iterable[str]:
|
|
|
10
11
|
return ["database_view1"]
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
def main(sqrl:
|
|
14
|
+
def main(sqrl: ModelArgs) -> pd.DataFrame:
|
|
14
15
|
"""
|
|
15
16
|
Create federated models by joining/processing dependent database views and/or other federated models to
|
|
16
17
|
form and return the result as a new pandas DataFrame.
|
|
17
18
|
"""
|
|
18
19
|
df = sqrl.ref("database_view1")
|
|
19
|
-
|
|
20
|
-
return df.sort_values(
|
|
20
|
+
order_by_cols: str = sqrl.ctx["order_by_cols_list"]
|
|
21
|
+
return df.sort_values(order_by_cols, ascending=False)
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
parameters:
|
|
2
2
|
- type: SingleSelectParameter
|
|
3
|
-
factory: Create
|
|
4
|
-
arguments:
|
|
3
|
+
factory: Create ## one of 'Create', 'CreateSimple', or 'CreateFromSource'
|
|
4
|
+
arguments: ## arguments to specify depend on values for 'type' and 'factory'
|
|
5
5
|
name: group_by
|
|
6
6
|
label: Group By
|
|
7
|
+
description: Dimension to aggregate by ## optional, default is empty string
|
|
7
8
|
all_options:
|
|
8
9
|
- id: g0
|
|
9
10
|
label: Transaction
|
|
10
|
-
columns: [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
columns: ["masked_id", "date", "description"] ## custom field
|
|
12
|
+
aliases: ["id", "date", "description"] ## custom field
|
|
13
|
+
is_default: false ## optional, default, exists for SingleSelect or MultiSelect options only
|
|
14
|
+
user_groups: [] ## optional, default, exists for all parameter options
|
|
15
|
+
parent_option_ids: [] ## optional, default, exists for all parameter options
|
|
14
16
|
- id: g1
|
|
15
17
|
label: Date
|
|
16
18
|
columns: [date]
|
|
@@ -20,90 +22,117 @@ parameters:
|
|
|
20
22
|
- id: g3
|
|
21
23
|
label: Subcategory
|
|
22
24
|
columns: [category, subcategory]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
user_attribute: null ## optional, default, exists for all parameter types
|
|
26
|
+
parent_name: null ## optional, default, exists for all parameter types
|
|
27
|
+
|
|
28
|
+
- type: TextParameter
|
|
29
|
+
factory: Create
|
|
30
|
+
arguments:
|
|
31
|
+
name: description_filter
|
|
32
|
+
label: Description Contains
|
|
33
|
+
description: Filter by transactions with this description
|
|
34
|
+
parent_name: group_by
|
|
35
|
+
all_options:
|
|
36
|
+
- parent_option_ids: g0
|
|
37
|
+
|
|
26
38
|
- type: DateParameter
|
|
27
39
|
factory: Create
|
|
28
40
|
arguments:
|
|
29
41
|
name: start_date
|
|
30
42
|
label: Start Date
|
|
43
|
+
description: Filter by transactions after this date
|
|
31
44
|
all_options:
|
|
32
45
|
- default_date: 2023-01-01
|
|
33
|
-
date_format: '%Y-%m-%d'
|
|
46
|
+
date_format: '%Y-%m-%d' ## optional, default, format comes from python datetime, exists for Date and DateRange parameter options
|
|
47
|
+
|
|
34
48
|
- type: DateParameter
|
|
35
49
|
factory: Create
|
|
36
50
|
arguments:
|
|
37
51
|
name: end_date
|
|
38
52
|
label: End Date
|
|
53
|
+
description: Filter by transactions before this date
|
|
39
54
|
all_options:
|
|
40
55
|
- default_date: 2023-12-31
|
|
56
|
+
|
|
41
57
|
- type: DateRangeParameter
|
|
42
58
|
factory: Create
|
|
43
59
|
arguments:
|
|
44
60
|
name: date_range
|
|
45
61
|
label: Date Range
|
|
62
|
+
description: Filter by transactions within this date range
|
|
46
63
|
all_options:
|
|
47
64
|
- default_start_date: 2023-01-01
|
|
48
65
|
default_end_date: 2023-12-31
|
|
66
|
+
|
|
49
67
|
- type: MultiSelectParameter
|
|
50
68
|
factory: CreateFromSource
|
|
51
69
|
arguments:
|
|
52
70
|
name: category
|
|
53
71
|
label: Category Filter
|
|
72
|
+
description: The expense categories to filter by
|
|
54
73
|
data_source:
|
|
55
|
-
table_or_query:
|
|
74
|
+
table_or_query: seed_categories
|
|
56
75
|
id_col: category_id
|
|
57
76
|
options_col: category
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
77
|
+
from_seeds: true ## optional, default is false, exists for data_source of any parameters
|
|
78
|
+
order_by_col: null ## optional, default, exists for data_source of SingleSelect and MultiSelect
|
|
79
|
+
is_default_col: null ## optional, default, exists for data_source of SingleSelect and MultiSelect
|
|
80
|
+
custom_cols: {} ## optional, default, exists for data_source of SingleSelect and MultiSelect
|
|
81
|
+
include_all: true ## optional, default, exists for data_source of MultiSelect only
|
|
82
|
+
order_matters: false ## optional, default, exists for data_source of MultiSelect only
|
|
83
|
+
user_group_col: null ## optional, default, exists for data_source of any parameters
|
|
84
|
+
connection_name: default ## optional, default, exists for data_source of any parameters
|
|
85
|
+
|
|
65
86
|
- type: MultiSelectParameter
|
|
66
87
|
factory: CreateFromSource
|
|
67
88
|
arguments:
|
|
68
89
|
name: subcategory
|
|
69
90
|
label: Subcategory Filter
|
|
91
|
+
description: The expense subcategories to filter by (available options based on selected 'Category Filter')
|
|
92
|
+
parent_name: category
|
|
70
93
|
data_source:
|
|
71
|
-
table_or_query:
|
|
94
|
+
table_or_query: seed_subcategories
|
|
72
95
|
id_col: subcategory_id
|
|
73
96
|
options_col: subcategory
|
|
74
|
-
|
|
75
|
-
|
|
97
|
+
from_seeds: true
|
|
98
|
+
parent_id_col: category_id ## optional, default is null, exists for all parameter types
|
|
99
|
+
|
|
76
100
|
- type: NumberParameter
|
|
77
101
|
factory: Create
|
|
78
102
|
arguments:
|
|
79
103
|
name: min_filter
|
|
80
104
|
label: Amounts Greater Than
|
|
105
|
+
description: Filter by transactions greater than this amount
|
|
81
106
|
all_options:
|
|
82
107
|
- min_value: 0
|
|
83
108
|
max_value: 500
|
|
84
|
-
increment:
|
|
85
|
-
default_value: null
|
|
109
|
+
increment: 10 ## optional, default is 1, exists for Number and NumberRange options
|
|
110
|
+
default_value: null ## optional, default, exists for Number options only
|
|
111
|
+
|
|
86
112
|
- type: NumberParameter
|
|
87
113
|
factory: CreateFromSource
|
|
88
114
|
arguments:
|
|
89
115
|
name: max_filter
|
|
90
116
|
label: Amounts Less Than
|
|
117
|
+
description: Filter by transactions less than this amount
|
|
91
118
|
data_source:
|
|
92
119
|
table_or_query: "SELECT 0 as min_value, max(-amount) as max_value, 10 as increment FROM transactions WHERE category <> 'Income'"
|
|
93
120
|
min_value_col: min_value
|
|
94
121
|
max_value_col: max_value
|
|
95
|
-
increment_col: increment
|
|
96
|
-
default_value_col: max_value
|
|
97
|
-
id_col: null
|
|
122
|
+
increment_col: increment ## optional, default is null
|
|
123
|
+
default_value_col: max_value ## optional, default is null
|
|
124
|
+
id_col: null ## optional, default, required for SingleSelect and MultiSelect, optional for all others
|
|
125
|
+
|
|
98
126
|
- type: NumberRangeParameter
|
|
99
127
|
factory: Create
|
|
100
128
|
arguments:
|
|
101
129
|
name: between_filter
|
|
102
130
|
label: Amounts Between
|
|
131
|
+
description: Filter by transaction amounts within this range
|
|
103
132
|
all_options:
|
|
104
133
|
- min_value: 0
|
|
105
134
|
max_value: 500
|
|
106
|
-
default_lower_value: 10
|
|
107
|
-
default_upper_value: 400
|
|
135
|
+
default_lower_value: 10 ## optional, default is null (or min_value), exists for NumRange options only
|
|
136
|
+
default_upper_value: 400 ## optional, default is null (or max_value), exists for NumRange options only
|
|
108
137
|
|
|
109
138
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
from typing import Union
|
|
1
|
+
from typing import Union
|
|
2
2
|
from squirrels import User as UserBase, AuthArgs, WrongPassword
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class User(UserBase):
|
|
6
|
-
def set_attributes(self,
|
|
6
|
+
def set_attributes(self, **kwargs) -> None:
|
|
7
7
|
"""
|
|
8
|
-
Use this method to add custom attributes in the User model that don't exist in UserBase
|
|
8
|
+
Use this method to add custom attributes in the User model that don't exist in UserBase
|
|
9
|
+
(i.e., anything that's not 'username' or 'is_internal')
|
|
9
10
|
"""
|
|
10
|
-
self.
|
|
11
|
+
self.role = kwargs["role"]
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def get_user_if_valid(sqrl: AuthArgs) -> Union[User, WrongPassword, None]:
|
|
@@ -23,22 +24,22 @@ def get_user_if_valid(sqrl: AuthArgs) -> Union[User, WrongPassword, None]:
|
|
|
23
24
|
"johndoe": {
|
|
24
25
|
"username": "johndoe",
|
|
25
26
|
"is_admin": True,
|
|
26
|
-
"
|
|
27
|
+
"role": "manager",
|
|
27
28
|
"hashed_password": str(hash("I<3Squirrels"))
|
|
28
29
|
},
|
|
29
30
|
"mattdoe": {
|
|
30
31
|
"username": "mattdoe",
|
|
31
32
|
"is_admin": False,
|
|
32
|
-
"
|
|
33
|
+
"role": "customer",
|
|
33
34
|
"hashed_password": str(hash("abcd5678"))
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
if
|
|
38
|
+
user_obj = mock_users_db.get(sqrl.username)
|
|
39
|
+
if user_obj is None:
|
|
39
40
|
return None
|
|
40
41
|
|
|
41
|
-
if str(hash(sqrl.password)) ==
|
|
42
|
-
return User.Create(sqrl.username,
|
|
42
|
+
if str(hash(sqrl.password)) == user_obj["hashed_password"]:
|
|
43
|
+
return User.Create(sqrl.username, is_internal=user_obj["is_admin"], role=user_obj["role"])
|
|
43
44
|
else:
|
|
44
45
|
return WrongPassword()
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
from sqlalchemy import create_engine, Engine
|
|
2
|
-
|
|
2
|
+
from squirrels import ConnectionsArgs
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def main(connections: dict[str, Engine], sqrl:
|
|
5
|
+
def main(connections: dict[str, Engine], sqrl: ConnectionsArgs) -> None:
|
|
6
6
|
"""
|
|
7
7
|
Define sqlalchemy engines by adding them to the "connections" dictionary
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
# username, password = sqrl.get_credential('my_key')
|
|
12
|
-
|
|
13
|
-
""" SQLAlchemy URL for a connection engine """
|
|
10
|
+
## SQLAlchemy URL for a connection engine
|
|
14
11
|
conn_str = 'sqlite:///database/expenses.db'
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
## Can also leverage environment variables and credentials in the environcfg.yml file for connection details
|
|
17
14
|
# conn_str_raw: str = sqrl.env_vars["sqlite_conn_str"]
|
|
15
|
+
# username, password = sqrl.get_credential('my_key')
|
|
18
16
|
# conn_str = conn_str_raw.format(username=username, password=password)
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
## Assigning names to connection engines
|
|
21
19
|
connections["default"] = create_engine(conn_str)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from typing import Any
|
|
2
|
-
|
|
2
|
+
from squirrels import ContextArgs, parameters as p
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def main(ctx: dict[str, Any], sqrl:
|
|
5
|
+
def main(ctx: dict[str, Any], sqrl: ContextArgs) -> None:
|
|
6
6
|
"""
|
|
7
7
|
Define context variables AFTER parameter selections are made by adding entries to the dictionary "ctx".
|
|
8
8
|
These context variables can then be used in the models.
|
|
@@ -10,46 +10,62 @@ def main(ctx: dict[str, Any], sqrl: sr.ContextArgs) -> None:
|
|
|
10
10
|
Note that the code here is used by all datasets, regardless of the parameters they use. You can use
|
|
11
11
|
sqrl.prms and/or sqrl.traits to determine the conditions to execute certain blocks of code.
|
|
12
12
|
"""
|
|
13
|
+
if sqrl.param_exists("group_by"):
|
|
14
|
+
group_by_param: p.SingleSelectParameter = sqrl.prms["group_by"]
|
|
15
|
+
columns = group_by_param.get_selected("columns")
|
|
16
|
+
aliases = group_by_param.get_selected("aliases", default_field="columns")
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ctx["
|
|
17
|
-
ctx["
|
|
18
|
-
ctx["order_by_cols"] = ",".join((x+" DESC") for x in ctx["group_by_cols_list"])
|
|
18
|
+
ctx["select_dim_cols"] = ", ".join(x+" as "+y for x, y in zip(columns, aliases))
|
|
19
|
+
ctx["group_by_cols"] = ", ".join(columns)
|
|
20
|
+
ctx["order_by_cols"] = ", ".join((x+" DESC") for x in aliases)
|
|
21
|
+
ctx["order_by_cols_list"] = aliases
|
|
19
22
|
|
|
20
|
-
if "
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
if sqrl.param_exists("description_filter"):
|
|
24
|
+
descript_param: p.TextParameter = sqrl.prms["description_filter"]
|
|
25
|
+
desc_pattern = descript_param.get_entered_text().apply_percent_wrap()
|
|
26
|
+
sqrl.set_placeholder("desc_pattern", desc_pattern)
|
|
27
|
+
|
|
28
|
+
if sqrl.param_exists("start_date"):
|
|
29
|
+
start_date_param: p.DateParameter = sqrl.prms["start_date"]
|
|
30
|
+
start_date = start_date_param.get_selected_date()
|
|
31
|
+
sqrl.set_placeholder("start_date", start_date)
|
|
23
32
|
|
|
24
|
-
if "end_date"
|
|
25
|
-
end_date_param:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
if sqrl.param_exists("end_date"):
|
|
34
|
+
end_date_param: p.DateParameter = sqrl.prms["end_date"]
|
|
35
|
+
end_date = end_date_param.get_selected_date()
|
|
36
|
+
sqrl.set_placeholder("end_date", end_date)
|
|
37
|
+
|
|
38
|
+
if sqrl.param_exists("date_range"):
|
|
39
|
+
date_range_param: p.DateRangeParameter = sqrl.prms["date_range"]
|
|
40
|
+
start_date = date_range_param.get_selected_start_date()
|
|
41
|
+
end_date = date_range_param.get_selected_end_date()
|
|
42
|
+
sqrl.set_placeholder("start_date", start_date)
|
|
43
|
+
sqrl.set_placeholder("end_date", end_date)
|
|
32
44
|
|
|
33
|
-
if "category"
|
|
34
|
-
category_param:
|
|
45
|
+
if sqrl.param_exists("category"):
|
|
46
|
+
category_param: p.MultiSelectParameter = sqrl.prms["category"]
|
|
35
47
|
ctx["has_categories"] = category_param.has_non_empty_selection()
|
|
36
48
|
ctx["categories"] = category_param.get_selected_labels_quoted_joined()
|
|
37
49
|
|
|
38
|
-
if "subcategory"
|
|
39
|
-
subcategory_param:
|
|
50
|
+
if sqrl.param_exists("subcategory"):
|
|
51
|
+
subcategory_param: p.MultiSelectParameter = sqrl.prms["subcategory"]
|
|
40
52
|
ctx["has_subcategories"] = subcategory_param.has_non_empty_selection()
|
|
41
53
|
ctx["subcategories"] = subcategory_param.get_selected_labels_quoted_joined()
|
|
42
54
|
|
|
43
|
-
if "min_filter"
|
|
44
|
-
min_amount_filter:
|
|
45
|
-
|
|
55
|
+
if sqrl.param_exists("min_filter"):
|
|
56
|
+
min_amount_filter: p.NumberParameter = sqrl.prms["min_filter"]
|
|
57
|
+
min_amount = min_amount_filter.get_selected_value()
|
|
58
|
+
sqrl.set_placeholder("min_amount", min_amount)
|
|
46
59
|
|
|
47
|
-
if "max_filter"
|
|
48
|
-
max_amount_filter:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
if sqrl.param_exists("max_filter"):
|
|
61
|
+
max_amount_filter: p.NumberParameter = sqrl.prms["max_filter"]
|
|
62
|
+
max_amount = max_amount_filter.get_selected_value()
|
|
63
|
+
sqrl.set_placeholder("max_amount", max_amount)
|
|
64
|
+
|
|
65
|
+
if sqrl.param_exists("between_filter"):
|
|
66
|
+
between_filter: p.NumberRangeParameter = sqrl.prms["between_filter"]
|
|
67
|
+
min_amount = between_filter.get_selected_lower_value()
|
|
68
|
+
max_amount = between_filter.get_selected_upper_value()
|
|
69
|
+
sqrl.set_placeholder("min_amount", min_amount)
|
|
70
|
+
sqrl.set_placeholder("max_amount", max_amount)
|
|
55
71
|
|