squirrels 0.4.0__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.0.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.0.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.0.dist-info/METADATA +0 -117
- squirrels-0.4.0.dist-info/RECORD +0 -60
- squirrels-0.4.0.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/project.py
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
import typing as _t, functools as _ft, asyncio as _aio, os as _os, shutil as _shutil, json as _json
|
|
2
|
-
import logging as _l, uuid as _uu, matplotlib.pyplot as _plt, networkx as _nx, pandas as _pd
|
|
3
|
-
|
|
4
|
-
from . import _utils as _u, _constants as _c, _environcfg as _ec, _manifest as _mf, _authenticator as _auth
|
|
5
|
-
from . import _seeds as _s, _connection_set as _cs, _models as _m, _dashboards_io as _d, _parameter_sets as _ps
|
|
6
|
-
from . import dashboards as _dash
|
|
7
|
-
|
|
8
|
-
T = _t.TypeVar('T', bound=_dash.Dashboard)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class _CustomJsonFormatter(_l.Formatter):
|
|
12
|
-
def format(self, record: _l.LogRecord) -> str:
|
|
13
|
-
super().format(record)
|
|
14
|
-
info = {
|
|
15
|
-
"timestamp": self.formatTime(record),
|
|
16
|
-
"project_id": record.name,
|
|
17
|
-
"level": record.levelname,
|
|
18
|
-
"message": record.getMessage(),
|
|
19
|
-
"thread": record.thread,
|
|
20
|
-
"thread_name": record.threadName,
|
|
21
|
-
"process": record.process,
|
|
22
|
-
**record.__dict__.get("info", {})
|
|
23
|
-
}
|
|
24
|
-
output = {
|
|
25
|
-
"data": record.__dict__.get("data", {}),
|
|
26
|
-
"info": info
|
|
27
|
-
}
|
|
28
|
-
return _json.dumps(output)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class SquirrelsProject:
|
|
32
|
-
"""
|
|
33
|
-
Initiate an instance of this class to interact with a Squirrels project through Python code. For example this can be handy to experiment with the datasets produced by Squirrels in a Jupyter notebook.
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
def __init__(self, *, filepath: str = ".", log_file: str | None = _c.LOGS_FILE, log_level: str = "INFO", log_format: str = "text") -> None:
|
|
37
|
-
"""
|
|
38
|
-
Constructor for SquirrelsProject class. Loads the file contents of the Squirrels project into memory as member fields.
|
|
39
|
-
|
|
40
|
-
Arguments:
|
|
41
|
-
filepath: The path to the Squirrels project file. Defaults to the current working directory.
|
|
42
|
-
log_level: The logging level to use. Options are "DEBUG", "INFO", and "WARNING". Default is "INFO".
|
|
43
|
-
log_file: The name of the log file to write to from the "logs/" subfolder. If None or empty string, then file logging is disabled. Default is "squirrels.log".
|
|
44
|
-
log_format: The format of the log records. Options are "text" and "json". Default is "text".
|
|
45
|
-
"""
|
|
46
|
-
self._filepath = filepath
|
|
47
|
-
self._logger = self._get_logger(self._filepath, log_file, log_level, log_format)
|
|
48
|
-
|
|
49
|
-
def _get_logger(self, base_path: str, log_file: str | None, log_level: str, log_format: str) -> _u.Logger:
|
|
50
|
-
logger = _u.Logger(name=_uu.uuid4().hex)
|
|
51
|
-
logger.setLevel(log_level.upper())
|
|
52
|
-
|
|
53
|
-
if log_file:
|
|
54
|
-
path = _u.Path(base_path, _c.LOGS_FOLDER, log_file)
|
|
55
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
56
|
-
|
|
57
|
-
handler = _l.FileHandler(path)
|
|
58
|
-
if log_format.lower() == "json":
|
|
59
|
-
handler.setFormatter(_CustomJsonFormatter())
|
|
60
|
-
elif log_format.lower() == "text":
|
|
61
|
-
formatter = _l.Formatter("[%(name)s] %(asctime)s - %(levelname)s - %(message)s")
|
|
62
|
-
handler.setFormatter(formatter)
|
|
63
|
-
else:
|
|
64
|
-
raise ValueError("log_format must be either 'text' or 'json'")
|
|
65
|
-
logger.addHandler(handler)
|
|
66
|
-
else:
|
|
67
|
-
logger.disabled = True
|
|
68
|
-
|
|
69
|
-
return logger
|
|
70
|
-
|
|
71
|
-
@property
|
|
72
|
-
@_ft.cache
|
|
73
|
-
def _env_cfg(self) -> _ec.EnvironConfig:
|
|
74
|
-
return _ec.EnvironConfigIO.load_from_file(self._logger, self._filepath)
|
|
75
|
-
|
|
76
|
-
@property
|
|
77
|
-
@_ft.cache
|
|
78
|
-
def _manifest_cfg(self) -> _mf.ManifestConfig:
|
|
79
|
-
return _mf.ManifestIO.load_from_file(self._logger, self._filepath, self._env_cfg)
|
|
80
|
-
|
|
81
|
-
@property
|
|
82
|
-
@_ft.cache
|
|
83
|
-
def _seeds(self) -> _s.Seeds:
|
|
84
|
-
return _s.SeedsIO.load_files(self._logger, self._filepath, self._manifest_cfg)
|
|
85
|
-
|
|
86
|
-
@property
|
|
87
|
-
@_ft.cache
|
|
88
|
-
def _model_files(self) -> dict[str, _m.QueryFile]:
|
|
89
|
-
return _m.ModelsIO.load_files(self._logger, self._filepath)
|
|
90
|
-
|
|
91
|
-
@property
|
|
92
|
-
@_ft.cache
|
|
93
|
-
def _context_func(self) -> _m.ContextFunc:
|
|
94
|
-
return _m.ModelsIO.load_context_func(self._logger, self._filepath)
|
|
95
|
-
|
|
96
|
-
@property
|
|
97
|
-
@_ft.cache
|
|
98
|
-
def _dashboards(self) -> dict[str, _d.DashboardFunction]:
|
|
99
|
-
return _d.DashboardsIO.load_files(self._logger, self._filepath)
|
|
100
|
-
|
|
101
|
-
@property
|
|
102
|
-
@_ft.cache
|
|
103
|
-
def _conn_args(self) -> _cs.ConnectionsArgs:
|
|
104
|
-
return _cs.ConnectionSetIO.load_conn_py_args(self._logger, self._env_cfg, self._manifest_cfg)
|
|
105
|
-
|
|
106
|
-
@property
|
|
107
|
-
def _conn_set(self) -> _cs.ConnectionSet:
|
|
108
|
-
if not hasattr(self, "__conn_set") or self.__conn_set is None:
|
|
109
|
-
self.__conn_set = _cs.ConnectionSetIO.load_from_file(self._logger, self._filepath, self._manifest_cfg, self._conn_args)
|
|
110
|
-
return self.__conn_set
|
|
111
|
-
|
|
112
|
-
@property
|
|
113
|
-
@_ft.cache
|
|
114
|
-
def _authenticator(self) -> _auth.Authenticator:
|
|
115
|
-
token_expiry_minutes = self._manifest_cfg.settings.get(_c.AUTH_TOKEN_EXPIRE_SETTING, 30)
|
|
116
|
-
return _auth.Authenticator(self._filepath, self._env_cfg, self._conn_args, self._conn_set, token_expiry_minutes)
|
|
117
|
-
|
|
118
|
-
@property
|
|
119
|
-
@_ft.cache
|
|
120
|
-
def _param_args(self) -> _ps.ParametersArgs:
|
|
121
|
-
return _ps.ParameterConfigsSetIO.get_param_args(self._conn_args)
|
|
122
|
-
|
|
123
|
-
@property
|
|
124
|
-
@_ft.cache
|
|
125
|
-
def _param_cfg_set(self) -> _ps.ParameterConfigsSet:
|
|
126
|
-
return _ps.ParameterConfigsSetIO.load_from_file(
|
|
127
|
-
self._logger, self._filepath, self._manifest_cfg, self._seeds, self._conn_set, self._param_args
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
@property
|
|
131
|
-
@_ft.cache
|
|
132
|
-
def _j2_env(self) -> _u.EnvironmentWithMacros:
|
|
133
|
-
return _u.EnvironmentWithMacros(self._logger, loader=_u.j2.FileSystemLoader(self._filepath))
|
|
134
|
-
|
|
135
|
-
@property
|
|
136
|
-
@_ft.cache
|
|
137
|
-
def User(self) -> type[_auth.User]:
|
|
138
|
-
"""
|
|
139
|
-
A direct reference to the User class in the `auth.py` file (if applicable). If `auth.py` does not exist, then this returns the `squirrels.User` class.
|
|
140
|
-
"""
|
|
141
|
-
return self._authenticator.user_cls
|
|
142
|
-
|
|
143
|
-
def close(self) -> None:
|
|
144
|
-
"""
|
|
145
|
-
Deliberately close any open resources within the Squirrels project, such as database connections (instead of relying on the garbage collector).
|
|
146
|
-
"""
|
|
147
|
-
if hasattr(self, "__conn_set") and self.__conn_set is not None:
|
|
148
|
-
self.__conn_set.dispose()
|
|
149
|
-
self.__conn_set = None
|
|
150
|
-
|
|
151
|
-
def __exit__(self, exc_type, exc_val, traceback):
|
|
152
|
-
self.close()
|
|
153
|
-
|
|
154
|
-
def _generate_dag(self, dataset: str, *, target_model_name: str | None = None, always_pandas: bool = False) -> _m.DAG:
|
|
155
|
-
seeds_dict = self._seeds.get_dataframes()
|
|
156
|
-
|
|
157
|
-
models_dict: dict[str, _m.Referable] = {key: _m.Seed(key, df) for key, df in seeds_dict.items()}
|
|
158
|
-
for key, val in self._model_files.items():
|
|
159
|
-
models_dict[key] = _m.Model(key, val, self._manifest_cfg, self._conn_set, self._logger, j2_env=self._j2_env)
|
|
160
|
-
models_dict[key].needs_pandas = always_pandas
|
|
161
|
-
|
|
162
|
-
dataset_config = self._manifest_cfg.datasets[dataset]
|
|
163
|
-
target_model_name = dataset_config.model if target_model_name is None else target_model_name
|
|
164
|
-
target_model = models_dict[target_model_name]
|
|
165
|
-
target_model.is_target = True
|
|
166
|
-
|
|
167
|
-
return _m.DAG(self._manifest_cfg, dataset_config, target_model, models_dict, self._logger)
|
|
168
|
-
|
|
169
|
-
def _draw_dag(self, dag: _m.DAG, output_folder: _u.Path) -> None:
|
|
170
|
-
color_map = {_m.ModelType.SEED: "green", _m.ModelType.DBVIEW: "red", _m.ModelType.FEDERATE: "skyblue"}
|
|
171
|
-
|
|
172
|
-
G = dag.to_networkx_graph()
|
|
173
|
-
|
|
174
|
-
fig, _ = _plt.subplots()
|
|
175
|
-
pos = _nx.multipartite_layout(G, subset_key="layer")
|
|
176
|
-
colors = [color_map[node[1]] for node in G.nodes(data="model_type")] # type: ignore
|
|
177
|
-
_nx.draw(G, pos=pos, node_shape='^', node_size=1000, node_color=colors, arrowsize=20)
|
|
178
|
-
|
|
179
|
-
y_values = [val[1] for val in pos.values()]
|
|
180
|
-
scale = max(y_values) - min(y_values) if len(y_values) > 0 else 0
|
|
181
|
-
label_pos = {key: (val[0], val[1]-0.002-0.1*scale) for key, val in pos.items()}
|
|
182
|
-
_nx.draw_networkx_labels(G, pos=label_pos, font_size=8)
|
|
183
|
-
|
|
184
|
-
fig.tight_layout()
|
|
185
|
-
_plt.margins(x=0.1, y=0.1)
|
|
186
|
-
fig.savefig(_u.Path(output_folder, "dag.png"))
|
|
187
|
-
_plt.close(fig)
|
|
188
|
-
|
|
189
|
-
async def _write_dataset_outputs_given_test_set(
|
|
190
|
-
self, dataset: str, select: str, test_set: str | None, runquery: bool, recurse: bool
|
|
191
|
-
) -> _t.Any | None:
|
|
192
|
-
dataset_conf = self._manifest_cfg.datasets[dataset]
|
|
193
|
-
default_test_set_conf = self._manifest_cfg.get_default_test_set(dataset)
|
|
194
|
-
if test_set in self._manifest_cfg.selection_test_sets:
|
|
195
|
-
test_set_conf = self._manifest_cfg.selection_test_sets[test_set]
|
|
196
|
-
elif test_set is None or test_set == default_test_set_conf.name:
|
|
197
|
-
test_set, test_set_conf = default_test_set_conf.name, default_test_set_conf
|
|
198
|
-
else:
|
|
199
|
-
raise _u.ConfigurationError(f"No test set named '{test_set}' was found when compiling dataset '{dataset}'. The test set must be defined if not default for dataset.")
|
|
200
|
-
|
|
201
|
-
error_msg_intro = f"Cannot compile dataset '{dataset}' with test set '{test_set}'."
|
|
202
|
-
if test_set_conf.datasets is not None and dataset not in test_set_conf.datasets:
|
|
203
|
-
raise _u.ConfigurationError(f"{error_msg_intro}\n Applicable datasets for test set '{test_set}' does not include dataset '{dataset}'.")
|
|
204
|
-
|
|
205
|
-
user_attributes = test_set_conf.user_attributes.copy()
|
|
206
|
-
selections = test_set_conf.parameters.copy()
|
|
207
|
-
username, is_internal = user_attributes.pop("username", ""), user_attributes.pop("is_internal", False)
|
|
208
|
-
if test_set_conf.is_authenticated:
|
|
209
|
-
user = self.User.Create(username, is_internal=is_internal, **user_attributes)
|
|
210
|
-
elif dataset_conf.scope == _mf.DatasetScope.PUBLIC:
|
|
211
|
-
user = None
|
|
212
|
-
else:
|
|
213
|
-
raise _u.ConfigurationError(f"{error_msg_intro}\n Non-public datasets require a test set with 'user_attributes' section defined")
|
|
214
|
-
|
|
215
|
-
if dataset_conf.scope == _mf.DatasetScope.PRIVATE and not is_internal:
|
|
216
|
-
raise _u.ConfigurationError(f"{error_msg_intro}\n Private datasets require a test set with user_attribute 'is_internal' set to true")
|
|
217
|
-
|
|
218
|
-
# always_pandas is set to True for creating CSV files from results (when runquery is True)
|
|
219
|
-
dag = self._generate_dag(dataset, target_model_name=select, always_pandas=True)
|
|
220
|
-
placeholders = await dag.execute(self._param_args, self._param_cfg_set, self._context_func, user, selections, runquery=runquery, recurse=recurse)
|
|
221
|
-
|
|
222
|
-
output_folder = _u.Path(self._filepath, _c.TARGET_FOLDER, _c.COMPILE_FOLDER, dataset, test_set)
|
|
223
|
-
if _os.path.exists(output_folder):
|
|
224
|
-
_shutil.rmtree(output_folder)
|
|
225
|
-
_os.makedirs(output_folder, exist_ok=True)
|
|
226
|
-
|
|
227
|
-
def write_placeholders() -> None:
|
|
228
|
-
output_filepath = _u.Path(output_folder, "placeholders.json")
|
|
229
|
-
with open(output_filepath, 'w') as f:
|
|
230
|
-
_json.dump(placeholders, f, indent=4)
|
|
231
|
-
|
|
232
|
-
def write_model_outputs(model: _m.Referable) -> None:
|
|
233
|
-
assert isinstance(model, _m.Model)
|
|
234
|
-
subfolder = _c.DBVIEWS_FOLDER if model.query_file.model_type == _m.ModelType.DBVIEW else _c.FEDERATES_FOLDER
|
|
235
|
-
subpath = _u.Path(output_folder, subfolder)
|
|
236
|
-
_os.makedirs(subpath, exist_ok=True)
|
|
237
|
-
if isinstance(model.compiled_query, _m.SqlModelQuery):
|
|
238
|
-
output_filepath = _u.Path(subpath, model.name+'.sql')
|
|
239
|
-
query = model.compiled_query.query
|
|
240
|
-
with open(output_filepath, 'w') as f:
|
|
241
|
-
f.write(query)
|
|
242
|
-
if runquery and isinstance(model.result, _pd.DataFrame):
|
|
243
|
-
output_filepath = _u.Path(subpath, model.name+'.csv')
|
|
244
|
-
model.result.to_csv(output_filepath, index=False)
|
|
245
|
-
|
|
246
|
-
write_placeholders()
|
|
247
|
-
all_model_names = dag.get_all_query_models()
|
|
248
|
-
coroutines = [_aio.to_thread(write_model_outputs, dag.models_dict[name]) for name in all_model_names]
|
|
249
|
-
await _aio.gather(*coroutines)
|
|
250
|
-
|
|
251
|
-
if recurse:
|
|
252
|
-
self._draw_dag(dag, output_folder)
|
|
253
|
-
|
|
254
|
-
if isinstance(dag.target_model, _m.Model) and dag.target_model.compiled_query is not None:
|
|
255
|
-
return dag.target_model.compiled_query.query # else return None
|
|
256
|
-
|
|
257
|
-
async def compile(
|
|
258
|
-
self, *, dataset: str | None = None, do_all_datasets: bool = False, selected_model: str | None = None, test_set: str | None = None,
|
|
259
|
-
do_all_test_sets: bool = False, runquery: bool = False
|
|
260
|
-
) -> None:
|
|
261
|
-
"""
|
|
262
|
-
Async method to compile the SQL templates into files in the "target/" folder. Same functionality as the "sqrl compile" CLI.
|
|
263
|
-
|
|
264
|
-
Although all arguments are "optional", the "dataset" argument is required if "do_all_datasets" argument is False.
|
|
265
|
-
|
|
266
|
-
Arguments:
|
|
267
|
-
dataset: The name of the dataset to compile. Ignored if "do_all_datasets" argument is True, but required (i.e., cannot be None) if "do_all_datasets" is False. Default is None.
|
|
268
|
-
do_all_datasets: If True, compile all datasets and ignore the "dataset" argument. Default is False.
|
|
269
|
-
selected_model: The name of the model to compile. If specified, the compiled SQL query is also printed in the terminal. If None, all models for the selected dataset are compiled. Default is None.
|
|
270
|
-
test_set: The name of the test set to compile with. If None, the default test set is used (which can vary by dataset). Ignored if `do_all_test_sets` argument is True. Default is None.
|
|
271
|
-
do_all_test_sets: Whether to compile all applicable test sets for the selected dataset(s). If True, the `test_set` argument is ignored. Default is False.
|
|
272
|
-
runquery**: Whether to run all compiled queries and save each result as a CSV file. If True and `selected_model` is specified, all upstream models of the selected model is compiled as well. Default is False.
|
|
273
|
-
"""
|
|
274
|
-
recurse = True
|
|
275
|
-
if do_all_datasets:
|
|
276
|
-
selected_models = [(dataset.name, dataset.model) for dataset in self._manifest_cfg.datasets.values()]
|
|
277
|
-
else:
|
|
278
|
-
assert isinstance(dataset, str), "argument 'dataset' must be provided a string value if argument 'do_all_datasets' is False"
|
|
279
|
-
assert dataset in self._manifest_cfg.datasets, f"dataset '{dataset}' not found in {_c.MANIFEST_FILE}"
|
|
280
|
-
if selected_model is None:
|
|
281
|
-
selected_model = self._manifest_cfg.datasets[dataset].model
|
|
282
|
-
else:
|
|
283
|
-
recurse = False
|
|
284
|
-
selected_models = [(dataset, selected_model)]
|
|
285
|
-
|
|
286
|
-
coroutines: list[_t.Coroutine] = []
|
|
287
|
-
for dataset, selected_model in selected_models:
|
|
288
|
-
if do_all_test_sets:
|
|
289
|
-
for test_set_name in self._manifest_cfg.get_applicable_test_sets(dataset):
|
|
290
|
-
coroutine = self._write_dataset_outputs_given_test_set(dataset, selected_model, test_set_name, runquery, recurse)
|
|
291
|
-
coroutines.append(coroutine)
|
|
292
|
-
|
|
293
|
-
coroutine = self._write_dataset_outputs_given_test_set(dataset, selected_model, test_set, runquery, recurse)
|
|
294
|
-
coroutines.append(coroutine)
|
|
295
|
-
|
|
296
|
-
queries = await _aio.gather(*coroutines)
|
|
297
|
-
|
|
298
|
-
print(f"Compiled successfully! See the '{_c.TARGET_FOLDER}/' folder for results.")
|
|
299
|
-
print()
|
|
300
|
-
if not recurse and len(queries) == 1 and isinstance(queries[0], str):
|
|
301
|
-
print(queries[0])
|
|
302
|
-
print()
|
|
303
|
-
|
|
304
|
-
def _permission_error(self, user: _auth.User | None, data_type: str, data_name: str, scope: str) -> PermissionError:
|
|
305
|
-
username = None if user is None else user.username
|
|
306
|
-
return PermissionError(f"User '{username}' does not have permission to access {scope} {data_type}: {data_name}")
|
|
307
|
-
|
|
308
|
-
def seed(self, name: str) -> _pd.DataFrame:
|
|
309
|
-
"""
|
|
310
|
-
Method to retrieve a seed as a pandas DataFrame given a seed name.
|
|
311
|
-
|
|
312
|
-
Arguments:
|
|
313
|
-
name: The name of the seed to retrieve
|
|
314
|
-
|
|
315
|
-
Returns:
|
|
316
|
-
The seed as a pandas DataFrame
|
|
317
|
-
"""
|
|
318
|
-
seeds_dict = self._seeds.get_dataframes()
|
|
319
|
-
try:
|
|
320
|
-
return seeds_dict[name]
|
|
321
|
-
except KeyError:
|
|
322
|
-
available_seeds = list(seeds_dict.keys())
|
|
323
|
-
raise KeyError(f"Seed '{name}' not found. Available seeds are: {available_seeds}")
|
|
324
|
-
|
|
325
|
-
async def _dataset_helper(
|
|
326
|
-
self, name: str, selections: dict[str, _t.Any], user: _auth.User | None
|
|
327
|
-
) -> _pd.DataFrame:
|
|
328
|
-
dag = self._generate_dag(name)
|
|
329
|
-
await dag.execute(self._param_args, self._param_cfg_set, self._context_func, user, dict(selections))
|
|
330
|
-
return _pd.DataFrame(dag.target_model.result)
|
|
331
|
-
|
|
332
|
-
async def dataset(
|
|
333
|
-
self, name: str, *, selections: dict[str, _t.Any] = {}, user: _auth.User | None = None
|
|
334
|
-
) -> _pd.DataFrame:
|
|
335
|
-
"""
|
|
336
|
-
Async method to retrieve a dataset as a pandas DataFrame given parameter selections.
|
|
337
|
-
|
|
338
|
-
Arguments:
|
|
339
|
-
name: The name of the dataset to retrieve.
|
|
340
|
-
selections: A dictionary of parameter selections to apply to the dataset. Optional, default is empty dictionary.
|
|
341
|
-
user: The user to use for authentication. If None, no user is used. Optional, default is None.
|
|
342
|
-
|
|
343
|
-
Returns:
|
|
344
|
-
A pandas DataFrame containing the dataset.
|
|
345
|
-
"""
|
|
346
|
-
scope = self._manifest_cfg.datasets[name].scope
|
|
347
|
-
if not self._authenticator.can_user_access_scope(user, scope):
|
|
348
|
-
raise self._permission_error(user, "dataset", name, scope.name)
|
|
349
|
-
return await self._dataset_helper(name, selections, user)
|
|
350
|
-
|
|
351
|
-
async def dashboard(
|
|
352
|
-
self, name: str, *, selections: dict[str, _t.Any] = {}, user: _auth.User | None = None, dashboard_type: _t.Type[T] = _dash.Dashboard
|
|
353
|
-
) -> T:
|
|
354
|
-
"""
|
|
355
|
-
Async method to retrieve a dashboard given parameter selections.
|
|
356
|
-
|
|
357
|
-
Arguments:
|
|
358
|
-
name: The name of the dashboard to retrieve.
|
|
359
|
-
selections: A dictionary of parameter selections to apply to the dashboard. Optional, default is empty dictionary.
|
|
360
|
-
user: The user to use for authentication. If None, no user is used. Optional, default is None.
|
|
361
|
-
dashboard_type: Return type of the method (mainly used for type hints). For instance, provide PngDashboard if you want the return type to be a PngDashboard. Optional, default is squirrels.Dashboard.
|
|
362
|
-
|
|
363
|
-
Returns:
|
|
364
|
-
The dashboard type specified by the "dashboard_type" argument.
|
|
365
|
-
"""
|
|
366
|
-
scope = self._manifest_cfg.dashboards[name].scope
|
|
367
|
-
if not self._authenticator.can_user_access_scope(user, scope):
|
|
368
|
-
raise self._permission_error(user, "dashboard", name, scope.name)
|
|
369
|
-
|
|
370
|
-
async def get_dataset(dataset_name: str, fixed_params: dict[str, _t.Any]) -> _pd.DataFrame:
|
|
371
|
-
final_selections = {**selections, **fixed_params}
|
|
372
|
-
return await self._dataset_helper(dataset_name, final_selections, user)
|
|
373
|
-
|
|
374
|
-
args = _d.DashboardArgs(self._param_args.proj_vars, self._param_args.env_vars, get_dataset)
|
|
375
|
-
try:
|
|
376
|
-
return await self._dashboards[name].get_dashboard(args, dashboard_type=dashboard_type)
|
|
377
|
-
except KeyError:
|
|
378
|
-
raise KeyError(f"No dashboard file found for: {name}")
|
squirrels/user_base.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import typing as _t, dataclasses as _dc
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
@_dc.dataclass
|
|
5
|
-
class User:
|
|
6
|
-
"""
|
|
7
|
-
Base class for extending the custom User model class
|
|
8
|
-
|
|
9
|
-
Attributes:
|
|
10
|
-
username: The identifier for the user
|
|
11
|
-
is_internal: Setting this to True lets the user access "private" datasets
|
|
12
|
-
"""
|
|
13
|
-
username: str
|
|
14
|
-
is_internal: bool
|
|
15
|
-
|
|
16
|
-
def __hash__(self) -> int:
|
|
17
|
-
return hash(self.username)
|
|
18
|
-
|
|
19
|
-
def set_attributes(self, **kwargs) -> None:
|
|
20
|
-
"""
|
|
21
|
-
Can be overwritten in "auth.py" to introduce custom attributes. Does nothing by default
|
|
22
|
-
"""
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
@classmethod
|
|
26
|
-
def Create(cls, username: str, *, is_internal: bool = False, **kwargs):
|
|
27
|
-
"""
|
|
28
|
-
Creates an instance of the User class and calls the `set_attributes` method on the new instance.
|
|
29
|
-
|
|
30
|
-
We may overwrite the `set_attributes` method in `auth.py`. We do not overwrite the constructor to guarantee that `username` and `is_internal` are always set.
|
|
31
|
-
|
|
32
|
-
Arguments:
|
|
33
|
-
username: The identifier for the user
|
|
34
|
-
is_internal: Setting this to True lets the user access "private" datasets. Default is False
|
|
35
|
-
"""
|
|
36
|
-
user = cls(username, is_internal)
|
|
37
|
-
user.set_attributes(**kwargs)
|
|
38
|
-
return user
|
|
39
|
-
|
|
40
|
-
@classmethod
|
|
41
|
-
def _FromDict(cls, user_obj_as_dict: dict[str, _t.Any]):
|
|
42
|
-
username, is_internal = user_obj_as_dict["username"], user_obj_as_dict["is_internal"]
|
|
43
|
-
user = cls(username=username, is_internal=is_internal)
|
|
44
|
-
for key, val in user_obj_as_dict.items():
|
|
45
|
-
setattr(user, key, val)
|
|
46
|
-
return user
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@_dc.dataclass
|
|
50
|
-
class WrongPassword:
|
|
51
|
-
"""
|
|
52
|
-
Return this object if the username was found but the password was incorrect
|
|
53
|
-
|
|
54
|
-
This ensures that if the username exists as a real user, we won't continue to use the env.yml file to authenticate
|
|
55
|
-
"""
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: squirrels
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: Squirrels - API Framework for Data Analytics
|
|
5
|
-
Home-page: https://squirrels-analytics.github.io
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Author: Tim Huang
|
|
8
|
-
Author-email: tim.yuting@hotmail.com
|
|
9
|
-
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Classifier: Typing :: Typed
|
|
19
|
-
Provides-Extra: duckdb
|
|
20
|
-
Requires-Dist: cachetools (>=5.3.2,<6.0.0)
|
|
21
|
-
Requires-Dist: duckdb-engine (>=0.13.0,<1.0.0) ; extra == "duckdb"
|
|
22
|
-
Requires-Dist: fastapi (>=0.112.1,<0.113.0)
|
|
23
|
-
Requires-Dist: gitpython (>=3.1.41,<4.0.0)
|
|
24
|
-
Requires-Dist: inquirer (>=3.2.1,<4.0.0)
|
|
25
|
-
Requires-Dist: jinja2 (>=3.1.3,<4.0.0)
|
|
26
|
-
Requires-Dist: matplotlib (>=3.8.3,<4.0.0)
|
|
27
|
-
Requires-Dist: networkx (>=3.2.1,<4.0.0)
|
|
28
|
-
Requires-Dist: pandas (>=2.1.4,<3.0.0)
|
|
29
|
-
Requires-Dist: pydantic (>=2.8.2,<3.0.0)
|
|
30
|
-
Requires-Dist: pyjwt (>=2.8.0,<3.0.0)
|
|
31
|
-
Requires-Dist: python-multipart (>=0.0.9,<0.0.10)
|
|
32
|
-
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
33
|
-
Requires-Dist: sqlalchemy (>=2.0.25,<3.0.0)
|
|
34
|
-
Requires-Dist: uvicorn (>=0.30.6,<0.31.0)
|
|
35
|
-
Project-URL: Documentation, https://squirrels-analytics.github.io
|
|
36
|
-
Project-URL: Repository, https://github.com/squirrels-analytics/squirrels
|
|
37
|
-
Description-Content-Type: text/markdown
|
|
38
|
-
|
|
39
|
-
# Squirrels
|
|
40
|
-
|
|
41
|
-
Squirrels is an API framework that lets you create REST APIs for dynamic data analytics!
|
|
42
|
-
|
|
43
|
-
**Documentation**: <a href="https://squirrels-analytics.github.io/" target="_blank">https://squirrels-analytics.github.io/</a>
|
|
44
|
-
|
|
45
|
-
**Source Code**: <a href="https://github.com/squirrels-analytics/squirrels" target="_blank">https://github.com/squirrels-analytics/squirrels</a>
|
|
46
|
-
|
|
47
|
-
## Table of Contents
|
|
48
|
-
|
|
49
|
-
- [Main Features](#main-features)
|
|
50
|
-
- [License](#license)
|
|
51
|
-
- [Contributing to squirrels](#contributing-to-squirrels)
|
|
52
|
-
- [Setup](#setup)
|
|
53
|
-
- [Testing](#testing)
|
|
54
|
-
- [Project Structure](#project-structure)
|
|
55
|
-
|
|
56
|
-
## Main Features
|
|
57
|
-
|
|
58
|
-
Here are a few of the things that squirrels can do:
|
|
59
|
-
|
|
60
|
-
- Connect to any database by specifying its SQLAlchemy url (in `squirrels.yml`) or by using its native connector library in python (in `connections.py`).
|
|
61
|
-
- Configure API routes for datasets (in `squirrels.yml`) without writing code.
|
|
62
|
-
- Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
|
|
63
|
-
- Use Jinja SQL templates (just like dbt!) or python functions (that return a pandas dataframe) to define dynamic query logic based on parameter selections.
|
|
64
|
-
- Query multiple databases and join the results together in a final view in one API endpoint/dataset!
|
|
65
|
-
- Test your API endpoints with an interactive UI or by a command line that generates rendered sql queries and results (for a given set of parameter selections).
|
|
66
|
-
- Define authentication logic (in `auth.py`) and authorize privacy scope per dataset (in `squirrels.yml`). The user's attributes can even be used in your query logic!
|
|
67
|
-
|
|
68
|
-
## License
|
|
69
|
-
|
|
70
|
-
Squirrels is released under the Apache 2.0 license.
|
|
71
|
-
|
|
72
|
-
See the file LICENSE for more details.
|
|
73
|
-
|
|
74
|
-
## Contributing to squirrels
|
|
75
|
-
|
|
76
|
-
The sections below describe how to set up your local environment for squirrels development and run unit tests. A high level overview of the project structure is also provided.
|
|
77
|
-
|
|
78
|
-
### Setup
|
|
79
|
-
|
|
80
|
-
This project requires python version 3.10 or above to be installed. It also uses the python build tool `poetry`. Information on setting up poetry can be found at: https://python-poetry.org/docs/.
|
|
81
|
-
|
|
82
|
-
Then, to install all dependencies, run:
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
poetry install
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
And activate the virtual environment created by poetry with:
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
-
poetry shell
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
To confirm that the setup worked, run the following to show the help page for all squirrels CLI commands:
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
sqrl -h
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
You can enter `exit` to exit the virtual environment shell. You can also run `poetry run sqrl -h` to run squirrels commands without activating the virtual environment.
|
|
101
|
-
|
|
102
|
-
### Testing
|
|
103
|
-
|
|
104
|
-
In poetry's virtual environment, run `pytest`.
|
|
105
|
-
|
|
106
|
-
### Project Structure
|
|
107
|
-
|
|
108
|
-
From the root of the git repo, the source code can be found in the `squirrels` folder and unit tests can be found in the `tests` folder.
|
|
109
|
-
|
|
110
|
-
To understand what a specific squirrels command is doing, start from the `_command_line.py` file as your entry point.
|
|
111
|
-
|
|
112
|
-
The library version is maintained in both the `pyproject.toml` and the `squirrels/_version.py` files.
|
|
113
|
-
|
|
114
|
-
When a user initializes a squirrels project using `sqrl init`, the files are copied from the `squirrels/package_data/base_project` folder. The contents in the `database` subfolder were constructed from the scripts in the `database_elt` folder.
|
|
115
|
-
|
|
116
|
-
For the Squirrels UI activated by `sqrl run`, the HTML, CSS, and Javascript files can be found in the `static` and `templates` subfolders of `squirrels/package_data`. The CSS and Javascript files are minified and built from the source files in this project: https://github.com/squirrels-analytics/squirrels-testing-ui.
|
|
117
|
-
|
squirrels-0.4.0.dist-info/RECORD
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
squirrels/__init__.py,sha256=EbovxLpc9QcDooaME7887ur1lSjouWS36sealX_QOoo,946
|
|
2
|
-
squirrels/_api_response_models.py,sha256=dHATCC80lJyQNRHhnCbG1uMWWOinxPfkq7YIG4GqD1I,9786
|
|
3
|
-
squirrels/_api_server.py,sha256=GLn31Ki7gSxvYIOnVCp6wzZkuDT1MjHKnvhBEsasnB0,30902
|
|
4
|
-
squirrels/_authenticator.py,sha256=21BADaTQ-ZfnTZMXTfNYiZFntTdm8QfRsh7X8tFJ3mA,4038
|
|
5
|
-
squirrels/_command_line.py,sha256=UzD3B6JJY9rBzb2WBCNsOwUkBWaSFz0zZnsaHATp7qU,8115
|
|
6
|
-
squirrels/_connection_set.py,sha256=oH5qp6PZwkLANCUEKkjKASe80V8SLK98-ZBaPtbUCpk,2867
|
|
7
|
-
squirrels/_constants.py,sha256=k0KF2VIIvzeyfR1ycAwDR8uHZYfQJSBho90GbTRnvIM,2983
|
|
8
|
-
squirrels/_dashboards_io.py,sha256=Qf2ow2hI57NRGtNB0rxtXy7XoNVxkvKuAFnVRxslLqY,2417
|
|
9
|
-
squirrels/_environcfg.py,sha256=rAgDpJ4-h4yzFEbSG6gKh2XbQO3pAULB6Vo-66QAYp8,2875
|
|
10
|
-
squirrels/_initializer.py,sha256=OzPEnb3uN_lf4o5bCA_r9I9D3XHD2-v-27d6trEjMjE,9809
|
|
11
|
-
squirrels/_manifest.py,sha256=9DpDidPbKJ5GvXeW31fcg4nL7tRu8hqLmkBRbimep24,7626
|
|
12
|
-
squirrels/_models.py,sha256=1Th1Vl_a1ddxjAGHCwq5sqx7RQsi5wYyyNUpRYvTx18,23641
|
|
13
|
-
squirrels/_package_loader.py,sha256=DugsBkmm6OP8KPcJxmtuAMFnhf7t43rCRPjXXSwHXGs,1041
|
|
14
|
-
squirrels/_parameter_configs.py,sha256=NF7ChgkAVlsTFrL5QkhEbsIbohcSFfuu4IvhxDRao0s,24731
|
|
15
|
-
squirrels/_parameter_sets.py,sha256=YwyDp-8_VuKUPZ3oS9JWC9ynFlWy2sbiRDSXTGDBc4c,9702
|
|
16
|
-
squirrels/_py_module.py,sha256=w4AYGrks121_osRxL9F8hiX0ZuPXC6krnJB8cOqhWBI,2564
|
|
17
|
-
squirrels/_seeds.py,sha256=gLMlDphnwQKzSWLjpQBGYNPy5XtS0xu2PReqkk55na4,1462
|
|
18
|
-
squirrels/_utils.py,sha256=ykV3hjQTxG9-blmAPxCfd78bJXR1YfH_rQdUBsTaRJM,8829
|
|
19
|
-
squirrels/_version.py,sha256=vsjw-CsgmLFBE8OTwqlzxn_6L-jeLXsXgAlWcncGcJs,105
|
|
20
|
-
squirrels/arguments/init_time_args.py,sha256=Rc6n9vVZ7p4WpbyAvryG-tGb4vRQKrRyfRdzy3w5WTc,925
|
|
21
|
-
squirrels/arguments/run_time_args.py,sha256=Dk3H-iPlsy4i3swAVs8Uk6yyQPko7CEb9acfqHnzcvc,7031
|
|
22
|
-
squirrels/dashboards.py,sha256=NeLcBxfIOJV-hlBllQ4mB4sSHFxAF-odjyiioUF19z4,1978
|
|
23
|
-
squirrels/data_sources.py,sha256=qPtH5JFtCd8axjrXa40UlsZHU3geWTzxIkw3UAjj8lA,30019
|
|
24
|
-
squirrels/dateutils.py,sha256=dvbXf9-VEt8PhDnY3rd6kjqFgVkWPOhsfsq8cq9kHdE,16734
|
|
25
|
-
squirrels/package_data/assets/favicon.ico,sha256=FZx26dn50cp0rgYdyBptJJob2TTVNiY0NZ-MeeL_uY0,61022
|
|
26
|
-
squirrels/package_data/assets/index.css,sha256=BJVYY2dse9-vhASMsls6j5gpf33Z9tCzR1VyX1rWkH0,12181
|
|
27
|
-
squirrels/package_data/assets/index.js,sha256=TJDFw8B5OCGdvU9pFbf2BXKVOmtDNMTdSdCY4xK2QUM,265433
|
|
28
|
-
squirrels/package_data/base_project/.gitignore,sha256=x7VR530sbdKJU83mF9OGD8b-nI92MbWqUnMPyB0Xz4U,137
|
|
29
|
-
squirrels/package_data/base_project/assets/expenses.db,sha256=47ichjBqC25th7B0G5rz_yYJ4V3uQeF3cnttekQYa68,28672
|
|
30
|
-
squirrels/package_data/base_project/assets/weather.db,sha256=dsHPO36gQdZ4ULAA726Hg3jp8a1dCdig1DhrGg8wTeg,86016
|
|
31
|
-
squirrels/package_data/base_project/connections.yml,sha256=-rR6kjWl8FyHTzf2RnPqR1JEfhpX_pBrOJUxf5grRQY,259
|
|
32
|
-
squirrels/package_data/base_project/dashboards/dashboard_example.py,sha256=pAvxMdrKT_-7cFcRH7qqVIaQs1oFXCg5PEwiwCi0xdY,1781
|
|
33
|
-
squirrels/package_data/base_project/dashboards.yml,sha256=UEa9YI8ymi3tH15357oCWF5ZynNmyG2yu8B2F5Cr6d4,158
|
|
34
|
-
squirrels/package_data/base_project/docker/.dockerignore,sha256=0BJ26C2eyVJPwbW1e4cmBg1TPADio4tqakd3hdojsq8,176
|
|
35
|
-
squirrels/package_data/base_project/docker/Dockerfile,sha256=Sz4g9liLD40ci2iG2ohTjth3kvmNaCAo2LWZl6mCGR8,474
|
|
36
|
-
squirrels/package_data/base_project/docker/compose.yml,sha256=u-P7Zh7IRfYAcebx4zE119OWn2m_TugGMh1IgPDl_2g,111
|
|
37
|
-
squirrels/package_data/base_project/env.yml,sha256=5BUtjX8JEkVbtp7Xj-V-KcBkBJ6GEV1n05vasDSEYxw,943
|
|
38
|
-
squirrels/package_data/base_project/models/dbviews/dbview_example.py,sha256=Kx32kJFqmwJSu519nqls0cDuN8h_YnuyxAcD_eLeNWk,2036
|
|
39
|
-
squirrels/package_data/base_project/models/dbviews/dbview_example.sql,sha256=BJGTU25fdwCDXSEMFvXjn5Lnfi_Gu0wY2nir-d5VvUo,747
|
|
40
|
-
squirrels/package_data/base_project/models/federates/federate_example.py,sha256=rLPA8UTpQN_vbCQcID22QWfI1Mu7s_Xly76medWq7DQ,712
|
|
41
|
-
squirrels/package_data/base_project/models/federates/federate_example.sql,sha256=bHK1k8Q0uc2tZI21cw1y-w1w6Ah9dKRHRDwWVd80_Tk,75
|
|
42
|
-
squirrels/package_data/base_project/parameters.yml,sha256=UUZoip-_X9axROZ9v_j-CkrUS-0x5lI0Kq02cRAkym8,6397
|
|
43
|
-
squirrels/package_data/base_project/pyconfigs/auth.py,sha256=GyV84-lSfn0uGSP_kjvEFIHJCs3II3m97cYBYYYoQSc,1553
|
|
44
|
-
squirrels/package_data/base_project/pyconfigs/connections.py,sha256=yb46igO8qH64tYczf0SJDtqyqxq5CANutzfKMtNJ96k,753
|
|
45
|
-
squirrels/package_data/base_project/pyconfigs/context.py,sha256=tXQvdiHtESiHJup3yAkaKIuAEgFnV8H-Bz35VlqxIE0,3977
|
|
46
|
-
squirrels/package_data/base_project/pyconfigs/parameters.py,sha256=5liqj98vLUus0kQGo4ae29ncr6zUpopNTXPhcnE3E5M,4940
|
|
47
|
-
squirrels/package_data/base_project/seeds/seed_categories.csv,sha256=jppjf1nOIxy7-bi5lJn5CVqmnLfJHHq0ABgp6UqbXnw,104
|
|
48
|
-
squirrels/package_data/base_project/seeds/seed_subcategories.csv,sha256=aZkBJ6KioyYjEwRunYiA8ec0X1ygiEmLRVicJecFzfY,327
|
|
49
|
-
squirrels/package_data/base_project/squirrels.yml.j2,sha256=OhYjBjDLRtCWhSiAssslGrX5MsHQO8jC6HKz_NFnR_w,3076
|
|
50
|
-
squirrels/package_data/base_project/tmp/.gitignore,sha256=XImoqcWvJY0C0L_TWCx1ljvqU7qh9fUTJmK4ACCmNFI,13
|
|
51
|
-
squirrels/package_data/templates/index.html,sha256=41kyznAFLnE41IdPDCFdIvYLe7MgbIGrPOugk4YNXkQ,617
|
|
52
|
-
squirrels/parameter_options.py,sha256=cWYKNoBUopHq6VfaeBu-nN2V0_IY3OgYpmYhKODNCew,16956
|
|
53
|
-
squirrels/parameters.py,sha256=aSciLRxjY_-07VAv9mGxFQnhtjnuk4XT43MhZwVfoFY,56018
|
|
54
|
-
squirrels/project.py,sha256=LxQbSOjEWdq5ZCj2u-03qMX1WJqKBk1_GPb3nCmsU0U,19256
|
|
55
|
-
squirrels/user_base.py,sha256=gFtdi9K6RJovfKqRU-b-L1bOSdgNYQCQgfez3hz9EOo,1852
|
|
56
|
-
squirrels-0.4.0.dist-info/LICENSE,sha256=CItkBKs5m4J5jhkjXoW7IAnFyOC86x4hyOATpczUMmM,11338
|
|
57
|
-
squirrels-0.4.0.dist-info/METADATA,sha256=33BPbXUJCx3RAqa_yYWYAQw_g5APL2FaA7FdiQI_7rI,5217
|
|
58
|
-
squirrels-0.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
59
|
-
squirrels-0.4.0.dist-info/entry_points.txt,sha256=mYQRuGxbg8X82hjNRJuWiON4S6kE5CPvmXmxNPtYTbg,92
|
|
60
|
-
squirrels-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|