squirrels 0.1.1.post1__py3-none-any.whl → 0.2.0.dev0__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.
- squirrels/__init__.py +10 -16
- squirrels/_api_server.py +234 -80
- squirrels/_authenticator.py +84 -0
- squirrels/_command_line.py +60 -72
- squirrels/_connection_set.py +96 -0
- squirrels/_constants.py +114 -33
- squirrels/_environcfg.py +77 -0
- squirrels/_initializer.py +126 -67
- squirrels/_manifest.py +195 -168
- squirrels/_models.py +495 -0
- squirrels/_package_loader.py +26 -0
- squirrels/_parameter_configs.py +401 -0
- squirrels/_parameter_sets.py +188 -0
- squirrels/_py_module.py +60 -0
- squirrels/_timer.py +36 -0
- squirrels/_utils.py +81 -49
- squirrels/_version.py +2 -2
- squirrels/arguments/init_time_args.py +32 -0
- squirrels/arguments/run_time_args.py +82 -0
- squirrels/data_sources.py +380 -155
- squirrels/dateutils.py +86 -57
- squirrels/package_data/base_project/Dockerfile +15 -0
- squirrels/package_data/base_project/connections.yml +7 -0
- squirrels/package_data/base_project/database/{sample_database.db → expenses.db} +0 -0
- squirrels/package_data/base_project/environcfg.yml +29 -0
- squirrels/package_data/base_project/ignores/.dockerignore +8 -0
- squirrels/package_data/base_project/ignores/.gitignore +7 -0
- squirrels/package_data/base_project/models/dbviews/database_view1.py +36 -0
- squirrels/package_data/base_project/models/dbviews/database_view1.sql +15 -0
- squirrels/package_data/base_project/models/federates/dataset_example.py +20 -0
- squirrels/package_data/base_project/models/federates/dataset_example.sql +3 -0
- squirrels/package_data/base_project/parameters.yml +109 -0
- squirrels/package_data/base_project/pyconfigs/auth.py +47 -0
- squirrels/package_data/base_project/pyconfigs/connections.py +28 -0
- squirrels/package_data/base_project/pyconfigs/context.py +45 -0
- squirrels/package_data/base_project/pyconfigs/parameters.py +55 -0
- squirrels/package_data/base_project/seeds/mocks/category.csv +3 -0
- squirrels/package_data/base_project/seeds/mocks/max_filter.csv +2 -0
- squirrels/package_data/base_project/seeds/mocks/subcategory.csv +6 -0
- squirrels/package_data/base_project/squirrels.yml.j2 +57 -0
- squirrels/package_data/base_project/tmp/.gitignore +2 -0
- squirrels/package_data/static/script.js +159 -63
- squirrels/package_data/static/style.css +79 -15
- squirrels/package_data/static/widgets.js +133 -0
- squirrels/package_data/templates/index.html +65 -23
- squirrels/package_data/templates/index2.html +22 -0
- squirrels/parameter_options.py +216 -119
- squirrels/parameters.py +407 -478
- squirrels/user_base.py +58 -0
- squirrels-0.2.0.dev0.dist-info/METADATA +126 -0
- squirrels-0.2.0.dev0.dist-info/RECORD +56 -0
- {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/WHEEL +1 -2
- squirrels-0.2.0.dev0.dist-info/entry_points.txt +3 -0
- squirrels/_credentials_manager.py +0 -87
- squirrels/_module_loader.py +0 -37
- squirrels/_parameter_set.py +0 -151
- squirrels/_renderer.py +0 -286
- squirrels/_timed_imports.py +0 -37
- squirrels/connection_set.py +0 -126
- squirrels/package_data/base_project/.gitignore +0 -4
- squirrels/package_data/base_project/connections.py +0 -20
- squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -22
- squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -29
- squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -12
- squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -11
- squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -3
- squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -47
- squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -9
- squirrels/package_data/base_project/squirrels.yaml +0 -22
- squirrels-0.1.1.post1.dist-info/METADATA +0 -67
- squirrels-0.1.1.post1.dist-info/RECORD +0 -40
- squirrels-0.1.1.post1.dist-info/entry_points.txt +0 -2
- squirrels-0.1.1.post1.dist-info/top_level.txt +0 -1
- {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/LICENSE +0 -0
squirrels/user_base.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class User:
|
|
7
|
+
"""
|
|
8
|
+
Base class for extending the custom User model class
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
username: The identifier for the user
|
|
12
|
+
is_internal: Setting this to True lets the user access "private" datasets
|
|
13
|
+
"""
|
|
14
|
+
username: str
|
|
15
|
+
is_internal: bool
|
|
16
|
+
|
|
17
|
+
def __init__(self, username: str, *, is_internal: bool = False, **kwargs):
|
|
18
|
+
"""
|
|
19
|
+
Constructor for the User base class
|
|
20
|
+
|
|
21
|
+
Parameters:
|
|
22
|
+
...see Attributes of User
|
|
23
|
+
"""
|
|
24
|
+
self.username = username
|
|
25
|
+
self.is_internal = is_internal
|
|
26
|
+
|
|
27
|
+
def __hash__(self) -> int:
|
|
28
|
+
return hash(self.username)
|
|
29
|
+
|
|
30
|
+
def set_attributes(self, user_dict: dict[str, Any]) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Can be overwritten in "auth.py" to introduce custom attributes. Does nothing by default
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def with_attributes(self, user_dict: dict[str, Any]):
|
|
37
|
+
self.set_attributes(user_dict)
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def _FromDict(cls, user_dict: dict[str, Any]):
|
|
42
|
+
user = cls(username="TBA")
|
|
43
|
+
for key, val in user_dict.items():
|
|
44
|
+
setattr(user, key, val)
|
|
45
|
+
return user
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class WrongPassword:
|
|
50
|
+
"""
|
|
51
|
+
Return this object if the username was found but the password was incorrect
|
|
52
|
+
|
|
53
|
+
This ensures that if the username exists as a real user, we won't continue to use the environcfg.yml file to authenticate
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
username: The identifier for the user
|
|
57
|
+
"""
|
|
58
|
+
username: str
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: squirrels
|
|
3
|
+
Version: 0.2.0.dev0
|
|
4
|
+
Summary: Squirrels - API Framework for Data Analytics
|
|
5
|
+
Home-page: https://squirrels-nest.github.io/squirrels-docs/
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Tim Huang
|
|
8
|
+
Author-email: tim.yuting@hotmail.com
|
|
9
|
+
Requires-Python: >=3.9,<4.0
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Dist: cachetools (>=5.3.2,<6.0.0)
|
|
21
|
+
Requires-Dist: cryptography (>=41.0.7,<42.0.0)
|
|
22
|
+
Requires-Dist: fastapi (>=0.104.1,<0.105.0)
|
|
23
|
+
Requires-Dist: gitpython (>=3.1.40,<4.0.0)
|
|
24
|
+
Requires-Dist: inquirer (>=3.1.4,<4.0.0)
|
|
25
|
+
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
|
26
|
+
Requires-Dist: pandas (>=2.1.4,<3.0.0)
|
|
27
|
+
Requires-Dist: python-jose (>=3.3.0,<4.0.0)
|
|
28
|
+
Requires-Dist: python-multipart (>=0.0.6,<0.0.7)
|
|
29
|
+
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
30
|
+
Requires-Dist: sqlalchemy (>=2.0.23,<3.0.0)
|
|
31
|
+
Requires-Dist: uvicorn (>=0.24.0.post1,<0.25.0)
|
|
32
|
+
Project-URL: Documentation, https://squirrels-nest.github.io/squirrels-docs/
|
|
33
|
+
Project-URL: Repository, https://github.com/squirrels-nest/squirrels
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Squirrels
|
|
37
|
+
|
|
38
|
+
Squirrels is an API framework that lets you create REST APIs for dynamic data analytics!
|
|
39
|
+
|
|
40
|
+
**Documentation**: <a href="https://squirrels-nest.github.io/squirrels-docs" target="_blank">https://squirrels-nest.github.io/squirrels-docs</a>
|
|
41
|
+
|
|
42
|
+
**Source Code**: <a href="https://github.com/squirrels-nest/squirrels" target="_blank">https://github.com/squirrels-nest/squirrels</a>
|
|
43
|
+
|
|
44
|
+
## Table of Contents
|
|
45
|
+
|
|
46
|
+
- [Main Features](#main-features)
|
|
47
|
+
- [License](#license)
|
|
48
|
+
- [Contributing to squirrels](#contributing-to-squirrels)
|
|
49
|
+
- [Setup](#setup)
|
|
50
|
+
- [Testing](#testing)
|
|
51
|
+
- [Project Structure](#project-structure)
|
|
52
|
+
|
|
53
|
+
## Main Features
|
|
54
|
+
|
|
55
|
+
Here are a few of the things that squirrels can do:
|
|
56
|
+
|
|
57
|
+
- Connect to any database by specifying its sqlalchemy url without code (in `squirrels.yml`) or by using its native connector library in python (in `connections.py`).
|
|
58
|
+
- Configure API routes without code (in `squirrels.yml`) for all datasets.
|
|
59
|
+
- Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
|
|
60
|
+
- Use Jinja SQL templates (just like dbt!) or python functions (that return a pandas dataframe) to define dynamic query logic based on parameter selections.
|
|
61
|
+
- Query multiple databases and join the results together in a final view in one API endpoint/dataset!
|
|
62
|
+
- 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).
|
|
63
|
+
- 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!
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
Squirrels is released under the MIT license.
|
|
68
|
+
|
|
69
|
+
See the file LICENSE for more details.
|
|
70
|
+
|
|
71
|
+
## Contributing to squirrels
|
|
72
|
+
|
|
73
|
+
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.
|
|
74
|
+
|
|
75
|
+
### Setup
|
|
76
|
+
|
|
77
|
+
This project requires python version 3.9 or above to be installed. It also uses the python build tool `poetry` which can be installed as follows.
|
|
78
|
+
|
|
79
|
+
**Linux, MacOS, Windows (WSL):**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
curl -sSL https://install.python-poetry.org | python3 -
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Windows (Powershell):**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then, to install all dependencies, run:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
poetry install
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
And activate the virtual environment created by poetry with:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
poetry shell
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
To confirm that the setup worked, run the following to show the help page for all squirrels CLI commands:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
squirrels -h
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
You can enter `exit` to exit the virtual environment shell. You can also run `poetry run squirrels -h` to run squirrels commands without activating the virtual environment.
|
|
110
|
+
|
|
111
|
+
### Testing
|
|
112
|
+
|
|
113
|
+
In poetry's virtual environment, run `pytest`.
|
|
114
|
+
|
|
115
|
+
### Project Structure
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
To understand what a specific squirrels command is doing, start from the `_command_line.py` file as your entry point.
|
|
120
|
+
|
|
121
|
+
The library version is maintained in both the `pyproject.toml` and the `squirrels/__init__.py` files.
|
|
122
|
+
|
|
123
|
+
When a user initializes a squirrels project using `squirrels 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.
|
|
124
|
+
|
|
125
|
+
For the Squirrels UI activated by `squirrels run`, the HTML, CSS, and Javascript files can be found in the `static` and `templates` subfolders of `squirrels/package_data`.
|
|
126
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
squirrels/__init__.py,sha256=SpcfLD5xBY5xxbf00E1Mpo7Ty57P4fm1yXBRaX0S2Rw,756
|
|
2
|
+
squirrels/_api_server.py,sha256=bg6dslizgzNerVUl7uxSL2fsl7ldItWHIeXzDxZ1rQA,14552
|
|
3
|
+
squirrels/_authenticator.py,sha256=77hcm6vPsxIboaa1JaglJ3_Rnh24zYvMjy-OYCtJaos,3569
|
|
4
|
+
squirrels/_command_line.py,sha256=i6e9A2bHRYqiVXNunr02wIWUWOV7uTzXEHhwaW420rM,5727
|
|
5
|
+
squirrels/_connection_set.py,sha256=tg27ZgHnRbFssVltaaSlmtc7rHyu1jz3EJ2aoMkgvMc,3410
|
|
6
|
+
squirrels/_constants.py,sha256=_sUSLmd-cGW6k0tUmdx23bfHKOesK19zk_1U3Ymdm8M,3944
|
|
7
|
+
squirrels/_environcfg.py,sha256=YR2mJ9s4wSxsxdJmd17Gk2WjslsBkpvJYnLx4RYgWPU,2743
|
|
8
|
+
squirrels/_initializer.py,sha256=bGEiKQ_4q1oXpV11HkwzgY8kdHkRl0L5gcE_wbo31wI,7812
|
|
9
|
+
squirrels/_manifest.py,sha256=R9SZNCcAJlBqNW2gkcvKzqFV5LUs5m5RN2tDlMpfP3M,7757
|
|
10
|
+
squirrels/_models.py,sha256=3tfx4oDfvI46tjXrv2WqcWGq2TEdVRWNnBPWOX1jUSo,20641
|
|
11
|
+
squirrels/_package_loader.py,sha256=-EZ8qsmp6QEdZyHUKYi5e4OPOx3EnL2rxr1IYe1NwEI,1020
|
|
12
|
+
squirrels/_parameter_configs.py,sha256=vFoziu8sP_EA03Kazr0-WpQK3LalHud-zUzy5TzD_AY,16925
|
|
13
|
+
squirrels/_parameter_sets.py,sha256=3NPQmB-1gqbmfBoMwCDWDUCChNa4zNZ74zihXA-mUYM,9041
|
|
14
|
+
squirrels/_py_module.py,sha256=dFoOt53WUtDdZHR-ePbLTnQSNTqe0k_AjT-BOOmpAHc,2559
|
|
15
|
+
squirrels/_timer.py,sha256=gwsp5pDjOJZoAGv8LiCsr7B3av5EYeGGCVHUzWUfiiQ,1398
|
|
16
|
+
squirrels/_utils.py,sha256=7JA5ZOJMkTAktT4r9QbOCwBrtbcsqtFIdiE-_Th09RA,5262
|
|
17
|
+
squirrels/_version.py,sha256=ySOetuKYm0mnm1J9-D3IcEZkf4kFrP8SVDKX97awBxo,109
|
|
18
|
+
squirrels/arguments/init_time_args.py,sha256=EiK5O4JSLMWJLbcrzwOP8UTLCWIb4GRdLNAV_WmQu94,706
|
|
19
|
+
squirrels/arguments/run_time_args.py,sha256=ZmQtX06FeuG3s5yo6BzdRonEgohVy0XQcDOfHl9THdI,2728
|
|
20
|
+
squirrels/data_sources.py,sha256=0BnCXcZg-IjccCwNialPXnFl-o8qbt9j_M3fjGUcC0U,25557
|
|
21
|
+
squirrels/dateutils.py,sha256=o-jQ0p3wGKomzFVn59qDdaadLtzKPeyXyKSfm0GMQPs,16487
|
|
22
|
+
squirrels/package_data/base_project/Dockerfile,sha256=kJHFKeQ7V5rHK5twGBPSFGkHIPvakpYpiCujhcO_-iU,484
|
|
23
|
+
squirrels/package_data/base_project/connections.yml,sha256=0xAnpOKoEsDVWjsznh8y9YMBxSfDtDnmLQbNVq34GdU,299
|
|
24
|
+
squirrels/package_data/base_project/database/expenses.db,sha256=MzNEY2WFJBGZJiyCH-NGgj8epg0idbMH-fdOnQIFcqQ,36864
|
|
25
|
+
squirrels/package_data/base_project/database/seattle_weather.db,sha256=PldWB7PLY_ltY-OWxN9ALcFEdiVDaP3Kef0I2k7WPso,188416
|
|
26
|
+
squirrels/package_data/base_project/environcfg.yml,sha256=c_5YRPMFfHzNNpqBnjpdxeKmR1qNS0jQL1buINPWsd4,852
|
|
27
|
+
squirrels/package_data/base_project/ignores/.dockerignore,sha256=sY6T-_UtTVw1jFBOfiMQ8XFS9191YqEotxYPCEsdNJ4,90
|
|
28
|
+
squirrels/package_data/base_project/ignores/.gitignore,sha256=lY0WxUkktLwaEhtyh5ynLTlg1ucigzMV7x6rifn734g,85
|
|
29
|
+
squirrels/package_data/base_project/models/dbviews/database_view1.py,sha256=1ZMbuwi4k-n-_7Eyavo43w0SO3aX_X--BmO6Sfbw2nw,1750
|
|
30
|
+
squirrels/package_data/base_project/models/dbviews/database_view1.sql,sha256=uTo-SoRn5TgNQ3R_sqcxCoNDJg1w7TBW44Lt6r1zfns,492
|
|
31
|
+
squirrels/package_data/base_project/models/federates/dataset_example.py,sha256=7OGgPS-m1nkRhF3JfF8T1XEEM-gJL2RGfthZe-Ct0pk,686
|
|
32
|
+
squirrels/package_data/base_project/models/federates/dataset_example.sql,sha256=cH9rVjXEJIFcxqZpW8dCvHLf_c-YwDuiGlcjoTIy6CM,78
|
|
33
|
+
squirrels/package_data/base_project/parameters.yml,sha256=v0ujjvm2VWSyt5u02fiGqRCrQMN3gZKT89tHk2f8Imc,4445
|
|
34
|
+
squirrels/package_data/base_project/pyconfigs/auth.py,sha256=kQA21ZU_ZnejyhHuOcRLegMhaHW-DS5XF0-eqyMxZEI,1701
|
|
35
|
+
squirrels/package_data/base_project/pyconfigs/connections.py,sha256=sjmyB7zD4P8yUVNchiuZcVOR4EdTJfo_ALux9w-lri8,1277
|
|
36
|
+
squirrels/package_data/base_project/pyconfigs/context.py,sha256=zYOql0Ebn2nA43mUwtz2g6LsnYHUujlDWUL2zu2Df1M,2087
|
|
37
|
+
squirrels/package_data/base_project/pyconfigs/parameters.py,sha256=9_D75x3okFxTU7uQoF7n1jUnrl-__A9kw5UBx-c2QAE,3057
|
|
38
|
+
squirrels/package_data/base_project/seeds/mocks/category.csv,sha256=MoYsx7tSek-JdLvDb2YlYE2vP1dKhZMq_QrQYssmRxI,35
|
|
39
|
+
squirrels/package_data/base_project/seeds/mocks/max_filter.csv,sha256=H34nwpvZVEhfTOgIJZez_UJ8N3nslxkh3CCSYUgs6w0,38
|
|
40
|
+
squirrels/package_data/base_project/seeds/mocks/subcategory.csv,sha256=ok0lPm-S4_HT-Oe1damne5_pYFHf_yveNzUqY8I4kCs,105
|
|
41
|
+
squirrels/package_data/base_project/squirrels.yml.j2,sha256=QfCzwnEApJmBdx-6wJpoWhNLH6Eoeajmw3-cnf6hJE8,1417
|
|
42
|
+
squirrels/package_data/base_project/tmp/.gitignore,sha256=XImoqcWvJY0C0L_TWCx1ljvqU7qh9fUTJmK4ACCmNFI,13
|
|
43
|
+
squirrels/package_data/static/favicon.ico,sha256=369AMckPCruYh3O2PnSLLmy8kLvTwhoaS_uu2pP6Tqs,4638
|
|
44
|
+
squirrels/package_data/static/script.js,sha256=7OyjoE5Q7KOmxbWkcM17DFE3w-Y8hSsSTcmQW4cww28,11564
|
|
45
|
+
squirrels/package_data/static/style.css,sha256=vs_TXZrd_2CAicCc-BKlU1OArA8-Rxe2iQGtCJUn0N8,2744
|
|
46
|
+
squirrels/package_data/static/widgets.js,sha256=llmDCNxd_nasTO8B9RgJEAMY-X9jzIvi8LhKhgEaipQ,3612
|
|
47
|
+
squirrels/package_data/templates/index.html,sha256=oqTewKPx2Mr9-inWPJt786K6L5qwiNYr1iPTFYjZ27E,3428
|
|
48
|
+
squirrels/package_data/templates/index2.html,sha256=LzYDWTYAhgrfRL6AqjcYRLXfYY9W0_xckehIE-38Qr8,941
|
|
49
|
+
squirrels/parameter_options.py,sha256=qu-qJzYizyLlk3FVSlQkSWpsjYufsL00AVffLABEpJE,15626
|
|
50
|
+
squirrels/parameters.py,sha256=_knDtwF0aDP-hC52oDyIj11CAHcJT8m3Ydw2tUiJdDc,31568
|
|
51
|
+
squirrels/user_base.py,sha256=1LAUPMHMtON8ohJRsG73QmeKqnPPszFAixyQI4k7-iA,1560
|
|
52
|
+
squirrels-0.2.0.dev0.dist-info/LICENSE,sha256=rW94rBQiFGQtHU_v2IQJbvg715cQF-bXup4pEJVFHXA,1067
|
|
53
|
+
squirrels-0.2.0.dev0.dist-info/METADATA,sha256=BHBZSDclfKoY4H7CexnxluQZ4bhChQUCC3PjbMohH44,5174
|
|
54
|
+
squirrels-0.2.0.dev0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
55
|
+
squirrels-0.2.0.dev0.dist-info/entry_points.txt,sha256=5ZwITOayR4jFq3eIf_SIfFaS5l_1lqxvg0u_WVb56xM,58
|
|
56
|
+
squirrels-0.2.0.dev0.dist-info/RECORD,,
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from configparser import ConfigParser
|
|
4
|
-
import os, json
|
|
5
|
-
|
|
6
|
-
from squirrels._utils import ConfigurationError
|
|
7
|
-
from squirrels import _constants as c, _utils
|
|
8
|
-
|
|
9
|
-
_SQUIRRELS_CFG_PATH = _utils.join_paths(os.path.expanduser('~'), '.squirrelscfg')
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class Credential:
|
|
14
|
-
username: str
|
|
15
|
-
password: str
|
|
16
|
-
|
|
17
|
-
def __str__(self) -> str:
|
|
18
|
-
redacted_pass = '*'*len(self.password)
|
|
19
|
-
return f'username={self.username}, password={redacted_pass}'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class SquirrelsConfigParser(ConfigParser):
|
|
23
|
-
def _get_creds_section(self):
|
|
24
|
-
section_name: str = c.CREDENTIALS_KEY
|
|
25
|
-
if not self.has_section(section_name):
|
|
26
|
-
self.add_section(section_name)
|
|
27
|
-
return self[section_name]
|
|
28
|
-
|
|
29
|
-
def _json_str_to_credential(self, json_str: str) -> Credential:
|
|
30
|
-
cred_dict = json.loads(json_str)
|
|
31
|
-
return Credential(cred_dict[c.USERNAME_KEY], cred_dict[c.PASSWORD_KEY])
|
|
32
|
-
|
|
33
|
-
def get_credential(self, key: str) -> Credential:
|
|
34
|
-
section = self._get_creds_section()
|
|
35
|
-
try:
|
|
36
|
-
value = section[key]
|
|
37
|
-
except KeyError as e:
|
|
38
|
-
raise ConfigurationError(f'Credential key "{key}" has not been set. To set it, use $ squirrels set-credential {key}')
|
|
39
|
-
return self._json_str_to_credential(value)
|
|
40
|
-
|
|
41
|
-
def get_all_credentials(self) -> Dict[str, Credential]:
|
|
42
|
-
section = self._get_creds_section()
|
|
43
|
-
result = {}
|
|
44
|
-
for key, value in section.items():
|
|
45
|
-
result[key] = self._json_str_to_credential(value)
|
|
46
|
-
return result
|
|
47
|
-
|
|
48
|
-
def set_credential(self, key: str, credential: Credential) -> ConfigParser:
|
|
49
|
-
section = self._get_creds_section()
|
|
50
|
-
section[key] = json.dumps(credential.__dict__)
|
|
51
|
-
return self
|
|
52
|
-
|
|
53
|
-
def delete_credential(self, key: str) -> ConfigParser:
|
|
54
|
-
section = self._get_creds_section()
|
|
55
|
-
section.pop(key)
|
|
56
|
-
return self
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class SquirrelsConfigIOWrapper:
|
|
60
|
-
def __init__(self) -> None:
|
|
61
|
-
self.config = SquirrelsConfigParser()
|
|
62
|
-
self.config.read(_SQUIRRELS_CFG_PATH)
|
|
63
|
-
|
|
64
|
-
def get_credential(self, key: str) -> Credential:
|
|
65
|
-
return self.config.get_credential(key)
|
|
66
|
-
|
|
67
|
-
def print_all_credentials(self) -> None:
|
|
68
|
-
credentials_dict = self.config.get_all_credentials()
|
|
69
|
-
for key, cred in credentials_dict.items():
|
|
70
|
-
print(f'{key}:', cred)
|
|
71
|
-
|
|
72
|
-
def _write_config(self) -> None:
|
|
73
|
-
with open(_SQUIRRELS_CFG_PATH, 'w') as f:
|
|
74
|
-
self.config.write(f)
|
|
75
|
-
|
|
76
|
-
def set_credential(self, key: str, user: str, pw: str) -> None:
|
|
77
|
-
credential = Credential(user, pw)
|
|
78
|
-
self.config.set_credential(key, credential)
|
|
79
|
-
print(f'Credential key "{key}" set to: {credential}')
|
|
80
|
-
self._write_config()
|
|
81
|
-
|
|
82
|
-
def delete_credential(self, key: str) -> None:
|
|
83
|
-
self.config.delete_credential(key)
|
|
84
|
-
print(f'Credential key "{key}" has been deleted')
|
|
85
|
-
self._write_config()
|
|
86
|
-
|
|
87
|
-
squirrels_config_io = SquirrelsConfigIOWrapper()
|
squirrels/_module_loader.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from typing import List, Tuple
|
|
2
|
-
from squirrels import _constants as c, _manifest as mf
|
|
3
|
-
import git, shutil, os, stat
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def parse_module_repo_strings(repo_strings: List[str]) -> List[Tuple[str]]:
|
|
7
|
-
output = []
|
|
8
|
-
for repo in repo_strings:
|
|
9
|
-
try:
|
|
10
|
-
url, tag = repo.split('@')
|
|
11
|
-
except ValueError:
|
|
12
|
-
raise RuntimeError(f'cannot split git repo "{repo}" into url and tag/branch... format must be like "url@tag"')
|
|
13
|
-
|
|
14
|
-
try:
|
|
15
|
-
name, url = url.split('=')
|
|
16
|
-
except ValueError:
|
|
17
|
-
name = url.split('/')[-1].replace('.git', '')
|
|
18
|
-
|
|
19
|
-
output.append((name, url, tag))
|
|
20
|
-
|
|
21
|
-
return output
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def load_modules(manifest: mf.Manifest) -> None:
|
|
25
|
-
repo_strings: List[str] = manifest.get_modules()
|
|
26
|
-
|
|
27
|
-
# Recreate the modules directory if it exists
|
|
28
|
-
if os.path.exists(c.MODULES_FOLDER):
|
|
29
|
-
def del_rw(action, name, exc):
|
|
30
|
-
os.chmod(name, stat.S_IWRITE)
|
|
31
|
-
os.remove(name)
|
|
32
|
-
shutil.rmtree(c.MODULES_FOLDER, onerror=del_rw)
|
|
33
|
-
os.mkdir(c.MODULES_FOLDER)
|
|
34
|
-
|
|
35
|
-
module_repos = parse_module_repo_strings(repo_strings)
|
|
36
|
-
for name, url, tag in module_repos:
|
|
37
|
-
git.Repo.clone_from(url, f"modules/{name}", branch=tag, depth=1)
|
squirrels/_parameter_set.py
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import Sequence, Dict, Any
|
|
3
|
-
from collections import OrderedDict
|
|
4
|
-
|
|
5
|
-
from squirrels import data_sources as d, parameters as p
|
|
6
|
-
from squirrels._timed_imports import pandas as pd
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class ParameterSetBase:
|
|
10
|
-
def __init__(self) -> None:
|
|
11
|
-
"""
|
|
12
|
-
Constructor for ParameterSetBase, the base class for ParameterSet. Similar to ParameterSet but without
|
|
13
|
-
a separate collection for DataSourceParameter's, and does not pre-set the parameters in constructor.
|
|
14
|
-
"""
|
|
15
|
-
self._parameters_dict: OrderedDict[str, p.Parameter] = OrderedDict()
|
|
16
|
-
|
|
17
|
-
def add_parameter(self, parameter: p.Parameter) -> None:
|
|
18
|
-
"""
|
|
19
|
-
Adds a parameter to the "parameter collection"
|
|
20
|
-
|
|
21
|
-
Parameters:
|
|
22
|
-
parameter: The parameter to add
|
|
23
|
-
"""
|
|
24
|
-
self._parameters_dict[parameter.name] = parameter
|
|
25
|
-
|
|
26
|
-
def get_parameter(self, param_name: str) -> p.Parameter:
|
|
27
|
-
"""
|
|
28
|
-
Gets the Parameter object given the parameter name
|
|
29
|
-
|
|
30
|
-
Parameters:
|
|
31
|
-
param_name: The parameter name
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
The Parameter object corresponding to the parameter name
|
|
35
|
-
"""
|
|
36
|
-
if param_name in self._parameters_dict:
|
|
37
|
-
return self._parameters_dict[param_name]
|
|
38
|
-
else:
|
|
39
|
-
raise KeyError(f'No such parameter exists called "{param_name}"')
|
|
40
|
-
|
|
41
|
-
def __getitem__(self, param_name: str) -> p.Parameter:
|
|
42
|
-
return self.get_parameter(param_name)
|
|
43
|
-
|
|
44
|
-
def get_parameters_as_ordered_dict(self) -> OrderedDict[str, p.Parameter]:
|
|
45
|
-
"""
|
|
46
|
-
Returns the inner dictionary of the "parameter collection"
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
A dictionary where key are the assigned names and values are the Parameter objects
|
|
50
|
-
"""
|
|
51
|
-
return OrderedDict(self._parameters_dict)
|
|
52
|
-
|
|
53
|
-
def merge(self, other: ParameterSetBase) -> ParameterSetBase:
|
|
54
|
-
"""
|
|
55
|
-
Merges the "parameter collection" of this and another ParameterSetBase
|
|
56
|
-
|
|
57
|
-
Parameters:
|
|
58
|
-
other: The other ParameterSetBase
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
A new copy of the ParameterSetBase as a result of the merge
|
|
62
|
-
"""
|
|
63
|
-
new_param_set = ParameterSetBase()
|
|
64
|
-
new_param_set._parameters_dict = OrderedDict(self._parameters_dict)
|
|
65
|
-
new_param_set._parameters_dict.update(other._parameters_dict)
|
|
66
|
-
return new_param_set
|
|
67
|
-
|
|
68
|
-
def to_json_dict(self, debug: bool = False) -> Dict[str, Any]:
|
|
69
|
-
"""
|
|
70
|
-
Converts this object, and all parameters contained, into a JSON dictionary
|
|
71
|
-
|
|
72
|
-
Parameters:
|
|
73
|
-
debug: Set to True to make the "hidden" parameters show as part of the result
|
|
74
|
-
|
|
75
|
-
Returns:
|
|
76
|
-
A collection of parameters as a JSON dictionary used for the "parameters" endpoint
|
|
77
|
-
"""
|
|
78
|
-
parameters = []
|
|
79
|
-
for x in self._parameters_dict.values():
|
|
80
|
-
if not x.is_hidden or debug:
|
|
81
|
-
parameters.append(x.to_json_dict())
|
|
82
|
-
|
|
83
|
-
output = {
|
|
84
|
-
"response_version": 0,
|
|
85
|
-
"parameters": parameters
|
|
86
|
-
}
|
|
87
|
-
return output
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class ParameterSet(ParameterSetBase):
|
|
91
|
-
def __init__(self, parameters: Sequence[p.Parameter]):
|
|
92
|
-
"""
|
|
93
|
-
Constructor for ParameterSet, a wrapper class for a sequence of parameters,
|
|
94
|
-
and stores the DataSourceParameters as a separate field as well
|
|
95
|
-
|
|
96
|
-
Parameters:
|
|
97
|
-
parameters: A sequence of parameters
|
|
98
|
-
"""
|
|
99
|
-
super().__init__()
|
|
100
|
-
self._data_source_params: OrderedDict[str, p.DataSourceParameter] = OrderedDict()
|
|
101
|
-
for param in parameters:
|
|
102
|
-
self._parameters_dict[param.name] = param
|
|
103
|
-
if isinstance(param, p.DataSourceParameter):
|
|
104
|
-
self._data_source_params[param.name] = param
|
|
105
|
-
|
|
106
|
-
def merge(self, other: ParameterSetBase) -> ParameterSet:
|
|
107
|
-
"""
|
|
108
|
-
Merges this object with another ParameterSet (by combining the parameters) to create a new ParameterSet.
|
|
109
|
-
|
|
110
|
-
The _parameters_dict are merged (with the other ParameterSet taking precedence when a name exist in both dict),
|
|
111
|
-
while the _data_source_params are only taken from this object. This object and the other ParameterSet remain
|
|
112
|
-
unchanged.
|
|
113
|
-
|
|
114
|
-
Parameters:
|
|
115
|
-
other: The other parameter set
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
A new ParameterSet that contains all the parameters from this and the other parameter set.
|
|
119
|
-
"""
|
|
120
|
-
new_param_set_base = super().merge(other)
|
|
121
|
-
new_param_set = ParameterSet(())
|
|
122
|
-
new_param_set._parameters_dict = new_param_set_base._parameters_dict
|
|
123
|
-
new_param_set._data_source_params = self._data_source_params
|
|
124
|
-
return new_param_set
|
|
125
|
-
|
|
126
|
-
def get_datasources(self) -> Dict[str, d.DataSource]:
|
|
127
|
-
"""
|
|
128
|
-
Gets all the DataSource objects as values to a dictionary where keys are the DataSource parameter names.
|
|
129
|
-
|
|
130
|
-
Each DataSource object represents a lookup table with table name, connection name, corresponding columns to ID, label, etc.
|
|
131
|
-
|
|
132
|
-
Returns:
|
|
133
|
-
A dictionary where keys are the names of DataSourceParameter's and values are the corresponding DataSource.
|
|
134
|
-
"""
|
|
135
|
-
new_dict = {}
|
|
136
|
-
for param_name, ds_param in self._data_source_params.items():
|
|
137
|
-
new_dict[param_name] = ds_param.data_source
|
|
138
|
-
return new_dict
|
|
139
|
-
|
|
140
|
-
def convert_datasource_params(self, df_dict: Dict[str, pd.DataFrame]) -> None:
|
|
141
|
-
"""
|
|
142
|
-
Changes all the DataSourceParameters into other Parameter types. The _data_source_params field gets cleared.
|
|
143
|
-
|
|
144
|
-
Parameters:
|
|
145
|
-
df_dict: A dictionary of DataSourceParameter name to the pandas DataFrame of the lookup table data.
|
|
146
|
-
"""
|
|
147
|
-
# Done sequentially since parents must be converted first before children
|
|
148
|
-
for key, ds_param in self._data_source_params.items():
|
|
149
|
-
ds_param.parent = self.get_parameter(ds_param.parent.name) if ds_param.parent is not None else None
|
|
150
|
-
self._parameters_dict[key] = ds_param.convert(df_dict[key])
|
|
151
|
-
self._data_source_params.clear()
|