squirrels 0.4.1__py3-none-any.whl → 0.5.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.
- dateutils/__init__.py +6 -0
- dateutils/_enums.py +25 -0
- squirrels/dateutils.py → dateutils/_implementation.py +58 -111
- dateutils/types.py +6 -0
- squirrels/__init__.py +13 -11
- squirrels/_api_routes/__init__.py +5 -0
- squirrels/_api_routes/auth.py +271 -0
- squirrels/_api_routes/base.py +165 -0
- squirrels/_api_routes/dashboards.py +150 -0
- squirrels/_api_routes/data_management.py +145 -0
- squirrels/_api_routes/datasets.py +257 -0
- squirrels/_api_routes/oauth2.py +298 -0
- squirrels/_api_routes/project.py +252 -0
- squirrels/_api_server.py +256 -450
- squirrels/_arguments/__init__.py +0 -0
- squirrels/_arguments/init_time_args.py +108 -0
- squirrels/_arguments/run_time_args.py +147 -0
- squirrels/_auth.py +960 -0
- squirrels/_command_line.py +126 -45
- squirrels/_compile_prompts.py +147 -0
- squirrels/_connection_set.py +48 -26
- squirrels/_constants.py +68 -38
- squirrels/_dashboards.py +160 -0
- squirrels/_data_sources.py +570 -0
- squirrels/_dataset_types.py +84 -0
- squirrels/_exceptions.py +29 -0
- squirrels/_initializer.py +177 -80
- squirrels/_logging.py +115 -0
- squirrels/_manifest.py +208 -79
- squirrels/_model_builder.py +69 -0
- squirrels/_model_configs.py +74 -0
- squirrels/_model_queries.py +52 -0
- squirrels/_models.py +926 -367
- squirrels/_package_data/base_project/.env +42 -0
- squirrels/_package_data/base_project/.env.example +42 -0
- squirrels/_package_data/base_project/assets/expenses.db +0 -0
- squirrels/_package_data/base_project/connections.yml +16 -0
- squirrels/_package_data/base_project/dashboards/dashboard_example.py +34 -0
- squirrels/_package_data/base_project/dashboards/dashboard_example.yml +22 -0
- squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +5 -2
- squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +3 -3
- squirrels/{package_data → _package_data}/base_project/docker/compose.yml +1 -1
- squirrels/_package_data/base_project/duckdb_init.sql +10 -0
- squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +3 -2
- squirrels/_package_data/base_project/macros/macros_example.sql +17 -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 +57 -0
- squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +12 -0
- squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +26 -0
- squirrels/_package_data/base_project/models/federates/federate_example.py +37 -0
- squirrels/_package_data/base_project/models/federates/federate_example.sql +19 -0
- squirrels/_package_data/base_project/models/federates/federate_example.yml +65 -0
- squirrels/_package_data/base_project/models/sources.yml +38 -0
- squirrels/{package_data → _package_data}/base_project/parameters.yml +56 -40
- squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
- squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +21 -40
- squirrels/_package_data/base_project/pyconfigs/parameters.py +141 -0
- squirrels/_package_data/base_project/pyconfigs/user.py +44 -0
- squirrels/_package_data/base_project/seeds/seed_categories.yml +15 -0
- squirrels/_package_data/base_project/seeds/seed_subcategories.csv +15 -0
- squirrels/_package_data/base_project/seeds/seed_subcategories.yml +21 -0
- squirrels/_package_data/base_project/squirrels.yml.j2 +61 -0
- squirrels/_package_data/templates/dataset_results.html +112 -0
- squirrels/_package_data/templates/oauth_login.html +271 -0
- squirrels/_package_data/templates/squirrels_studio.html +20 -0
- squirrels/_package_loader.py +8 -4
- squirrels/_parameter_configs.py +104 -103
- squirrels/_parameter_options.py +348 -0
- squirrels/_parameter_sets.py +57 -47
- squirrels/_parameters.py +1664 -0
- squirrels/_project.py +721 -0
- squirrels/_py_module.py +7 -5
- squirrels/_schemas/__init__.py +0 -0
- squirrels/_schemas/auth_models.py +167 -0
- squirrels/_schemas/query_param_models.py +75 -0
- squirrels/{_api_response_models.py → _schemas/response_models.py} +126 -47
- squirrels/_seeds.py +35 -16
- squirrels/_sources.py +110 -0
- squirrels/_utils.py +248 -73
- squirrels/_version.py +1 -1
- squirrels/arguments.py +7 -0
- squirrels/auth.py +4 -0
- squirrels/connections.py +3 -0
- squirrels/dashboards.py +2 -81
- squirrels/data_sources.py +14 -631
- squirrels/parameter_options.py +13 -348
- squirrels/parameters.py +14 -1266
- squirrels/types.py +16 -0
- squirrels-0.5.0.dist-info/METADATA +113 -0
- squirrels-0.5.0.dist-info/RECORD +97 -0
- {squirrels-0.4.1.dist-info → squirrels-0.5.0.dist-info}/WHEEL +1 -1
- squirrels-0.5.0.dist-info/entry_points.txt +3 -0
- {squirrels-0.4.1.dist-info → squirrels-0.5.0.dist-info/licenses}/LICENSE +1 -1
- squirrels/_authenticator.py +0 -85
- squirrels/_dashboards_io.py +0 -61
- squirrels/_environcfg.py +0 -84
- squirrels/arguments/init_time_args.py +0 -40
- squirrels/arguments/run_time_args.py +0 -208
- 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/assets/expenses.db +0 -0
- squirrels/package_data/base_project/connections.yml +0 -7
- squirrels/package_data/base_project/dashboards/dashboard_example.py +0 -32
- 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/models/dbviews/dbview_example.sql +0 -22
- squirrels/package_data/base_project/models/federates/federate_example.py +0 -21
- squirrels/package_data/base_project/models/federates/federate_example.sql +0 -3
- squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
- squirrels/package_data/base_project/pyconfigs/connections.py +0 -19
- squirrels/package_data/base_project/pyconfigs/parameters.py +0 -95
- squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -15
- squirrels/package_data/base_project/squirrels.yml.j2 +0 -94
- squirrels/package_data/templates/index.html +0 -18
- squirrels/project.py +0 -378
- squirrels/user_base.py +0 -55
- squirrels-0.4.1.dist-info/METADATA +0 -117
- squirrels-0.4.1.dist-info/RECORD +0 -60
- squirrels-0.4.1.dist-info/entry_points.txt +0 -4
- /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
squirrels/_constants.py
CHANGED
|
@@ -1,35 +1,78 @@
|
|
|
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
|
+
SQRL_AUTH_CREDENTIAL_ORIGINS = 'SQRL_AUTH__ALLOWED_ORIGINS_FOR_COOKIES'
|
|
17
|
+
|
|
18
|
+
SQRL_PARAMETERS_CACHE_SIZE = 'SQRL_PARAMETERS__CACHE_SIZE'
|
|
19
|
+
SQRL_PARAMETERS_CACHE_TTL_MINUTES = 'SQRL_PARAMETERS__CACHE_TTL_MINUTES'
|
|
20
|
+
SQRL_PARAMETERS_DATASOURCE_REFRESH_MINUTES = 'SQRL_PARAMETERS__DATASOURCE_REFRESH_MINUTES'
|
|
21
|
+
|
|
22
|
+
SQRL_DATASETS_CACHE_SIZE = 'SQRL_DATASETS__CACHE_SIZE'
|
|
23
|
+
SQRL_DATASETS_CACHE_TTL_MINUTES = 'SQRL_DATASETS__CACHE_TTL_MINUTES'
|
|
24
|
+
SQRL_DATASETS_MAX_ROWS_FOR_AI = 'SQRL_DATASETS__MAX_ROWS_FOR_AI'
|
|
25
|
+
|
|
26
|
+
SQRL_DASHBOARDS_CACHE_SIZE = 'SQRL_DASHBOARDS__CACHE_SIZE'
|
|
27
|
+
SQRL_DASHBOARDS_CACHE_TTL_MINUTES = 'SQRL_DASHBOARDS__CACHE_TTL_MINUTES'
|
|
28
|
+
|
|
29
|
+
SQRL_PERMISSIONS_ELEVATED_ACCESS_LEVEL = 'SQRL_PERMISSIONS__ELEVATED_ACCESS_LEVEL'
|
|
30
|
+
|
|
31
|
+
SQRL_SEEDS_INFER_SCHEMA = 'SQRL_SEEDS__INFER_SCHEMA'
|
|
32
|
+
SQRL_SEEDS_NA_VALUES = 'SQRL_SEEDS__NA_VALUES'
|
|
33
|
+
|
|
34
|
+
SQRL_CONNECTIONS_DEFAULT_NAME_USED = 'SQRL_CONNECTIONS__DEFAULT_NAME_USED'
|
|
35
|
+
|
|
36
|
+
SQRL_VDL_CATALOG_DB_PATH = 'SQRL_VDL__CATALOG_DB_PATH'
|
|
37
|
+
SQRL_VDL_DATA_PATH = 'SQRL_VDL__DATA_PATH'
|
|
38
|
+
|
|
39
|
+
SQRL_STUDIO_BASE_URL = 'SQRL_STUDIO__BASE_URL'
|
|
40
|
+
|
|
41
|
+
SQRL_LOGGING_LOG_LEVEL = 'SQRL_LOGGING__LOG_LEVEL'
|
|
42
|
+
SQRL_LOGGING_LOG_FORMAT = 'SQRL_LOGGING__LOG_FORMAT'
|
|
43
|
+
SQRL_LOGGING_LOG_TO_FILE = 'SQRL_LOGGING__LOG_TO_FILE'
|
|
44
|
+
SQRL_LOGGING_LOG_FILE_SIZE_MB = 'SQRL_LOGGING__LOG_FILE_SIZE_MB'
|
|
45
|
+
SQRL_LOGGING_LOG_FILE_BACKUP_COUNT = 'SQRL_LOGGING__LOG_FILE_BACKUP_COUNT'
|
|
10
46
|
|
|
11
47
|
# Folder/File names
|
|
12
|
-
PACKAGE_DATA_FOLDER = '
|
|
48
|
+
PACKAGE_DATA_FOLDER = '_package_data'
|
|
13
49
|
BASE_PROJECT_FOLDER = 'base_project'
|
|
14
|
-
ASSETS_FOLDER = 'assets'
|
|
15
|
-
TEMPLATES_FOLDER = 'templates'
|
|
16
50
|
|
|
17
|
-
|
|
51
|
+
GLOBAL_ENV_FOLDER = '.squirrels'
|
|
18
52
|
MANIFEST_JINJA_FILE = 'squirrels.yml.j2'
|
|
19
53
|
CONNECTIONS_YML_FILE = 'connections.yml'
|
|
20
54
|
PARAMETERS_YML_FILE = 'parameters.yml'
|
|
21
55
|
DASHBOARDS_YML_FILE = 'dashboards.yml'
|
|
22
56
|
MANIFEST_FILE = 'squirrels.yml'
|
|
57
|
+
DUCKDB_INIT_FILE = 'duckdb_init.sql'
|
|
58
|
+
DOTENV_FILE = '.env'
|
|
59
|
+
DOTENV_LOCAL_FILE = '.env.local'
|
|
60
|
+
GITIGNORE_FILE = '.gitignore'
|
|
23
61
|
|
|
24
62
|
LOGS_FOLDER = 'logs'
|
|
25
63
|
LOGS_FILE = 'squirrels.log'
|
|
26
64
|
|
|
27
65
|
DATABASE_FOLDER = 'assets'
|
|
28
66
|
PACKAGES_FOLDER = 'sqrl_packages'
|
|
67
|
+
PUBLIC_FOLDER = 'public'
|
|
29
68
|
|
|
30
69
|
MACROS_FOLDER = 'macros'
|
|
70
|
+
MACROS_FILE = 'macros_example.sql'
|
|
31
71
|
|
|
32
72
|
MODELS_FOLDER = 'models'
|
|
73
|
+
SOURCES_FILE = 'sources.yml'
|
|
74
|
+
BUILDS_FOLDER = 'builds'
|
|
75
|
+
BUILD_FILE_STEM = 'build_example'
|
|
33
76
|
DBVIEWS_FOLDER = 'dbviews'
|
|
34
77
|
DBVIEW_FILE_STEM = 'dbview_example'
|
|
35
78
|
FEDERATES_FOLDER = 'federates'
|
|
@@ -43,39 +86,23 @@ AUTH_FILE = 'auth.py'
|
|
|
43
86
|
CONNECTIONS_FILE = 'connections.py'
|
|
44
87
|
CONTEXT_FILE = 'context.py'
|
|
45
88
|
PARAMETERS_FILE = 'parameters.py'
|
|
89
|
+
USER_FILE = 'user.py'
|
|
90
|
+
ADMIN_USERNAME = 'admin'
|
|
46
91
|
|
|
47
92
|
TARGET_FOLDER = 'target'
|
|
48
93
|
COMPILE_FOLDER = 'compile'
|
|
94
|
+
COMPILE_BUILDTIME_FOLDER = 'buildtime'
|
|
95
|
+
COMPILE_RUNTIME_FOLDER = 'runtime'
|
|
96
|
+
DB_FILE = 'auth.sqlite'
|
|
97
|
+
DEFAULT_VDL_CATALOG_DB_PATH = 'ducklake:{project_path}/target/vdl_catalog.duckdb'
|
|
98
|
+
DEFAULT_VDL_DATA_PATH = '{project_path}/target/vdl_data/'
|
|
49
99
|
|
|
50
100
|
SEEDS_FOLDER = 'seeds'
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#
|
|
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'
|
|
101
|
+
SEED_CATEGORY_FILE_STEM = 'seed_categories'
|
|
102
|
+
SEED_SUBCATEGORY_FILE_STEM = 'seed_subcategories'
|
|
103
|
+
|
|
104
|
+
# Test sets
|
|
65
105
|
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'
|
|
75
|
-
|
|
76
|
-
# Selection cfg sections
|
|
77
|
-
USER_ATTRIBUTES_SECTION = 'user_attributes'
|
|
78
|
-
PARAMETERS_SECTION = 'parameters'
|
|
79
106
|
|
|
80
107
|
# Init Command Choices
|
|
81
108
|
SQL_FILE_TYPE = 'sql'
|
|
@@ -97,10 +124,13 @@ PNG = "png"
|
|
|
97
124
|
HTML = "html"
|
|
98
125
|
|
|
99
126
|
# Function names
|
|
100
|
-
GET_USER_FUNC = "get_user_if_valid"
|
|
101
|
-
DEP_FUNC = "dependencies"
|
|
102
127
|
MAIN_FUNC = "main"
|
|
103
128
|
|
|
104
129
|
# Regex
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
DATE_REGEX = r"^\d{4}\-\d{2}\-\d{2}$"
|
|
131
|
+
COLOR_REGEX = r"^#[0-9a-fA-F]{6}$"
|
|
132
|
+
|
|
133
|
+
# OAuth2
|
|
134
|
+
SUPPORTED_SCOPES = ['read']
|
|
135
|
+
SUPPORTED_GRANT_TYPES = ['authorization_code', 'refresh_token']
|
|
136
|
+
SUPPORTED_RESPONSE_TYPES = ['code']
|
squirrels/_dashboards.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from typing import Type, TypeVar, Callable, Coroutine, Any
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
import matplotlib.figure as figure
|
|
6
|
+
import os, time, io, abc, typing
|
|
7
|
+
|
|
8
|
+
from ._arguments.run_time_args import DashboardArgs
|
|
9
|
+
from ._py_module import PyModule
|
|
10
|
+
from ._manifest import AnalyticsOutputConfig
|
|
11
|
+
from ._exceptions import InvalidInputError, ConfigurationError, FileExecutionError
|
|
12
|
+
from . import _constants as c, _utils as u
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Dashboard(metaclass=abc.ABCMeta):
|
|
16
|
+
"""
|
|
17
|
+
Abstract parent class for all Dashboard classes.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
@abc.abstractmethod
|
|
22
|
+
def _content(self) -> bytes | str:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def _format(self) -> str:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PngDashboard(Dashboard):
|
|
32
|
+
"""
|
|
33
|
+
Instantiate a Dashboard in PNG format from a matplotlib figure or bytes
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, content: figure.Figure | io.BytesIO | bytes) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Constructor for PngDashboard
|
|
39
|
+
|
|
40
|
+
Arguments:
|
|
41
|
+
content: The content of the dashboard as a matplotlib.figure.Figure or bytes
|
|
42
|
+
"""
|
|
43
|
+
if isinstance(content, figure.Figure):
|
|
44
|
+
buffer = io.BytesIO()
|
|
45
|
+
content.savefig(buffer, format=c.PNG)
|
|
46
|
+
content = buffer.getvalue()
|
|
47
|
+
|
|
48
|
+
if isinstance(content, io.BytesIO):
|
|
49
|
+
content = content.getvalue()
|
|
50
|
+
|
|
51
|
+
self.__content = content
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def _content(self) -> bytes:
|
|
55
|
+
return self.__content
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def _format(self) -> typing.Literal['png']:
|
|
59
|
+
return c.PNG
|
|
60
|
+
|
|
61
|
+
def _repr_png_(self):
|
|
62
|
+
return self._content
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class HtmlDashboard(Dashboard):
|
|
66
|
+
"""
|
|
67
|
+
Instantiate a Dashboard from an HTML string
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, content: io.StringIO | str) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Constructor for HtmlDashboard
|
|
73
|
+
|
|
74
|
+
Arguments:
|
|
75
|
+
content: The content of the dashboard as HTML string
|
|
76
|
+
"""
|
|
77
|
+
if isinstance(content, io.StringIO):
|
|
78
|
+
content = content.getvalue()
|
|
79
|
+
|
|
80
|
+
self.__content = content
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def _content(self) -> str:
|
|
84
|
+
return self.__content
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def _format(self) -> typing.Literal['html']:
|
|
88
|
+
return c.HTML
|
|
89
|
+
|
|
90
|
+
def _repr_html_(self):
|
|
91
|
+
return self._content
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
T = TypeVar('T', bound=Dashboard)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class DashboardFormat(Enum):
|
|
98
|
+
PNG = "png"
|
|
99
|
+
HTML = "html"
|
|
100
|
+
|
|
101
|
+
class DashboardDependencies(BaseModel):
|
|
102
|
+
name: str
|
|
103
|
+
dataset: str
|
|
104
|
+
fixed_parameters: list[dict[str, str]] = Field(default_factory=list)
|
|
105
|
+
|
|
106
|
+
class DashboardConfig(AnalyticsOutputConfig):
|
|
107
|
+
format: DashboardFormat = Field(default=DashboardFormat.PNG)
|
|
108
|
+
depends_on: list[DashboardDependencies] = Field(default_factory=list)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class DashboardDefinition:
|
|
113
|
+
dashboard_name: str
|
|
114
|
+
filepath: str
|
|
115
|
+
config: DashboardConfig
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, Dashboard]]:
|
|
119
|
+
if not hasattr(self, '_dashboard_func'):
|
|
120
|
+
module = PyModule(self.filepath)
|
|
121
|
+
self._dashboard_func = module.get_func_or_class(c.MAIN_FUNC)
|
|
122
|
+
return self._dashboard_func
|
|
123
|
+
|
|
124
|
+
def get_dashboard_format(self) -> str:
|
|
125
|
+
return self.config.format.value
|
|
126
|
+
|
|
127
|
+
async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = Dashboard) -> T:
|
|
128
|
+
try:
|
|
129
|
+
dashboard = await self.dashboard_func(args)
|
|
130
|
+
assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
|
|
131
|
+
except (InvalidInputError, ConfigurationError, FileExecutionError) as e:
|
|
132
|
+
raise e
|
|
133
|
+
except Exception as e:
|
|
134
|
+
raise FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
|
|
135
|
+
|
|
136
|
+
return dashboard
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class DashboardsIO:
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def load_files(cls, logger: u.Logger, base_path: str) -> dict[str, DashboardDefinition]:
|
|
143
|
+
start = time.time()
|
|
144
|
+
|
|
145
|
+
dashboards_by_name = {}
|
|
146
|
+
for dp, _, filenames in os.walk(u.Path(base_path, c.DASHBOARDS_FOLDER)):
|
|
147
|
+
for file in filenames:
|
|
148
|
+
filepath = os.path.join(dp, file)
|
|
149
|
+
file_stem, extension = os.path.splitext(file)
|
|
150
|
+
if not extension == '.py':
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
# Check for corresponding .yml file
|
|
154
|
+
yml_path = os.path.join(dp, file_stem + '.yml')
|
|
155
|
+
config_dict = u.load_yaml_config(yml_path) if os.path.exists(yml_path) else {}
|
|
156
|
+
config = DashboardConfig(name=file_stem, **config_dict)
|
|
157
|
+
dashboards_by_name[file_stem] = DashboardDefinition(file_stem, filepath, config)
|
|
158
|
+
|
|
159
|
+
logger.log_activity_time("loading files for dashboards", start)
|
|
160
|
+
return dashboards_by_name
|