squirrels 0.3.3__py3-none-any.whl → 0.4.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.

Files changed (56) hide show
  1. squirrels/__init__.py +7 -3
  2. squirrels/_api_response_models.py +96 -72
  3. squirrels/_api_server.py +375 -201
  4. squirrels/_authenticator.py +23 -22
  5. squirrels/_command_line.py +70 -46
  6. squirrels/_connection_set.py +23 -25
  7. squirrels/_constants.py +29 -78
  8. squirrels/_dashboards_io.py +61 -0
  9. squirrels/_environcfg.py +53 -50
  10. squirrels/_initializer.py +184 -141
  11. squirrels/_manifest.py +168 -195
  12. squirrels/_models.py +159 -292
  13. squirrels/_package_loader.py +7 -8
  14. squirrels/_parameter_configs.py +173 -141
  15. squirrels/_parameter_sets.py +49 -38
  16. squirrels/_py_module.py +7 -7
  17. squirrels/_seeds.py +13 -12
  18. squirrels/_utils.py +114 -54
  19. squirrels/_version.py +1 -1
  20. squirrels/arguments/init_time_args.py +16 -10
  21. squirrels/arguments/run_time_args.py +89 -24
  22. squirrels/dashboards.py +82 -0
  23. squirrels/data_sources.py +212 -232
  24. squirrels/dateutils.py +29 -26
  25. squirrels/package_data/assets/index.css +1 -1
  26. squirrels/package_data/assets/index.js +27 -18
  27. squirrels/package_data/base_project/.gitignore +2 -2
  28. squirrels/package_data/base_project/connections.yml +1 -1
  29. squirrels/package_data/base_project/dashboards/dashboard_example.py +32 -0
  30. squirrels/package_data/base_project/dashboards.yml +10 -0
  31. squirrels/package_data/base_project/docker/.dockerignore +9 -4
  32. squirrels/package_data/base_project/docker/Dockerfile +7 -6
  33. squirrels/package_data/base_project/docker/compose.yml +1 -1
  34. squirrels/package_data/base_project/env.yml +2 -2
  35. squirrels/package_data/base_project/models/dbviews/{database_view1.py → dbview_example.py} +2 -1
  36. squirrels/package_data/base_project/models/dbviews/{database_view1.sql → dbview_example.sql} +3 -2
  37. squirrels/package_data/base_project/models/federates/{dataset_example.py → federate_example.py} +6 -6
  38. squirrels/package_data/base_project/models/federates/{dataset_example.sql → federate_example.sql} +1 -1
  39. squirrels/package_data/base_project/parameters.yml +6 -4
  40. squirrels/package_data/base_project/pyconfigs/auth.py +1 -1
  41. squirrels/package_data/base_project/pyconfigs/connections.py +1 -1
  42. squirrels/package_data/base_project/pyconfigs/context.py +38 -10
  43. squirrels/package_data/base_project/pyconfigs/parameters.py +15 -7
  44. squirrels/package_data/base_project/squirrels.yml.j2 +14 -7
  45. squirrels/package_data/templates/index.html +3 -3
  46. squirrels/parameter_options.py +103 -106
  47. squirrels/parameters.py +347 -195
  48. squirrels/project.py +378 -0
  49. squirrels/user_base.py +14 -6
  50. {squirrels-0.3.3.dist-info → squirrels-0.4.0.dist-info}/METADATA +9 -21
  51. squirrels-0.4.0.dist-info/RECORD +60 -0
  52. squirrels/_timer.py +0 -23
  53. squirrels-0.3.3.dist-info/RECORD +0 -56
  54. {squirrels-0.3.3.dist-info → squirrels-0.4.0.dist-info}/LICENSE +0 -0
  55. {squirrels-0.3.3.dist-info → squirrels-0.4.0.dist-info}/WHEEL +0 -0
  56. {squirrels-0.3.3.dist-info → squirrels-0.4.0.dist-info}/entry_points.txt +0 -0
