squirrels 0.1.0__py3-none-any.whl → 0.6.0.post0__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.
Files changed (127) hide show
  1. dateutils/__init__.py +6 -0
  2. dateutils/_enums.py +25 -0
  3. squirrels/dateutils.py → dateutils/_implementation.py +409 -380
  4. dateutils/types.py +6 -0
  5. squirrels/__init__.py +21 -18
  6. squirrels/_api_routes/__init__.py +5 -0
  7. squirrels/_api_routes/auth.py +337 -0
  8. squirrels/_api_routes/base.py +196 -0
  9. squirrels/_api_routes/dashboards.py +156 -0
  10. squirrels/_api_routes/data_management.py +148 -0
  11. squirrels/_api_routes/datasets.py +220 -0
  12. squirrels/_api_routes/project.py +289 -0
  13. squirrels/_api_server.py +552 -134
  14. squirrels/_arguments/__init__.py +0 -0
  15. squirrels/_arguments/init_time_args.py +83 -0
  16. squirrels/_arguments/run_time_args.py +111 -0
  17. squirrels/_auth.py +777 -0
  18. squirrels/_command_line.py +239 -107
  19. squirrels/_compile_prompts.py +147 -0
  20. squirrels/_connection_set.py +94 -0
  21. squirrels/_constants.py +141 -64
  22. squirrels/_dashboards.py +179 -0
  23. squirrels/_data_sources.py +570 -0
  24. squirrels/_dataset_types.py +91 -0
  25. squirrels/_env_vars.py +209 -0
  26. squirrels/_exceptions.py +29 -0
  27. squirrels/_http_error_responses.py +52 -0
  28. squirrels/_initializer.py +319 -110
  29. squirrels/_logging.py +121 -0
  30. squirrels/_manifest.py +357 -187
  31. squirrels/_mcp_server.py +578 -0
  32. squirrels/_model_builder.py +69 -0
  33. squirrels/_model_configs.py +74 -0
  34. squirrels/_model_queries.py +52 -0
  35. squirrels/_models.py +1201 -0
  36. squirrels/_package_data/base_project/.env +7 -0
  37. squirrels/_package_data/base_project/.env.example +44 -0
  38. squirrels/_package_data/base_project/connections.yml +16 -0
  39. squirrels/_package_data/base_project/dashboards/dashboard_example.py +40 -0
  40. squirrels/_package_data/base_project/dashboards/dashboard_example.yml +22 -0
  41. squirrels/_package_data/base_project/docker/.dockerignore +16 -0
  42. squirrels/_package_data/base_project/docker/Dockerfile +16 -0
  43. squirrels/_package_data/base_project/docker/compose.yml +7 -0
  44. squirrels/_package_data/base_project/duckdb_init.sql +10 -0
  45. squirrels/_package_data/base_project/gitignore +13 -0
  46. squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  47. squirrels/_package_data/base_project/models/builds/build_example.py +26 -0
  48. squirrels/_package_data/base_project/models/builds/build_example.sql +16 -0
  49. squirrels/_package_data/base_project/models/builds/build_example.yml +57 -0
  50. squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +17 -0
  51. squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +32 -0
  52. squirrels/_package_data/base_project/models/federates/federate_example.py +51 -0
  53. squirrels/_package_data/base_project/models/federates/federate_example.sql +21 -0
  54. squirrels/_package_data/base_project/models/federates/federate_example.yml +65 -0
  55. squirrels/_package_data/base_project/models/sources.yml +38 -0
  56. squirrels/_package_data/base_project/parameters.yml +142 -0
  57. squirrels/_package_data/base_project/pyconfigs/connections.py +19 -0
  58. squirrels/_package_data/base_project/pyconfigs/context.py +96 -0
  59. squirrels/_package_data/base_project/pyconfigs/parameters.py +141 -0
  60. squirrels/_package_data/base_project/pyconfigs/user.py +56 -0
  61. squirrels/_package_data/base_project/resources/expenses.db +0 -0
  62. squirrels/_package_data/base_project/resources/public/.gitkeep +0 -0
  63. squirrels/_package_data/base_project/resources/weather.db +0 -0
  64. squirrels/_package_data/base_project/seeds/seed_categories.csv +6 -0
  65. squirrels/_package_data/base_project/seeds/seed_categories.yml +15 -0
  66. squirrels/_package_data/base_project/seeds/seed_subcategories.csv +15 -0
  67. squirrels/_package_data/base_project/seeds/seed_subcategories.yml +21 -0
  68. squirrels/_package_data/base_project/squirrels.yml.j2 +61 -0
  69. squirrels/_package_data/base_project/tmp/.gitignore +2 -0
  70. squirrels/_package_data/templates/login_successful.html +53 -0
  71. squirrels/_package_data/templates/squirrels_studio.html +22 -0
  72. squirrels/_package_loader.py +29 -0
  73. squirrels/_parameter_configs.py +592 -0
  74. squirrels/_parameter_options.py +348 -0
  75. squirrels/_parameter_sets.py +207 -0
  76. squirrels/_parameters.py +1703 -0
  77. squirrels/_project.py +796 -0
  78. squirrels/_py_module.py +122 -0
  79. squirrels/_request_context.py +33 -0
  80. squirrels/_schemas/__init__.py +0 -0
  81. squirrels/_schemas/auth_models.py +83 -0
  82. squirrels/_schemas/query_param_models.py +70 -0
  83. squirrels/_schemas/request_models.py +26 -0
  84. squirrels/_schemas/response_models.py +286 -0
  85. squirrels/_seeds.py +97 -0
  86. squirrels/_sources.py +112 -0
  87. squirrels/_utils.py +540 -149
  88. squirrels/_version.py +1 -3
  89. squirrels/arguments.py +7 -0
  90. squirrels/auth.py +4 -0
  91. squirrels/connections.py +3 -0
  92. squirrels/dashboards.py +3 -0
  93. squirrels/data_sources.py +14 -282
  94. squirrels/parameter_options.py +13 -189
  95. squirrels/parameters.py +14 -801
  96. squirrels/types.py +18 -0
  97. squirrels-0.6.0.post0.dist-info/METADATA +148 -0
  98. squirrels-0.6.0.post0.dist-info/RECORD +101 -0
  99. {squirrels-0.1.0.dist-info → squirrels-0.6.0.post0.dist-info}/WHEEL +1 -2
  100. {squirrels-0.1.0.dist-info → squirrels-0.6.0.post0.dist-info}/entry_points.txt +1 -0
  101. squirrels-0.6.0.post0.dist-info/licenses/LICENSE +201 -0
  102. squirrels/_credentials_manager.py +0 -87
  103. squirrels/_module_loader.py +0 -37
  104. squirrels/_parameter_set.py +0 -151
  105. squirrels/_renderer.py +0 -286
  106. squirrels/_timed_imports.py +0 -37
  107. squirrels/connection_set.py +0 -126
  108. squirrels/package_data/base_project/.gitignore +0 -4
  109. squirrels/package_data/base_project/connections.py +0 -21
  110. squirrels/package_data/base_project/database/sample_database.db +0 -0
  111. squirrels/package_data/base_project/database/seattle_weather.db +0 -0
  112. squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -8
  113. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -23
  114. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -7
  115. squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -10
  116. squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -2
  117. squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -30
  118. squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -6
  119. squirrels/package_data/base_project/squirrels.yaml +0 -26
  120. squirrels/package_data/static/favicon.ico +0 -0
  121. squirrels/package_data/static/script.js +0 -234
  122. squirrels/package_data/static/style.css +0 -110
  123. squirrels/package_data/templates/index.html +0 -32
  124. squirrels-0.1.0.dist-info/LICENSE +0 -22
  125. squirrels-0.1.0.dist-info/METADATA +0 -67
  126. squirrels-0.1.0.dist-info/RECORD +0 -40
  127. squirrels-0.1.0.dist-info/top_level.txt +0 -1
