squirrels 0.5.0b2__py3-none-any.whl → 0.5.0b3__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.
- dateutils/__init__.py +6 -460
- dateutils/_enums.py +25 -0
- dateutils/_implementation.py +409 -0
- dateutils/types.py +6 -0
- squirrels/__init__.py +7 -13
- squirrels/_api_server.py +5 -5
- squirrels/{arguments/init_time_args.py → _arguments/_init_time_args.py} +2 -2
- squirrels/{arguments/run_time_args.py → _arguments/_run_time_args.py} +4 -26
- squirrels/_auth.py +2 -2
- squirrels/_connection_set.py +5 -5
- squirrels/_constants.py +1 -1
- squirrels/_dashboard_types.py +82 -0
- squirrels/_dashboards_io.py +2 -2
- squirrels/_data_sources.py +564 -0
- squirrels/_exceptions.py +1 -1
- squirrels/_initializer.py +31 -26
- squirrels/_manifest.py +5 -5
- squirrels/_model_configs.py +2 -2
- squirrels/_model_queries.py +1 -1
- squirrels/_models.py +28 -16
- squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.py +4 -4
- squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.yml +2 -2
- squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
- squirrels/{package_data → _package_data}/base_project/models/builds/build_example.py +2 -2
- squirrels/{package_data → _package_data}/base_project/models/builds/build_example.sql +1 -1
- squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.sql +1 -1
- squirrels/_package_data/base_project/models/federates/federate_example.py +41 -0
- squirrels/_package_data/base_project/models/federates/federate_example.sql +25 -0
- squirrels/{package_data → _package_data}/base_project/models/federates/federate_example.yml +6 -6
- squirrels/{package_data → _package_data}/base_project/parameters.yml +9 -8
- squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
- squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +14 -16
- squirrels/{package_data → _package_data}/base_project/pyconfigs/parameters.py +13 -8
- squirrels/{package_data → _package_data}/base_project/pyconfigs/user.py +2 -2
- squirrels/_parameter_configs.py +34 -34
- squirrels/_parameter_options.py +348 -0
- squirrels/_parameter_sets.py +18 -18
- squirrels/_parameters.py +1266 -0
- squirrels/_project.py +37 -12
- squirrels/_utils.py +4 -2
- squirrels/arguments.py +2 -0
- squirrels/connections.py +1 -0
- squirrels/dashboards.py +1 -82
- squirrels/data_sources.py +8 -563
- squirrels/parameter_options.py +8 -348
- squirrels/parameters.py +9 -1266
- squirrels/types.py +11 -0
- {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/METADATA +1 -1
- squirrels-0.5.0b3.dist-info/RECORD +80 -0
- squirrels/package_data/base_project/macros/macros_example.sql +0 -15
- squirrels/package_data/base_project/models/federates/federate_example.py +0 -44
- squirrels/package_data/base_project/models/federates/federate_example.sql +0 -17
- squirrels/package_data/base_project/pyconfigs/connections.py +0 -14
- squirrels-0.5.0b2.dist-info/RECORD +0 -70
- /squirrels/{dataset_result.py → _dataset_types.py} +0 -0
- /squirrels/{package_data → _package_data}/base_project/.env +0 -0
- /squirrels/{package_data → _package_data}/base_project/.env.example +0 -0
- /squirrels/{package_data → _package_data}/base_project/assets/expenses.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/connections.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +0 -0
- /squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +0 -0
- /squirrels/{package_data → _package_data}/base_project/docker/compose.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/duckdb_init.sql +0 -0
- /squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +0 -0
- /squirrels/{package_data → _package_data}/base_project/models/builds/build_example.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/models/sources.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/squirrels.yml.j2 +0 -0
- /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
- {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/WHEEL +0 -0
- {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/entry_points.txt +0 -0
- {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/licenses/LICENSE +0 -0
squirrels/_project.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dotenv import dotenv_values
|
|
2
2
|
from uuid import uuid4
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
import asyncio, typing as t, functools as ft, shutil, json, os
|
|
4
5
|
import logging as l, matplotlib.pyplot as plt, networkx as nx, polars as pl
|
|
5
6
|
import sqlglot, sqlglot.expressions
|
|
@@ -9,7 +10,7 @@ from ._model_builder import ModelBuilder
|
|
|
9
10
|
from ._exceptions import InvalidInputError, ConfigurationError
|
|
10
11
|
from . import _utils as u, _constants as c, _manifest as mf, _connection_set as cs, _api_response_models as arm
|
|
11
12
|
from . import _seeds as s, _models as m, _model_configs as mc, _model_queries as mq, _sources as so
|
|
12
|
-
from . import _parameter_sets as ps, _dashboards_io as d,
|
|
13
|
+
from . import _parameter_sets as ps, _dashboards_io as d, _dashboard_types as dash, _dataset_types as dr
|
|
13
14
|
|
|
14
15
|
T = t.TypeVar("T", bound=dash.Dashboard)
|
|
15
16
|
M = t.TypeVar("M", bound=m.DataModel)
|
|
@@ -70,7 +71,7 @@ class SquirrelsProject:
|
|
|
70
71
|
raise ValueError("log_format must be either 'text' or 'json'")
|
|
71
72
|
|
|
72
73
|
if log_file:
|
|
73
|
-
path =
|
|
74
|
+
path = Path(base_path, c.LOGS_FOLDER, log_file)
|
|
74
75
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
75
76
|
|
|
76
77
|
handler = l.FileHandler(path)
|
|
@@ -131,6 +132,10 @@ class SquirrelsProject:
|
|
|
131
132
|
def _auth(self) -> Authenticator:
|
|
132
133
|
return Authenticator(self._logger, self._filepath, self._env_vars)
|
|
133
134
|
|
|
135
|
+
@ft.cached_property
|
|
136
|
+
def User(self) -> t.Type[BaseUser]:
|
|
137
|
+
return self._auth.User
|
|
138
|
+
|
|
134
139
|
@ft.cached_property
|
|
135
140
|
def _param_args(self) -> ps.ParametersArgs:
|
|
136
141
|
return ps.ParameterConfigsSetIO.get_param_args(self._conn_args)
|
|
@@ -143,12 +148,32 @@ class SquirrelsProject:
|
|
|
143
148
|
|
|
144
149
|
@ft.cached_property
|
|
145
150
|
def _j2_env(self) -> u.EnvironmentWithMacros:
|
|
146
|
-
|
|
151
|
+
env = u.EnvironmentWithMacros(self._logger, loader=u.j2.FileSystemLoader(self._filepath))
|
|
152
|
+
|
|
153
|
+
def value_to_str(value: t.Any, attribute: str | None = None) -> str:
|
|
154
|
+
if attribute is None:
|
|
155
|
+
return str(value)
|
|
156
|
+
else:
|
|
157
|
+
return str(getattr(value, attribute))
|
|
158
|
+
|
|
159
|
+
def join(value: list[t.Any], d: str = ", ", attribute: str | None = None) -> str:
|
|
160
|
+
return d.join(map(lambda x: value_to_str(x, attribute), value))
|
|
161
|
+
|
|
162
|
+
def quote(value: t.Any, q: str = "'", attribute: str | None = None) -> str:
|
|
163
|
+
return q + value_to_str(value, attribute) + q
|
|
164
|
+
|
|
165
|
+
def quote_and_join(value: list[t.Any], q: str = "'", d: str = ", ", attribute: str | None = None) -> str:
|
|
166
|
+
return d.join(map(lambda x: quote(x, q, attribute), value))
|
|
167
|
+
|
|
168
|
+
env.filters["join"] = join
|
|
169
|
+
env.filters["quote"] = quote
|
|
170
|
+
env.filters["quote_and_join"] = quote_and_join
|
|
171
|
+
return env
|
|
147
172
|
|
|
148
173
|
@ft.cached_property
|
|
149
174
|
def _duckdb_venv_path(self) -> str:
|
|
150
175
|
duckdb_filepath_setting_val = self._env_vars.get(c.SQRL_DUCKDB_VENV_DB_FILE_PATH, f"{c.TARGET_FOLDER}/{c.DUCKDB_VENV_FILE}")
|
|
151
|
-
return str(
|
|
176
|
+
return str(Path(self._filepath, duckdb_filepath_setting_val))
|
|
152
177
|
|
|
153
178
|
def close(self) -> None:
|
|
154
179
|
"""
|
|
@@ -255,7 +280,7 @@ class SquirrelsProject:
|
|
|
255
280
|
dag = m.DAG(None, fake_target_model, models_dict, self._duckdb_venv_path, self._logger)
|
|
256
281
|
return dag
|
|
257
282
|
|
|
258
|
-
def _draw_dag(self, dag: m.DAG, output_folder:
|
|
283
|
+
def _draw_dag(self, dag: m.DAG, output_folder: Path) -> None:
|
|
259
284
|
color_map = {
|
|
260
285
|
m.ModelType.SEED: "green", m.ModelType.DBVIEW: "red", m.ModelType.FEDERATE: "skyblue",
|
|
261
286
|
m.ModelType.BUILD: "purple", m.ModelType.SOURCE: "orange"
|
|
@@ -275,7 +300,7 @@ class SquirrelsProject:
|
|
|
275
300
|
|
|
276
301
|
fig.tight_layout()
|
|
277
302
|
plt.margins(x=0.1, y=0.1)
|
|
278
|
-
fig.savefig(
|
|
303
|
+
fig.savefig(Path(output_folder, "dag.png"))
|
|
279
304
|
plt.close(fig)
|
|
280
305
|
|
|
281
306
|
async def _get_compiled_dag(self, *, sql_query: str | None = None, selections: dict[str, t.Any] = {}, user: BaseUser | None = None) -> m.DAG:
|
|
@@ -371,28 +396,28 @@ class SquirrelsProject:
|
|
|
371
396
|
runquery=runquery, recurse=recurse, default_traits=self._manifest_cfg.get_default_traits()
|
|
372
397
|
)
|
|
373
398
|
|
|
374
|
-
output_folder =
|
|
399
|
+
output_folder = Path(self._filepath, c.TARGET_FOLDER, c.COMPILE_FOLDER, dataset, test_set)
|
|
375
400
|
if output_folder.exists():
|
|
376
401
|
shutil.rmtree(output_folder)
|
|
377
402
|
output_folder.mkdir(parents=True, exist_ok=True)
|
|
378
403
|
|
|
379
404
|
def write_placeholders() -> None:
|
|
380
|
-
output_filepath =
|
|
405
|
+
output_filepath = Path(output_folder, "placeholders.json")
|
|
381
406
|
with open(output_filepath, 'w') as f:
|
|
382
407
|
json.dump(dag.placeholders, f, indent=4)
|
|
383
408
|
|
|
384
409
|
def write_model_outputs(model: m.DataModel) -> None:
|
|
385
410
|
assert isinstance(model, m.QueryModel)
|
|
386
411
|
subfolder = c.DBVIEWS_FOLDER if model.model_type == m.ModelType.DBVIEW else c.FEDERATES_FOLDER
|
|
387
|
-
subpath =
|
|
412
|
+
subpath = Path(output_folder, subfolder)
|
|
388
413
|
subpath.mkdir(parents=True, exist_ok=True)
|
|
389
414
|
if isinstance(model.compiled_query, mq.SqlModelQuery):
|
|
390
|
-
output_filepath =
|
|
415
|
+
output_filepath = Path(subpath, model.name+'.sql')
|
|
391
416
|
query = model.compiled_query.query
|
|
392
417
|
with open(output_filepath, 'w') as f:
|
|
393
418
|
f.write(query)
|
|
394
419
|
if runquery and isinstance(model.result, pl.LazyFrame):
|
|
395
|
-
output_filepath =
|
|
420
|
+
output_filepath = Path(subpath, model.name+'.csv')
|
|
396
421
|
model.result.collect().write_csv(output_filepath)
|
|
397
422
|
|
|
398
423
|
write_placeholders()
|
|
@@ -520,7 +545,7 @@ class SquirrelsProject:
|
|
|
520
545
|
)
|
|
521
546
|
|
|
522
547
|
async def dashboard(
|
|
523
|
-
self, name: str, *, selections: dict[str, t.Any] = {}, user: BaseUser | None = None, dashboard_type: t.Type[T] = dash.
|
|
548
|
+
self, name: str, *, selections: dict[str, t.Any] = {}, user: BaseUser | None = None, dashboard_type: t.Type[T] = dash.PngDashboard
|
|
524
549
|
) -> T:
|
|
525
550
|
"""
|
|
526
551
|
Async method to retrieve a dashboard given parameter selections.
|
squirrels/_utils.py
CHANGED
|
@@ -290,7 +290,8 @@ def load_yaml_config(filepath: FilePath) -> dict:
|
|
|
290
290
|
|
|
291
291
|
|
|
292
292
|
def run_duckdb_stmt(
|
|
293
|
-
logger: Logger, duckdb_conn: duckdb.DuckDBPyConnection, stmt: str, *, params: dict[str, Any] | None = None,
|
|
293
|
+
logger: Logger, duckdb_conn: duckdb.DuckDBPyConnection, stmt: str, *, params: dict[str, Any] | None = None,
|
|
294
|
+
model_name: str | None = None, redacted_values: list[str] = []
|
|
294
295
|
) -> duckdb.DuckDBPyConnection:
|
|
295
296
|
"""
|
|
296
297
|
Runs a statement on a DuckDB connection
|
|
@@ -306,7 +307,8 @@ def run_duckdb_stmt(
|
|
|
306
307
|
for value in redacted_values:
|
|
307
308
|
redacted_stmt = redacted_stmt.replace(value, "[REDACTED]")
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
for_model_name = f" for model '{model_name}'" if model_name is not None else ""
|
|
311
|
+
logger.info(f"Running SQL statement{for_model_name}:\n{redacted_stmt}", extra={"data": {"params": params}})
|
|
310
312
|
try:
|
|
311
313
|
return duckdb_conn.execute(stmt, params)
|
|
312
314
|
except duckdb.ParserException as e:
|
squirrels/arguments.py
ADDED
squirrels/connections.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ._manifest import ConnectionProperties, ConnectionTypeEnum
|
squirrels/dashboards.py
CHANGED
|
@@ -1,82 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from . import _constants as c
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Dashboard(metaclass=_abc.ABCMeta):
|
|
7
|
-
"""
|
|
8
|
-
Abstract parent class for all Dashboard classes.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
@property
|
|
12
|
-
@_abc.abstractmethod
|
|
13
|
-
def _content(self) -> bytes | str:
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
@property
|
|
17
|
-
@_abc.abstractmethod
|
|
18
|
-
def _format(self) -> str:
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class PngDashboard(Dashboard):
|
|
23
|
-
"""
|
|
24
|
-
Instantiate a Dashboard in PNG format from a matplotlib figure or bytes
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, content: _figure.Figure | _io.BytesIO | bytes) -> None:
|
|
28
|
-
"""
|
|
29
|
-
Constructor for PngDashboard
|
|
30
|
-
|
|
31
|
-
Arguments:
|
|
32
|
-
content: The content of the dashboard as a matplotlib.figure.Figure or bytes
|
|
33
|
-
"""
|
|
34
|
-
if isinstance(content, _figure.Figure):
|
|
35
|
-
buffer = _io.BytesIO()
|
|
36
|
-
content.savefig(buffer, format=c.PNG)
|
|
37
|
-
content = buffer.getvalue()
|
|
38
|
-
|
|
39
|
-
if isinstance(content, _io.BytesIO):
|
|
40
|
-
content = content.getvalue()
|
|
41
|
-
|
|
42
|
-
self.__content = content
|
|
43
|
-
|
|
44
|
-
@property
|
|
45
|
-
def _content(self) -> bytes:
|
|
46
|
-
return self.__content
|
|
47
|
-
|
|
48
|
-
@property
|
|
49
|
-
def _format(self) -> _t.Literal['png']:
|
|
50
|
-
return c.PNG
|
|
51
|
-
|
|
52
|
-
def _repr_png_(self):
|
|
53
|
-
return self._content
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class HtmlDashboard(Dashboard):
|
|
57
|
-
"""
|
|
58
|
-
Instantiate a Dashboard from an HTML string
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
def __init__(self, content: _io.StringIO | str) -> None:
|
|
62
|
-
"""
|
|
63
|
-
Constructor for HtmlDashboard
|
|
64
|
-
|
|
65
|
-
Arguments:
|
|
66
|
-
content: The content of the dashboard as HTML string
|
|
67
|
-
"""
|
|
68
|
-
if isinstance(content, _io.StringIO):
|
|
69
|
-
content = content.getvalue()
|
|
70
|
-
|
|
71
|
-
self.__content = content
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def _content(self) -> str:
|
|
75
|
-
return self.__content
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def _format(self) -> _t.Literal['html']:
|
|
79
|
-
return c.HTML
|
|
80
|
-
|
|
81
|
-
def _repr_html_(self):
|
|
82
|
-
return self._content
|
|
1
|
+
from ._dashboard_types import PngDashboard, HtmlDashboard
|