squirrels/_environcfg.py CHANGED
@@ -1,81 +1,84 @@
1
- from typing import Any, Optional, Callable
2
- from dataclasses import dataclass
3
- import os, yaml
1
+ from pathlib import Path
2
+ from typing import Any, Callable
3
+ from pydantic import BaseModel, Field, field_validator, ValidationError
4
+ import os, yaml, time
4
5
 
5
6
  from . import _constants as c, _utils as u
6
- from ._timer import timer, time
7
7
 
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)
8
+ _GLOBAL_SQUIRRELS_CFG_FILE = u.Path(os.path.expanduser('~'), '.squirrels', c.ENV_CONFIG_FILE)
10
9
 
11
10
 
12
- @dataclass
13
- class _EnvironConfig:
14
- _users: dict[str, dict[str, Any]]
15
- _env_vars: dict[str, str]
16
- _credentials: dict[str, dict[str, str]]
17
- _secrets: dict[str, str]
18
-
19
- def __post_init__(self):
20
- for username, user in self._users.items():
21
- user[c.USER_NAME_KEY] = username
22
- if c.USER_PWD_KEY not in user:
23
- raise u.ConfigurationError(f"All users must have password in environcfg.yml")
24
-
25
- for key, cred in self._credentials.items():
26
- if c.USERNAME_KEY not in cred or c.PASSWORD_KEY not in cred:
27
- raise u.ConfigurationError(f'Either "{c.USERNAME_KEY}" or "{c.PASSWORD_KEY}" was not specified for credential "{key}"')
11
+ class _UserConfig(BaseModel, extra="allow"):
12
+ username: str
13
+ password: str
14
+ is_internal: bool = False
15
+
16
+
17
+ class _CredentialsConfig(BaseModel):
18
+ username: str
19
+ password: str
20
+
21
+
22
+ class EnvironConfig(BaseModel):
23
+ users: dict[str, _UserConfig] = Field(default_factory=dict)
24
+ env_vars: dict[str, str] = Field(default_factory=dict)
25
+ credentials: dict[str, _CredentialsConfig] = Field(default_factory=dict)
26
+ secrets: dict[str, Any | None] = Field(default_factory=dict)
27
+
28
+ @field_validator("users", mode="before")
29
+ @classmethod
30
+ def inject_username(cls, users: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]:
31
+ processed_users = {}
32
+ for username, user in users.items():
33
+ processed_users[username] = {"username": username, **user}
34
+ return processed_users
28
35
 
29
- def get_users(self) -> dict[str, dict[str, Any]]:
30
- return self._users.copy()
36
+ def get_users(self) -> dict[str, _UserConfig]:
37
+ return self.users.copy()
31
38
 
32
39
  def get_all_env_vars(self) -> dict[str, str]:
33
- return self._env_vars.copy()
40
+ return self.env_vars.copy()
34
41
 
35
- def get_credential(self, key: Optional[str]) -> tuple[str, str]:
42
+ def get_credential(self, key: str | None) -> tuple[str, str]:
36
43
  if not key:
37
44
  return "", ""
38
45
 
39
46
  try:
40
- credential = self._credentials[key]
47
+ credential = self.credentials[key]
41
48
  except KeyError as e:
42
49
  raise u.ConfigurationError(f'No credentials configured for "{key}"') from e
43
50
 
44
- return credential[c.USERNAME_KEY], credential[c.PASSWORD_KEY]
51
+ return credential.username, credential.password
45
52
 
46
- def get_secret(self, key: str, *, default_factory: Optional[Callable[[],str]] = None) -> str:
47
- if not self._secrets.get(key) and default_factory is not None:
48
- self._secrets[key] = default_factory()
49
- return self._secrets.get(key)
53
+ def get_secret(self, key: str, default_factory: Callable[[], Any]) -> Any:
54
+ if self.secrets.get(key) is None:
55
+ self.secrets[key] = default_factory()
56
+ return self.secrets[key]
50
57
 
51
58
 
52
59
  class EnvironConfigIO:
53
- obj: _EnvironConfig
54
60
 
55
61
  @classmethod
56
- def LoadFromFile(cls):
62
+ def load_from_file(cls, logger: u.Logger, base_path: str) -> EnvironConfig:
57
63
  start = time.time()