squirrels/_constants.py CHANGED
@@ -1,64 +1,141 @@
1
- # Squirrels CLI commands
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'
8
- RUN_CMD = 'run'
9
-
10
- # Manifest file keys
11
- PROJ_VARS_KEY = 'project_variables'
12
- 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'
18
- DATASETS_KEY = 'datasets'
19
- HEADERS_KEY = 'headers'
20
- DATABASE_VIEWS_KEY = 'database_views'
21
- FILE_KEY = 'file'
22
- FINAL_VIEW_KEY = 'final_view'
23
- BASE_PATH_KEY = 'base_path'
24
- SETTINGS_KEY = 'settings'
25
-
26
- # Project variable keys
27
- PRODUCT_KEY = 'product'
28
- MAJOR_VERSION_KEY = 'major_version'
29
- MINOR_VERSION_KEY = 'minor_version'
30
-
31
- # Database credentials keys
32
- CREDENTIALS_KEY = 'credentials'
33
- USERNAME_KEY = 'username'
34
- PASSWORD_KEY = 'password'
35
- DEFAULT_DB_CONN = 'default'
36
-
37
- # Folder/File names
38
- MANIFEST_FILE = 'squirrels.yaml'
39
- CONNECTIONS_FILE = 'connections.py'
40
- OUTPUTS_FOLDER = 'outputs'
41
- MODULES_FOLDER = 'modules'
42
- DATASETS_FOLDER = 'datasets'
43
- PARAMETERS_FILE = 'parameters.py'
44
- 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
- FINAL_VIEW_OUT_STEM = 'final_view'
50
- CONTEXT_FILE = 'context.py'
51
- SELECTIONS_CFG_FILE = 'selections.cfg'
52
-
53
- # Dataset setting names
54
- PARAMETERS_CACHE_SIZE_SETTING = 'parameters.cache.size'
55
- PARAMETERS_CACHE_TTL_SETTING = 'parameters.cache.ttl'
56
- RESULTS_CACHE_SIZE_SETTING = 'results.cache.size'
57
- RESULTS_CACHE_TTL_SETTING = 'results.cache.ttl'
58
-
59
- # Selection cfg sections
60
- PARAMETERS_SECTION = 'parameters'
61
-
62
- # Init Command Choices
63
- FILE_TYPE_CHOICES = ['sql', 'py']
64
- DATABASE_CHOICES = ['sample_database', 'seattle_weather']
1
+ # Squirrels CLI commands
2
+ NEW_CMD = 'new'
3
+ INIT_CMD = 'init'
4
+ GET_FILE_CMD = 'get-file'
5
+ DEPS_CMD = 'deps'
6
+ BUILD_CMD = 'build'
7
+ COMPILE_CMD = 'compile'
8
+ RUN_CMD = 'run'
9
+ DUCKDB_CMD = 'duckdb'
10
+
11
+ # Environment variables
12
+ SQRL_SECRET_KEY = 'SQRL_SECRET__KEY'
13
+ SQRL_SECRET_ADMIN_PASSWORD = 'SQRL_SECRET__ADMIN_PASSWORD'
14
+
15
+ SQRL_AUTH_DB_FILE_PATH = 'SQRL_AUTH__DB_FILE_PATH'
16
+ SQRL_AUTH_TOKEN_EXPIRE_MINUTES = 'SQRL_AUTH__TOKEN_EXPIRE_MINUTES'
17
+ SQRL_AUTH_CREDENTIAL_ORIGINS = 'SQRL_AUTH__ALLOWED_ORIGINS_FOR_COOKIES'
18
+
19
+ SQRL_PERMISSIONS_ELEVATED_ACCESS_LEVEL = 'SQRL_PERMISSIONS__ELEVATED_ACCESS_LEVEL'
20
+
21
+ SQRL_PARAMETERS_CACHE_SIZE = 'SQRL_PARAMETERS__CACHE_SIZE'
22
+ SQRL_PARAMETERS_CACHE_TTL_MINUTES = 'SQRL_PARAMETERS__CACHE_TTL_MINUTES'
23
+ SQRL_PARAMETERS_DATASOURCE_REFRESH_MINUTES = 'SQRL_PARAMETERS__DATASOURCE_REFRESH_MINUTES'
24
+
25
+ SQRL_DATASETS_CACHE_SIZE = 'SQRL_DATASETS__CACHE_SIZE'
26
+ SQRL_DATASETS_CACHE_TTL_MINUTES = 'SQRL_DATASETS__CACHE_TTL_MINUTES'
27
+ SQRL_DATASETS_MAX_ROWS_FOR_AI = 'SQRL_DATASETS__MAX_ROWS_FOR_AI'
28
+ SQRL_DATASETS_MAX_ROWS_OUTPUT = 'SQRL_DATASETS__MAX_ROWS_OUTPUT'
29
+ SQRL_DATASETS_SQL_TIMEOUT_SECONDS = 'SQRL_DATASETS__SQL_TIMEOUT_SECONDS'
30
+
31
+ SQRL_DASHBOARDS_CACHE_SIZE = 'SQRL_DASHBOARDS__CACHE_SIZE'
32
+ SQRL_DASHBOARDS_CACHE_TTL_MINUTES = 'SQRL_DASHBOARDS__CACHE_TTL_MINUTES'
33
+
34
+ SQRL_SEEDS_INFER_SCHEMA = 'SQRL_SEEDS__INFER_SCHEMA'
35
+ SQRL_SEEDS_NA_VALUES = 'SQRL_SEEDS__NA_VALUES'
36
+
37
+ SQRL_CONNECTIONS_DEFAULT_NAME_USED = 'SQRL_CONNECTIONS__DEFAULT_NAME_USED'
38
+
39
+ SQRL_VDL_CATALOG_DB_PATH = 'SQRL_VDL__CATALOG_DB_PATH'
40
+ SQRL_VDL_DATA_PATH = 'SQRL_VDL__DATA_PATH'
41
+
42
+ SQRL_STUDIO_BASE_URL = 'SQRL_STUDIO__BASE_URL'
43
+
44
+ SQRL_LOGGING_LEVEL = 'SQRL_LOGGING__LEVEL'
45
+ SQRL_LOGGING_FORMAT = 'SQRL_LOGGING__FORMAT'
46
+ SQRL_LOGGING_TO_FILE = 'SQRL_LOGGING__TO_FILE'
47
+ SQRL_LOGGING_FILE_SIZE_MB = 'SQRL_LOGGING__FILE_SIZE_MB'
48
+ SQRL_LOGGING_FILE_BACKUP_COUNT = 'SQRL_LOGGING__FILE_BACKUP_COUNT'
49
+
50
+ # Folder/File names
51
+ PACKAGE_DATA_FOLDER = '_package_data'
52
+ BASE_PROJECT_FOLDER = 'base_project'
53
+
54
+ GLOBAL_ENV_FOLDER = '.squirrels'
55
+ MANIFEST_JINJA_FILE = 'squirrels.yml.j2'
56
+ CONNECTIONS_YML_FILE = 'connections.yml'
57
+ PARAMETERS_YML_FILE = 'parameters.yml'
58
+ DASHBOARDS_YML_FILE = 'dashboards.yml'
59
+ MANIFEST_FILE = 'squirrels.yml'
60
+ DUCKDB_INIT_FILE = 'duckdb_init.sql'
61
+ DOTENV_FILE = '.env'
62
+ DOTENV_LOCAL_FILE = '.env.local'
63
+ GITIGNORE_FILE = '.gitignore'
64
+
65
+ LOGS_FOLDER = 'logs'
66
+ LOGS_FILE = 'squirrels.log'
67
+
68
+ RESOURCES_FOLDER = 'resources'
69
+ PUBLIC_FOLDER = 'public'
70
+ LOGOS_FOLDER = 'logos'
71
+ PACKAGES_FOLDER = 'sqrl_packages'
72
+
73
+ MACROS_FOLDER = 'macros'
74
+ MACROS_FILE = 'macros_example.sql'
75
+
76
+ MODELS_FOLDER = 'models'
77
+ SOURCES_FILE = 'sources.yml'
78
+ BUILDS_FOLDER = 'builds'
79
+ BUILD_FILE_STEM = 'build_example'
80
+ DBVIEWS_FOLDER = 'dbviews'
81
+ DBVIEW_FILE_STEM = 'dbview_example'
82
+ FEDERATES_FOLDER = 'federates'
83
+ FEDERATE_FILE_STEM = 'federate_example'
84
+
85
+ DASHBOARDS_FOLDER = 'dashboards'
86
+ DASHBOARD_FILE_STEM = 'dashboard_example'
87
+
88
+ PYCONFIGS_FOLDER = 'pyconfigs'
89
+ CONNECTIONS_FILE = 'connections.py'
90
+ CONTEXT_FILE = 'context.py'
91
+ PARAMETERS_FILE = 'parameters.py'
92
+ USER_FILE = 'user.py'
93
+ ADMIN_USERNAME = 'admin'
94
+
95
+ TARGET_FOLDER = 'target'
96
+ COMPILE_FOLDER = 'compile'
97
+ COMPILE_BUILDTIME_FOLDER = 'buildtime'
98
+ COMPILE_RUNTIME_FOLDER = 'runtime'
99
+ DB_FILE = 'auth.sqlite'
100
+
101
+ SEEDS_FOLDER = 'seeds'
102
+ SEED_CATEGORY_FILE_STEM = 'seed_categories'
103
+ SEED_SUBCATEGORY_FILE_STEM = 'seed_subcategories'
104
+
105
+ # Test sets
106
+ DEFAULT_TEST_SET_NAME = 'default'
107
+
108
+ # Init Command Choices
109
+ SQL_FILE_TYPE = 'sql'
110
+ PYTHON_FILE_TYPE = 'py'
111
+ FILE_TYPE_CHOICES = [SQL_FILE_TYPE, PYTHON_FILE_TYPE]
112
+
113
+ YML_FORMAT = 'yml'
114
+ PYTHON_FORMAT = 'py'
115
+ CONF_FORMAT_CHOICES = [YML_FORMAT, PYTHON_FORMAT]
116
+
117
+ PYTHON_FORMAT2 = 'py (recommended)'
118
+ CONF_FORMAT_CHOICES2 = [(PYTHON_FORMAT2, PYTHON_FORMAT), YML_FORMAT]
119
+
120
+ EXPENSES_DB = 'expenses.db'
121
+ WEATHER_DB = 'weather.db'
122
+ GOOGLE_LOGO = 'google.ico'
123
+
124
+ # Dashboard formats
125
+ PNG = "png"
126
+ HTML = "html"
127
+
128
+ # Function names
129
+ MAIN_FUNC = "main"
130
+
131
+ # Regex
132
+ DATE_REGEX = r"^\d{4}\-\d{2}\-\d{2}$"
133
+ COLOR_REGEX = r"^#[0-9a-fA-F]{6}$"
134
+
135
+ # OAuth2
136
+ SUPPORTED_SCOPES = ['read']
137
+ SUPPORTED_GRANT_TYPES = ['authorization_code', 'refresh_token']
138
+ SUPPORTED_RESPONSE_TYPES = ['code']
139
+
140
+ # Miscellaneous
141
+ LATEST_API_VERSION_MOUNT_PATH = "/api/0"
@@ -0,0 +1,179 @@
1
+ from typing import Type, TypeVar, Callable, Coroutine, Any
2
+ from typing_extensions import Self
3
+ from enum import Enum
4
+ from dataclasses import dataclass
5
+ from pydantic import BaseModel, Field, model_validator, ValidationError
6
+ import matplotlib.figure as figure
7
+ import os, time, io, abc, typing
8
+
9
+ from ._arguments.run_time_args import DashboardArgs
10
+ from ._py_module import PyModule
11
+ from ._manifest import AnalyticsOutputConfig, AuthType, PermissionScope, ConfigurableOverride
12
+ from ._exceptions import InvalidInputError, ConfigurationError, FileExecutionError
13
+ from . import _constants as c, _utils as u
14
+
15
+
16
+ class Dashboard(metaclass=abc.ABCMeta):
17
+ """
18
+ Abstract parent class for all Dashboard classes.
19
+ """
20
+
21
+ @property
22
+ @abc.abstractmethod
23
+ def _content(self) -> bytes | str:
24
+ pass
25
+
26
+ @property
27
+ @abc.abstractmethod
28
+ def _format(self) -> str:
29
+ pass
30
+
31
+
32
+ class PngDashboard(Dashboard):
33
+ """
34
+ Instantiate a Dashboard in PNG format from a matplotlib figure or bytes
35
+ """
36
+
37
+ def __init__(self, content: figure.Figure | io.BytesIO | bytes) -> None:
38
+ """
39
+ Constructor for PngDashboard
40
+
41
+ Arguments:
42
+ content: The content of the dashboard as a matplotlib.figure.Figure or bytes
43
+ """
44
+ if isinstance(content, figure.Figure):
45
+ buffer = io.BytesIO()
46
+ content.savefig(buffer, format=c.PNG)
47
+ content = buffer.getvalue()
48
+
49
+ if isinstance(content, io.BytesIO):
50
+ content = content.getvalue()
51
+
52
+ self.__content = content
53
+
54
+ @property
55
+ def _content(self) -> bytes:
56
+ return self.__content
57
+
58
+ @property
59
+ def _format(self) -> typing.Literal['png']:
60
+ return c.PNG
61
+
62
+ def _repr_png_(self):
63
+ return self._content
64
+
65
+
66
+ class HtmlDashboard(Dashboard):
67
+ """
68
+ Instantiate a Dashboard from an HTML string
69
+ """
70
+
71
+ def __init__(self, content: io.StringIO | str) -> None:
72
+ """
73
+ Constructor for HtmlDashboard
74
+
75
+ Arguments:
76
+ content: The content of the dashboard as HTML string
77
+ """
78
+ if isinstance(content, io.StringIO):
79
+ content = content.getvalue()
80
+
81
+ self.__content = content
82
+
83
+ @property
84
+ def _content(self) -> str:
85
+ return self.__content
86
+
87
+ @property
88
+ def _format(self) -> typing.Literal['html']:
89
+ return c.HTML
90
+
91
+ def _repr_html_(self):
92
+ return self._content
93
+
94
+
95
+ T = TypeVar('T', bound=Dashboard)
96
+
97
+
98
+ class DashboardFormat(Enum):
99
+ PNG = "png"
100
+ HTML = "html"
101
+
102
+ class DashboardDependencies(BaseModel):
103
+ name: str
104
+ dataset: str
105
+ fixed_parameters: dict[str, str] = Field(default_factory=dict)
106
+
107
+ class DashboardConfig(AnalyticsOutputConfig):
108
+ format: DashboardFormat = Field(default=DashboardFormat.PNG)
109
+ depends_on: list[DashboardDependencies] = Field(default_factory=list)
110
+
111
+
112
+ @dataclass
113
+ class DashboardDefinition:
114
+ dashboard_name: str
115
+ filepath: str
116
+ config: DashboardConfig
117
+ project_path: str
118
+
119
+ @property
120
+ def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, Dashboard]]:
121
+ if not hasattr(self, '_dashboard_func'):
122
+ module = PyModule(self.filepath, self.project_path)
123
+ self._dashboard_func = module.get_func_or_class(c.MAIN_FUNC)
124
+ return self._dashboard_func
125
+
126
+ def get_dashboard_format(self) -> str:
127
+ return self.config.format.value
128
+
129
+ async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = Dashboard) -> T:
130
+ try:
131
+ dashboard = await self.dashboard_func(args)
132
+ assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
133
+ except (InvalidInputError, ConfigurationError, FileExecutionError) as e:
134
+ raise e
135
+ except Exception as e:
136
+ raise FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
137
+
138
+ return dashboard
139
+
140
+
141
+ class DashboardsIO:
142
+
143
+ @classmethod
144
+ def load_files(
145
+ cls, logger: u.Logger, project_path: str, auth_type: AuthType = AuthType.OPTIONAL, project_configurables: dict[str, Any] | None = None
146
+ ) -> dict[str, DashboardDefinition]:
147
+ start = time.time()
148
+
149
+ if project_configurables is None:
150
+ project_configurables = {}
151
+
152
+ default_scope = PermissionScope.PROTECTED if auth_type == AuthType.REQUIRED else PermissionScope.PUBLIC
153
+
154
+ dashboards_by_name = {}
155
+ for dp, _, filenames in os.walk(u.Path(project_path, c.DASHBOARDS_FOLDER)):
156
+ for file in filenames:
157
+ filepath = os.path.join(dp, file)
158
+ file_stem, extension = os.path.splitext(file)
159
+ if not extension == '.py':
160
+ continue
161
+
162
+ # Check for corresponding .yml file
163
+ yml_path = os.path.join(dp, file_stem + '.yml')
164
+ config_dict = u.load_yaml_config(yml_path) if os.path.exists(yml_path) else {}
165
+ try:
166
+ config = DashboardConfig(name=file_stem, project_configurables=project_configurables, **config_dict)
167
+ except ValidationError as e:
168
+ raise ConfigurationError(f'Failed to process dashboard config "{file_stem}". ' + str(e)) from e
169
+
170
+ if config.scope is None:
171
+ config.scope = default_scope
172
+
173
+ if auth_type == AuthType.REQUIRED and config.scope == PermissionScope.PUBLIC:
174
+ raise ConfigurationError(f'Authentication is required, so dashboard "{file_stem}" cannot be public. Update the scope in "{yml_path}"')
175
+
176
+ dashboards_by_name[file_stem] = DashboardDefinition(file_stem, filepath, config, project_path)
177
+
178
+ logger.log_activity_time("loading files for dashboards", start)
179
+ return dashboards_by_name