squirrels 0.2.2__py3-none-any.whl → 0.3.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.
- squirrels/__init__.py +11 -4
- squirrels/_api_response_models.py +118 -0
- squirrels/_api_server.py +146 -75
- squirrels/_authenticator.py +10 -8
- squirrels/_command_line.py +17 -11
- squirrels/_connection_set.py +4 -3
- squirrels/_constants.py +15 -6
- squirrels/_environcfg.py +15 -11
- squirrels/_initializer.py +25 -15
- squirrels/_manifest.py +22 -12
- squirrels/_models.py +316 -154
- 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/.gitignore +1 -0
- squirrels/package_data/base_project/{database → assets}/expenses.db +0 -0
- squirrels/package_data/base_project/assets/weather.db +0 -0
- squirrels/package_data/base_project/connections.yml +1 -1
- squirrels/package_data/base_project/docker/Dockerfile +1 -1
- squirrels/package_data/base_project/{environcfg.yml → env.yml} +8 -8
- squirrels/package_data/base_project/models/dbviews/database_view1.py +25 -14
- squirrels/package_data/base_project/models/dbviews/database_view1.sql +20 -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 +7 -9
- 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.1.dist-info/LICENSE +201 -0
- {squirrels-0.2.2.dist-info → squirrels-0.3.1.dist-info}/METADATA +17 -17
- squirrels-0.3.1.dist-info/RECORD +56 -0
- squirrels/package_data/base_project/database/weather.db +0 -0
- 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.2.dist-info/LICENSE +0 -22
- squirrels-0.2.2.dist-info/RECORD +0 -55
- {squirrels-0.2.2.dist-info → squirrels-0.3.1.dist-info}/WHEEL +0 -0
- {squirrels-0.2.2.dist-info → squirrels-0.3.1.dist-info}/entry_points.txt +0 -0
squirrels/_constants.py
CHANGED
|
@@ -39,6 +39,7 @@ PARAMETER_ARGS_KEY = 'arguments'
|
|
|
39
39
|
TEST_SETS_KEY = 'selection_test_sets'
|
|
40
40
|
TEST_SET_NAME_KEY = 'name'
|
|
41
41
|
DEFAULT_TEST_SET_NAME = 'default'
|
|
42
|
+
TEST_SET_DATASETS_KEY = 'datasets'
|
|
42
43
|
TEST_SET_USER_ATTR_KEY = 'user_attributes'
|
|
43
44
|
TEST_SET_PARAMETERS_KEY = 'parameters'
|
|
44
45
|
|
|
@@ -48,6 +49,7 @@ DATASET_LABEL_KEY = 'label'
|
|
|
48
49
|
DATASET_MODEL_KEY = 'model'
|
|
49
50
|
DATASET_PARAMETERS_KEY = 'parameters'
|
|
50
51
|
DATASET_TRAITS_KEY = 'traits'
|
|
52
|
+
DATASET_DEFAULT_TEST_SET_KEY = 'default_test_set'
|
|
51
53
|
|
|
52
54
|
DATASET_SCOPE_KEY = 'scope'
|
|
53
55
|
PUBLIC_SCOPE = 'public'
|
|
@@ -77,13 +79,14 @@ ASSETS_FOLDER = 'assets'
|
|
|
77
79
|
TEMPLATES_FOLDER = 'templates'
|
|
78
80
|
|
|
79
81
|
ENVIRON_CONFIG_FILE = 'environcfg.yml'
|
|
82
|
+
ENV_CONFIG_FILE = 'env.yml'
|
|
80
83
|
MANIFEST_JINJA_FILE = 'squirrels.yml.j2'
|
|
81
84
|
CONNECTIONS_YML_FILE = 'connections.yml'
|
|
82
85
|
PARAMETERS_YML_FILE = 'parameters.yml'
|
|
83
86
|
MANIFEST_FILE = 'squirrels.yml'
|
|
84
87
|
LU_DATA_FILE = 'lu_data.xlsx'
|
|
85
88
|
|
|
86
|
-
DATABASE_FOLDER = '
|
|
89
|
+
DATABASE_FOLDER = 'assets'
|
|
87
90
|
PACKAGES_FOLDER = 'sqrl_packages'
|
|
88
91
|
|
|
89
92
|
MODELS_FOLDER = 'models'
|
|
@@ -94,7 +97,7 @@ FEDERATES_FOLDER = 'federates'
|
|
|
94
97
|
FEDERATE_SQL_NAME = 'dataset_example.sql'
|
|
95
98
|
FEDERATE_PY_NAME = 'dataset_example.py'
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
PYCONFIGS_FOLDER = 'pyconfigs'
|
|
98
101
|
AUTH_FILE = 'auth.py'
|
|
99
102
|
CONNECTIONS_FILE = 'connections.py'
|
|
100
103
|
CONTEXT_FILE = 'context.py'
|
|
@@ -103,9 +106,9 @@ PARAMETERS_FILE = 'parameters.py'
|
|
|
103
106
|
TARGET_FOLDER = 'target'
|
|
104
107
|
COMPILE_FOLDER = 'compile'
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
SEEDS_FOLDER = 'seeds'
|
|
110
|
+
CATEGORY_SEED_FILE = 'seed_categories.csv'
|
|
111
|
+
SUBCATEGORY_SEED_FILE = 'seed_subcategories.csv'
|
|
109
112
|
|
|
110
113
|
# Dataset setting names
|
|
111
114
|
AUTH_TOKEN_EXPIRE_SETTING = 'auth.token.expire_minutes'
|
|
@@ -116,6 +119,8 @@ RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl_minutes'
|
|
|
116
119
|
TEST_SET_DEFAULT_USED_SETTING = 'selection_test_sets.default_name_used'
|
|
117
120
|
DB_CONN_DEFAULT_USED_SETTING = 'connections.default_name_used'
|
|
118
121
|
DEFAULT_MATERIALIZE_SETTING = 'defaults.federates.materialized'
|
|
122
|
+
SEEDS_INFER_SCHEMA_SETTING = 'seeds.infer_schema'
|
|
123
|
+
SEEDS_NA_VALUES_SETTING = 'seeds.na_values'
|
|
119
124
|
IN_MEMORY_DB_SETTING = 'in_memory_database'
|
|
120
125
|
SQLITE = 'sqlite'
|
|
121
126
|
DUCKDB = 'duckdb'
|
|
@@ -138,9 +143,13 @@ CONF_FORMAT_CHOICES2 = [(PYTHON_FORMAT2, PYTHON_FORMAT), YML_FORMAT]
|
|
|
138
143
|
|
|
139
144
|
EXPENSES_DB_NAME = 'expenses'
|
|
140
145
|
WEATHER_DB_NAME = 'weather'
|
|
141
|
-
|
|
146
|
+
NO_DB = 'none'
|
|
147
|
+
DATABASE_CHOICES = [EXPENSES_DB_NAME, WEATHER_DB_NAME, NO_DB]
|
|
142
148
|
|
|
143
149
|
# Function names
|
|
144
150
|
GET_USER_FUNC = "get_user_if_valid"
|
|
145
151
|
DEP_FUNC = "dependencies"
|
|
146
152
|
MAIN_FUNC = "main"
|
|
153
|
+
|
|
154
|
+
# Regex
|
|
155
|
+
date_regex = r'^\d{4}\-\d{2}\-\d{2}$'
|
squirrels/_environcfg.py
CHANGED
|
@@ -5,7 +5,8 @@ import os, yaml
|
|
|
5
5
|
from . import _constants as c, _utils as u
|
|
6
6
|
from ._timer import timer, time
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
_GLOBAL_SQUIRRELS_CFG_FILE1 = u.join_paths(os.path.expanduser('~'), '.squirrels', c.ENVIRON_CONFIG_FILE)
|
|
9
|
+
_GLOBAL_SQUIRRELS_CFG_FILE2 = u.join_paths(os.path.expanduser('~'), '.squirrels', c.ENV_CONFIG_FILE)
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@dataclass
|
|
@@ -61,17 +62,20 @@ class EnvironConfigIO:
|
|
|
61
62
|
except FileNotFoundError:
|
|
62
63
|
return {}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
master_env_config1 = load_yaml(_GLOBAL_SQUIRRELS_CFG_FILE1)
|
|
66
|
+
master_env_config2 = load_yaml(_GLOBAL_SQUIRRELS_CFG_FILE2)
|
|
67
|
+
proj_env_config1 = load_yaml(c.ENVIRON_CONFIG_FILE)
|
|
68
|
+
proj_env_config2 = load_yaml(c.ENV_CONFIG_FILE)
|
|
66
69
|
|
|
67
|
-
for
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
for project_config in [master_env_config2, proj_env_config1, proj_env_config2]:
|
|
71
|
+
for key in project_config:
|
|
72
|
+
master_env_config1.setdefault(key, {})
|
|
73
|
+
master_env_config1[key].update(project_config[key])
|
|
70
74
|
|
|
71
|
-
users =
|
|
72
|
-
env_vars =
|
|
73
|
-
credentials =
|
|
74
|
-
secrets =
|
|
75
|
+
users = master_env_config1.get(c.USERS_KEY, {})
|
|
76
|
+
env_vars = master_env_config1.get(c.ENV_VARS_KEY, {})
|
|
77
|
+
credentials = master_env_config1.get(c.CREDENTIALS_KEY, {})
|
|
78
|
+
secrets = master_env_config1.get(c.SECRETS_KEY, {})
|
|
75
79
|
|
|
76
80
|
cls.obj = _EnvironConfig(users, env_vars, credentials, secrets)
|
|
77
|
-
timer.add_activity_time(f"loading {c.
|
|
81
|
+
timer.add_activity_time(f"loading {c.ENV_CONFIG_FILE} file", start)
|
squirrels/_initializer.py
CHANGED
|
@@ -33,8 +33,11 @@ class Initializer:
|
|
|
33
33
|
def _copy_database_file(self, filepath: str):
|
|
34
34
|
self._copy_file(u.join_paths(c.DATABASE_FOLDER, filepath))
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
self._copy_file(u.join_paths(c.
|
|
36
|
+
def _copy_pyconfig_file(self, filepath: str):
|
|
37
|
+
self._copy_file(u.join_paths(c.PYCONFIGS_FOLDER, filepath))
|
|
38
|
+
|
|
39
|
+
def _copy_seed_file(self, filepath: str):
|
|
40
|
+
self._copy_file(u.join_paths(c.SEEDS_FOLDER, filepath))
|
|
38
41
|
|
|
39
42
|
def init_project(self, args):
|
|
40
43
|
options = ["core", "connections", "parameters", "dbview", "federate", "auth", "sample_db"]
|
|
@@ -69,11 +72,11 @@ class Initializer:
|
|
|
69
72
|
|
|
70
73
|
remaining_questions = [
|
|
71
74
|
inquirer.Confirm(AUTH,
|
|
72
|
-
message=f"Do you want to add the '{c.AUTH_FILE}' file?" ,
|
|
75
|
+
message=f"Do you want to add the '{c.AUTH_FILE}' file to enable custom API authentication?" ,
|
|
73
76
|
default=False),
|
|
74
77
|
inquirer.List(SAMPLE_DB,
|
|
75
78
|
message="What sample sqlite database do you wish to use (if any)?",
|
|
76
|
-
choices=
|
|
79
|
+
choices= c.DATABASE_CHOICES)
|
|
77
80
|
]
|
|
78
81
|
answers.update(inquirer.prompt(remaining_questions))
|
|
79
82
|
|
|
@@ -124,11 +127,9 @@ class Initializer:
|
|
|
124
127
|
"connections": c.CONNECTIONS_YML_FILE if connections_use_yaml else None
|
|
125
128
|
}
|
|
126
129
|
substitutions = {key: get_content(val) for key, val in file_name_dict.items()}
|
|
127
|
-
substitutions["db_view_file"] = db_view_file
|
|
128
|
-
substitutions["final_view_file"] = federate_file
|
|
129
130
|
|
|
130
131
|
manifest_template = get_content(c.MANIFEST_JINJA_FILE)
|
|
131
|
-
manifest_content = u.render_string(manifest_template, substitutions)
|
|
132
|
+
manifest_content = u.render_string(manifest_template, **substitutions)
|
|
132
133
|
output_path = u.join_paths(base_proj_dir, TMP_FOLDER, c.MANIFEST_FILE)
|
|
133
134
|
with open(u.join_paths(output_path), "w") as f:
|
|
134
135
|
f.write(manifest_content)
|
|
@@ -139,26 +140,35 @@ class Initializer:
|
|
|
139
140
|
self._copy_file(c.MANIFEST_FILE, src_folder=TMP_FOLDER)
|
|
140
141
|
|
|
141
142
|
if connections_use_py:
|
|
142
|
-
self.
|
|
143
|
-
elif
|
|
143
|
+
self._copy_pyconfig_file(c.CONNECTIONS_FILE)
|
|
144
|
+
elif connections_use_yaml:
|
|
145
|
+
pass # already included in squirrels.yml
|
|
146
|
+
else:
|
|
144
147
|
raise NotImplementedError(f"Format '{connections_format}' not supported for configuring database connections")
|
|
145
148
|
|
|
146
149
|
if parameters_use_py:
|
|
147
|
-
self.
|
|
148
|
-
elif
|
|
150
|
+
self._copy_pyconfig_file(c.PARAMETERS_FILE)
|
|
151
|
+
elif parameters_use_yaml:
|
|
152
|
+
pass # already included in squirrels.yml
|
|
153
|
+
else:
|
|
149
154
|
raise NotImplementedError(f"Format '{parameters_format}' not supported for configuring widget parameters")
|
|
150
155
|
|
|
151
|
-
self.
|
|
152
|
-
self._copy_file(c.
|
|
156
|
+
self._copy_pyconfig_file(c.CONTEXT_FILE)
|
|
157
|
+
self._copy_file(c.ENV_CONFIG_FILE)
|
|
158
|
+
self._copy_seed_file(c.CATEGORY_SEED_FILE)
|
|
159
|
+
self._copy_seed_file(c.SUBCATEGORY_SEED_FILE)
|
|
153
160
|
|
|
154
161
|
self._copy_dbview_file(db_view_file)
|
|
155
162
|
self._copy_federate_file(federate_file)
|
|
156
163
|
|
|
157
164
|
if answers.get(AUTH, False):
|
|
158
|
-
self.
|
|
165
|
+
self._copy_pyconfig_file(c.AUTH_FILE)
|
|
159
166
|
|
|
160
167
|
sample_db = answers.get(SAMPLE_DB)
|
|
161
|
-
if sample_db is
|
|
168
|
+
if sample_db is None: # None if not prompt mode and '--sample-db' option was not specified
|
|
169
|
+
sample_db = c.EXPENSES_DB_NAME
|
|
170
|
+
|
|
171
|
+
if sample_db != c.NO_DB:
|
|
162
172
|
if sample_db == c.EXPENSES_DB_NAME:
|
|
163
173
|
self._copy_database_file(c.EXPENSES_DB_NAME+".db")
|
|
164
174
|
elif sample_db == c.WEATHER_DB_NAME:
|
squirrels/_manifest.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
|
-
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
3
|
from enum import Enum
|
|
4
4
|
import yaml
|
|
5
5
|
|
|
@@ -96,16 +96,20 @@ class ParametersConfig(ManifestComponentConfig):
|
|
|
96
96
|
@dataclass
|
|
97
97
|
class TestSetsConfig(ManifestComponentConfig):
|
|
98
98
|
name: str
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
datasets: Optional[list[str]] = None
|
|
100
|
+
is_authenticated: bool = False
|
|
101
|
+
user_attributes: dict = field(default_factory=dict)
|
|
102
|
+
parameters: dict = field(default_factory=dict)
|
|
101
103
|
|
|
102
104
|
@classmethod
|
|
103
105
|
def from_dict(cls, kwargs: dict):
|
|
104
106
|
cls._validate_required(kwargs, [c.TEST_SET_NAME_KEY], c.TEST_SETS_KEY)
|
|
105
107
|
name = str(kwargs[c.TEST_SET_NAME_KEY])
|
|
108
|
+
datasets = kwargs.get(c.TEST_SET_DATASETS_KEY)
|
|
109
|
+
is_authenticated = (c.TEST_SET_USER_ATTR_KEY in kwargs)
|
|
106
110
|
user_attributes = kwargs.get(c.TEST_SET_USER_ATTR_KEY, {})
|
|
107
111
|
parameters = kwargs.get(c.TEST_SET_PARAMETERS_KEY, {})
|
|
108
|
-
return cls(name, user_attributes, parameters)
|
|
112
|
+
return cls(name, datasets, is_authenticated, user_attributes, parameters)
|
|
109
113
|
|
|
110
114
|
|
|
111
115
|
@dataclass
|
|
@@ -145,8 +149,9 @@ class DatasetsConfig(ManifestComponentConfig):
|
|
|
145
149
|
label: str
|
|
146
150
|
model: str
|
|
147
151
|
scope: DatasetScope
|
|
148
|
-
parameters:
|
|
152
|
+
parameters: list[str]
|
|
149
153
|
traits: dict
|
|
154
|
+
default_test_set: Optional[str]
|
|
150
155
|
|
|
151
156
|
@classmethod
|
|
152
157
|
def from_dict(cls, kwargs: dict):
|
|
@@ -161,9 +166,10 @@ class DatasetsConfig(ManifestComponentConfig):
|
|
|
161
166
|
scope_list = [scope.name.lower() for scope in DatasetScope]
|
|
162
167
|
raise u.ConfigurationError(f'Scope not found for dataset "{name}". Scope must be one of {scope_list}') from e
|
|
163
168
|
|
|
164
|
-
parameters = kwargs.get(c.DATASET_PARAMETERS_KEY)
|
|
169
|
+
parameters = kwargs.get(c.DATASET_PARAMETERS_KEY, [])
|
|
165
170
|
traits = kwargs.get(c.DATASET_TRAITS_KEY, {})
|
|
166
|
-
|
|
171
|
+
default_test_set = kwargs.get(c.DATASET_DEFAULT_TEST_SET_KEY)
|
|
172
|
+
return cls(name, label, model, scope, parameters, traits, default_test_set)
|
|
167
173
|
|
|
168
174
|
|
|
169
175
|
@dataclass
|
|
@@ -208,14 +214,18 @@ class _ManifestConfig:
|
|
|
208
214
|
params = [ParametersConfig.from_dict(x) for x in kwargs.get(c.PARAMETERS_KEY, [])]
|
|
209
215
|
|
|
210
216
|
test_sets = cls._create_configs_as_dict(TestSetsConfig, kwargs, c.TEST_SETS_KEY, c.TEST_SET_NAME_KEY)
|
|
211
|
-
default_test_set: str = settings.get(c.TEST_SET_DEFAULT_USED_SETTING, c.DEFAULT_TEST_SET_NAME)
|
|
212
|
-
test_sets.setdefault(default_test_set, TestSetsConfig.from_dict({c.TEST_SET_NAME_KEY: default_test_set}))
|
|
213
|
-
|
|
214
217
|
dbviews = cls._create_configs_as_dict(DbviewConfig, kwargs, c.DBVIEWS_KEY, c.DBVIEW_NAME_KEY)
|
|
215
218
|
federates = cls._create_configs_as_dict(FederateConfig, kwargs, c.FEDERATES_KEY, c.FEDERATE_NAME_KEY)
|
|
216
219
|
datasets = cls._create_configs_as_dict(DatasetsConfig, kwargs, c.DATASETS_KEY, c.DATASET_NAME_KEY)
|
|
217
220
|
|
|
218
221
|
return cls(proj_vars, packages, db_conns, params, test_sets, dbviews, federates, datasets, settings)
|
|
222
|
+
|
|
223
|
+
def get_default_test_set(self, dataset_name: str) -> tuple[str, dict]:
|
|
224
|
+
default_1 = self.datasets[dataset_name].default_test_set
|
|
225
|
+
default_2 = self.settings.get(c.TEST_SET_DEFAULT_USED_SETTING, c.DEFAULT_TEST_SET_NAME)
|
|
226
|
+
default_name = default_1 if default_1 is not None else default_2
|
|
227
|
+
default_test_set = self.selection_test_sets.get(default_name, TestSetsConfig.from_dict({c.TEST_SET_NAME_KEY: default_name}))
|
|
228
|
+
return default_name, default_test_set
|
|
219
229
|
|
|
220
230
|
|
|
221
231
|
class ManifestIO:
|
|
@@ -227,8 +237,8 @@ class ManifestIO:
|
|
|
227
237
|
|
|
228
238
|
start = time.time()
|
|
229
239
|
raw_content = u.read_file(c.MANIFEST_FILE)
|
|
230
|
-
|
|
231
|
-
content = u.render_string(raw_content,
|
|
240
|
+
env_vars = EnvironConfigIO.obj.get_all_env_vars()
|
|
241
|
+
content = u.render_string(raw_content, env_vars=env_vars, **env_vars) # TODO: deprecate **env_vars
|
|
232
242
|
proj_config = yaml.safe_load(content)
|
|
233
243
|
cls.obj = _ManifestConfig.from_dict(proj_config)
|
|
234
244
|
timer.add_activity_time(f"loading {c.MANIFEST_FILE} file", start)
|