58
- def load_yaml(filename: str) -> dict[str, dict]:
64
+ def load_yaml(filename: str | Path) -> dict[str, dict]:
59
65
  try:
60
66
  with open(filename, 'r') as f:
61
67
  return yaml.safe_load(f)
62
68
  except FileNotFoundError:
63
69
  return {}
64
70
 
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)
71
+ master_env_config = load_yaml(_GLOBAL_SQUIRRELS_CFG_FILE)
72
+ proj_env_config = load_yaml(Path(base_path, c.ENV_CONFIG_FILE))
69
73
 
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])
74
+ for key in proj_env_config:
75
+ master_env_config.setdefault(key, {})
76
+ master_env_config[key].update(proj_env_config[key])
74
77
 
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, {})
79
-
80
- cls.obj = _EnvironConfig(users, env_vars, credentials, secrets)
81
- timer.add_activity_time(f"loading {c.ENV_CONFIG_FILE} file", start)
78
+ try:
79
+ env_cfg = EnvironConfig(**master_env_config)
80
+ except ValidationError as e:
81
+ raise u.ConfigurationError(f"Failed to process {c.ENV_CONFIG_FILE} file. " + str(e)) from e
82
+
83
+ logger.log_activity_time(f"loading {c.ENV_CONFIG_FILE} file", start)
84
+ return env_cfg
squirrels/_initializer.py CHANGED
@@ -1,178 +1,221 @@
1
1
  from typing import Optional
2
+ from datetime import datetime
2
3
  import inquirer, os, shutil
3
4
 
4
5
  from . import _constants as c, _utils as u
5
6
 
6
- base_proj_dir = u.join_paths(os.path.dirname(__file__), c.PACKAGE_DATA_FOLDER, c.BASE_PROJECT_FOLDER)
7
+ base_proj_dir = u.Path(os.path.dirname(__file__), c.PACKAGE_DATA_FOLDER, c.BASE_PROJECT_FOLDER)
7
8
 
8
9
 
9
10
  class Initializer:
10
- def __init__(self, overwrite: bool):
11
+ def __init__(self, *, overwrite: bool = False):
11
12
  self.overwrite = overwrite
12
13
 
13
- def _path_exists(self, filepath: str) -> bool:
14
- if not self.overwrite and os.path.exists(filepath):
15
- print(f'File "{filepath}" already exists. Creation skipped.')
16
- return True
17
- return False
14
+ def _path_exists(self, filepath: u.Path) -> bool:
15
+ return os.path.exists(filepath)
18
16
 
19
- def _copy_file(self, filepath: str, *, src_folder: str = ""):
20
- if not self._path_exists(filepath):
21
- dest_dir = os.path.dirname(filepath)
22
- if dest_dir != "":
23
- os.makedirs(dest_dir, exist_ok=True)
24
- src_path = u.join_paths(base_proj_dir, src_folder, filepath)
17
+ def _files_have_same_content(self, file1: u.Path, file2: u.Path) -> bool:
18
+ with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
19
+ return f1.read() == f2.read()
20
+
21
+ def _add_timestamp_to_filename(self, path: u.Path) -> u.Path:
22
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
23
+ new_filename = f"{path.stem}_{timestamp}{path.suffix}"
24
+ return path.with_name(new_filename)
25
+
26
+ def _copy_file(self, filepath: u.Path, *, src_folder: str = ""):
27
+ src_path = u.Path(base_proj_dir, src_folder, filepath)
28
+
29
+ dest_dir = os.path.dirname(filepath)
30
+ if dest_dir != "":
31
+ os.makedirs(dest_dir, exist_ok=True)
32
+
33
+ perform_copy = True
34
+ if self._path_exists(filepath):
35
+ old_filepath = filepath
36
+ if self._files_have_same_content(src_path, filepath):
37
+ perform_copy = False
38
+ extra_msg = "Skipping... file contents is same as source"
39
+ elif self.overwrite:
40
+ extra_msg = "Overwriting file..."
41
+ else:
42
+ filepath = self._add_timestamp_to_filename(old_filepath)
43
+ extra_msg = f'Creating file as "{filepath}" instead...'
44
+ print(f'File "{old_filepath}" already exists.', extra_msg)
45
+ else:
46
+ print(f'Creating file "{filepath}"...')
47
+
48
+ if perform_copy:
25
49
  shutil.copy(src_path, filepath)
