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.

Files changed (52) hide show
  1. squirrels/__init__.py +11 -4
  2. squirrels/_api_response_models.py +118 -0
  3. squirrels/_api_server.py +146 -75
  4. squirrels/_authenticator.py +10 -8
  5. squirrels/_command_line.py +17 -11
  6. squirrels/_connection_set.py +4 -3
  7. squirrels/_constants.py +15 -6
  8. squirrels/_environcfg.py +15 -11
  9. squirrels/_initializer.py +25 -15
  10. squirrels/_manifest.py +22 -12
  11. squirrels/_models.py +316 -154
  12. squirrels/_parameter_configs.py +195 -57
  13. squirrels/_parameter_sets.py +14 -17
  14. squirrels/_py_module.py +2 -4
  15. squirrels/_seeds.py +38 -0
  16. squirrels/_utils.py +41 -33
  17. squirrels/arguments/run_time_args.py +76 -34
  18. squirrels/data_sources.py +172 -51
  19. squirrels/dateutils.py +3 -3
  20. squirrels/package_data/assets/index.js +14 -14
  21. squirrels/package_data/base_project/.gitignore +1 -0
  22. squirrels/package_data/base_project/{database → assets}/expenses.db +0 -0
  23. squirrels/package_data/base_project/assets/weather.db +0 -0
  24. squirrels/package_data/base_project/connections.yml +1 -1
  25. squirrels/package_data/base_project/docker/Dockerfile +1 -1
  26. squirrels/package_data/base_project/{environcfg.yml → env.yml} +8 -8
  27. squirrels/package_data/base_project/models/dbviews/database_view1.py +25 -14
  28. squirrels/package_data/base_project/models/dbviews/database_view1.sql +20 -14
  29. squirrels/package_data/base_project/models/federates/dataset_example.py +6 -5
  30. squirrels/package_data/base_project/models/federates/dataset_example.sql +1 -1
  31. squirrels/package_data/base_project/parameters.yml +57 -28
  32. squirrels/package_data/base_project/pyconfigs/auth.py +11 -10
  33. squirrels/package_data/base_project/pyconfigs/connections.py +7 -9
  34. squirrels/package_data/base_project/pyconfigs/context.py +49 -33
  35. squirrels/package_data/base_project/pyconfigs/parameters.py +62 -30
  36. squirrels/package_data/base_project/seeds/seed_categories.csv +6 -0
  37. squirrels/package_data/base_project/seeds/seed_subcategories.csv +15 -0
  38. squirrels/package_data/base_project/squirrels.yml.j2 +37 -20
  39. squirrels/parameter_options.py +30 -10
  40. squirrels/parameters.py +300 -70
  41. squirrels/user_base.py +3 -13
  42. squirrels-0.3.1.dist-info/LICENSE +201 -0
  43. {squirrels-0.2.2.dist-info → squirrels-0.3.1.dist-info}/METADATA +17 -17
  44. squirrels-0.3.1.dist-info/RECORD +56 -0
  45. squirrels/package_data/base_project/database/weather.db +0 -0
  46. squirrels/package_data/base_project/seeds/mocks/category.csv +0 -3
  47. squirrels/package_data/base_project/seeds/mocks/max_filter.csv +0 -2
  48. squirrels/package_data/base_project/seeds/mocks/subcategory.csv +0 -6
  49. squirrels-0.2.2.dist-info/LICENSE +0 -22
  50. squirrels-0.2.2.dist-info/RECORD +0 -55
  51. {squirrels-0.2.2.dist-info → squirrels-0.3.1.dist-info}/WHEEL +0 -0
  52. {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 = 'database'
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
- PYCONFIG_FOLDER = 'pyconfigs'
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
- OUTPUTS_FOLDER = 'outputs'
107
- PARAMETERS_OUTPUT = 'parameters.json'
108
- FINAL_VIEW_OUT_STEM = 'final_view'
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
- DATABASE_CHOICES = [EXPENSES_DB_NAME, WEATHER_DB_NAME]
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
- _GLOBAL_SQUIRRELS_CFG_FILE = u.join_paths(os.path.expanduser('~'), '.squirrels', c.ENVIRON_CONFIG_FILE)
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
- config1 = load_yaml(_GLOBAL_SQUIRRELS_CFG_FILE)
65
- config2 = load_yaml(c.ENVIRON_CONFIG_FILE)
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 key in config2:
68
- config1.setdefault(key, {})
69
- config1[key].update(config2[key])
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 = config1.get(c.USERS_KEY, {})
72
- env_vars = config1.get(c.ENV_VARS_KEY, {})
73
- credentials = config1.get(c.CREDENTIALS_KEY, {})
74
- secrets = config1.get(c.SECRETS_KEY, {})
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.ENVIRON_CONFIG_FILE} file", start)
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 _copy_pyconfigs_file(self, filepath: str):
37
- self._copy_file(u.join_paths(c.PYCONFIG_FOLDER, filepath))
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=["none"] + c.DATABASE_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._copy_pyconfigs_file(c.CONNECTIONS_FILE)
143
- elif not connections_use_yaml:
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._copy_pyconfigs_file(c.PARAMETERS_FILE)
148
- elif not parameters_use_yaml:
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._copy_pyconfigs_file(c.CONTEXT_FILE)
152
- self._copy_file(c.ENVIRON_CONFIG_FILE)
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._copy_pyconfigs_file(c.AUTH_FILE)
165
+ self._copy_pyconfig_file(c.AUTH_FILE)
159
166
 
160
167
  sample_db = answers.get(SAMPLE_DB)
161
- if sample_db is not None and sample_db != "none":
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
- user_attributes: dict
100
- parameters: dict
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: Optional[list[str]]
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
- return cls(name, label, model, scope, parameters, traits)
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
- env_config = EnvironConfigIO.obj.get_all_env_vars()
231
- content = u.render_string(raw_content, env_config)
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)