squirrels 0.1.1.post1__py3-none-any.whl → 0.2.0.dev0__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 (74) hide show
  1. squirrels/__init__.py +10 -16
  2. squirrels/_api_server.py +234 -80
  3. squirrels/_authenticator.py +84 -0
  4. squirrels/_command_line.py +60 -72
  5. squirrels/_connection_set.py +96 -0
  6. squirrels/_constants.py +114 -33
  7. squirrels/_environcfg.py +77 -0
  8. squirrels/_initializer.py +126 -67
  9. squirrels/_manifest.py +195 -168
  10. squirrels/_models.py +495 -0
  11. squirrels/_package_loader.py +26 -0
  12. squirrels/_parameter_configs.py +401 -0
  13. squirrels/_parameter_sets.py +188 -0
  14. squirrels/_py_module.py +60 -0
  15. squirrels/_timer.py +36 -0
  16. squirrels/_utils.py +81 -49
  17. squirrels/_version.py +2 -2
  18. squirrels/arguments/init_time_args.py +32 -0
  19. squirrels/arguments/run_time_args.py +82 -0
  20. squirrels/data_sources.py +380 -155
  21. squirrels/dateutils.py +86 -57
  22. squirrels/package_data/base_project/Dockerfile +15 -0
  23. squirrels/package_data/base_project/connections.yml +7 -0
  24. squirrels/package_data/base_project/database/{sample_database.db → expenses.db} +0 -0
  25. squirrels/package_data/base_project/environcfg.yml +29 -0
  26. squirrels/package_data/base_project/ignores/.dockerignore +8 -0
  27. squirrels/package_data/base_project/ignores/.gitignore +7 -0
  28. squirrels/package_data/base_project/models/dbviews/database_view1.py +36 -0
  29. squirrels/package_data/base_project/models/dbviews/database_view1.sql +15 -0
  30. squirrels/package_data/base_project/models/federates/dataset_example.py +20 -0
  31. squirrels/package_data/base_project/models/federates/dataset_example.sql +3 -0
  32. squirrels/package_data/base_project/parameters.yml +109 -0
  33. squirrels/package_data/base_project/pyconfigs/auth.py +47 -0
  34. squirrels/package_data/base_project/pyconfigs/connections.py +28 -0
  35. squirrels/package_data/base_project/pyconfigs/context.py +45 -0
  36. squirrels/package_data/base_project/pyconfigs/parameters.py +55 -0
  37. squirrels/package_data/base_project/seeds/mocks/category.csv +3 -0
  38. squirrels/package_data/base_project/seeds/mocks/max_filter.csv +2 -0
  39. squirrels/package_data/base_project/seeds/mocks/subcategory.csv +6 -0
  40. squirrels/package_data/base_project/squirrels.yml.j2 +57 -0
  41. squirrels/package_data/base_project/tmp/.gitignore +2 -0
  42. squirrels/package_data/static/script.js +159 -63
  43. squirrels/package_data/static/style.css +79 -15
  44. squirrels/package_data/static/widgets.js +133 -0
  45. squirrels/package_data/templates/index.html +65 -23
  46. squirrels/package_data/templates/index2.html +22 -0
  47. squirrels/parameter_options.py +216 -119
  48. squirrels/parameters.py +407 -478
  49. squirrels/user_base.py +58 -0
  50. squirrels-0.2.0.dev0.dist-info/METADATA +126 -0
  51. squirrels-0.2.0.dev0.dist-info/RECORD +56 -0
  52. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/WHEEL +1 -2
  53. squirrels-0.2.0.dev0.dist-info/entry_points.txt +3 -0
  54. squirrels/_credentials_manager.py +0 -87
  55. squirrels/_module_loader.py +0 -37
  56. squirrels/_parameter_set.py +0 -151
  57. squirrels/_renderer.py +0 -286
  58. squirrels/_timed_imports.py +0 -37
  59. squirrels/connection_set.py +0 -126
  60. squirrels/package_data/base_project/.gitignore +0 -4
  61. squirrels/package_data/base_project/connections.py +0 -20
  62. squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -22
  63. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -29
  64. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -12
  65. squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -11
  66. squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -3
  67. squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -47
  68. squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -9
  69. squirrels/package_data/base_project/squirrels.yaml +0 -22
  70. squirrels-0.1.1.post1.dist-info/METADATA +0 -67
  71. squirrels-0.1.1.post1.dist-info/RECORD +0 -40
  72. squirrels-0.1.1.post1.dist-info/entry_points.txt +0 -2
  73. squirrels-0.1.1.post1.dist-info/top_level.txt +0 -1
  74. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/LICENSE +0 -0
