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.
- squirrels/__init__.py +10 -16
- squirrels/_api_server.py +234 -80
- squirrels/_authenticator.py +84 -0
- squirrels/_command_line.py +60 -72
- squirrels/_connection_set.py +96 -0
- squirrels/_constants.py +114 -33
- squirrels/_environcfg.py +77 -0
- squirrels/_initializer.py +126 -67
- squirrels/_manifest.py +195 -168
- squirrels/_models.py +495 -0
- squirrels/_package_loader.py +26 -0
- squirrels/_parameter_configs.py +401 -0
- squirrels/_parameter_sets.py +188 -0
- squirrels/_py_module.py +60 -0
- squirrels/_timer.py +36 -0
- squirrels/_utils.py +81 -49
- squirrels/_version.py +2 -2
- squirrels/arguments/init_time_args.py +32 -0
- squirrels/arguments/run_time_args.py +82 -0
- squirrels/data_sources.py +380 -155
- squirrels/dateutils.py +86 -57
- squirrels/package_data/base_project/Dockerfile +15 -0
- squirrels/package_data/base_project/connections.yml +7 -0
- squirrels/package_data/base_project/database/{sample_database.db → expenses.db} +0 -0
- squirrels/package_data/base_project/environcfg.yml +29 -0
- squirrels/package_data/base_project/ignores/.dockerignore +8 -0
- squirrels/package_data/base_project/ignores/.gitignore +7 -0
- squirrels/package_data/base_project/models/dbviews/database_view1.py +36 -0
- squirrels/package_data/base_project/models/dbviews/database_view1.sql +15 -0
- squirrels/package_data/base_project/models/federates/dataset_example.py +20 -0
- squirrels/package_data/base_project/models/federates/dataset_example.sql +3 -0
- squirrels/package_data/base_project/parameters.yml +109 -0
- squirrels/package_data/base_project/pyconfigs/auth.py +47 -0
- squirrels/package_data/base_project/pyconfigs/connections.py +28 -0
- squirrels/package_data/base_project/pyconfigs/context.py +45 -0
- squirrels/package_data/base_project/pyconfigs/parameters.py +55 -0
- squirrels/package_data/base_project/seeds/mocks/category.csv +3 -0
- squirrels/package_data/base_project/seeds/mocks/max_filter.csv +2 -0
- squirrels/package_data/base_project/seeds/mocks/subcategory.csv +6 -0
- squirrels/package_data/base_project/squirrels.yml.j2 +57 -0
- squirrels/package_data/base_project/tmp/.gitignore +2 -0
- squirrels/package_data/static/script.js +159 -63
- squirrels/package_data/static/style.css +79 -15
- squirrels/package_data/static/widgets.js +133 -0
- squirrels/package_data/templates/index.html +65 -23
- squirrels/package_data/templates/index2.html +22 -0
- squirrels/parameter_options.py +216 -119
- squirrels/parameters.py +407 -478
- squirrels/user_base.py +58 -0
- squirrels-0.2.0.dev0.dist-info/METADATA +126 -0
- squirrels-0.2.0.dev0.dist-info/RECORD +56 -0
- {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/WHEEL +1 -2
- squirrels-0.2.0.dev0.dist-info/entry_points.txt +3 -0
- squirrels/_credentials_manager.py +0 -87
- squirrels/_module_loader.py +0 -37
- squirrels/_parameter_set.py +0 -151
- squirrels/_renderer.py +0 -286
- squirrels/_timed_imports.py +0 -37
- squirrels/connection_set.py +0 -126
- squirrels/package_data/base_project/.gitignore +0 -4
- squirrels/package_data/base_project/connections.py +0 -20
- squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -22
- squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -29
- squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -12
- squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -11
- squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -3
- squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -47
- squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -9
- squirrels/package_data/base_project/squirrels.yaml +0 -22
- squirrels-0.1.1.post1.dist-info/METADATA +0 -67
- squirrels-0.1.1.post1.dist-info/RECORD +0 -40
- squirrels-0.1.1.post1.dist-info/entry_points.txt +0 -2
- squirrels-0.1.1.post1.dist-info/top_level.txt +0 -1
- {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/LICENSE +0 -0
squirrels/_command_line.py
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
from typing import List, Tuple, Optional
|
|
2
1
|
from argparse import ArgumentParser
|
|
3
|
-
import sys, time,
|
|
2
|
+
import sys, time, asyncio
|
|
4
3
|
sys.path.append('.')
|
|
5
4
|
|
|
6
|
-
from
|
|
7
|
-
from
|
|
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('-
|
|
31
|
-
parser.add_argument('--
|
|
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('--
|
|
36
|
-
init_parser.add_argument('--
|
|
37
|
-
init_parser.add_argument('--
|
|
38
|
-
init_parser.add_argument('--connections',
|
|
39
|
-
init_parser.add_argument('--
|
|
40
|
-
init_parser.add_argument('--
|
|
41
|
-
init_parser.add_argument('--
|
|
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.
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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.
|
|
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='
|
|
67
|
-
run_parser.add_argument('--host', type=str, default='127.0.0.1')
|
|
68
|
-
run_parser.add_argument('--port', type=int, default=
|
|
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.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
elif args.command
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
59
|
+
DATASET_SCOPE_KEY = 'scope'
|
|
60
|
+
PUBLIC_SCOPE = 'public'
|
|
61
|
+
PROTECTED_SCOPE = 'protected'
|
|
62
|
+
PRIVATE_SCOPE = 'private'
|
|
30
63
|
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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"
|
squirrels/_environcfg.py
ADDED
|
@@ -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)
|