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.

Files changed (74) hide show
  1. squirrels/__init__.py +10 -16
  2. squirrels/_api_server.py +234 -80
  3. squirrels/_authenticator.py +84 -0
  4. squirrels/_command_line.py +60 -72
  5. squirrels/_connection_set.py +96 -0
  6. squirrels/_constants.py +114 -33
  7. squirrels/_environcfg.py +77 -0
  8. squirrels/_initializer.py +126 -67
  9. squirrels/_manifest.py +195 -168
  10. squirrels/_models.py +495 -0
  11. squirrels/_package_loader.py +26 -0
  12. squirrels/_parameter_configs.py +401 -0
  13. squirrels/_parameter_sets.py +188 -0
  14. squirrels/_py_module.py +60 -0
  15. squirrels/_timer.py +36 -0
  16. squirrels/_utils.py +81 -49
  17. squirrels/_version.py +2 -2
  18. squirrels/arguments/init_time_args.py +32 -0
  19. squirrels/arguments/run_time_args.py +82 -0
  20. squirrels/data_sources.py +380 -155
  21. squirrels/dateutils.py +86 -57
  22. squirrels/package_data/base_project/Dockerfile +15 -0
  23. squirrels/package_data/base_project/connections.yml +7 -0
  24. squirrels/package_data/base_project/database/{sample_database.db → expenses.db} +0 -0
  25. squirrels/package_data/base_project/environcfg.yml +29 -0
  26. squirrels/package_data/base_project/ignores/.dockerignore +8 -0
  27. squirrels/package_data/base_project/ignores/.gitignore +7 -0
  28. squirrels/package_data/base_project/models/dbviews/database_view1.py +36 -0
  29. squirrels/package_data/base_project/models/dbviews/database_view1.sql +15 -0
  30. squirrels/package_data/base_project/models/federates/dataset_example.py +20 -0
  31. squirrels/package_data/base_project/models/federates/dataset_example.sql +3 -0
  32. squirrels/package_data/base_project/parameters.yml +109 -0
  33. squirrels/package_data/base_project/pyconfigs/auth.py +47 -0
  34. squirrels/package_data/base_project/pyconfigs/connections.py +28 -0
  35. squirrels/package_data/base_project/pyconfigs/context.py +45 -0
  36. squirrels/package_data/base_project/pyconfigs/parameters.py +55 -0
  37. squirrels/package_data/base_project/seeds/mocks/category.csv +3 -0
  38. squirrels/package_data/base_project/seeds/mocks/max_filter.csv +2 -0
  39. squirrels/package_data/base_project/seeds/mocks/subcategory.csv +6 -0
  40. squirrels/package_data/base_project/squirrels.yml.j2 +57 -0
  41. squirrels/package_data/base_project/tmp/.gitignore +2 -0
  42. squirrels/package_data/static/script.js +159 -63
  43. squirrels/package_data/static/style.css +79 -15
  44. squirrels/package_data/static/widgets.js +133 -0
  45. squirrels/package_data/templates/index.html +65 -23
  46. squirrels/package_data/templates/index2.html +22 -0
  47. squirrels/parameter_options.py +216 -119
  48. squirrels/parameters.py +407 -478
  49. squirrels/user_base.py +58 -0
  50. squirrels-0.2.0.dev0.dist-info/METADATA +126 -0
  51. squirrels-0.2.0.dev0.dist-info/RECORD +56 -0
  52. {squirrels-0.1.1.post1.dist-info → squirrels-0.2.0.dev0.dist-info}/WHEEL +1 -2
  53. squirrels-0.2.0.dev0.dist-info/entry_points.txt +3 -0
  54. squirrels/_credentials_manager.py +0 -87
  55. squirrels/_module_loader.py +0 -37
  56. squirrels/_parameter_set.py +0 -151
  57. squirrels/_renderer.py +0 -286
  58. squirrels/_timed_imports.py +0 -37
  59. squirrels/connection_set.py +0 -126
  60. squirrels/package_data/base_project/.gitignore +0 -4
  61. squirrels/package_data/base_project/connections.py +0 -20
  62. squirrels/package_data/base_project/datasets/sample_dataset/context.py +0 -22
  63. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.py +0 -29
  64. squirrels/package_data/base_project/datasets/sample_dataset/database_view1.sql.j2 +0 -12
  65. squirrels/package_data/base_project/datasets/sample_dataset/final_view.py +0 -11
  66. squirrels/package_data/base_project/datasets/sample_dataset/final_view.sql.j2 +0 -3
  67. squirrels/package_data/base_project/datasets/sample_dataset/parameters.py +0 -47
  68. squirrels/package_data/base_project/datasets/sample_dataset/selections.cfg +0 -9
  69. squirrels/package_data/base_project/squirrels.yaml +0 -22
  70. squirrels-0.1.1.post1.dist-info/METADATA +0 -67
  71. squirrels-0.1.1.post1.dist-info/RECORD +0 -40
  72. squirrels-0.1.1.post1.dist-info/entry_points.txt +0 -2
  73. squirrels-0.1.1.post1.dist-info/top_level.txt +0 -1
  74. {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,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.40.0)
2
+ Generator: poetry-core 1.8.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ squirrels=squirrels._command_line:main
3
+
@@ -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()
@@ -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)
@@ -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()