@@ -1,24 +1,9 @@
1
- from typing import List, Tuple, Optional
2
1
  from argparse import ArgumentParser
3
- import sys, time, pwinput
2
+ import sys, time, asyncio
4
3
  sys.path.append('.')
5
4
 
6
- from squirrels import _constants as c, _credentials_manager as cm, _manifest as mf, _module_loader as ml
7
- from squirrels import connection_set as cs
8
- from squirrels._version import __version__
9
- from squirrels._api_server import ApiServer
10
- from squirrels._renderer import RendererIOWrapper
11
- from squirrels._initializer import Initializer
12
- from squirrels._timed_imports import timer
13
-
14
-
15
- def _prompt_user_pw(args_values: Optional[List[str]]) -> Tuple[str, str]:
16
- if args_values is not None:
17
- user, pw = args_values
18
- else:
19
- user = input("Enter username: ")
20
- pw = pwinput.pwinput("Enter password: ")
21
- return user, pw
5
+ from . import _constants as c
6
+ from ._timer import timer
22
7
 
23
8
 
24
9
  def main():
@@ -26,81 +11,84 @@ def main():
26
11
  Main entry point for the squirrels command line utilities.
27
12
  """
28
13
  start = time.time()
29
- parser = ArgumentParser(description="Command line utilities from the squirrels python package")
30
- parser.add_argument('-v', '--version', action='store_true', help='Show the version and exit')
31
- parser.add_argument('--verbose', action='store_true', help='Enable verbose output')
14
+ parser = ArgumentParser(description="Command line utilities from the squirrels python package", add_help=False)
15
+ parser.add_argument('-h', '--help', action="help", help="Show this help message and exit")
16
+ parser.add_argument('-V', '--version', action='store_true', help='Show the version and exit')
17
+ parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output')
32
18
  subparsers = parser.add_subparsers(title='commands', dest='command')
33
19
 
34
- init_parser = subparsers.add_parser(c.INIT_CMD, help='Initialize a squirrels project')
35
- init_parser.add_argument('--overwrite', action='store_true', help="Overwrite files that already exist")
36
- init_parser.add_argument('--core', action='store_true', help='Include all core files (squirrels.yaml, parameters.py, database view, etc.)')
37
- init_parser.add_argument('--db-view', type=str, choices=c.FILE_TYPE_CHOICES, help='Create database view as sql (default) or python file. Ignored if "--core" is not specified')
38
- init_parser.add_argument('--connections', action='store_true', help=f'Include the {c.CONNECTIONS_FILE} file')
39
- init_parser.add_argument('--context', action='store_true', help=f'Include the {c.CONTEXT_FILE} file')
40
- init_parser.add_argument('--selections-cfg', action='store_true', help=f'Include the {c.SELECTIONS_CFG_FILE} file')
41
- init_parser.add_argument('--final-view', type=str, choices=c.FILE_TYPE_CHOICES, help='Include final view as sql or python file')
20
+ init_parser = subparsers.add_parser(c.INIT_CMD, help='Initialize a squirrels project', add_help=False)
21
+ init_parser.add_argument('-h', '--help', action="help", help="Show this help message and exit")
22
+ init_parser.add_argument('-o', '--overwrite', action='store_true', help="Overwrite files that already exist")
23
+ init_parser.add_argument('--core', action='store_true', help='Include all core files')
24
+ init_parser.add_argument('--connections', type=str, choices=c.CONF_FORMAT_CHOICES, help=f'Configure database connections as yaml (default) or python. Ignored if "--core" is not specified')
25
+ init_parser.add_argument('--parameters', type=str, choices=c.CONF_FORMAT_CHOICES, help=f'Configure parameters as python (default) or yaml. Ignored if "--core" is not specified')
26
+ init_parser.add_argument('--dbview', type=str, choices=c.FILE_TYPE_CHOICES, help='Create database view model as sql (default) or python file. Ignored if "--core" is not specified')
27
+ init_parser.add_argument('--federate', type=str, choices=c.FILE_TYPE_CHOICES, help='Create federated model as sql (default) or python file. Ignored if "--core" is not specified')
28
+ init_parser.add_argument('--auth', action='store_true', help=f'Include the {c.AUTH_FILE} file')
42
29
  init_parser.add_argument('--sample-db', type=str, choices=c.DATABASE_CHOICES, help='Sample sqlite database to include')
43
30
 
44
- subparsers.add_parser(c.LOAD_MODULES_CMD, help='Load all the modules specified in squirrels.yaml from git')
45
-
46
- def _add_profile_argument(parser: ArgumentParser):
47
- parser.add_argument('key', type=str, help='Key to the database connection credential')
31
+ module_parser = subparsers.add_parser(c.DEPS_CMD, help=f'Load all packages specified in {c.MANIFEST_FILE} (from git)', add_help=False)
32
+ module_parser.add_argument('-h', '--help', action="help", help="Show this help message and exit")
48
33
 
49
- set_cred_parser = subparsers.add_parser(c.SET_CRED_CMD, help='Set a database connection credential key')
50
- _add_profile_argument(set_cred_parser)
51
- set_cred_parser.add_argument('--values', type=str, nargs=2, help='The username and password')
34
+ compile_parser = subparsers.add_parser(c.COMPILE_CMD, help='Create files for rendered sql queries in the "target/compile" folder', add_help=False)
35
+ compile_parser.add_argument('-h', '--help', action="help", help="Show this help message and exit")
36
+ compile_parser.add_argument('-d', '--dataset', type=str, help="Select dataset to use for dataset args. If not specified, all models for all datasets are compiled")
37
+ compile_parser.add_argument('-a', '--all-test-sets', action="store_true", help="Compile models for all test sets")
38
+ compile_parser.add_argument('-t', '--test-set', type=str, help="The selection test set to use. Default selections are used if not specified. Ignored if using --all-test-sets")
39
+ compile_parser.add_argument('-s', '--select', type=str, help="Select single model to compile. If not specified, all models for the dataset are compiled. Also, ignored if --dataset is not specified")
40
+ compile_parser.add_argument('-r', '--runquery', action='store_true', help='Runs all target models, and produce the results as csv files')
52
41
 
53
- subparsers.add_parser(c.GET_CREDS_CMD, help='Get all database connection credential keys')
54
-
55
- delete_cred_parser = subparsers.add_parser(c.DELETE_CRED_CMD, help='Delete a database connection credential key')
56
- _add_profile_argument(delete_cred_parser)
57
-
58
- test_parser = subparsers.add_parser(c.TEST_CMD, help='For a given dataset, create outputs for parameter API response and rendered sql queries')
59
- test_parser.add_argument('dataset', type=str, help='Name of dataset (provided in squirrels.yaml) to test. Results are written in an "outputs" folder')
60
- test_parser.add_argument('-c', '--cfg', type=str, help="Configuration file for parameter selections. Path is relative to the dataset's folder")
61
- test_parser.add_argument('-d', '--data', type=str, help="Excel file with lookup data to avoid making a database connection. Path is relative to the dataset's folder")
62
- test_parser.add_argument('-r', '--runquery', action='store_true', help='Runs all database queries and final view, and produce the results as csv files')
63
-
64
- run_parser = subparsers.add_parser(c.RUN_CMD, help='Run the builtin API server')
42
+ run_parser = subparsers.add_parser(c.RUN_CMD, help='Run the builtin API server', add_help=False)
43
+ run_parser.add_argument('-h', '--help', action="help", help="Show this help message and exit")
65
44
  run_parser.add_argument('--no-cache', action='store_true', help='Do not cache any api results')
66
- run_parser.add_argument('--debug', action='store_true', help='In debug mode, all "hidden parameters" show in the parameters response')
67
- run_parser.add_argument('--host', type=str, default='127.0.0.1')
68
- run_parser.add_argument('--port', type=int, default=8000)
45
+ run_parser.add_argument('--debug', action='store_true', help='Show all "hidden parameters" in the parameters response')
46
+ run_parser.add_argument('--host', type=str, default='127.0.0.1', help="The host to run on")
47
+ run_parser.add_argument('--port', type=int, default=4465, help="The port to run on")
69
48
 
70
49
  args, _ = parser.parse_known_args()
71
50
  timer.verbose = args.verbose
72
51
  timer.add_activity_time('parsing arguments', start)
52
+
53
+ from . import __version__
54
+ from ._api_server import ApiServer
55
+ from ._models import ModelsIO
56
+ from ._initializer import Initializer
57
+ from ._manifest import ManifestIO
58
+ from ._package_loader import PackageLoaderIO
59
+ from ._connection_set import ConnectionSetIO
60
+ from ._parameter_sets import ParameterConfigsSetIO
73
61
 
74
62
  if args.version:
75
63
  print(__version__)
76
64
  elif args.command == c.INIT_CMD:
77
65
  Initializer(args.overwrite).init_project(args)
78
- elif args.command == c.LOAD_MODULES_CMD:
79
- manifest = mf._from_file()
80
- ml.load_modules(manifest)
81
- elif args.command == c.SET_CRED_CMD:
82
- user, pw = _prompt_user_pw(args.values)
83
- cm.squirrels_config_io.set_credential(args.key, user, pw)
84
- elif args.command == c.GET_CREDS_CMD:
85
- cm.squirrels_config_io.print_all_credentials()
86
- elif args.command == c.DELETE_CRED_CMD:
87
- cm.squirrels_config_io.delete_credential(args.key)
88
- elif args.command in [c.RUN_CMD, c.TEST_CMD]:
89
- manifest = mf._from_file()
90
- conn_set = cs._from_file(manifest)
91
- if args.command == c.RUN_CMD:
92
- server = ApiServer(manifest, conn_set, args.no_cache, args.debug)
93
- server.run(args)
94
- elif args.command == c.TEST_CMD:
95
- rendererIO = RendererIOWrapper(args.dataset, manifest, conn_set, args.data)
96
- rendererIO.write_outputs(args.cfg, args.runquery)
97
- conn_set._dispose()
66
+ elif args.command == c.DEPS_CMD:
67
+ ManifestIO.LoadFromFile()
68
+ PackageLoaderIO.LoadPackages(reload=True)
69
+ elif args.command in [c.RUN_CMD, c.COMPILE_CMD]:
70
+ ManifestIO.LoadFromFile()
71
+ PackageLoaderIO.LoadPackages()
72
+ ConnectionSetIO.LoadFromFile()
73
+ try:
74
+ ParameterConfigsSetIO.LoadFromFile()
75
+ ModelsIO.LoadFiles()
76
+
77
+ if args.command == c.RUN_CMD:
78
+ server = ApiServer(args.no_cache, args.debug)
79
+ server.run(args)
80
+ pass
81
+ elif args.command == c.COMPILE_CMD:
82
+ task = ModelsIO.WriteOutputs(args.dataset, args.select, args.all_test_sets, args.test_set, args.runquery)
83
+ asyncio.run(task)
84
+ finally:
85
+ ConnectionSetIO.Dispose()
98
86
  elif args.command is None:
99
87
  print(f'Command is missing. Enter "squirrels -h" for help.')
100
88
  else:
101
89
  print(f'Error: No such command "{args.command}". Enter "squirrels -h" for help.')
102
90
 
103
- timer.report_times()
91
+ # timer.report_times()
104
92
 
105
93
 
106
94
  if __name__ == '__main__':
@@ -0,0 +1,96 @@
1
+ from typing import Union
2
+ from dataclasses import dataclass
3
+ from sqlalchemy import Engine, Pool, create_engine
4
+ import pandas as pd
5
+
6
+ from . import _utils as u, _constants as c, _py_module as pm
7
+ from .arguments.init_time_args import ConnectionsArgs
8
+ from ._environcfg import EnvironConfigIO
9
+ from ._manifest import ManifestIO
10
+ from ._timer import timer, time
11
+
12
+
13
+ @dataclass
14
+ class ConnectionSet:
15
+ """
16
+ A wrapper class around a collection of Connection Pools or Sqlalchemy Engines
17
+
18
+ Attributes:
19
+ conn_pools: A dictionary of connection pool name to the corresponding Pool or Engine from sqlalchemy
20
+ """
21
+ _conn_pools: dict[str, Union[Engine, Pool]]
22
+
23
+ def get_connections_as_dict(self):
24
+ return self._conn_pools
25
+
26
+ def get_connection_pool(self, conn_name: str) -> Union[Engine, Pool]:
27
+ try:
28
+ connection_pool = self._conn_pools[conn_name]
29
+ except KeyError as e:
30
+ raise u.ConfigurationError(f'Connection name "{conn_name}" was not configured') from e
31
+ return connection_pool
32
+
33
+ def run_sql_query_from_conn_name(self, query: str, conn_name: str) -> pd.DataFrame:
34
+ connection_pool = self.get_connection_pool(conn_name)
35
+ if isinstance(connection_pool, Pool):
36
+ conn = connection_pool.connect()
37
+ elif isinstance(connection_pool, Engine):
38
+ conn = connection_pool.raw_connection()
39
+ else:
40
+ raise u.ConfigurationError(f'Value type not supported for connection name "{conn_name}". Must be sqlalchemy engine or pool')
41
+
42
+ try:
43
+ cur = conn.cursor()
44
+ try:
45
+ cur.execute(query)
46
+ except Exception as e:
47
+ raise RuntimeError(e)
48
+ df = pd.DataFrame(data=cur.fetchall(), columns=[x[0] for x in cur.description])
49
+ finally:
50
+ conn.close()
51
+
52
+ return df
53
+
54
+ def _dispose(self) -> None:
55
+ """
56
+ Disposes of all the connection pools in this ConnectionSet
57
+ """
58
+ for pool in self._conn_pools.values():
59
+ if isinstance(pool, (Engine, Pool)):
60
+ pool.dispose()
61
+
62
+
63
+ class ConnectionSetIO:
64
+ args: ConnectionsArgs
65
+ obj: ConnectionSet
66
+
67
+ @classmethod
68
+ def LoadFromFile(cls):
69
+ """
70
+ Takes the DB Connections from both the squirrels.yml and connections.py files and merges them
71
+ into a single ConnectionSet
72
+
73
+ Returns:
74
+ A ConnectionSet with the DB connections from both squirrels.yml and connections.py
75
+ """
76
+ start = time.time()
77
+ connections: dict[str, Union[Engine, Pool]] = {}
78
+ cls.obj = ConnectionSet(connections)
79
+ try:
80
+ for config in ManifestIO.obj.db_connections:
81
+ connections[config.connection_name] = create_engine(config.url)
82
+
83
+ proj_vars = ManifestIO.obj.project_variables
84
+ env_vars = EnvironConfigIO.obj.get_all_env_vars()
85
+ get_credential = EnvironConfigIO.obj.get_credential
86
+ cls.args = ConnectionsArgs(proj_vars, env_vars, get_credential)
87
+ pm.run_pyconfig_main(c.CONNECTIONS_FILE, {"connections": connections, "sqrl": cls.args})
88
+ except Exception as e:
89
+ cls.Dispose()
90
+ raise e
91
+
92
+ timer.add_activity_time("creating sqlalchemy engines or pools", start)
93
+
94
+ @classmethod
95
+ def Dispose(cls):
96
+ cls.obj._dispose()
squirrels/_constants.py CHANGED
@@ -1,64 +1,145 @@
1
1
  # Squirrels CLI commands
2
2
  INIT_CMD = 'init'
3
- LOAD_MODULES_CMD = 'load-modules'
4
- GET_CREDS_CMD = 'get-all-credentials'
5
- SET_CRED_CMD = 'set-credential'
6
- DELETE_CRED_CMD = 'delete-credential'
7
- TEST_CMD = 'test'
3
+ DEPS_CMD = 'deps'
4
+ COMPILE_CMD = 'compile'
8
5
  RUN_CMD = 'run'
9
6
 
10
7
  # Manifest file keys
11
8
  PROJ_VARS_KEY = 'project_variables'
9
+ PROJECT_NAME_KEY = 'name'
10
+ PROJECT_LABEL_KEY = 'label'
11
+ MAJOR_VERSION_KEY = 'major_version'
12
+ MINOR_VERSION_KEY = 'minor_version'
13
+
14
+ PACKAGES_KEY = 'packages'
15
+ PACKAGE_GIT_KEY = 'git'
16
+ PACKAGE_DIRECTORY_KEY = 'directory'
17
+ PACKAGE_REVISION_KEY = 'revision'
18
+
12
19
  DB_CONNECTIONS_KEY = 'db_connections'
13
- DB_CREDENTIALS_KEY = 'credential_key'
14
- URL_KEY = 'url'
15
- DB_CONNECTION_KEY = 'db_connection'
16
- MODULES_KEY = 'modules'
17
- DATASET_LABEL_KEY = 'label'
20
+ DB_CONN_NAME_KEY = 'connection_name'
21
+ DB_CONN_CRED_KEY = 'credential_key'
22
+ DB_CONN_URL_KEY = 'url'
23
+
24
+ DBVIEWS_KEY = 'dbviews'
25
+ DBVIEW_NAME_KEY = 'name'
26
+ DBVIEW_CONN_KEY = 'connection_name'
27
+ DEFAULT_DB_CONN = 'default'
28
+
29
+ FEDERATES_KEY = 'federates'
30
+ FEDERATE_NAME_KEY = 'name'
31
+ MATERIALIZED_KEY = 'materialized'
32
+ DEFAULT_TABLE_MATERIALIZE = 'table'
33
+
34
+ PARAMETERS_KEY = 'parameters'
35
+ PARAMETER_NAME_KEY = 'name'
36
+ PARAMETER_TYPE_KEY = 'type'
37
+ PARAMETER_FACTORY_KEY = 'factory'
38
+ PARAMETER_ARGS_KEY = 'arguments'
39
+
40
+ TEST_SETS_KEY = 'selection_test_sets'
41
+ TEST_SET_NAME_KEY = 'name'
42
+ DEFAULT_TEST_SET_NAME = 'default'
43
+ TEST_SET_USER_ATTR_KEY = 'user_attributes'
44
+ TEST_SET_PARAMETERS_KEY = 'parameters'
45
+
18
46
  DATASETS_KEY = 'datasets'
19
- HEADERS_KEY = 'headers'
47
+ DATASET_NAME_KEY = 'name'
48
+ DATASET_LABEL_KEY = 'label'
49
+ DATASET_MODEL_KEY = 'model'
50
+ DATASET_PARAMETERS_KEY = 'parameters'
51
+ DATASET_ARGS_KEY = 'args'
52
+
53
+ # deprecate these later
20
54
  DATABASE_VIEWS_KEY = 'database_views'
21
55
  FILE_KEY = 'file'
56
+ DB_CONNECTION_KEY = 'db_connection'
22
57
  FINAL_VIEW_KEY = 'final_view'
23
- BASE_PATH_KEY = 'base_path'
24
- SETTINGS_KEY = 'settings'
25
58
 
26
- # Project variable keys
27
- PRODUCT_KEY = 'product'
28
- MAJOR_VERSION_KEY = 'major_version'
29
- MINOR_VERSION_KEY = 'minor_version'
59
+ DATASET_SCOPE_KEY = 'scope'
60
+ PUBLIC_SCOPE = 'public'
61
+ PROTECTED_SCOPE = 'protected'
62
+ PRIVATE_SCOPE = 'private'
30
63
 
31
- # Database credentials keys
64
+ SETTINGS_KEY = 'settings'
65
+
66
+ # Environment config keys
32
67
  CREDENTIALS_KEY = 'credentials'
33
68
  USERNAME_KEY = 'username'
34
69
  PASSWORD_KEY = 'password'
35
- DEFAULT_DB_CONN = 'default'
70
+ USER_NAME_KEY = 'username'
71
+ USER_PWD_KEY = 'password'
72
+ SECRETS_KEY = 'secrets'
73
+ JWT_SECRET_KEY = 'jwt_secret'
36
74
 
37
75
  # Folder/File names
38
- MANIFEST_FILE = 'squirrels.yaml'
76
+ PACKAGE_DATA_FOLDER = 'package_data'
77
+ BASE_PROJECT_FOLDER = 'base_project'
78
+ STATIC_FOLDER = 'static'
79
+ TEMPLATES_FOLDER = 'templates'
80
+
81
+ ENVIRON_CONFIG_FILE = 'environcfg.yml'
82
+ MANIFEST_JINJA_FILE = 'squirrels.yml.j2'
83
+ CONNECTIONS_YML_FILE = 'connections.yml'
84
+ PARAMETERS_YML_FILE = 'parameters.yml'
85
+ MANIFEST_FILE = 'squirrels.yml'
86
+ LU_DATA_FILE = 'lu_data.xlsx'
87
+
88
+ DATABASE_FOLDER = 'database'
89
+ PACKAGES_FOLDER = 'sqrl_packages'
90
+
91
+ MODELS_FOLDER = 'models'
92
+ DBVIEWS_FOLDER = 'dbviews'
93
+ DATABASE_VIEW_SQL_FILE = 'database_view1.sql'
94
+ DATABASE_VIEW_PY_FILE = 'database_view1.py'
95
+ FEDERATES_FOLDER = 'federates'
96
+ FEDERATE_SQL_NAME = 'dataset_example.sql'
97
+ FEDERATE_PY_NAME = 'dataset_example.py'
98
+
99
+ PYCONFIG_FOLDER = 'pyconfigs'
100
+ AUTH_FILE = 'auth.py'
39
101
  CONNECTIONS_FILE = 'connections.py'
40
- OUTPUTS_FOLDER = 'outputs'
41
- MODULES_FOLDER = 'modules'
42
- DATASETS_FOLDER = 'datasets'
102
+ CONTEXT_FILE = 'context.py'
43
103
  PARAMETERS_FILE = 'parameters.py'
104
+
105
+ TARGET_FOLDER = 'target'
106
+ COMPILE_FOLDER = 'compile'
107
+
108
+ OUTPUTS_FOLDER = 'outputs'
44
109
  PARAMETERS_OUTPUT = 'parameters.json'
45
- DATABASE_VIEW_SQL_FILE = 'database_view1.sql.j2'
46
- DATABASE_VIEW_PY_FILE = 'database_view1.py'
47
- FINAL_VIEW_SQL_NAME = 'final_view.sql.j2'
48
- FINAL_VIEW_PY_NAME = 'final_view.py'
49
110
  FINAL_VIEW_OUT_STEM = 'final_view'
50
- CONTEXT_FILE = 'context.py'
51
- SELECTIONS_CFG_FILE = 'selections.cfg'
52
111
 
53
112
  # Dataset setting names
113
+ AUTH_TOKEN_EXPIRE_SETTING = 'auth.token.expire.minutes'
54
114
  PARAMETERS_CACHE_SIZE_SETTING = 'parameters.cache.size'
55
- PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl'
115
+ PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl.minutes'
56
116
  RESULTS_CACHE_SIZE_SETTING = 'results.cache.size'
57
- RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl'
117
+ RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl.minutes'
118
+ TEST_SET_DEFAULT_USED_SETTING = 'selection_test_sets.default_name_used'
119
+ DB_CONN_DEFAULT_USED_SETTING = 'db_connections.default_name_used'
120
+ DEFAULT_MATERIALIZE_SETTING = 'defaults.federates.materialized'
58
121
 
59
122
  # Selection cfg sections
123
+ USER_ATTRIBUTES_SECTION = 'user_attributes'
60
124
  PARAMETERS_SECTION = 'parameters'
61
125
 
62
126
  # Init Command Choices
63
- FILE_TYPE_CHOICES = ['sql', 'py']
64
- DATABASE_CHOICES = ['sample_database', 'seattle_weather']
127
+ SQL_FILE_TYPE = 'sql'
128
+ PYTHON_FILE_TYPE = 'py'
129
+ FILE_TYPE_CHOICES = [SQL_FILE_TYPE, PYTHON_FILE_TYPE]
130
+
131
+ YML_FORMAT = 'yml'
132
+ PYTHON_FORMAT = 'py'
133
+ CONF_FORMAT_CHOICES = [YML_FORMAT, PYTHON_FORMAT]
134
+
135
+ PYTHON_FORMAT2 = 'py (recommended)'
136
+ CONF_FORMAT_CHOICES2 = [(PYTHON_FORMAT2, PYTHON_FORMAT), YML_FORMAT]
137
+
138
+ EXPENSES_DB_NAME = 'expenses'
139
+ WEATHER_DB_NAME = 'seattle-weather'
140
+ DATABASE_CHOICES = [EXPENSES_DB_NAME, WEATHER_DB_NAME]
141
+
142
+ # Function names
143
+ GET_USER_FUNC = "get_user_if_valid"
144
+ DEP_FUNC = "dependencies"
145
+ MAIN_FUNC = "main"
@@ -0,0 +1,77 @@
1
+ from typing import Any, Optional, Callable
2
+ from dataclasses import dataclass
3
+ import os, yaml
4
+
5
+ from . import _constants as c, _utils as u
6
+ from ._timer import timer, time
7
+
8
+ _GLOBAL_SQUIRRELS_CFG_FILE = u.join_paths(os.path.expanduser('~'), '.squirrels', c.ENVIRON_CONFIG_FILE)
9
+
10
+
11
+ @dataclass
12
+ class _EnvironConfig:
13
+ _users: dict[str, dict[str, Any]]
14
+ _env_vars: dict[str, str]
15
+ _credentials: dict[str, dict[str, str]]
16
+ _secrets: dict[str, str]
17
+
18
+ def __post_init__(self):
19
+ for username, user in self._users.items():
20
+ user[c.USER_NAME_KEY] = username
21
+ if c.USER_PWD_KEY not in user:
22
+ raise u.ConfigurationError(f"All users must have password in environcfg.yml")
23
+
24
+ for key, cred in self._credentials.items():
25
+ if c.USERNAME_KEY not in cred or c.PASSWORD_KEY not in cred:
26
+ raise u.ConfigurationError(f'Either "{c.USERNAME_KEY}" or "{c.PASSWORD_KEY}" was not specified for credential "{key}"')
27
+
28
+ def get_users(self) -> dict[str, dict[str, Any]]:
29
+ return self._users.copy()
30
+
31
+ def get_all_env_vars(self) -> dict[str, str]:
32
+ return self._env_vars.copy()
33
+
34
+ def get_credential(self, key: Optional[str]) -> tuple[str, str]:
35
+ if not key:
36
+ return "", ""
37
+
38
+ try:
39
+ credential = self._credentials[key]
40
+ except KeyError as e:
41
+ raise u.ConfigurationError(f'No credentials configured for "{key}"') from e
42
+
43
+ return credential[c.USERNAME_KEY], credential[c.PASSWORD_KEY]
44
+
45
+ def get_secret(self, key: str, *, default_factory: Optional[Callable[[],str]] = None) -> str:
46
+ if key not in self._secrets and default_factory is not None:
47
+ self._secrets[key] = default_factory()
48
+ return self._secrets.get(key)
49
+
50
+
51
+ class EnvironConfigIO:
52
+ obj: _EnvironConfig
53
+
54
+ @classmethod
55
+ def LoadFromFile(cls):
56
+ start = time.time()
57
+ def load_yaml(filename: str) -> dict[str, dict]:
58
+ try:
59
+ with open(filename, 'r') as f:
60
+ return yaml.safe_load(f)
61
+ except FileNotFoundError:
62
+ return {}
63
+
64
+ config1 = load_yaml(_GLOBAL_SQUIRRELS_CFG_FILE)
65
+ config2 = load_yaml(c.ENVIRON_CONFIG_FILE)
66
+
67
+ for key in config2:
68
+ config1.setdefault(key, {})
69
+ config1[key].update(config2[key])
70
+
71
+ users = config1.get("users", {})
72
+ env_vars = config1.get("env_vars", {})
73
+ credentials = config1.get("credentials", {})
74
+ secrets = config1.get("secrets", {})
75
+
76
+ cls.obj = _EnvironConfig(users, env_vars, credentials, secrets)
77
+ timer.add_activity_time(f"loading {c.ENVIRON_CONFIG_FILE} file", start)