26
50
 
27
51
  def _copy_dbview_file(self, filepath: str):
28
- self._copy_file(u.join_paths(c.MODELS_FOLDER, c.DBVIEWS_FOLDER, filepath))
52
+ self._copy_file(u.Path(c.MODELS_FOLDER, c.DBVIEWS_FOLDER, filepath))
29
53
 
30
54
  def _copy_federate_file(self, filepath: str):
31
- self._copy_file(u.join_paths(c.MODELS_FOLDER, c.FEDERATES_FOLDER, filepath))
55
+ self._copy_file(u.Path(c.MODELS_FOLDER, c.FEDERATES_FOLDER, filepath))
32
56
 
33
57
  def _copy_database_file(self, filepath: str):
34
- self._copy_file(u.join_paths(c.DATABASE_FOLDER, filepath))
58
+ self._copy_file(u.Path(c.DATABASE_FOLDER, filepath))
35
59
 
36
60
  def _copy_pyconfig_file(self, filepath: str):
37
- self._copy_file(u.join_paths(c.PYCONFIGS_FOLDER, filepath))
61
+ self._copy_file(u.Path(c.PYCONFIGS_FOLDER, filepath))
38
62
 
39
63
  def _copy_seed_file(self, filepath: str):
40
- self._copy_file(u.join_paths(c.SEEDS_FOLDER, filepath))
64
+ self._copy_file(u.Path(c.SEEDS_FOLDER, filepath))
65
+
66
+ def _copy_dashboard_file(self, filepath: str):
67
+ self._copy_file(u.Path(c.DASHBOARDS_FOLDER, filepath))
41
68
 
42
- def init_project(self, args):
43
- options = ["core", "connections", "parameters", "dbview", "federate", "auth", "sample_db"]
44
- CORE, CONNECTIONS, PARAMETERS, DBVIEW, FEDERATE, AUTH, SAMPLE_DB = options
69
+ def _create_manifest_file(self, has_connections: bool, has_parameters: bool, has_dashboards: bool):
45
70
  TMP_FOLDER = "tmp"
46
71
 
72
+ def get_content(file_name: Optional[str]) -> str:
73
+ if file_name is None:
74
+ return ""
75
+
76
+ yaml_path = u.Path(base_proj_dir, file_name)
77
+ return u.read_file(yaml_path)
78
+
79
+ file_name_dict = {
80
+ "parameters": c.PARAMETERS_YML_FILE if has_parameters else None,
81
+ "connections": c.CONNECTIONS_YML_FILE if has_connections else None,
82
+ "dashboards": c.DASHBOARDS_YML_FILE if has_dashboards else None
83
+ }
84
+ substitutions = {key: get_content(val) for key, val in file_name_dict.items()}
85
+
86
+ manifest_template = get_content(c.MANIFEST_JINJA_FILE)
87
+ manifest_content = u.render_string(manifest_template, **substitutions)
88
+ output_path = u.Path(base_proj_dir, TMP_FOLDER, c.MANIFEST_FILE)
89
+ with open(u.Path(output_path), "w") as f:
90
+ f.write(manifest_content)
91
+
92
+ self._copy_file(u.Path(c.MANIFEST_FILE), src_folder=TMP_FOLDER)
93
+
94
+ def init_project(self, args):
95
+ options = ["core", "connections", "parameters", "dbview", "federate", "dashboard", "auth"]
96
+ _, CONNECTIONS, PARAMETERS, DBVIEW, FEDERATE, DASHBOARD, AUTH = options
97
+
47
98
  answers = { x: getattr(args, x) for x in options }
48
99
  if not any(answers.values()):
