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.
Files changed (77) hide show
  1. dateutils/__init__.py +6 -460
  2. dateutils/_enums.py +25 -0
  3. dateutils/_implementation.py +409 -0
  4. dateutils/types.py +6 -0
  5. squirrels/__init__.py +7 -13
  6. squirrels/_api_server.py +5 -5
  7. squirrels/{arguments/init_time_args.py → _arguments/_init_time_args.py} +2 -2
  8. squirrels/{arguments/run_time_args.py → _arguments/_run_time_args.py} +4 -26
  9. squirrels/_auth.py +2 -2
  10. squirrels/_connection_set.py +5 -5
  11. squirrels/_constants.py +1 -1
  12. squirrels/_dashboard_types.py +82 -0
  13. squirrels/_dashboards_io.py +2 -2
  14. squirrels/_data_sources.py +564 -0
  15. squirrels/_exceptions.py +1 -1
  16. squirrels/_initializer.py +31 -26
  17. squirrels/_manifest.py +5 -5
  18. squirrels/_model_configs.py +2 -2
  19. squirrels/_model_queries.py +1 -1
  20. squirrels/_models.py +28 -16
  21. squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.py +4 -4
  22. squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.yml +2 -2
  23. squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  24. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.py +2 -2
  25. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.sql +1 -1
  26. squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.sql +1 -1
  27. squirrels/_package_data/base_project/models/federates/federate_example.py +41 -0
  28. squirrels/_package_data/base_project/models/federates/federate_example.sql +25 -0
  29. squirrels/{package_data → _package_data}/base_project/models/federates/federate_example.yml +6 -6
  30. squirrels/{package_data → _package_data}/base_project/parameters.yml +9 -8
  31. squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
  32. squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +14 -16
  33. squirrels/{package_data → _package_data}/base_project/pyconfigs/parameters.py +13 -8
  34. squirrels/{package_data → _package_data}/base_project/pyconfigs/user.py +2 -2
  35. squirrels/_parameter_configs.py +34 -34
  36. squirrels/_parameter_options.py +348 -0
  37. squirrels/_parameter_sets.py +18 -18
  38. squirrels/_parameters.py +1266 -0
  39. squirrels/_project.py +37 -12
  40. squirrels/_utils.py +4 -2
  41. squirrels/arguments.py +2 -0
  42. squirrels/connections.py +1 -0
  43. squirrels/dashboards.py +1 -82
  44. squirrels/data_sources.py +8 -563
  45. squirrels/parameter_options.py +8 -348
  46. squirrels/parameters.py +9 -1266
  47. squirrels/types.py +11 -0
  48. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/METADATA +1 -1
  49. squirrels-0.5.0b3.dist-info/RECORD +80 -0
  50. squirrels/package_data/base_project/macros/macros_example.sql +0 -15
  51. squirrels/package_data/base_project/models/federates/federate_example.py +0 -44
  52. squirrels/package_data/base_project/models/federates/federate_example.sql +0 -17
  53. squirrels/package_data/base_project/pyconfigs/connections.py +0 -14
  54. squirrels-0.5.0b2.dist-info/RECORD +0 -70
  55. /squirrels/{dataset_result.py → _dataset_types.py} +0 -0
  56. /squirrels/{package_data → _package_data}/base_project/.env +0 -0
  57. /squirrels/{package_data → _package_data}/base_project/.env.example +0 -0
  58. /squirrels/{package_data → _package_data}/base_project/assets/expenses.db +0 -0
  59. /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
  60. /squirrels/{package_data → _package_data}/base_project/connections.yml +0 -0
  61. /squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +0 -0
  62. /squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +0 -0
  63. /squirrels/{package_data → _package_data}/base_project/docker/compose.yml +0 -0
  64. /squirrels/{package_data → _package_data}/base_project/duckdb_init.sql +0 -0
  65. /squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +0 -0
  66. /squirrels/{package_data → _package_data}/base_project/models/builds/build_example.yml +0 -0
  67. /squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.yml +0 -0
  68. /squirrels/{package_data → _package_data}/base_project/models/sources.yml +0 -0
  69. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
  70. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.yml +0 -0
  71. /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.csv +0 -0
  72. /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.yml +0 -0
  73. /squirrels/{package_data → _package_data}/base_project/squirrels.yml.j2 +0 -0
  74. /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
  75. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/WHEEL +0 -0
  76. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/entry_points.txt +0 -0
  77. {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, dashboards as dash, dataset_result as dr
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 = u.Path(base_path, c.LOGS_FOLDER, log_file)
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
- return u.EnvironmentWithMacros(self._logger, loader=u.j2.FileSystemLoader(self._filepath))
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(u.Path(self._filepath, duckdb_filepath_setting_val))
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: u.Path) -> None:
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(u.Path(output_folder, "dag.png"))
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 = u.Path(self._filepath, c.TARGET_FOLDER, c.COMPILE_FOLDER, dataset, test_set)
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 = u.Path(output_folder, "placeholders.json")
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 = u.Path(output_folder, subfolder)
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 = u.Path(subpath, model.name+'.sql')
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 = u.Path(subpath, model.name+'.csv')
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.Dashboard
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, redacted_values: list[str] = []
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
- logger.info(f"Running statement: {redacted_stmt}", extra={"data": {"params": params}})
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
@@ -0,0 +1,2 @@
1
+ from ._arguments._init_time_args import ConnectionsArgs, ParametersArgs, BuildModelArgs
2
+ from ._arguments._run_time_args import ContextArgs, ModelArgs, DashboardArgs
@@ -0,0 +1 @@
1
+ from ._manifest import ConnectionProperties, ConnectionTypeEnum
squirrels/dashboards.py CHANGED
@@ -1,82 +1 @@
1
- import matplotlib.figure as _figure, io as _io, abc as _abc, typing as _t
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