squirrels 0.4.1__tar.gz → 0.5.0b2__tar.gz
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-0.5.0b2/.gitignore +81 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/LICENSE +1 -1
- {squirrels-0.4.1 → squirrels-0.5.0b2}/PKG-INFO +41 -48
- {squirrels-0.4.1 → squirrels-0.5.0b2}/README.md +11 -17
- squirrels-0.4.1/squirrels/dateutils.py → squirrels-0.5.0b2/dateutils/__init__.py +3 -5
- squirrels-0.5.0b2/pyproject.toml +75 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/__init__.py +10 -6
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_api_response_models.py +93 -44
- squirrels-0.5.0b2/squirrels/_api_server.py +904 -0
- squirrels-0.5.0b2/squirrels/_auth.py +451 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_command_line.py +69 -24
- squirrels-0.5.0b2/squirrels/_connection_set.py +93 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_constants.py +44 -34
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_dashboards_io.py +34 -16
- squirrels-0.5.0b2/squirrels/_exceptions.py +57 -0
- squirrels-0.5.0b2/squirrels/_initializer.py +313 -0
- squirrels-0.5.0b2/squirrels/_manifest.py +279 -0
- squirrels-0.5.0b2/squirrels/_model_builder.py +113 -0
- squirrels-0.5.0b2/squirrels/_model_configs.py +74 -0
- squirrels-0.5.0b2/squirrels/_model_queries.py +52 -0
- squirrels-0.5.0b2/squirrels/_models.py +1056 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_package_loader.py +8 -4
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_parameter_configs.py +45 -65
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_parameter_sets.py +15 -13
- squirrels-0.5.0b2/squirrels/_project.py +561 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_py_module.py +4 -3
- squirrels-0.5.0b2/squirrels/_seeds.py +58 -0
- squirrels-0.5.0b2/squirrels/_sources.py +106 -0
- squirrels-0.5.0b2/squirrels/_utils.py +359 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/_version.py +1 -1
- squirrels-0.5.0b2/squirrels/arguments/init_time_args.py +103 -0
- squirrels-0.5.0b2/squirrels/arguments/run_time_args.py +169 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/dashboards.py +4 -4
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/data_sources.py +94 -162
- squirrels-0.5.0b2/squirrels/dataset_result.py +86 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/.env +30 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/.env.example +30 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/.gitignore +3 -2
- squirrels-0.5.0b2/squirrels/package_data/base_project/assets/expenses.db +0 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/connections.yml +15 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/dashboards/dashboard_example.py +34 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/dashboards/dashboard_example.yml +22 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/docker/.dockerignore +5 -2
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/docker/Dockerfile +3 -3
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/docker/compose.yml +1 -1
- squirrels-0.5.0b2/squirrels/package_data/base_project/duckdb_init.sql +9 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/macros/macros_example.sql +15 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/builds/build_example.py +26 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/builds/build_example.sql +16 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/builds/build_example.yml +57 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/dbviews/dbview_example.sql +12 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/dbviews/dbview_example.yml +26 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/federates/federate_example.py +44 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/federates/federate_example.sql +17 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/federates/federate_example.yml +65 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/models/sources.yml +39 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/parameters.yml +36 -21
- squirrels-0.5.0b2/squirrels/package_data/base_project/pyconfigs/connections.py +14 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/pyconfigs/context.py +20 -33
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/pyconfigs/parameters.py +19 -21
- squirrels-0.5.0b2/squirrels/package_data/base_project/pyconfigs/user.py +23 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/seeds/seed_categories.yml +15 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/seeds/seed_subcategories.csv +15 -0
- squirrels-0.5.0b2/squirrels/package_data/base_project/seeds/seed_subcategories.yml +21 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/squirrels.yml.j2 +17 -40
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/parameters.py +20 -20
- squirrels-0.4.1/pyproject.toml +0 -54
- squirrels-0.4.1/squirrels/_api_server.py +0 -552
- squirrels-0.4.1/squirrels/_authenticator.py +0 -85
- squirrels-0.4.1/squirrels/_connection_set.py +0 -80
- squirrels-0.4.1/squirrels/_environcfg.py +0 -84
- squirrels-0.4.1/squirrels/_initializer.py +0 -221
- squirrels-0.4.1/squirrels/_manifest.py +0 -217
- squirrels-0.4.1/squirrels/_models.py +0 -548
- squirrels-0.4.1/squirrels/_seeds.py +0 -39
- squirrels-0.4.1/squirrels/_utils.py +0 -256
- squirrels-0.4.1/squirrels/arguments/init_time_args.py +0 -40
- squirrels-0.4.1/squirrels/arguments/run_time_args.py +0 -208
- squirrels-0.4.1/squirrels/package_data/assets/favicon.ico +0 -0
- squirrels-0.4.1/squirrels/package_data/assets/index.css +0 -1
- squirrels-0.4.1/squirrels/package_data/assets/index.js +0 -58
- squirrels-0.4.1/squirrels/package_data/base_project/assets/expenses.db +0 -0
- squirrels-0.4.1/squirrels/package_data/base_project/connections.yml +0 -7
- squirrels-0.4.1/squirrels/package_data/base_project/dashboards/dashboard_example.py +0 -32
- squirrels-0.4.1/squirrels/package_data/base_project/dashboards.yml +0 -10
- squirrels-0.4.1/squirrels/package_data/base_project/env.yml +0 -29
- squirrels-0.4.1/squirrels/package_data/base_project/models/dbviews/dbview_example.py +0 -47
- squirrels-0.4.1/squirrels/package_data/base_project/models/dbviews/dbview_example.sql +0 -22
- squirrels-0.4.1/squirrels/package_data/base_project/models/federates/federate_example.py +0 -21
- squirrels-0.4.1/squirrels/package_data/base_project/models/federates/federate_example.sql +0 -3
- squirrels-0.4.1/squirrels/package_data/base_project/pyconfigs/auth.py +0 -45
- squirrels-0.4.1/squirrels/package_data/base_project/pyconfigs/connections.py +0 -19
- squirrels-0.4.1/squirrels/package_data/base_project/seeds/seed_subcategories.csv +0 -15
- squirrels-0.4.1/squirrels/package_data/templates/index.html +0 -18
- squirrels-0.4.1/squirrels/project.py +0 -378
- squirrels-0.4.1/squirrels/user_base.py +0 -55
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/assets/weather.db +0 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/seeds/seed_categories.csv +0 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/package_data/base_project/tmp/.gitignore +0 -0
- {squirrels-0.4.1 → squirrels-0.5.0b2}/squirrels/parameter_options.py +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
sandbox*
|
|
2
|
+
playground*
|
|
3
|
+
site/
|
|
4
|
+
.venv/
|
|
5
|
+
|
|
6
|
+
# pyenv files
|
|
7
|
+
.python-version
|
|
8
|
+
|
|
9
|
+
# Pipenv files
|
|
10
|
+
Pipfile
|
|
11
|
+
Pipfile.lock
|
|
12
|
+
|
|
13
|
+
### PythonVanilla ###
|
|
14
|
+
# Byte-compiled / optimized / DLL files
|
|
15
|
+
__pycache__/
|
|
16
|
+
*.py[cod]
|
|
17
|
+
*$py.class
|
|
18
|
+
|
|
19
|
+
# C extensions
|
|
20
|
+
*.so
|
|
21
|
+
|
|
22
|
+
# Distribution / packaging
|
|
23
|
+
.Python
|
|
24
|
+
build/
|
|
25
|
+
develop-eggs/
|
|
26
|
+
dist/
|
|
27
|
+
downloads/
|
|
28
|
+
eggs/
|
|
29
|
+
.eggs/
|
|
30
|
+
lib/
|
|
31
|
+
lib64/
|
|
32
|
+
parts/
|
|
33
|
+
sdist/
|
|
34
|
+
var/
|
|
35
|
+
wheels/
|
|
36
|
+
share/python-wheels/
|
|
37
|
+
*.egg-info/
|
|
38
|
+
.installed.cfg
|
|
39
|
+
*.egg
|
|
40
|
+
MANIFEST
|
|
41
|
+
|
|
42
|
+
# Installer logs
|
|
43
|
+
pip-log.txt
|
|
44
|
+
pip-delete-this-directory.txt
|
|
45
|
+
|
|
46
|
+
# Unit test / coverage reports
|
|
47
|
+
htmlcov/
|
|
48
|
+
.tox/
|
|
49
|
+
.nox/
|
|
50
|
+
.coverage
|
|
51
|
+
.coverage.*
|
|
52
|
+
.cache
|
|
53
|
+
nosetests.xml
|
|
54
|
+
coverage.xml
|
|
55
|
+
*.cover
|
|
56
|
+
*.py,cover
|
|
57
|
+
.hypothesis/
|
|
58
|
+
.pytest_cache/
|
|
59
|
+
cover/
|
|
60
|
+
|
|
61
|
+
# Translations
|
|
62
|
+
*.mo
|
|
63
|
+
*.pot
|
|
64
|
+
|
|
65
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
66
|
+
__pypackages__/
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
### VisualStudioCode ###
|
|
70
|
+
.vscode
|
|
71
|
+
|
|
72
|
+
# Local History for Visual Studio Code
|
|
73
|
+
.history/
|
|
74
|
+
|
|
75
|
+
# Built Visual Studio Code Extensions
|
|
76
|
+
*.vsix
|
|
77
|
+
|
|
78
|
+
### VisualStudioCode Patch ###
|
|
79
|
+
# Ignore all local history of files
|
|
80
|
+
.history
|
|
81
|
+
.ionide
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2025 Tim Huang
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: squirrels
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0b2
|
|
4
4
|
Summary: Squirrels - API Framework for Data Analytics
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Author-email: tim.yuting@hotmail.com
|
|
9
|
-
|
|
5
|
+
Project-URL: Homepage, https://squirrels-analytics.github.io
|
|
6
|
+
Project-URL: Repository, https://github.com/squirrels-analytics/squirrels
|
|
7
|
+
Project-URL: Documentation, https://squirrels-analytics.github.io
|
|
8
|
+
Author-email: Tim Huang <tim.yuting@hotmail.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
10
11
|
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
12
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
13
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
14
|
Classifier: Typing :: Typed
|
|
19
|
-
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
|
|
36
|
-
|
|
15
|
+
Requires-Python: ~=3.10
|
|
16
|
+
Requires-Dist: bcrypt<5,>=4.0.1
|
|
17
|
+
Requires-Dist: cachetools<6,>=5.3.2
|
|
18
|
+
Requires-Dist: duckdb<2,>=1.1.3
|
|
19
|
+
Requires-Dist: fastapi<1,>=0.112.1
|
|
20
|
+
Requires-Dist: gitpython<4,>=3.1.41
|
|
21
|
+
Requires-Dist: inquirer<4,>=3.2.1
|
|
22
|
+
Requires-Dist: jinja2<4,>=3.1.3
|
|
23
|
+
Requires-Dist: libpass<2,>=1.9.0
|
|
24
|
+
Requires-Dist: matplotlib<4,>=3.8.3
|
|
25
|
+
Requires-Dist: networkx<4,>=3.2.1
|
|
26
|
+
Requires-Dist: pandas<3,>=2.1.4
|
|
27
|
+
Requires-Dist: polars<2,>=1.14.0
|
|
28
|
+
Requires-Dist: pyarrow>=19.0.1
|
|
29
|
+
Requires-Dist: pydantic<3,>=2.8.2
|
|
30
|
+
Requires-Dist: pyjwt<3,>=2.8.0
|
|
31
|
+
Requires-Dist: python-dotenv<2,>=1.0.1
|
|
32
|
+
Requires-Dist: python-multipart<1,>=0.0.9
|
|
33
|
+
Requires-Dist: pyyaml<7,>=6.0.1
|
|
34
|
+
Requires-Dist: sqlalchemy<3,>=2.0.25
|
|
35
|
+
Requires-Dist: sqlglot>=26.12.1
|
|
36
|
+
Requires-Dist: uvicorn<1,>=0.30.6
|
|
37
37
|
Description-Content-Type: text/markdown
|
|
38
38
|
|
|
39
39
|
# Squirrels
|
|
@@ -60,10 +60,10 @@ Here are a few of the things that squirrels can do:
|
|
|
60
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
61
|
- Configure API routes for datasets (in `squirrels.yml`) without writing code.
|
|
62
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
|
|
63
|
+
- Use Jinja SQL templates (just like dbt!) or python functions (that return a Python dataframe such as polars or pandas) to define dynamic query logic based on parameter selections.
|
|
64
64
|
- Query multiple databases and join the results together in a final view in one API endpoint/dataset!
|
|
65
|
-
- Test your API endpoints with
|
|
66
|
-
- Define
|
|
65
|
+
- Test your API endpoints with Squirrels Studio or by a command line that generates rendered sql queries and results (for a given set of parameter selections).
|
|
66
|
+
- Define User model (in `user.py`) and authorize privacy scope per dataset (in `squirrels.yml`). The user's attributes can even be used in your query logic!
|
|
67
67
|
|
|
68
68
|
## License
|
|
69
69
|
|
|
@@ -77,18 +77,18 @@ The sections below describe how to set up your local environment for squirrels d
|
|
|
77
77
|
|
|
78
78
|
### Setup
|
|
79
79
|
|
|
80
|
-
This project requires python version 3.10 or above to be installed. It also uses the python
|
|
80
|
+
This project requires python version 3.10 or above to be installed. It also uses the python package manager `uv`. Information on setting up poetry can be found at: https://docs.astral.sh/uv/getting-started/installation/.
|
|
81
81
|
|
|
82
|
-
Then, to install all dependencies, run:
|
|
82
|
+
Then, to install all dependencies in a virtual environment, run:
|
|
83
83
|
|
|
84
|
-
```
|
|
85
|
-
|
|
84
|
+
```bash
|
|
85
|
+
uv sync
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
And activate the virtual environment
|
|
88
|
+
And activate the virtual environment with:
|
|
89
89
|
|
|
90
|
-
```
|
|
91
|
-
|
|
90
|
+
```bash
|
|
91
|
+
source .venv/bin/activate
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
To confirm that the setup worked, run the following to show the help page for all squirrels CLI commands:
|
|
@@ -97,11 +97,9 @@ To confirm that the setup worked, run the following to show the help page for al
|
|
|
97
97
|
sqrl -h
|
|
98
98
|
```
|
|
99
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
100
|
### Testing
|
|
103
101
|
|
|
104
|
-
|
|
102
|
+
Run `uv run pytest`. Or if you have the virtual environment activated, simply run `pytest`.
|
|
105
103
|
|
|
106
104
|
### Project Structure
|
|
107
105
|
|
|
@@ -110,8 +108,3 @@ From the root of the git repo, the source code can be found in the `squirrels` f
|
|
|
110
108
|
To understand what a specific squirrels command is doing, start from the `_command_line.py` file as your entry point.
|
|
111
109
|
|
|
112
110
|
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
|
-
|
|
@@ -22,10 +22,10 @@ Here are a few of the things that squirrels can do:
|
|
|
22
22
|
- Connect to any database by specifying its SQLAlchemy url (in `squirrels.yml`) or by using its native connector library in python (in `connections.py`).
|
|
23
23
|
- Configure API routes for datasets (in `squirrels.yml`) without writing code.
|
|
24
24
|
- Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
|
|
25
|
-
- Use Jinja SQL templates (just like dbt!) or python functions (that return a
|
|
25
|
+
- Use Jinja SQL templates (just like dbt!) or python functions (that return a Python dataframe such as polars or pandas) to define dynamic query logic based on parameter selections.
|
|
26
26
|
- Query multiple databases and join the results together in a final view in one API endpoint/dataset!
|
|
27
|
-
- Test your API endpoints with
|
|
28
|
-
- Define
|
|
27
|
+
- Test your API endpoints with Squirrels Studio or by a command line that generates rendered sql queries and results (for a given set of parameter selections).
|
|
28
|
+
- Define User model (in `user.py`) and authorize privacy scope per dataset (in `squirrels.yml`). The user's attributes can even be used in your query logic!
|
|
29
29
|
|
|
30
30
|
## License
|
|
31
31
|
|
|
@@ -39,18 +39,18 @@ The sections below describe how to set up your local environment for squirrels d
|
|
|
39
39
|
|
|
40
40
|
### Setup
|
|
41
41
|
|
|
42
|
-
This project requires python version 3.10 or above to be installed. It also uses the python
|
|
42
|
+
This project requires python version 3.10 or above to be installed. It also uses the python package manager `uv`. Information on setting up poetry can be found at: https://docs.astral.sh/uv/getting-started/installation/.
|
|
43
43
|
|
|
44
|
-
Then, to install all dependencies, run:
|
|
44
|
+
Then, to install all dependencies in a virtual environment, run:
|
|
45
45
|
|
|
46
|
-
```
|
|
47
|
-
|
|
46
|
+
```bash
|
|
47
|
+
uv sync
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
And activate the virtual environment
|
|
50
|
+
And activate the virtual environment with:
|
|
51
51
|
|
|
52
|
-
```
|
|
53
|
-
|
|
52
|
+
```bash
|
|
53
|
+
source .venv/bin/activate
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
To confirm that the setup worked, run the following to show the help page for all squirrels CLI commands:
|
|
@@ -59,11 +59,9 @@ To confirm that the setup worked, run the following to show the help page for al
|
|
|
59
59
|
sqrl -h
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
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.
|
|
63
|
-
|
|
64
62
|
### Testing
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
Run `uv run pytest`. Or if you have the virtual environment activated, simply run `pytest`.
|
|
67
65
|
|
|
68
66
|
### Project Structure
|
|
69
67
|
|
|
@@ -72,7 +70,3 @@ From the root of the git repo, the source code can be found in the `squirrels` f
|
|
|
72
70
|
To understand what a specific squirrels command is doing, start from the `_command_line.py` file as your entry point.
|
|
73
71
|
|
|
74
72
|
The library version is maintained in both the `pyproject.toml` and the `squirrels/_version.py` files.
|
|
75
|
-
|
|
76
|
-
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.
|
|
77
|
-
|
|
78
|
-
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.
|
|
@@ -5,8 +5,6 @@ from dateutil.relativedelta import relativedelta
|
|
|
5
5
|
from abc import ABCMeta, abstractmethod
|
|
6
6
|
from enum import Enum
|
|
7
7
|
|
|
8
|
-
from . import _utils as _u
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
class DayOfWeek(Enum):
|
|
12
10
|
Sunday = 0
|
|
@@ -62,7 +60,7 @@ class _DayIdxOfCalendarUnit(DateModifier):
|
|
|
62
60
|
super().__init__()
|
|
63
61
|
self.idx = idx
|
|
64
62
|
if self.idx == 0:
|
|
65
|
-
raise
|
|
63
|
+
raise ValueError(f"For constructors of class names that start with DayIdxOf_, idx cannot be zero")
|
|
66
64
|
self.incr = self.idx - 1 if self.idx > 0 else self.idx
|
|
67
65
|
|
|
68
66
|
|
|
@@ -84,7 +82,7 @@ class DayIdxOfMonthsCycle(_DayIdxOfCalendarUnit):
|
|
|
84
82
|
self._num_months_in_cycle = num_months_in_cycle
|
|
85
83
|
self._first_month_of_cycle = first_month_of_cycle
|
|
86
84
|
if 12 % self._num_months_in_cycle != 0:
|
|
87
|
-
raise
|
|
85
|
+
raise ValueError(f"Value X must fit evenly in 12")
|
|
88
86
|
self.first_month_of_first_cycle = (self._first_month_of_cycle.value - 1) % self._num_months_in_cycle + 1
|
|
89
87
|
|
|
90
88
|
def modify(self, date: Date) -> Date:
|
|
@@ -302,7 +300,7 @@ class DateModPipeline(DateModifier):
|
|
|
302
300
|
"""
|
|
303
301
|
assert isinstance(step, _OffsetUnits)
|
|
304
302
|
if step.offset == 0:
|
|
305
|
-
raise
|
|
303
|
+
raise ValueError(f"The length of 'step' must not be zero")
|
|
306
304
|
|
|
307
305
|
output: Sequence[Date] = []
|
|
308
306
|
end_date = self.modify(start_date)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "squirrels"
|
|
3
|
+
version = "0.5.0b2"
|
|
4
|
+
description = "Squirrels - API Framework for Data Analytics"
|
|
5
|
+
authors = [{ name = "Tim Huang", email = "tim.yuting@hotmail.com" }]
|
|
6
|
+
requires-python = "~=3.10"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
license = "Apache-2.0"
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
12
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
13
|
+
"Typing :: Typed",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"cachetools>=5.3.2,<6",
|
|
17
|
+
"fastapi>=0.112.1,<1",
|
|
18
|
+
"gitpython>=3.1.41,<4",
|
|
19
|
+
"inquirer>=3.2.1,<4",
|
|
20
|
+
"jinja2>=3.1.3,<4",
|
|
21
|
+
"matplotlib>=3.8.3,<4",
|
|
22
|
+
"networkx>=3.2.1,<4",
|
|
23
|
+
"pandas>=2.1.4,<3",
|
|
24
|
+
"pydantic>=2.8.2,<3",
|
|
25
|
+
"pyjwt>=2.8.0,<3",
|
|
26
|
+
"python-multipart>=0.0.9,<1",
|
|
27
|
+
"pyyaml>=6.0.1,<7",
|
|
28
|
+
"sqlalchemy>=2.0.25,<3",
|
|
29
|
+
"uvicorn>=0.30.6,<1",
|
|
30
|
+
"polars>=1.14.0,<2",
|
|
31
|
+
"pyarrow>=19.0.1",
|
|
32
|
+
"duckdb>=1.1.3,<2",
|
|
33
|
+
"sqlglot>=26.12.1",
|
|
34
|
+
"bcrypt>=4.0.1,<5",
|
|
35
|
+
"python-dotenv>=1.0.1,<2",
|
|
36
|
+
"libpass>=1.9.0,<2",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://squirrels-analytics.github.io"
|
|
41
|
+
Repository = "https://github.com/squirrels-analytics/squirrels"
|
|
42
|
+
Documentation = "https://squirrels-analytics.github.io"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
squirrels = "squirrels._command_line:main"
|
|
46
|
+
sqrl = "squirrels._command_line:main"
|
|
47
|
+
|
|
48
|
+
[dependency-groups]
|
|
49
|
+
test = ["pytest>=7.4.4,<8"]
|
|
50
|
+
dev = [
|
|
51
|
+
"ipykernel>=6.29.4,<7",
|
|
52
|
+
"plotly>=5.24.0,<6",
|
|
53
|
+
"psycopg2-binary>=2.9.10,<3",
|
|
54
|
+
"adbc-driver-postgresql>=1.3.0,<2",
|
|
55
|
+
"adbc-driver-sqlite>=1.3.0,<2",
|
|
56
|
+
"connectorx>=0.4.0,<0.5",
|
|
57
|
+
"faker>=33.1.0,<34",
|
|
58
|
+
"tqdm>=4.67.1,<5",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.hatch.build.targets.sdist]
|
|
62
|
+
include = ["squirrels", "dateutils"]
|
|
63
|
+
|
|
64
|
+
[tool.hatch.build.targets.wheel]
|
|
65
|
+
include = ["squirrels", "dateutils"]
|
|
66
|
+
|
|
67
|
+
[tool.uv]
|
|
68
|
+
default-groups = [
|
|
69
|
+
"test",
|
|
70
|
+
"dev",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[build-system]
|
|
74
|
+
requires = ["hatchling"]
|
|
75
|
+
build-backend = "hatchling.build"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from ._version import __version__
|
|
2
2
|
|
|
3
|
-
from .arguments.init_time_args import ConnectionsArgs, ParametersArgs
|
|
4
|
-
from .arguments.run_time_args import
|
|
3
|
+
from .arguments.init_time_args import ConnectionsArgs, ParametersArgs, BuildModelArgs
|
|
4
|
+
from .arguments.run_time_args import AuthLoginArgs, AuthTokenArgs, ContextArgs, ModelArgs, DashboardArgs
|
|
5
5
|
|
|
6
6
|
from .parameter_options import SelectParameterOption, DateParameterOption, DateRangeParameterOption
|
|
7
7
|
from .parameter_options import NumberParameterOption, NumberRangeParameterOption, TextParameterOption
|
|
@@ -9,11 +9,15 @@ from .parameter_options import NumberParameterOption, NumberRangeParameterOption
|
|
|
9
9
|
from .parameters import SingleSelectParameter, MultiSelectParameter, DateParameter, DateRangeParameter
|
|
10
10
|
from .parameters import NumberParameter, NumberRangeParameter, TextParameter, TextValue
|
|
11
11
|
|
|
12
|
-
from .data_sources import
|
|
12
|
+
from .data_sources import SelectDataSource, DateDataSource, DateRangeDataSource
|
|
13
13
|
from .data_sources import NumberDataSource, NumberRangeDataSource, TextDataSource
|
|
14
14
|
|
|
15
|
-
from .user_base import User, WrongPassword
|
|
16
|
-
|
|
17
15
|
from .dashboards import PngDashboard, HtmlDashboard
|
|
18
16
|
|
|
19
|
-
from .
|
|
17
|
+
from ._auth import BaseUser
|
|
18
|
+
|
|
19
|
+
from ._manifest import ConnectionProperties, ConnectionType
|
|
20
|
+
|
|
21
|
+
from ._project import SquirrelsProject
|
|
22
|
+
|
|
23
|
+
from .dataset_result import DatasetResult
|
|
@@ -1,43 +1,18 @@
|
|
|
1
|
-
from typing import Annotated
|
|
1
|
+
from typing import Annotated, Literal
|
|
2
2
|
from pydantic import BaseModel, Field
|
|
3
3
|
from datetime import datetime, date
|
|
4
4
|
|
|
5
|
+
from . import _model_configs as mc, _sources as s
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class LoginReponse(BaseModel):
|
|
7
9
|
access_token: Annotated[str, Field(examples=["encoded_jwt_token"], description="An encoded JSON web token to use subsequent API requests")]
|
|
8
10
|
token_type: Annotated[str, Field(examples=["bearer"], description='Always "bearer" for Bearer token')]
|
|
9
11
|
username: Annotated[str, Field(examples=["johndoe"], description='The username authenticated with from the form data')]
|
|
12
|
+
is_admin: Annotated[bool, Field(examples=[False], description="A boolean for whether the user is an admin")]
|
|
10
13
|
expiry_time: Annotated[datetime, Field(examples=["2023-08-01T12:00:00.000000Z"], description="The expiry time of the access token in yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z' format")]
|
|
11
14
|
|
|
12
15
|
|
|
13
|
-
## Datasets / Dashboards Catalog Response Models
|
|
14
|
-
|
|
15
|
-
name_description = "The name of the dataset / dashboard (usually in snake case)"
|
|
16
|
-
label_description = "The human-friendly display name for the dataset / dashboard"
|
|
17
|
-
description_description = "The description of the dataset / dashboard"
|
|
18
|
-
parameters_path_description = "The API path to the parameters for the dataset / dashboard"
|
|
19
|
-
result_path_description = "The API path to the results for the dataset / dashboard"
|
|
20
|
-
|
|
21
|
-
class DatasetItemModel(BaseModel):
|
|
22
|
-
name: Annotated[str, Field(examples=["mydataset"], description=name_description)]
|
|
23
|
-
label: Annotated[str, Field(examples=["My Dataset"], description=label_description)]
|
|
24
|
-
description: Annotated[str, Field(examples=[""], description=description_description)]
|
|
25
|
-
parameters_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dataset/mydataset/parameters"], description=parameters_path_description)]
|
|
26
|
-
result_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dataset/mydataset"], description=result_path_description)]
|
|
27
|
-
|
|
28
|
-
class DashboardItemModel(BaseModel):
|
|
29
|
-
name: Annotated[str, Field(examples=["mydashboard"], description=name_description)]
|
|
30
|
-
label: Annotated[str, Field(examples=["My Dashboard"], description=label_description)]
|
|
31
|
-
description: Annotated[str, Field(examples=[""], description=description_description)]
|
|
32
|
-
parameters_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dashboard/mydashboard/parameters"], description=parameters_path_description)]
|
|
33
|
-
result_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dashboard/mydashboard"], description=result_path_description)]
|
|
34
|
-
result_format: Annotated[str, Field(examples=["png", "html"], description="The format of the dashboard's result API response (one of 'png' or 'html')")]
|
|
35
|
-
|
|
36
|
-
class CatalogModel(BaseModel):
|
|
37
|
-
datasets: Annotated[list[DatasetItemModel], Field(description="The list of accessible datasets")]
|
|
38
|
-
dashboards: Annotated[list[DashboardItemModel], Field(description="The list of accessible dashboards")]
|
|
39
|
-
|
|
40
|
-
|
|
41
16
|
## Parameters Response Models
|
|
42
17
|
|
|
43
18
|
class ParameterOptionModel(BaseModel):
|
|
@@ -45,7 +20,7 @@ class ParameterOptionModel(BaseModel):
|
|
|
45
20
|
label: Annotated[str, Field(examples=["My Option"], description="The human-friendly display name for the option")]
|
|
46
21
|
|
|
47
22
|
class ParameterModelBase(BaseModel):
|
|
48
|
-
widget_type: Annotated[str, Field(examples=["
|
|
23
|
+
widget_type: Annotated[str, Field(examples=["disabled"], description="The parameter type")]
|
|
49
24
|
name: Annotated[str, Field(examples=["my_unique_param_name"], description="The name of the parameter. Use this as the key when providing the API request parameters")]
|
|
50
25
|
label: Annotated[str, Field(examples=["My Parameter"], description="The human-friendly display name for the parameter")]
|
|
51
26
|
description: Annotated[str, Field(examples=[""], description="The description of the parameter")]
|
|
@@ -102,27 +77,101 @@ class TextParameterModel(ParameterModelBase):
|
|
|
102
77
|
description='A string for the input type (one of "text", "textarea", "number", "date", "datetime-local", "month", "time", "color", or "password")'
|
|
103
78
|
)]
|
|
104
79
|
|
|
80
|
+
ParametersListType = list[
|
|
81
|
+
NoneParameterModel | SingleSelectParameterModel | MultiSelectParameterModel | DateParameterModel | DateRangeParameterModel |
|
|
82
|
+
NumberParameterModel | NumberRangeParameterModel | TextParameterModel
|
|
83
|
+
]
|
|
84
|
+
|
|
105
85
|
class ParametersModel(BaseModel):
|
|
106
|
-
parameters: list
|
|
107
|
-
NoneParameterModel | SingleSelectParameterModel | MultiSelectParameterModel | DateParameterModel | DateRangeParameterModel |
|
|
108
|
-
NumberParameterModel | NumberRangeParameterModel | TextParameterModel
|
|
109
|
-
]
|
|
86
|
+
parameters: Annotated[ParametersListType, Field(description="The list of parameters for the dataset / dashboard")]
|
|
110
87
|
|
|
111
88
|
|
|
112
|
-
##
|
|
89
|
+
## Datasets / Dashboards Catalog Response Models
|
|
90
|
+
|
|
91
|
+
name_description = "The name of the dataset / dashboard (usually in snake case)"
|
|
92
|
+
label_description = "The human-friendly display name for the dataset / dashboard"
|
|
93
|
+
description_description = "The description of the dataset / dashboard"
|
|
94
|
+
parameters_path_description = "The API path to the parameters for the dataset / dashboard"
|
|
95
|
+
metadata_path_description = "The API path to the metadata (i.e., description and schema) for the dataset"
|
|
96
|
+
result_path_description = "The API path to the results for the dataset / dashboard"
|
|
113
97
|
|
|
114
98
|
class ColumnModel(BaseModel):
|
|
115
99
|
name: Annotated[str, Field(examples=["mycol"], description="Name of column")]
|
|
116
|
-
type: Annotated[str, Field(examples=["string", "
|
|
100
|
+
type: Annotated[str, Field(examples=["string", "integer", "boolean", "datetime"], description='Column type (such as "string", "integer", "boolean", "datetime", etc.)')]
|
|
101
|
+
description: Annotated[str, Field(examples=["My column description"], description="The description of the column")]
|
|
102
|
+
category: Annotated[str, Field(examples=["dimension", "measure", "misc"], description="The category of the column (such as 'dimension', 'measure', or 'misc')")]
|
|
103
|
+
|
|
104
|
+
class ColumnWithConditionModel(ColumnModel):
|
|
105
|
+
condition: Annotated[str | None, Field(None, examples=["My condition"], description="The condition of when the column is included (such as based on a parameter selection)")]
|
|
117
106
|
|
|
118
107
|
class SchemaModel(BaseModel):
|
|
119
108
|
fields: Annotated[list[ColumnModel], Field(description="A list of JSON objects containing the 'name' and 'type' for each of the columns in the result")]
|
|
120
|
-
|
|
109
|
+
|
|
110
|
+
class SchemaWithConditionModel(BaseModel):
|
|
111
|
+
fields: Annotated[list[ColumnWithConditionModel], Field(description="A list of JSON objects containing the 'name' and 'type' for each of the columns in the result")]
|
|
112
|
+
|
|
113
|
+
class DatasetItemModel(BaseModel):
|
|
114
|
+
name: Annotated[str, Field(examples=["mydataset"], description=name_description)]
|
|
115
|
+
label: Annotated[str, Field(examples=["My Dataset"], description=label_description)]
|
|
116
|
+
description: Annotated[str, Field(examples=[""], description=description_description)]
|
|
117
|
+
parameters: Annotated[list[str], Field(examples=["myparam1", "myparam2"], description="The list of parameter names used by the dataset")]
|
|
118
|
+
data_schema: Annotated[SchemaWithConditionModel, Field(alias="schema", description="JSON object describing the schema of the dataset")]
|
|
119
|
+
parameters_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dataset/mydataset/parameters"], description=parameters_path_description)]
|
|
120
|
+
result_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dataset/mydataset"], description=result_path_description)]
|
|
121
|
+
|
|
122
|
+
class DashboardItemModel(ParametersModel):
|
|
123
|
+
name: Annotated[str, Field(examples=["mydashboard"], description=name_description)]
|
|
124
|
+
label: Annotated[str, Field(examples=["My Dashboard"], description=label_description)]
|
|
125
|
+
description: Annotated[str, Field(examples=[""], description=description_description)]
|
|
126
|
+
parameters: Annotated[list[str], Field(examples=["myparam1", "myparam2"], description="The list of parameter names used by the dashboard")]
|
|
127
|
+
parameters_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dashboard/mydashboard/parameters"], description=parameters_path_description)]
|
|
128
|
+
result_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/dashboard/mydashboard"], description=result_path_description)]
|
|
129
|
+
result_format: Annotated[str, Field(examples=["png", "html"], description="The format of the dashboard's result API response (one of 'png' or 'html')")]
|
|
130
|
+
|
|
131
|
+
ModelConfigType = mc.ModelConfig | s.Source | mc.SeedConfig | mc.BuildModelConfig | mc.DbviewModelConfig | mc.FederateModelConfig
|
|
132
|
+
|
|
133
|
+
class ConnectionItemModel(BaseModel):
|
|
134
|
+
name: Annotated[str, Field(examples=["myconnection"], description="The name of the connection")]
|
|
135
|
+
label: Annotated[str, Field(examples=["My Connection"], description="The human-friendly display name for the connection")]
|
|
136
|
+
|
|
137
|
+
class DataModelItem(BaseModel):
|
|
138
|
+
name: Annotated[str, Field(examples=["model_name"], description="The name of the model")]
|
|
139
|
+
model_type: Annotated[Literal["source", "dbview", "federate", "seed", "build"], Field(
|
|
140
|
+
examples=["source", "dbview", "federate", "seed", "build"], description="The type of the model"
|
|
141
|
+
)]
|
|
142
|
+
config: Annotated[ModelConfigType, Field(description="The configuration of the model")]
|
|
143
|
+
is_queryable: Annotated[bool, Field(examples=[True], description="Whether the model is queryable")]
|
|
144
|
+
|
|
145
|
+
class LineageNode(BaseModel):
|
|
146
|
+
name: str
|
|
147
|
+
type: Literal["model", "dataset", "dashboard"]
|
|
148
|
+
|
|
149
|
+
class LineageRelation(BaseModel):
|
|
150
|
+
type: Literal["buildtime", "runtime"]
|
|
151
|
+
source: LineageNode
|
|
152
|
+
target: LineageNode
|
|
153
|
+
|
|
154
|
+
class CatalogModel(BaseModel):
|
|
155
|
+
parameters: Annotated[ParametersListType, Field(description="The list of all parameters in the project")]
|
|
156
|
+
datasets: Annotated[list[DatasetItemModel], Field(description="The list of accessible datasets")]
|
|
157
|
+
dashboards: Annotated[list[DashboardItemModel], Field(description="The list of accessible dashboards")]
|
|
158
|
+
connections: Annotated[list[ConnectionItemModel], Field(description="The list of connections in the project (only provided for admin users)")]
|
|
159
|
+
models: Annotated[list[DataModelItem], Field(description="The list of data models in the project (only provided for admin users)")]
|
|
160
|
+
lineage: Annotated[list[LineageRelation], Field(description="The lineage information between data assets (only provided for admin users)")]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
## Dataset Results Response Models
|
|
164
|
+
|
|
165
|
+
class DataDetailsModel(BaseModel):
|
|
166
|
+
num_rows: Annotated[int, Field(examples=[2], description="The number of rows in the data field")]
|
|
167
|
+
orientation: Annotated[Literal["records", "rows", "columns"], Field(examples=["records", "rows", "columns"], description="The orientation of the data field")]
|
|
121
168
|
|
|
122
169
|
class DatasetResultModel(BaseModel):
|
|
123
170
|
data_schema: Annotated[SchemaModel, Field(alias="schema", description="JSON object describing the schema of the dataset")]
|
|
124
|
-
|
|
125
|
-
|
|
171
|
+
total_num_rows: Annotated[int, Field(examples=[2], description="The total number of rows for the dataset")]
|
|
172
|
+
data_details: Annotated[DataDetailsModel, Field(description="A JSON object containing the details of the data field")]
|
|
173
|
+
data: Annotated[list[dict] | list[list] | dict[str, list], Field(
|
|
174
|
+
examples=[[{"mycol": "col_value1"}, {"mycol": "col_value2"}], [["col_value1"], ["col_value2"]], {"mycol": ["col_value1", "col_value2"]}],
|
|
126
175
|
description="A list of JSON objects where each object is a row of the tabular results. The keys and values of the object are column names (described in fields) and values of the row."
|
|
127
176
|
)]
|
|
128
177
|
|
|
@@ -130,12 +179,12 @@ class DatasetResultModel(BaseModel):
|
|
|
130
179
|
## Project Metadata Response Models
|
|
131
180
|
|
|
132
181
|
class ProjectVersionModel(BaseModel):
|
|
133
|
-
major_version: int
|
|
134
|
-
|
|
135
|
-
token_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/token"])]
|
|
136
|
-
data_catalog_path: Annotated[str, Field(examples=["/squirrels-v0/myproject/v1/datasets"])]
|
|
182
|
+
major_version: Annotated[int, Field(examples=[1])]
|
|
183
|
+
data_catalog_path: Annotated[str, Field(examples=["/squirrels-v0/project/myproject/v1/data-catalog"])]
|
|
137
184
|
|
|
138
185
|
class ProjectModel(BaseModel):
|
|
139
186
|
name: Annotated[str, Field(examples=["myproject"])]
|
|
187
|
+
version: Annotated[str, Field(examples=["v1"])]
|
|
140
188
|
label: Annotated[str, Field(examples=["My Project"])]
|
|
141
|
-
|
|
189
|
+
description: Annotated[str, Field(examples=["My project description"])]
|
|
190
|
+
squirrels_version: Annotated[str, Field(examples=["0.1.0"])]
|