49
- core_questions = [
50
- inquirer.Confirm(CORE,
51
- message="Include all core project files?",
52
- default=True)
53
- ]
54
- answers = inquirer.prompt(core_questions)
55
-
56
- if answers.get(CORE, False):
57
- conditional_questions = [
58
- inquirer.List(CONNECTIONS,
59
- message=f"How would you like to configure the database connections?" ,
60
- choices=c.CONF_FORMAT_CHOICES),
61
- inquirer.List(PARAMETERS,
62
- message=f"How would you like to configure the parameters?" ,
63
- choices=c.CONF_FORMAT_CHOICES2),
64
- inquirer.List(DBVIEW,
65
- message="What's the file format for the database view model?",
66
- choices=c.FILE_TYPE_CHOICES),
67
- inquirer.List(FEDERATE,
68
- message="What's the file format for the federated model?",
69
- choices=c.FILE_TYPE_CHOICES),
70
- ]
71
- answers.update(inquirer.prompt(conditional_questions))
72
-
73
- remaining_questions = [
74
- inquirer.Confirm(AUTH,
75
- message=f"Do you want to add the '{c.AUTH_FILE}' file to enable custom API authentication?" ,
76
- default=False),
77
- inquirer.List(SAMPLE_DB,
78
- message="What sample sqlite database do you wish to use (if any)?",
79
- choices= c.DATABASE_CHOICES)
100
+ questions = [
101
+ inquirer.List(
102
+ CONNECTIONS, message=f"How would you like to configure the database connections?", choices=c.CONF_FORMAT_CHOICES
103
+ ),
104
+ inquirer.List(
105
+ PARAMETERS, message=f"How would you like to configure the parameters?", choices=c.CONF_FORMAT_CHOICES2
106
+ ),
107
+ inquirer.List(
108
+ DBVIEW, message="What's the file format for the database view model?", choices=c.FILE_TYPE_CHOICES
109
+ ),
110
+ inquirer.List(
111
+ FEDERATE, message="What's the file format for the federated model?", choices=c.FILE_TYPE_CHOICES
112
+ ),
113
+ inquirer.Confirm(
114
+ DASHBOARD, message=f"Do you want to include a dashboard example?", default=False
115
+ ),
116
+ inquirer.Confirm(
117
+ AUTH, message=f"Do you want to add the '{c.AUTH_FILE}' file to enable custom API authentication?", default=False
118
+ ),
80
119
  ]
81
- answers.update(inquirer.prompt(remaining_questions))
120
+ answers = inquirer.prompt(questions)
121
+ assert isinstance(answers, dict)
82
122
 
83
- if answers.get(CONNECTIONS) is None:
84
- answers[CONNECTIONS] = c.YML_FORMAT
85
- if answers.get(PARAMETERS) is None:
86
- answers[PARAMETERS] = c.PYTHON_FORMAT
87
- if answers.get(DBVIEW) is None:
88
- answers[DBVIEW] = c.SQL_FILE_TYPE
89
- if answers.get(FEDERATE) is None:
90
- answers[FEDERATE] = c.SQL_FILE_TYPE
91
-
92
- if answers.get(CORE, False):
93
- connections_format = answers.get(CONNECTIONS)
94
- connections_use_yaml = (connections_format == c.YML_FORMAT)
95
- connections_use_py = (connections_format == c.PYTHON_FORMAT)
96
-
97
- parameters_format = answers.get(PARAMETERS)
98
- parameters_use_yaml = (parameters_format == c.YML_FORMAT)
99
- parameters_use_py = (parameters_format == c.PYTHON_FORMAT)
100
-
101
- db_view_format = answers.get(DBVIEW)
102
- if db_view_format == c.SQL_FILE_TYPE:
103
- db_view_file = c.DATABASE_VIEW_SQL_FILE
104
- elif db_view_format == c.PYTHON_FILE_TYPE:
105
- db_view_file = c.DATABASE_VIEW_PY_FILE
106
- else:
107
- raise NotImplementedError(f"Database view format '{db_view_format}' not supported")
123
+ def get_answer(key, default):
124
+ """
125
+ If key is in answers dict as None, using `.get` on a dictionary will return None even if a default is provided.
126
+
127
+ For instance, the following prints None.
128
+ >>> test_dict = {"key": None}
129
+ >>> print(test_dict.get("key", "default"))
130
+
131
+ This function will return the default value if the key is in the dict with value None.
132
+ """
133
+ answer = answers.get(key)
134
+ return answer if answer is not None else default
108
135
 
109
- federate_format = answers.get(FEDERATE)
110
- if federate_format == c.SQL_FILE_TYPE:
111
- federate_file = c.FEDERATE_SQL_NAME
112
- elif federate_format == c.PYTHON_FILE_TYPE:
113
- federate_file = c.FEDERATE_PY_NAME
114
- else:
115
- raise NotImplementedError(f"Dataset format '{federate_format}' not supported")
136
+ connections_format = get_answer(CONNECTIONS, c.YML_FORMAT)
137
+ connections_use_yaml = (connections_format == c.YML_FORMAT)
138
+ connections_use_py = (connections_format == c.PYTHON_FORMAT)
139
+
140
+ parameters_format = get_answer(PARAMETERS, c.PYTHON_FORMAT)
141
+ parameters_use_yaml = (parameters_format == c.YML_FORMAT)
142
+ parameters_use_py = (parameters_format == c.PYTHON_FORMAT)
143
+
144
+ dbview_format = get_answer(DBVIEW, c.SQL_FILE_TYPE)
145
+ if dbview_format == c.SQL_FILE_TYPE:
146
+ db_view_file = c.DBVIEW_FILE_STEM + ".sql"
147
+ elif dbview_format == c.PYTHON_FILE_TYPE:
148
+ db_view_file = c.DBVIEW_FILE_STEM + ".py"
149
+ else:
150
+ raise NotImplementedError(f"Dbview model format '{dbview_format}' not supported")
116
151
 
117
- def create_manifest_file():
118
- def get_content(file_name: Optional[str]) -> str:
119
- if file_name is None:
120
- return ""
121
-
122
- yaml_path = u.join_paths(base_proj_dir, file_name)
123
- return u.read_file(yaml_path)
124
-
125
- file_name_dict = {
126
- "parameters": c.PARAMETERS_YML_FILE if parameters_use_yaml else None,
127
- "connections": c.CONNECTIONS_YML_FILE if connections_use_yaml else None
128
- }
129
- substitutions = {key: get_content(val) for key, val in file_name_dict.items()}
130
-
131
- manifest_template = get_content(c.MANIFEST_JINJA_FILE)
132
- manifest_content = u.render_string(manifest_template, **substitutions)
133
- output_path = u.join_paths(base_proj_dir, TMP_FOLDER, c.MANIFEST_FILE)
134
- with open(u.join_paths(output_path), "w") as f:
135
- f.write(manifest_content)
136
-
137
- create_manifest_file()
138
-
139
- self._copy_file(".gitignore")
140
- self._copy_file(c.MANIFEST_FILE, src_folder=TMP_FOLDER)
141
-
142
- if connections_use_py:
143
- self._copy_pyconfig_file(c.CONNECTIONS_FILE)
144
- elif connections_use_yaml:
145
- pass # already included in squirrels.yml
146
- else:
147
- raise NotImplementedError(f"Format '{connections_format}' not supported for configuring database connections")
148
-
149
- if parameters_use_py:
150
- self._copy_pyconfig_file(c.PARAMETERS_FILE)
151
- elif parameters_use_yaml:
152
- pass # already included in squirrels.yml
153
- else:
154
- raise NotImplementedError(f"Format '{parameters_format}' not supported for configuring widget parameters")
155
-
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)
152
+ federate_format = get_answer(FEDERATE, c.SQL_FILE_TYPE)
153
+ if federate_format == c.SQL_FILE_TYPE:
154
+ federate_file = c.FEDERATE_FILE_STEM + ".sql"
155
+ elif federate_format == c.PYTHON_FILE_TYPE:
156
+ federate_file = c.FEDERATE_FILE_STEM + ".py"
157
+ else:
158
+ raise NotImplementedError(f"Federate model format '{federate_format}' not supported")
160
159
 
