squirrels 0.4.0__py3-none-any.whl → 0.5.0rc0__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 -6
- squirrels/_api_response_models.py +93 -44
- squirrels/_api_server.py +571 -219
- squirrels/_auth.py +451 -0
- squirrels/_command_line.py +61 -20
- squirrels/_connection_set.py +38 -25
- squirrels/_constants.py +44 -34
- squirrels/_dashboards_io.py +34 -16
- squirrels/_exceptions.py +57 -0
- squirrels/_initializer.py +117 -44
- squirrels/_manifest.py +124 -62
- squirrels/_model_builder.py +111 -0
- squirrels/_model_configs.py +74 -0
- squirrels/_model_queries.py +52 -0
- squirrels/_models.py +860 -354
- squirrels/_package_loader.py +8 -4
- squirrels/_parameter_configs.py +45 -65
- squirrels/_parameter_sets.py +15 -13
- squirrels/_project.py +561 -0
- squirrels/_py_module.py +4 -3
- squirrels/_seeds.py +35 -16
- squirrels/_sources.py +106 -0
- squirrels/_utils.py +166 -63
- squirrels/_version.py +1 -1
- squirrels/arguments/init_time_args.py +78 -15
- squirrels/arguments/run_time_args.py +62 -101
- squirrels/dashboards.py +4 -4
- squirrels/data_sources.py +94 -162
- squirrels/dataset_result.py +86 -0
- squirrels/dateutils.py +4 -4
- squirrels/package_data/base_project/.env +30 -0
- squirrels/package_data/base_project/.env.example +30 -0
- squirrels/package_data/base_project/.gitignore +3 -2
- squirrels/package_data/base_project/assets/expenses.db +0 -0
- squirrels/package_data/base_project/connections.yml +11 -3
- squirrels/package_data/base_project/dashboards/dashboard_example.py +15 -13
- squirrels/package_data/base_project/dashboards/dashboard_example.yml +22 -0
- squirrels/package_data/base_project/docker/.dockerignore +5 -2
- squirrels/package_data/base_project/docker/Dockerfile +3 -3
- squirrels/package_data/base_project/docker/compose.yml +1 -1
- squirrels/package_data/base_project/duckdb_init.sql +9 -0
- squirrels/package_data/base_project/macros/macros_example.sql +15 -0
- squirrels/package_data/base_project/models/builds/build_example.py +26 -0
- squirrels/package_data/base_project/models/builds/build_example.sql +16 -0
- squirrels/package_data/base_project/models/builds/build_example.yml +55 -0
- squirrels/package_data/base_project/models/dbviews/dbview_example.sql +12 -22
- squirrels/package_data/base_project/models/dbviews/dbview_example.yml +26 -0
- squirrels/package_data/base_project/models/federates/federate_example.py +38 -15
- squirrels/package_data/base_project/models/federates/federate_example.sql +16 -2
- squirrels/package_data/base_project/models/federates/federate_example.yml +65 -0
- squirrels/package_data/base_project/models/sources.yml +39 -0
- squirrels/package_data/base_project/parameters.yml +36 -21
- squirrels/package_data/base_project/pyconfigs/connections.py +6 -11
- squirrels/package_data/base_project/pyconfigs/context.py +20 -33
- squirrels/package_data/base_project/pyconfigs/parameters.py +19 -21
- squirrels/package_data/base_project/pyconfigs/user.py +23 -0
- squirrels/package_data/base_project/seeds/seed_categories.yml +15 -0
- squirrels/package_data/base_project/seeds/seed_subcategories.csv +15 -15
- squirrels/package_data/base_project/seeds/seed_subcategories.yml +21 -0
- squirrels/package_data/base_project/squirrels.yml.j2 +17 -40
- squirrels/parameters.py +20 -20
- {squirrels-0.4.0.dist-info → squirrels-0.5.0rc0.dist-info}/METADATA +31 -32
- squirrels-0.5.0rc0.dist-info/RECORD +70 -0
- {squirrels-0.4.0.dist-info → squirrels-0.5.0rc0.dist-info}/WHEEL +1 -1
- squirrels-0.5.0rc0.dist-info/entry_points.txt +3 -0
- {squirrels-0.4.0.dist-info → squirrels-0.5.0rc0.dist-info/licenses}/LICENSE +1 -1
- squirrels/_authenticator.py +0 -85
- squirrels/_environcfg.py +0 -84
- squirrels/package_data/assets/favicon.ico +0 -0
- squirrels/package_data/assets/index.css +0 -1
- squirrels/package_data/assets/index.js +0 -58
- squirrels/package_data/base_project/dashboards.yml +0 -10
- squirrels/package_data/base_project/env.yml +0 -29
- squirrels/package_data/base_project/models/dbviews/dbview_example.py +0 -47
- squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
- squirrels/package_data/templates/index.html +0 -18
- squirrels/project.py +0 -378
- squirrels/user_base.py +0 -55
- squirrels-0.4.0.dist-info/RECORD +0 -60
- squirrels-0.4.0.dist-info/entry_points.txt +0 -4
squirrels/_connection_set.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
|
|
1
|
+
from typing import Any
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from sqlalchemy import Engine
|
|
4
|
+
import time, polars as pl
|
|
4
5
|
|
|
5
6
|
from . import _utils as u, _constants as c, _py_module as pm
|
|
6
7
|
from .arguments.init_time_args import ConnectionsArgs
|
|
7
|
-
from .
|
|
8
|
-
from ._manifest import ManifestConfig
|
|
8
|
+
from ._manifest import ManifestConfig, ConnectionProperties, ConnectionType
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@dataclass
|
|
@@ -16,44 +16,56 @@ class ConnectionSet:
|
|
|
16
16
|
Attributes:
|
|
17
17
|
_engines: A dictionary of connection name to the corresponding sqlalchemy engine
|
|
18
18
|
"""
|
|
19
|
-
|
|
19
|
+
_connections: dict[str, ConnectionProperties | Any] = field(default_factory=dict)
|
|
20
20
|
|
|
21
|
-
def
|
|
22
|
-
return self.
|
|
21
|
+
def get_connections_as_dict(self):
|
|
22
|
+
return self._connections.copy()
|
|
23
23
|
|
|
24
|
-
def
|
|
24
|
+
def get_connection(self, conn_name: str) -> ConnectionProperties | Any:
|
|
25
25
|
try:
|
|
26
|
-
|
|
26
|
+
connection = self._connections[conn_name]
|
|
27
27
|
except KeyError as e:
|
|
28
28
|
raise u.ConfigurationError(f'Connection name "{conn_name}" was not configured') from e
|
|
29
|
-
return
|
|
29
|
+
return connection
|
|
30
30
|
|
|
31
|
-
def run_sql_query_from_conn_name(self, query: str, conn_name: str, placeholders: dict = {}) ->
|
|
32
|
-
|
|
31
|
+
def run_sql_query_from_conn_name(self, query: str, conn_name: str, placeholders: dict = {}) -> pl.DataFrame:
|
|
32
|
+
conn = self.get_connection(conn_name)
|
|
33
33
|
try:
|
|
34
|
-
|
|
34
|
+
if isinstance(conn, ConnectionProperties) and (conn.type == ConnectionType.CONNECTORX or conn.type == ConnectionType.ADBC):
|
|
35
|
+
if len(placeholders) > 0:
|
|
36
|
+
raise u.ConfigurationError(f"Connection '{conn_name}' is a ConnectorX or ADBC connection, which does not support placeholders")
|
|
37
|
+
df = pl.read_database_uri(query, conn.uri, engine=conn.type.value)
|
|
38
|
+
elif isinstance(conn, ConnectionProperties) and conn.type == ConnectionType.SQLALCHEMY:
|
|
39
|
+
with conn.engine.connect() as connection:
|
|
40
|
+
df = pl.read_database(query, connection, execute_options={"parameters": placeholders})
|
|
41
|
+
else:
|
|
42
|
+
df = pl.read_database(query, conn, execute_options={"parameters": placeholders}) # type: ignore
|
|
35
43
|
return df
|
|
36
44
|
except Exception as e:
|
|
37
45
|
raise RuntimeError(e) from e
|
|
38
46
|
|
|
39
47
|
def dispose(self) -> None:
|
|
40
48
|
"""
|
|
41
|
-
Disposes
|
|
49
|
+
Disposes / closes all the connections in this ConnectionSet
|
|
42
50
|
"""
|
|
43
|
-
for
|
|
44
|
-
if isinstance(
|
|
45
|
-
|
|
51
|
+
for conn in self._connections.values():
|
|
52
|
+
if isinstance(conn, Engine):
|
|
53
|
+
conn.dispose()
|
|
54
|
+
elif isinstance(conn, ConnectionProperties):
|
|
55
|
+
if conn.type == ConnectionType.SQLALCHEMY:
|
|
56
|
+
conn.engine.dispose()
|
|
57
|
+
elif hasattr(conn, 'close'):
|
|
58
|
+
conn.close()
|
|
46
59
|
|
|
47
60
|
|
|
48
61
|
class ConnectionSetIO:
|
|
49
62
|
|
|
50
63
|
@classmethod
|
|
51
|
-
def load_conn_py_args(cls, logger: u.Logger,
|
|
64
|
+
def load_conn_py_args(cls, logger: u.Logger, base_path: str, env_vars: dict[str, str], manifest_cfg: ManifestConfig) -> ConnectionsArgs:
|
|
52
65
|
start = time.time()
|
|
53
66
|
|
|
54
67
|
proj_vars = manifest_cfg.project_variables.model_dump()
|
|
55
|
-
|
|
56
|
-
conn_args = ConnectionsArgs(proj_vars, env_vars, env_cfg.get_credential)
|
|
68
|
+
conn_args = ConnectionsArgs(base_path, proj_vars, env_vars)
|
|
57
69
|
|
|
58
70
|
logger.log_activity_time("setting up arguments for connections.py", start)
|
|
59
71
|
return conn_args
|
|
@@ -68,13 +80,14 @@ class ConnectionSetIO:
|
|
|
68
80
|
A ConnectionSet with the DB connections from both squirrels.yml and connections.py
|
|
69
81
|
"""
|
|
70
82
|
start = time.time()
|
|
71
|
-
|
|
83
|
+
connections: dict[str, ConnectionProperties | Any] = {}
|
|
72
84
|
|
|
73
85
|
for config in manifest_cfg.connections.values():
|
|
74
|
-
|
|
86
|
+
connections[config.name] = ConnectionProperties(label=config.label, type=config.type, uri=config.uri)
|
|
75
87
|
|
|
76
|
-
pm.run_pyconfig_main(base_path, c.CONNECTIONS_FILE, {"connections":
|
|
77
|
-
|
|
88
|
+
pm.run_pyconfig_main(base_path, c.CONNECTIONS_FILE, {"connections": connections, "sqrl": conn_args})
|
|
89
|
+
|
|
90
|
+
conn_set = ConnectionSet(connections)
|
|
78
91
|
|
|
79
92
|
logger.log_activity_time("creating sqlalchemy engines", start)
|
|
80
93
|
return conn_set
|
squirrels/_constants.py
CHANGED
|
@@ -1,25 +1,51 @@
|
|
|
1
1
|
# Squirrels CLI commands
|
|
2
|
-
INIT_CMD = '
|
|
2
|
+
INIT_CMD = 'new'
|
|
3
3
|
GET_FILE_CMD = 'get-file'
|
|
4
4
|
DEPS_CMD = 'deps'
|
|
5
|
+
BUILD_CMD = 'build'
|
|
5
6
|
COMPILE_CMD = 'compile'
|
|
6
7
|
RUN_CMD = 'run'
|
|
8
|
+
DUCKDB_CMD = 'duckdb'
|
|
7
9
|
|
|
8
|
-
# Environment
|
|
9
|
-
|
|
10
|
+
# Environment variables
|
|
11
|
+
SQRL_SECRET_KEY = 'SQRL_SECRET__KEY'
|
|
12
|
+
SQRL_SECRET_ADMIN_PASSWORD = 'SQRL_SECRET__ADMIN_PASSWORD'
|
|
13
|
+
|
|
14
|
+
SQRL_AUTH_DB_FILE_PATH = 'SQRL_AUTH__DB_FILE_PATH'
|
|
15
|
+
SQRL_AUTH_TOKEN_EXPIRE_MINUTES = 'SQRL_AUTH__TOKEN_EXPIRE_MINUTES'
|
|
16
|
+
|
|
17
|
+
SQRL_PARAMETERS_CACHE_SIZE = 'SQRL_PARAMETERS__CACHE_SIZE'
|
|
18
|
+
SQRL_PARAMETERS_CACHE_TTL_MINUTES = 'SQRL_PARAMETERS__CACHE_TTL_MINUTES'
|
|
19
|
+
|
|
20
|
+
SQRL_DATASETS_CACHE_SIZE = 'SQRL_DATASETS__CACHE_SIZE'
|
|
21
|
+
SQRL_DATASETS_CACHE_TTL_MINUTES = 'SQRL_DATASETS__CACHE_TTL_MINUTES'
|
|
22
|
+
|
|
23
|
+
SQRL_DASHBOARDS_CACHE_SIZE = 'SQRL_DASHBOARDS__CACHE_SIZE'
|
|
24
|
+
SQRL_DASHBOARDS_CACHE_TTL_MINUTES = 'SQRL_DASHBOARDS__CACHE_TTL_MINUTES'
|
|
25
|
+
|
|
26
|
+
SQRL_SEEDS_INFER_SCHEMA = 'SQRL_SEEDS__INFER_SCHEMA'
|
|
27
|
+
SQRL_SEEDS_NA_VALUES = 'SQRL_SEEDS__NA_VALUES'
|
|
28
|
+
|
|
29
|
+
SQRL_TEST_SETS_DEFAULT_NAME_USED = 'SQRL_TEST_SETS__DEFAULT_NAME_USED'
|
|
30
|
+
|
|
31
|
+
SQRL_CONNECTIONS_DEFAULT_NAME_USED = 'SQRL_CONNECTIONS__DEFAULT_NAME_USED'
|
|
32
|
+
|
|
33
|
+
SQRL_DUCKDB_VENV_DB_FILE_PATH = 'SQRL_DUCKDB_VENV__DB_FILE_PATH'
|
|
10
34
|
|
|
11
35
|
# Folder/File names
|
|
12
36
|
PACKAGE_DATA_FOLDER = 'package_data'
|
|
13
37
|
BASE_PROJECT_FOLDER = 'base_project'
|
|
14
|
-
ASSETS_FOLDER = 'assets'
|
|
15
|
-
TEMPLATES_FOLDER = 'templates'
|
|
16
38
|
|
|
17
|
-
|
|
39
|
+
GLOBAL_ENV_FOLDER = '.squirrels'
|
|
18
40
|
MANIFEST_JINJA_FILE = 'squirrels.yml.j2'
|
|
19
41
|
CONNECTIONS_YML_FILE = 'connections.yml'
|
|
20
42
|
PARAMETERS_YML_FILE = 'parameters.yml'
|
|
21
43
|
DASHBOARDS_YML_FILE = 'dashboards.yml'
|
|
22
44
|
MANIFEST_FILE = 'squirrels.yml'
|
|
45
|
+
DUCKDB_INIT_FILE = 'duckdb_init.sql'
|
|
46
|
+
DOTENV_FILE = '.env'
|
|
47
|
+
DOTENV_LOCAL_FILE = '.env.local'
|
|
48
|
+
GITIGNORE_FILE = '.gitignore'
|
|
23
49
|
|
|
24
50
|
LOGS_FOLDER = 'logs'
|
|
25
51
|
LOGS_FILE = 'squirrels.log'
|
|
@@ -28,8 +54,12 @@ DATABASE_FOLDER = 'assets'
|
|
|
28
54
|
PACKAGES_FOLDER = 'sqrl_packages'
|
|
29
55
|
|
|
30
56
|
MACROS_FOLDER = 'macros'
|
|
57
|
+
MACROS_FILE = 'macros_example.sql'
|
|
31
58
|
|
|
32
59
|
MODELS_FOLDER = 'models'
|
|
60
|
+
SOURCES_FILE = 'sources.yml'
|
|
61
|
+
BUILDS_FOLDER = 'builds'
|
|
62
|
+
BUILD_FILE_STEM = 'build_example'
|
|
33
63
|
DBVIEWS_FOLDER = 'dbviews'
|
|
34
64
|
DBVIEW_FILE_STEM = 'dbview_example'
|
|
35
65
|
FEDERATES_FOLDER = 'federates'
|
|
@@ -43,35 +73,17 @@ AUTH_FILE = 'auth.py'
|
|
|
43
73
|
CONNECTIONS_FILE = 'connections.py'
|
|
44
74
|
CONTEXT_FILE = 'context.py'
|
|
45
75
|
PARAMETERS_FILE = 'parameters.py'
|
|
76
|
+
USER_FILE = 'user.py'
|
|
77
|
+
ADMIN_USERNAME = 'admin'
|
|
46
78
|
|
|
47
79
|
TARGET_FOLDER = 'target'
|
|
80
|
+
DB_FILE = 'auth.sqlite'
|
|
48
81
|
COMPILE_FOLDER = 'compile'
|
|
82
|
+
DUCKDB_VENV_FILE = 'venv.duckdb'
|
|
49
83
|
|
|
50
84
|
SEEDS_FOLDER = 'seeds'
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# Dataset setting names
|
|
55
|
-
AUTH_TOKEN_EXPIRE_SETTING = 'auth.token.expire_minutes'
|
|
56
|
-
PARAMETERS_CACHE_SIZE_SETTING = 'parameters.cache.size'
|
|
57
|
-
PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl_minutes'
|
|
58
|
-
RESULTS_CACHE_SIZE_SETTING = 'results.cache.size' # deprecated
|
|
59
|
-
RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl_minutes' # deprecated
|
|
60
|
-
DATASETS_CACHE_SIZE_SETTING = 'datasets.cache.size'
|
|
61
|
-
DATASETS_CACHE_TTL_SETTING = 'datasets.cache.ttl_minutes'
|
|
62
|
-
DASHBOARDS_CACHE_SIZE_SETTING = 'dashboards.cache.size'
|
|
63
|
-
DASHBOARDS_CACHE_TTL_SETTING = 'dashboards.cache.ttl_minutes'
|
|
64
|
-
TEST_SET_DEFAULT_USED_SETTING = 'selection_test_sets.default_name_used'
|
|
65
|
-
DEFAULT_TEST_SET_NAME = 'default'
|
|
66
|
-
DB_CONN_DEFAULT_USED_SETTING = 'connections.default_name_used'
|
|
67
|
-
DEFAULT_DB_CONN = 'default'
|
|
68
|
-
DEFAULT_MATERIALIZE_SETTING = 'defaults.federates.materialized'
|
|
69
|
-
DEFAULT_MATERIALIZE = 'table'
|
|
70
|
-
SEEDS_INFER_SCHEMA_SETTING = 'seeds.infer_schema'
|
|
71
|
-
SEEDS_NA_VALUES_SETTING = 'seeds.na_values'
|
|
72
|
-
IN_MEMORY_DB_SETTING = 'in_memory_database'
|
|
73
|
-
SQLITE = 'sqlite'
|
|
74
|
-
DUCKDB = 'duckdb'
|
|
85
|
+
SEED_CATEGORY_FILE_STEM = 'seed_categories'
|
|
86
|
+
SEED_SUBCATEGORY_FILE_STEM = 'seed_subcategories'
|
|
75
87
|
|
|
76
88
|
# Selection cfg sections
|
|
77
89
|
USER_ATTRIBUTES_SECTION = 'user_attributes'
|
|
@@ -97,10 +109,8 @@ PNG = "png"
|
|
|
97
109
|
HTML = "html"
|
|
98
110
|
|
|
99
111
|
# Function names
|
|
100
|
-
GET_USER_FUNC = "get_user_if_valid"
|
|
101
|
-
DEP_FUNC = "dependencies"
|
|
102
112
|
MAIN_FUNC = "main"
|
|
103
113
|
|
|
104
114
|
# Regex
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
DATE_REGEX = r"^\d{4}\-\d{2}\-\d{2}$"
|
|
116
|
+
COLOR_REGEX = r"^#[0-9a-fA-F]{6}$"
|
squirrels/_dashboards_io.py
CHANGED
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
from typing import Type, TypeVar, Callable, Coroutine, Any
|
|
2
|
+
from enum import Enum
|
|
2
3
|
from dataclasses import dataclass
|
|
3
|
-
import
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
import os, time
|
|
4
6
|
|
|
5
7
|
from .arguments.run_time_args import DashboardArgs
|
|
6
8
|
from ._py_module import PyModule
|
|
9
|
+
from ._manifest import AnalyticsOutputConfig
|
|
10
|
+
from ._exceptions import InvalidInputError, ConfigurationError, FileExecutionError
|
|
7
11
|
from . import _constants as c, _utils as u, dashboards as d
|
|
8
12
|
|
|
9
13
|
T = TypeVar('T', bound=d.Dashboard)
|
|
10
14
|
|
|
11
15
|
|
|
16
|
+
class DashboardFormat(Enum):
|
|
17
|
+
PNG = "png"
|
|
18
|
+
HTML = "html"
|
|
19
|
+
|
|
20
|
+
class DashboardDependencies(BaseModel):
|
|
21
|
+
name: str
|
|
22
|
+
dataset: str
|
|
23
|
+
fixed_parameters: list[dict[str, str]] = Field(default_factory=list)
|
|
24
|
+
|
|
25
|
+
class DashboardConfig(AnalyticsOutputConfig):
|
|
26
|
+
format: DashboardFormat = Field(default=DashboardFormat.PNG)
|
|
27
|
+
depends_on: list[DashboardDependencies] = Field(default_factory=list)
|
|
28
|
+
|
|
29
|
+
|
|
12
30
|
@dataclass
|
|
13
|
-
class
|
|
31
|
+
class DashboardDefinition:
|
|
14
32
|
dashboard_name: str
|
|
15
33
|
filepath: str
|
|
34
|
+
config: DashboardConfig
|
|
16
35
|
|
|
17
36
|
@property
|
|
18
37
|
def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, d.Dashboard]]:
|
|
@@ -22,23 +41,16 @@ class DashboardFunction:
|
|
|
22
41
|
return self._dashboard_func
|
|
23
42
|
|
|
24
43
|
def get_dashboard_format(self) -> str:
|
|
25
|
-
|
|
26
|
-
assert issubclass(return_type, d.Dashboard), f"Function must return Dashboard type"
|
|
27
|
-
if return_type == d.PngDashboard:
|
|
28
|
-
return c.PNG
|
|
29
|
-
elif return_type == d.HtmlDashboard:
|
|
30
|
-
return c.HTML
|
|
31
|
-
else:
|
|
32
|
-
raise NotImplementedError(f"Dashboard format {return_type} not supported")
|
|
44
|
+
return self.config.format.value
|
|
33
45
|
|
|
34
46
|
async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = d.Dashboard) -> T:
|
|
35
47
|
try:
|
|
36
48
|
dashboard = await self.dashboard_func(args)
|
|
37
49
|
assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
|
|
38
|
-
except (
|
|
50
|
+
except (InvalidInputError, ConfigurationError, FileExecutionError) as e:
|
|
39
51
|
raise e
|
|
40
52
|
except Exception as e:
|
|
41
|
-
raise
|
|
53
|
+
raise FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
|
|
42
54
|
|
|
43
55
|
return dashboard
|
|
44
56
|
|
|
@@ -46,7 +58,7 @@ class DashboardFunction:
|
|
|
46
58
|
class DashboardsIO:
|
|
47
59
|
|
|
48
60
|
@classmethod
|
|
49
|
-
def load_files(cls, logger: u.Logger, base_path: str) -> dict[str,
|
|
61
|
+
def load_files(cls, logger: u.Logger, base_path: str) -> dict[str, DashboardDefinition]:
|
|
50
62
|
start = time.time()
|
|
51
63
|
|
|
52
64
|
dashboards_by_name = {}
|
|
@@ -54,8 +66,14 @@ class DashboardsIO:
|
|
|
54
66
|
for file in filenames:
|
|
55
67
|
filepath = os.path.join(dp, file)
|
|
56
68
|
file_stem, extension = os.path.splitext(file)
|
|
57
|
-
if extension == '.py':
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
if not extension == '.py':
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
# Check for corresponding .yml file
|
|
73
|
+
yml_path = os.path.join(dp, file_stem + '.yml')
|
|
74
|
+
config_dict = u.load_yaml_config(yml_path) if os.path.exists(yml_path) else {}
|
|
75
|
+
config = DashboardConfig(name=file_stem, **config_dict)
|
|
76
|
+
dashboards_by_name[file_stem] = DashboardDefinition(file_stem, filepath, config)
|
|
77
|
+
|
|
60
78
|
logger.log_activity_time("loading files for dashboards", start)
|
|
61
79
|
return dashboards_by_name
|
squirrels/_exceptions.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class InvalidInputError(Exception):
|
|
2
|
+
"""
|
|
3
|
+
Use this exception when the error is due to providing invalid inputs to the REST API
|
|
4
|
+
|
|
5
|
+
Specific error code ranges are reserved for specific categories of errors.
|
|
6
|
+
0-19: 401 unauthorized errors
|
|
7
|
+
20-39: 403 forbidden errors
|
|
8
|
+
40-59: 404 not found errors
|
|
9
|
+
60-69: 409 conflict errors
|
|
10
|
+
70-99: Reserved for future use
|
|
11
|
+
100-199: 400 bad request errors related to authentication
|
|
12
|
+
200-299: 400 bad request errors related to data analytics
|
|
13
|
+
|
|
14
|
+
Error code definitions:
|
|
15
|
+
0 - Incorrect username or password
|
|
16
|
+
1 - Invalid authorization token
|
|
17
|
+
2 - Username not found for password change
|
|
18
|
+
3 - Incorrect password for password change
|
|
19
|
+
20 - Authorized user is forbidden to add or update users
|
|
20
|
+
21 - Authorized user is forbidden to delete users
|
|
21
|
+
22 - Cannot delete your own user
|
|
22
|
+
23 - Cannot delete the admin user
|
|
23
|
+
24 - Cannot change the admin user
|
|
24
|
+
25 - User does not have permission to access the dataset / dashboard
|
|
25
|
+
26 - User does not have permission to build the virtual data environment
|
|
26
|
+
27 - User does not have permission to query data models
|
|
27
|
+
40 - No token found for token_id
|
|
28
|
+
41 - No user found for username
|
|
29
|
+
60 - An existing build process is already running and a concurrent build is not allowed
|
|
30
|
+
61 - Model depends on static data models that cannot be found
|
|
31
|
+
100 - Missing required field 'username' or 'password' when adding a new user
|
|
32
|
+
101 - Username already exists when adding a new user
|
|
33
|
+
102 - Invalid user data when adding a new user
|
|
34
|
+
200 - Invalid value for dataset parameter
|
|
35
|
+
201 - Invalid query parameter provided
|
|
36
|
+
202 - Could not determine parent parameter for parameter refresh
|
|
37
|
+
203 - SQL query must be provided
|
|
38
|
+
204 - Failed to run provided SQL query
|
|
39
|
+
"""
|
|
40
|
+
def __init__(self, error_code: int, message: str, *args) -> None:
|
|
41
|
+
self.error_code = error_code
|
|
42
|
+
super().__init__(message, *args)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConfigurationError(Exception):
|
|
46
|
+
"""
|
|
47
|
+
Use this exception when the server error is due to errors in the squirrels project instead of the squirrels framework/library
|
|
48
|
+
"""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FileExecutionError(Exception):
|
|
53
|
+
def __init__(self, message: str, error: Exception, *args) -> None:
|
|
54
|
+
t = " "
|
|
55
|
+
new_message = f"\n" + message + f"\n{t}Produced error message:\n{t}{t}{error} (see above for more details on handled exception)"
|
|
56
|
+
super().__init__(new_message, *args)
|
|
57
|
+
self.error = error
|