161
- self._copy_dbview_file(db_view_file)
162
- self._copy_federate_file(federate_file)
160
+ dashboards_enabled = get_answer(DASHBOARD, False)
161
+
162
+ self._create_manifest_file(connections_use_yaml, parameters_use_yaml, dashboards_enabled)
163
+
164
+ self._copy_file(u.Path(".gitignore"))
165
+ self._copy_file(u.Path(c.ENV_CONFIG_FILE))
166
+
167
+ if connections_use_py:
168
+ self._copy_pyconfig_file(c.CONNECTIONS_FILE)
169
+ elif connections_use_yaml:
170
+ pass # already included in squirrels.yml
171
+ else:
172
+ raise NotImplementedError(f"Format '{connections_format}' not supported for configuring database connections")
173
+
174
+ if parameters_use_py:
175
+ self._copy_pyconfig_file(c.PARAMETERS_FILE)
176
+ elif parameters_use_yaml:
177
+ pass # already included in squirrels.yml
178
+ else:
179
+ raise NotImplementedError(f"Format '{parameters_format}' not supported for configuring widget parameters")
163
180
 
164
- if answers.get(AUTH, False):
181
+ self._copy_pyconfig_file(c.CONTEXT_FILE)
182
+ self._copy_seed_file(c.CATEGORY_SEED_FILE)
183
+ self._copy_seed_file(c.SUBCATEGORY_SEED_FILE)
184
+
185
+ self._copy_dbview_file(db_view_file)
186
+ self._copy_federate_file(federate_file)
187
+
188
+ if dashboards_enabled:
189
+ self._copy_dashboard_file(c.DASHBOARD_FILE_STEM + ".py")
190
+
191
+ if get_answer(AUTH, False):
165
192
  self._copy_pyconfig_file(c.AUTH_FILE)
166
193
 
167
- sample_db = answers.get(SAMPLE_DB)
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
194
+ self._copy_database_file(c.EXPENSES_DB)
170
195
 
171
- if sample_db != c.NO_DB:
172
- if sample_db == c.EXPENSES_DB_NAME:
173
- self._copy_database_file(c.EXPENSES_DB_NAME+".db")
174
- elif sample_db == c.WEATHER_DB_NAME:
175
- self._copy_database_file(c.WEATHER_DB_NAME+".db")
196
+ print(f"\nSuccessfully created new Squirrels project in current directory!\n")
197
+
198
+ def get_file(self, args):
199
+ if args.file_name == c.ENV_CONFIG_FILE:
200
+ self._copy_file(u.Path(c.ENV_CONFIG_FILE))
201
+ print("PLEASE ENSURE THE FILE IS INCLUDED IN .gitignore")
202
+ elif args.file_name == c.MANIFEST_FILE:
203
+ self._create_manifest_file(not args.no_connections, args.parameters, args.dashboards)
204
+ elif args.file_name in (c.AUTH_FILE, c.CONNECTIONS_FILE, c.PARAMETERS_FILE, c.CONTEXT_FILE):
205
+ self._copy_pyconfig_file(args.file_name)
206
+ elif args.file_name in (c.DBVIEW_FILE_STEM, c.FEDERATE_FILE_STEM):
207
+ if args.format == c.SQL_FILE_TYPE:
208
+ extension = ".sql"
209
+ elif args.format == c.PYTHON_FILE_TYPE:
210
+ extension = ".py"
176
211
  else:
177
- raise NotImplementedError(f"No database found called '{sample_db}'")
178
-
212
+ raise NotImplementedError(f"Format '{args.format}' not supported for {args.file_name}")
213
+ copy_method = self._copy_dbview_file if args.file_name == c.DBVIEW_FILE_STEM else self._copy_federate_file
214
+ copy_method(args.file_name + extension)
215
+ elif args.file_name == c.DASHBOARD_FILE_STEM:
216
+ self._copy_dashboard_file(args.file_name + ".py")
217
+ elif args.file_name in (c.EXPENSES_DB, c.WEATHER_DB):
218
+ self._copy_database_file(args.file_name)
219
+ else:
220
+ raise NotImplementedError(f"File '{args.file_name}' not supported")
221
+