squirrels 0.5.0b3__py3-none-any.whl → 0.6.0.post0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. squirrels/__init__.py +4 -0
  2. squirrels/_api_routes/__init__.py +5 -0
  3. squirrels/_api_routes/auth.py +337 -0
  4. squirrels/_api_routes/base.py +196 -0
  5. squirrels/_api_routes/dashboards.py +156 -0
  6. squirrels/_api_routes/data_management.py +148 -0
  7. squirrels/_api_routes/datasets.py +220 -0
  8. squirrels/_api_routes/project.py +289 -0
  9. squirrels/_api_server.py +440 -792
  10. squirrels/_arguments/__init__.py +0 -0
  11. squirrels/_arguments/{_init_time_args.py → init_time_args.py} +23 -43
  12. squirrels/_arguments/{_run_time_args.py → run_time_args.py} +32 -68
  13. squirrels/_auth.py +590 -264
  14. squirrels/_command_line.py +130 -58
  15. squirrels/_compile_prompts.py +147 -0
  16. squirrels/_connection_set.py +16 -15
  17. squirrels/_constants.py +36 -11
  18. squirrels/_dashboards.py +179 -0
  19. squirrels/_data_sources.py +40 -34
  20. squirrels/_dataset_types.py +16 -11
  21. squirrels/_env_vars.py +209 -0
  22. squirrels/_exceptions.py +9 -37
  23. squirrels/_http_error_responses.py +52 -0
  24. squirrels/_initializer.py +7 -6
  25. squirrels/_logging.py +121 -0
  26. squirrels/_manifest.py +155 -77
  27. squirrels/_mcp_server.py +578 -0
  28. squirrels/_model_builder.py +11 -55
  29. squirrels/_model_configs.py +5 -5
  30. squirrels/_model_queries.py +1 -1
  31. squirrels/_models.py +276 -143
  32. squirrels/_package_data/base_project/.env +1 -24
  33. squirrels/_package_data/base_project/.env.example +31 -17
  34. squirrels/_package_data/base_project/connections.yml +4 -3
  35. squirrels/_package_data/base_project/dashboards/dashboard_example.py +13 -7
  36. squirrels/_package_data/base_project/dashboards/dashboard_example.yml +6 -6
  37. squirrels/_package_data/base_project/docker/Dockerfile +2 -2
  38. squirrels/_package_data/base_project/docker/compose.yml +1 -1
  39. squirrels/_package_data/base_project/duckdb_init.sql +1 -0
  40. squirrels/_package_data/base_project/models/builds/build_example.py +2 -2
  41. squirrels/_package_data/base_project/models/dbviews/dbview_example.sql +7 -2
  42. squirrels/_package_data/base_project/models/dbviews/dbview_example.yml +16 -10
  43. squirrels/_package_data/base_project/models/federates/federate_example.py +27 -17
  44. squirrels/_package_data/base_project/models/federates/federate_example.sql +3 -7
  45. squirrels/_package_data/base_project/models/federates/federate_example.yml +7 -7
  46. squirrels/_package_data/base_project/models/sources.yml +5 -6
  47. squirrels/_package_data/base_project/parameters.yml +24 -38
  48. squirrels/_package_data/base_project/pyconfigs/connections.py +8 -3
  49. squirrels/_package_data/base_project/pyconfigs/context.py +26 -14
  50. squirrels/_package_data/base_project/pyconfigs/parameters.py +124 -81
  51. squirrels/_package_data/base_project/pyconfigs/user.py +48 -15
  52. squirrels/_package_data/base_project/resources/public/.gitkeep +0 -0
  53. squirrels/_package_data/base_project/seeds/seed_categories.yml +1 -1
  54. squirrels/_package_data/base_project/seeds/seed_subcategories.yml +1 -1
  55. squirrels/_package_data/base_project/squirrels.yml.j2 +21 -31
  56. squirrels/_package_data/templates/login_successful.html +53 -0
  57. squirrels/_package_data/templates/squirrels_studio.html +22 -0
  58. squirrels/_parameter_configs.py +43 -22
  59. squirrels/_parameter_options.py +1 -1
  60. squirrels/_parameter_sets.py +41 -30
  61. squirrels/_parameters.py +560 -123
  62. squirrels/_project.py +487 -277
  63. squirrels/_py_module.py +71 -10
  64. squirrels/_request_context.py +33 -0
  65. squirrels/_schemas/__init__.py +0 -0
  66. squirrels/_schemas/auth_models.py +83 -0
  67. squirrels/_schemas/query_param_models.py +70 -0
  68. squirrels/_schemas/request_models.py +26 -0
  69. squirrels/_schemas/response_models.py +286 -0
  70. squirrels/_seeds.py +52 -13
  71. squirrels/_sources.py +29 -23
  72. squirrels/_utils.py +221 -42
  73. squirrels/_version.py +1 -3
  74. squirrels/arguments.py +7 -2
  75. squirrels/auth.py +4 -0
  76. squirrels/connections.py +2 -0
  77. squirrels/dashboards.py +3 -1
  78. squirrels/data_sources.py +6 -0
  79. squirrels/parameter_options.py +5 -0
  80. squirrels/parameters.py +5 -0
  81. squirrels/types.py +10 -3
  82. squirrels-0.6.0.post0.dist-info/METADATA +148 -0
  83. squirrels-0.6.0.post0.dist-info/RECORD +101 -0
  84. {squirrels-0.5.0b3.dist-info → squirrels-0.6.0.post0.dist-info}/WHEEL +1 -1
  85. squirrels/_api_response_models.py +0 -190
  86. squirrels/_dashboard_types.py +0 -82
  87. squirrels/_dashboards_io.py +0 -79
  88. squirrels-0.5.0b3.dist-info/METADATA +0 -110
  89. squirrels-0.5.0b3.dist-info/RECORD +0 -80
  90. /squirrels/_package_data/base_project/{assets → resources}/expenses.db +0 -0
  91. /squirrels/_package_data/base_project/{assets → resources}/weather.db +0 -0
  92. {squirrels-0.5.0b3.dist-info → squirrels-0.6.0.post0.dist-info}/entry_points.txt +0 -0
  93. {squirrels-0.5.0b3.dist-info → squirrels-0.6.0.post0.dist-info}/licenses/LICENSE +0 -0
squirrels/types.py CHANGED
@@ -1,11 +1,18 @@
1
+ from ._api_server import FastAPIComponents
2
+
1
3
  from ._data_sources import DataSource
2
4
 
3
5
  from ._parameter_options import ParameterOption
4
6
 
5
7
  from ._parameters import Parameter, TextValue
6
8
 
7
- from ._auth import BaseUser
8
-
9
9
  from ._dataset_types import DatasetMetadata, DatasetResult
10
10
 
11
- from ._dashboard_types import Dashboard
11
+ from ._dashboards import Dashboard
12
+
13
+ from ._parameter_configs import ParameterConfigBase
14
+
15
+ __all__ = [
16
+ "FastAPIComponents", "DataSource", "ParameterOption", "Parameter", "TextValue",
17
+ "DatasetMetadata", "DatasetResult", "Dashboard", "ParameterConfigBase"
18
+ ]
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: squirrels
3
+ Version: 0.6.0.post0
4
+ Summary: Squirrels - SDK for Data Analytics API & MCP Server
5
+ Project-URL: Homepage, https://docs.pysquirrels.com
6
+ Project-URL: Repository, https://github.com/squirrels-analytics/squirrels
7
+ Project-URL: Documentation, https://docs.pysquirrels.com
8
+ Author-email: Tim Huang <timhuang95@gmail.com>
9
+ License-Expression: Apache-2.0
10
+ License-File: LICENSE
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: authlib>=1.5.2
17
+ Requires-Dist: bcrypt>=4.0.1
18
+ Requires-Dist: cachetools>=5.3.2
19
+ Requires-Dist: duckdb>=1.4.0
20
+ Requires-Dist: fastapi>=0.112.1
21
+ Requires-Dist: gitpython>=3.1.41
22
+ Requires-Dist: inquirer>=3.2.1
23
+ Requires-Dist: itsdangerous>=2.2.0
24
+ Requires-Dist: jinja2>=3.1.3
25
+ Requires-Dist: libpass>=1.9.0
26
+ Requires-Dist: matplotlib>=3.8.3
27
+ Requires-Dist: mcp>=1.24.0
28
+ Requires-Dist: pandas>=2.1.4
29
+ Requires-Dist: polars>=1.14.0
30
+ Requires-Dist: pyarrow>=19.0.1
31
+ Requires-Dist: pydantic>=2.8.2
32
+ Requires-Dist: pyjwt>=2.8.0
33
+ Requires-Dist: python-dotenv>=1.0.1
34
+ Requires-Dist: python-multipart>=0.0.9
35
+ Requires-Dist: pyyaml>=6.0.1
36
+ Requires-Dist: requests>=2.32.5
37
+ Requires-Dist: sqlalchemy>=2.0.25
38
+ Requires-Dist: sqlglot>=26.12.1
39
+ Requires-Dist: update-checker>=0.18.0
40
+ Requires-Dist: uvicorn>=0.30.6
41
+ Description-Content-Type: text/markdown
42
+
43
+ # Squirrels
44
+
45
+ Squirrels is an API framework that lets you create REST APIs for dynamic data analytics!
46
+
47
+ **Documentation**: <a href="https://docs.pysquirrels.com" target="_blank">https://docs.pysquirrels.com</a>
48
+
49
+ **Source Code**: <a href="https://github.com/squirrels-analytics/squirrels" target="_blank">https://github.com/squirrels-analytics/squirrels</a>
50
+
51
+ ## Table of Contents
52
+
53
+ - [Main Features](#main-features)
54
+ - [License](#license)
55
+ - [Contributing to Squirrels](#contributing-to-squirrels)
56
+ - [Setup](#setup)
57
+ - [Testing](#testing)
58
+ - [Project Structure](#project-structure)
59
+
60
+ ## Main Features
61
+
62
+ Here are a few of the things that Squirrels can do:
63
+
64
+ - Connect to any database by specifying its SQLAlchemy url (in `squirrels.yml`) or by using its native connector library in python (in `connections.py`).
65
+ - Configure API routes for datasets (in `squirrels.yml`) without writing code.
66
+ - Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
67
+ - Use SQL templates (templated with Jinja, like dbt) or python functions (that return a Python dataframe in polars or pandas) to define dynamic query logic based on parameter selections.
68
+ - Query multiple databases and join the results together in a final view in one API endpoint/dataset!
69
+ - Test your API endpoints with Squirrels Studio or by a command line that generates rendered sql queries and results as files (for a given set of parameter selections).
70
+ - 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!
71
+ - Serve dataset metadata and results to AI agents via MCP (Model Context Protocol)
72
+
73
+ ## Quick Start
74
+
75
+ In a new virtual environment, install `squirrels`. Then, in your project directory, activate the virtual environment and run the following commands:
76
+
77
+ ```bash
78
+ sqrl new --use-defaults --curr-dir
79
+ sqrl build
80
+ ```
81
+
82
+ To run the API server, simply run:
83
+
84
+ ```bash
85
+ sqrl run
86
+ ```
87
+
88
+ ## License
89
+
90
+ Squirrels is released under the Apache 2.0 license.
91
+
92
+ See the file LICENSE for more details.
93
+
94
+ ## Contributing to Squirrels
95
+
96
+ 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.
97
+
98
+ ### Setup
99
+
100
+ This project requires the python package manager `uv` with Python 3.10 or above. Information on setting up uv can be found at: https://docs.astral.sh/uv/getting-started/installation/.
101
+
102
+ Then, to install all dependencies in a virtual environment, run:
103
+
104
+ ```bash
105
+ uv sync -p 3.10
106
+ ```
107
+
108
+ And activate the virtual environment with:
109
+
110
+ ```bash
111
+ source .venv/bin/activate
112
+ ```
113
+
114
+ To confirm that the setup worked, run the following to show the help page for all Squirrels CLI commands:
115
+
116
+ ```bash
117
+ sqrl -h
118
+ ```
119
+
120
+ ### Project Structure
121
+
122
+ 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.
123
+
124
+ The documentation contents are in the `docs/` folder and are built with Mintlify.
125
+
126
+ To understand what a specific Squirrels command is doing, start from the `_command_line.py` file as your entry point.
127
+
128
+ The library version is maintained in both the `pyproject.toml` and the `squirrels/_version.py` files.
129
+
130
+ ### Testing
131
+
132
+ Run `uv run pytest`. Or if you have the virtual environment activated, simply run `pytest`.
133
+
134
+ ### Documentation
135
+
136
+ Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview your documentation changes locally. To install, use the following command:
137
+
138
+ ```
139
+ npm i -g mint
140
+ ```
141
+
142
+ Run the following command in the `docs/` folder, where the `docs.json` is located:
143
+
144
+ ```
145
+ mint dev
146
+ ```
147
+
148
+ View your local preview at `http://localhost:3000`.
@@ -0,0 +1,101 @@
1
+ dateutils/__init__.py,sha256=dq4VSlJ5ztaDPdvYBRAvXSyanT_CZif3I4O0YVmWfa8,277
2
+ dateutils/_enums.py,sha256=WBrnLqta_iMMhGMEn24cCO1Vlr7bST0E8oEfAL3z0P8,373
3
+ dateutils/_implementation.py,sha256=PVJAdNolDdTpCZXwvokKoMIZzTSNpCUvZlVLWpMeSho,15173
4
+ dateutils/types.py,sha256=xcRwBoftQi-uHM1tlVTW5xgN84qdrqCo5tQT7A34S8Y,124
5
+ squirrels/__init__.py,sha256=nvzVVd5yHYbpQAfyKdQGDMxZpW7wVq3uUlpg4Urc_ws,320
6
+ squirrels/_api_server.py,sha256=wE3Wz-IfKWENrKiyC1b96ANjlG8STOplxqRe3eBhJJ0,25534
7
+ squirrels/_auth.py,sha256=aWYZ9tmx9FSIhWfA0OYVRVL04L9CnYVX2r56DycQ2Go,34961
8
+ squirrels/_command_line.py,sha256=gRXM1EkjRrwSdiC19BQGnlFQizdpf2LwcnP5JiUcvEU,15336
9
+ squirrels/_compile_prompts.py,sha256=XIbRciSXak6NuDD4KedeDoUV5YSHvctQGk6HMxKchBw,5562
10
+ squirrels/_connection_set.py,sha256=r8OXth9L3f_HUE7WU0Tjrsmu0EN3NFL_HVXDlQDCuUA,4032
11
+ squirrels/_constants.py,sha256=317Ab7EQz1VimzGghcQw26YAFmjNg5uJ2yIqWEZqOvg,4100
12
+ squirrels/_dashboards.py,sha256=momVodpSveuD-0HdKA9vIJTYYl0WHNfG9NxhfNQ9wmY,6004
13
+ squirrels/_data_sources.py,sha256=BsuNcUEt9fLdLARbjzaLcf7PhPVLyHNeZuEFbKlkw6I,26445
14
+ squirrels/_dataset_types.py,sha256=qp2z5SblubPKu2o-oiKuKJhKiyT4lAPZnsiDXUsFW2I,2959
15
+ squirrels/_env_vars.py,sha256=gPeZL1cZ8z_hDuOdyJH2h9uIfaIu9D3_0hgs42KICxU,7966
16
+ squirrels/_exceptions.py,sha256=-hfDZoV-JhvuNRAERTFg-5NFjr0uNk4i9ogNgJfAyyc,1171
17
+ squirrels/_http_error_responses.py,sha256=_LFBFWL0My4E_CStDBOJs8shdu0cp5mP_2WoiH-lqW8,2004
18
+ squirrels/_initializer.py,sha256=9isok4vJQriAPzKCHQ8sXgf50GIFSo5E5wPwl0CuXK8,14333
19
+ squirrels/_logging.py,sha256=WgL48t0-1aLhfLU235PQ9CaE1o2LOckdZD-jfYsQc1Y,4308
20
+ squirrels/_manifest.py,sha256=tspmREFlnl-mq0zFtrBzeAmfWph4CjUnJnMM71mxW9c,13236
21
+ squirrels/_mcp_server.py,sha256=jl60AMxekAGlG75ij8AE-LV101TAZVxmN9YqhYVR3-s,27446
22
+ squirrels/_model_builder.py,sha256=och7eKtde38xM-aZZ-VcSAFu8muuf0JtlELNaKIx-F4,2779
23
+ squirrels/_model_configs.py,sha256=szDu_byDoQOdSSui9pnZfD5ZGcY_UNNwKOJqtMgbK0c,3352
24
+ squirrels/_model_queries.py,sha256=2fl07feHtzddBpiXUWGfNBYeoN_EZ7K5mAz2Jc-vCvs,1087
25
+ squirrels/_models.py,sha256=PXzb-j8sRlJEXmJxZmTc29SKq2obQVoyJZrMOOC2gC8,55352
26
+ squirrels/_package_loader.py,sha256=xcIur5Z38OWd-OVjsueFstVV567zlkK9UBnLS4NawJY,1158
27
+ squirrels/_parameter_configs.py,sha256=K7p_pIobT-gPZ99ewzLKV1aUzhnmfbGO7WjuzgjhDM8,24867
28
+ squirrels/_parameter_options.py,sha256=HatFFPyHrZo2ct3BS-FCHPzNqcIst4dVh14SgkhhFyw,16874
29
+ squirrels/_parameter_sets.py,sha256=uvpSL_2eeqyQECKkBMv_gyLR2vKW-n42OuecN6h1730,10345
30
+ squirrels/_parameters.py,sha256=SQsVd7OEWFfoA21d_0uf_KD9Li2wgMNQzJMO8GhgyRw,77664
31
+ squirrels/_project.py,sha256=VKKJj8Srp8zNrWKHNuNm5a4VW-yBt5hdmlap22zX-OU,40490
32
+ squirrels/_py_module.py,sha256=62yfbIRrZvz8oO3wb2EHQW9SHvDpLptu36JVh50Q3YQ,4796
33
+ squirrels/_request_context.py,sha256=5FBxIQJ_Ki0sekZC_XrzS4GIegqoJuPnalQhGBsm-kQ,964
34
+ squirrels/_seeds.py,sha256=ROlMHhBz1xdS8mJYWPT9-zzH5KdzyCqgV_lDWNE_zEw,3311
35
+ squirrels/_sources.py,sha256=7zXrPZtJFR-JtAWH7NS7cJbNCiY1WE5GokGPUPwcZvs,5292
36
+ squirrels/_utils.py,sha256=12foyZTRw8rw5mj5b4E9VwHdGLEe7Tgde730jL9OMbw,18462
37
+ squirrels/_version.py,sha256=CBY3jsC-9HCm7eZ6CKD-sYLCejqOJ1pYWPQM4LGIXcI,22
38
+ squirrels/arguments.py,sha256=6rWDwv8jbimBUYv4peE2kEuFzOQ4jn9Tr_QXUmEj0_o,324
39
+ squirrels/auth.py,sha256=oX5-0QT4aKiUSyImKkLtPLYoki1SVNgaU3KBQzIizP4,193
40
+ squirrels/connections.py,sha256=FVK0se1LUAzsapPZYlWc1q6jTJ6hiU2RuYinBEvZ5a0,122
41
+ squirrels/dashboards.py,sha256=egYWL7jV9hvyxZ0vFziQb4q3sKDLfE_TNJN5niQbzX4,98
42
+ squirrels/data_sources.py,sha256=P3VIGWqspXLlxKUTDXc5u5eJ2yQoo4rvy9RGEb_OIbQ,347
43
+ squirrels/parameter_options.py,sha256=5ucgTWvdwefurMrk7xiJhmRn6fbBXXOhQdH7Mbnm-1I,382
44
+ squirrels/parameters.py,sha256=TJcsjtj0Kg0F69I5Zk7r0uDJC7p8duk3gtjXjcQPE40,366
45
+ squirrels/types.py,sha256=kUWz-S-CmCWS5Q9zwoJyLKJ46l202uVcBOPKjegztdo,502
46
+ squirrels/_api_routes/__init__.py,sha256=-oGMDfM5Qo7NDiwpjHt8BKVZNNZ4798-NZCJzYCUdbc,106
47
+ squirrels/_api_routes/auth.py,sha256=em2jE0_DxwbqHFHdMr3vQNMCUqx8WXuYhRGFaf4p5Bw,17983
48
+ squirrels/_api_routes/base.py,sha256=7ExlpxKXxGz2c6obw9gmFANYOB1nhIl48uASJf8B338,8878
49
+ squirrels/_api_routes/dashboards.py,sha256=4pDjPXSCm8BNRci_qYQSY4mLeqEEGwuPaIFhxlrqNyw,8574
50
+ squirrels/_api_routes/data_management.py,sha256=XzhIH1XILqwhI9gkr_b8J1c2eZTRBdM6Ht_5amUM1xs,8275
51
+ squirrels/_api_routes/datasets.py,sha256=h19sH8No9L9Bl0frnkRGq1piqCu1p4t9b4n85uexbUo,11803
52
+ squirrels/_api_routes/project.py,sha256=bY8jmLF3fzRl4Q3YlAndO8zWBnlY8uQ-A-tO58rF88g,16179
53
+ squirrels/_arguments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ squirrels/_arguments/init_time_args.py,sha256=a6oX_OwK-6SorGlyAIhR7BfMUNZcH4qQkAcYgAQ2T4I,2784
55
+ squirrels/_arguments/run_time_args.py,sha256=X3XZ2zkRtUE2c3yarb-LlNG4UTPB-sZTr9fUp1DerDw,3884
56
+ squirrels/_package_data/base_project/.env,sha256=gTk0lu-yEvPeoXI0xhXbJEOh2C7ECQgz9oT9FOPoGuM,352
57
+ squirrels/_package_data/base_project/.env.example,sha256=U9DpOZfSI1MLkGAKd7jH6SiczWsBjGE7lJlyq_HObg4,1812
58
+ squirrels/_package_data/base_project/connections.yml,sha256=eBIbTQqyp5FlxDs0HyQ_NuE2Nx87EW53Qp1Y_c1ygv0,1093
59
+ squirrels/_package_data/base_project/duckdb_init.sql,sha256=PYUiQZajja-TG7EU1__PoiyDajgngEil4Lxenv5FWT0,284
60
+ squirrels/_package_data/base_project/gitignore,sha256=B9OEkQ_j9fZGA2IAyVUvXeylxpya-AUwzLzqzMN4Bfc,155
61
+ squirrels/_package_data/base_project/parameters.yml,sha256=--N_u3vA2tnT4uDxnkgxSs135i37N9N01gnYjD2jMRY,6741
62
+ squirrels/_package_data/base_project/squirrels.yml.j2,sha256=6MDG1FnLYU507i6jkrlnBQDIlAnpO0tNUPLhqRlNSXM,1919
63
+ squirrels/_package_data/base_project/dashboards/dashboard_example.py,sha256=XDRUxzb5Kyecylb2YV0Q3RD56Bw4doye_hhdgiQ6ck0,1800
64
+ squirrels/_package_data/base_project/dashboards/dashboard_example.yml,sha256=2XU4zH8dBxnGxusMX8eOMOTPQAVLdQQ50w3B9GOiMR4,481
65
+ squirrels/_package_data/base_project/docker/.dockerignore,sha256=IN0ZmxwLdmYlw6I2ziTdzXkTbZWCUyV4kfUI9_lDz-A,201
66
+ squirrels/_package_data/base_project/docker/Dockerfile,sha256=0pi4IMXhZKcS6Nfg2wKATESZP84_r2Sp-Kh8cfMGOcY,470
67
+ squirrels/_package_data/base_project/docker/compose.yml,sha256=c5_Zng1lF4eOYM2tPNk7n4WroLjz0Apu4PCXni77fws,105
68
+ squirrels/_package_data/base_project/macros/macros_example.sql,sha256=kJMAxL9ihCd-ULPOfHkqt8TDIMcH6QAOFFw89VplGL0,508
69
+ squirrels/_package_data/base_project/models/sources.yml,sha256=axBHFoJtWjAcklZyibrr7zhyDRATxi9r_bGjI09Kulk,1816
70
+ squirrels/_package_data/base_project/models/builds/build_example.py,sha256=Obs1DpPTkXF4X7XxETJKxFnoPWPCrUWnaiV8N75IeWw,1024
71
+ squirrels/_package_data/base_project/models/builds/build_example.sql,sha256=52tqAIzFWJgX13CP4HNeBd5BFy7Z929Z0XxfLgGKthM,508
72
+ squirrels/_package_data/base_project/models/builds/build_example.yml,sha256=kUd6j5u8qU6UD294AowPBHFOb8yYDA_dxWlCIBrPl90,1407
73
+ squirrels/_package_data/base_project/models/dbviews/dbview_example.sql,sha256=DXBdikZayi7XJrE3a4_PFgFZwT1hBzpnOp89858IGPE,369
74
+ squirrels/_package_data/base_project/models/dbviews/dbview_example.yml,sha256=_wW3m0_pDMMbZHaJYjG5PKwOpcM5QlJbO0QiunOmEdA,1175
75
+ squirrels/_package_data/base_project/models/federates/federate_example.py,sha256=13dcIibmsyvbNGOEAHYoYzE5jisZizH-Sghevd4pqmo,2057
76
+ squirrels/_package_data/base_project/models/federates/federate_example.sql,sha256=9eTJfTSWAMV8lGtQ0Bg4A_Wpv0w1LRIf5fMx6OIZ7Uk,674
77
+ squirrels/_package_data/base_project/models/federates/federate_example.yml,sha256=9zxJrpY3V-wRfGTyD4cBNc8hy5gPxGgOsAnIh-3YMCY,2163
78
+ squirrels/_package_data/base_project/pyconfigs/connections.py,sha256=DGPQL39Aa3VyFQbFbmVmL_OvpjpQqmy2g_wfiPw5xfc,683
79
+ squirrels/_package_data/base_project/pyconfigs/context.py,sha256=ln5p8r2DZX98qqxuEMSn0GG4ZIc6_ISAq7EnlzYSDZA,4325
80
+ squirrels/_package_data/base_project/pyconfigs/parameters.py,sha256=-UlrCcuW2NqlNlEnYttQ-S09Lq5IpNkAVP5FNR2e33g,4794
81
+ squirrels/_package_data/base_project/pyconfigs/user.py,sha256=ZVXxvMSO6mcuAKy_m2VOR55urlj5RVdrf18ga1qG49k,2554
82
+ squirrels/_package_data/base_project/resources/expenses.db,sha256=aO0QdApW9ad8LRc73MW1o3eimryzmOAH5vz9Vc3dWK0,77824
83
+ squirrels/_package_data/base_project/resources/weather.db,sha256=dsHPO36gQdZ4ULAA726Hg3jp8a1dCdig1DhrGg8wTeg,86016
84
+ squirrels/_package_data/base_project/resources/public/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ squirrels/_package_data/base_project/seeds/seed_categories.csv,sha256=jppjf1nOIxy7-bi5lJn5CVqmnLfJHHq0ABgp6UqbXnw,104
86
+ squirrels/_package_data/base_project/seeds/seed_categories.yml,sha256=UM2gIrwHU2qogKdQqeqP6s4QfE7fEzUsPr7HjqlX7ow,425
87
+ squirrels/_package_data/base_project/seeds/seed_subcategories.csv,sha256=Tta1oIgnc2nukNMDlUkIErRKNH_8YT5EPp1A2kQKcow,327
88
+ squirrels/_package_data/base_project/seeds/seed_subcategories.yml,sha256=fqXzZfVIQIlrVjW83K3epnVYFvF0X2Kiqe3KpvHgI5c,573
89
+ squirrels/_package_data/base_project/tmp/.gitignore,sha256=XImoqcWvJY0C0L_TWCx1ljvqU7qh9fUTJmK4ACCmNFI,13
90
+ squirrels/_package_data/templates/login_successful.html,sha256=hWLQLG_cNxZhRD27p5k-C_ZZbQ-gl0I-mxgAX-BLoDk,1113
91
+ squirrels/_package_data/templates/squirrels_studio.html,sha256=n0ymweLcaQYeMDaLBXV3Dt7TPO6HuPirX-WwpuQznxA,945
92
+ squirrels/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
+ squirrels/_schemas/auth_models.py,sha256=iP-XSr20IurFMA-nb-vTEJeieEE58R97oFVKeXZgjNI,2985
94
+ squirrels/_schemas/query_param_models.py,sha256=_afevpPU_aTCce-cu721_w7c5-T1N3-1XNF-CMoiwrA,4021
95
+ squirrels/_schemas/request_models.py,sha256=_TYxdmy4zYpVzw2qIX2KjIba_-zvCVI7b1Nc3tE1BEw,919
96
+ squirrels/_schemas/response_models.py,sha256=xIrwNIYuhyDOkRLH8j4bu2JNBzqhoFUZLtN1Glb5XRQ,20059
97
+ squirrels-0.6.0.post0.dist-info/METADATA,sha256=LNQDfzm3A3px9BwBs3t84W4twm1W0V8NP4l7-InF-NA,5225
98
+ squirrels-0.6.0.post0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
99
+ squirrels-0.6.0.post0.dist-info/entry_points.txt,sha256=i6vgjhJ3o_cdSFYofFcNY9DFMPr4MIcuwnkskSTXfJc,95
100
+ squirrels-0.6.0.post0.dist-info/licenses/LICENSE,sha256=qqERuumQtQVsMrEXvJHuecJvV2sLxbleEubd_Zk8dY8,11338
101
+ squirrels-0.6.0.post0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,190 +0,0 @@
1
- from typing import Annotated, Literal
2
- from pydantic import BaseModel, Field
3
- from datetime import datetime, date
4
-
5
- from . import _model_configs as mc, _sources as s
6
-
7
-
8
- class LoginReponse(BaseModel):
9
- access_token: Annotated[str, Field(examples=["encoded_jwt_token"], description="An encoded JSON web token to use subsequent API requests")]
10
- token_type: Annotated[str, Field(examples=["bearer"], description='Always "bearer" for Bearer token')]
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")]
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")]
14
-
15
-
16
- ## Parameters Response Models
17
-
18
- class ParameterOptionModel(BaseModel):
19
- id: Annotated[str, Field(examples=["my_option_id"], description="The unique identifier for the option")]
20
- label: Annotated[str, Field(examples=["My Option"], description="The human-friendly display name for the option")]
21
-
22
- class ParameterModelBase(BaseModel):
23
- widget_type: Annotated[str, Field(examples=["disabled"], description="The parameter type")]
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")]
25
- label: Annotated[str, Field(examples=["My Parameter"], description="The human-friendly display name for the parameter")]
26
- description: Annotated[str, Field(examples=[""], description="The description of the parameter")]
27
-
28
- class NoneParameterModel(ParameterModelBase):
29
- pass
30
-
31
- class SelectParameterModel(ParameterModelBase):
32
- options: Annotated[list[ParameterOptionModel], Field(description="The list of dropdown options as JSON objects containing 'id' and 'label' fields")]
33
- trigger_refresh: Annotated[bool, Field(description="A boolean that's set to true for parent parameters that require a new parameters API call when the selection changes")]
34
-
35
- class SingleSelectParameterModel(SelectParameterModel):
36
- widget_type: Annotated[str, Field(examples=["single_select"], description="The parameter type (set to 'single_select' for this model)")]
37
- selected_id: Annotated[str | None, Field(examples=["my_option_id"], description="The ID of the selected / default option")]
38
-
39
- class MultiSelectParameterModel(SelectParameterModel):
40
- widget_type: Annotated[str, Field(examples=["multi_select"], description="The parameter type (set to 'multi_select' for this model)")]
41
- show_select_all: Annotated[bool, Field(description="A boolean for whether there should be a toggle to select all options")]
42
- order_matters: Annotated[bool, Field(description="A boolean for whether the ordering of the input selections would affect the result of the dataset")]
43
- selected_ids: Annotated[list[str], Field(examples=[["my_option_id"]], description="A list of ids of the selected / default options")]
44
-
45
- class _DateTypeParameterModel(ParameterModelBase):
46
- min_date: Annotated[date | None, Field(examples=["2023-01-01"], description='A string in "yyyy-MM-dd" format for the minimum date')]
47
- max_date: Annotated[date | None, Field(examples=["2023-12-31"], description='A string in "yyyy-MM-dd" format for the maximum date')]
48
-
49
- class DateParameterModel(_DateTypeParameterModel):
50
- widget_type: Annotated[str, Field(examples=["date"], description="The parameter type (set to 'date' for this model)")]
51
- selected_date: Annotated[date, Field(examples=["2023-01-01"], description='A string in "yyyy-MM-dd" format for the selected / default date')]
52
-
53
- class DateRangeParameterModel(_DateTypeParameterModel):
54
- widget_type: Annotated[str, Field(examples=["date_range"], description="The parameter type (set to 'date_range' for this model)")]
55
- selected_start_date: Annotated[date, Field(examples=["2023-01-01"], description='A string in "yyyy-MM-dd" format for the selected / default start date')]
56
- selected_end_date: Annotated[date, Field(examples=["2023-12-31"], description='A string in "yyyy-MM-dd" format for the selected / default end date')]
57
-
58
- class _NumericParameterModel(ParameterModelBase):
59
- min_value: Annotated[float, Field(examples=[0], description="A number for the lower bound of the selectable number")]
60
- max_value: Annotated[float, Field(examples=[10], description="A number for the upper bound of the selectable number")]
61
- increment: Annotated[float, Field(examples=[1], description="A number for the selectable increments between the lower bound and upper bound")]
62
-
63
- class NumberParameterModel(_NumericParameterModel):
64
- widget_type: Annotated[str, Field(examples=["number"], description="The parameter type (set to 'number' for this model)")]
65
- selected_value: Annotated[float, Field(examples=[2], description="A number for the selected / default number")]
66
-
67
- class NumberRangeParameterModel(_NumericParameterModel):
68
- widget_type: Annotated[str, Field(examples=["number_range"], description="The parameter type (set to 'number_range' for this model)")]
69
- selected_lower_value: Annotated[float, Field(examples=[2], description="A number for the selected / default lower number")]
70
- selected_upper_value: Annotated[float, Field(examples=[8], description="A number for the selected / default upper number")]
71
-
72
- class TextParameterModel(ParameterModelBase):
73
- widget_type: Annotated[str, Field(examples=["text"], description="The parameter type (set to 'text' for this model)")]
74
- entered_text: Annotated[str, Field(examples=["sushi"], description="A string for the default entered text")]
75
- input_type: Annotated[str, Field(
76
- examples=["text", "textarea", "number", "date", "datetime-local", "month", "time", "color", "password"],
77
- description='A string for the input type (one of "text", "textarea", "number", "date", "datetime-local", "month", "time", "color", or "password")'
78
- )]
79
-
80
- ParametersListType = list[
81
- NoneParameterModel | SingleSelectParameterModel | MultiSelectParameterModel | DateParameterModel | DateRangeParameterModel |
82
- NumberParameterModel | NumberRangeParameterModel | TextParameterModel
83
- ]
84
-
85
- class ParametersModel(BaseModel):
86
- parameters: Annotated[ParametersListType, Field(description="The list of parameters for the dataset / dashboard")]
87
-
88
-
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"
97
-
98
- class ColumnModel(BaseModel):
99
- name: Annotated[str, Field(examples=["mycol"], description="Name of column")]
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)")]
106
-
107
- class SchemaModel(BaseModel):
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")]
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")]
168
-
169
- class DatasetResultModel(BaseModel):
170
- data_schema: Annotated[SchemaModel, Field(alias="schema", description="JSON object describing the schema of the dataset")]
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"]}],
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."
176
- )]
177
-
178
-
179
- ## Project Metadata Response Models
180
-
181
- class ProjectVersionModel(BaseModel):
182
- major_version: Annotated[int, Field(examples=[1])]
183
- data_catalog_path: Annotated[str, Field(examples=["/squirrels-v0/project/myproject/v1/data-catalog"])]
184
-
185
- class ProjectModel(BaseModel):
186
- name: Annotated[str, Field(examples=["myproject"])]
187
- version: Annotated[str, Field(examples=["v1"])]
188
- label: Annotated[str, Field(examples=["My Project"])]
189
- description: Annotated[str, Field(examples=["My project description"])]
190
- squirrels_version: Annotated[str, Field(examples=["0.1.0"])]
@@ -1,82 +0,0 @@
1
- import matplotlib.figure as figure, io, abc, typing
2
-
3
- from . import _constants as c
4
-
5
-
6
- class Dashboard(metaclass=abc.ABCMeta):
7
- """
8
- Abstract parent class for all Dashboard classes.
9
- """
10
-
11
- @property
12
- @abc.abstractmethod
13
- def _content(self) -> bytes | str:
14
- pass
15
-
16
- @property
17
- @abc.abstractmethod
18
- def _format(self) -> str:
19
- pass
20
-
21
-
22
- class PngDashboard(Dashboard):
23
- """
24
- Instantiate a Dashboard in PNG format from a matplotlib figure or bytes
25
- """
26
-
27
- def __init__(self, content: figure.Figure | io.BytesIO | bytes) -> None:
28
- """
29
- Constructor for PngDashboard
30
-
31
- Arguments:
32
- content: The content of the dashboard as a matplotlib.figure.Figure or bytes
33
- """
34
- if isinstance(content, figure.Figure):
35
- buffer = io.BytesIO()
36
- content.savefig(buffer, format=c.PNG)
37
- content = buffer.getvalue()
38
-
39
- if isinstance(content, io.BytesIO):
40
- content = content.getvalue()
41
-
42
- self.__content = content
43
-
44
- @property
45
- def _content(self) -> bytes:
46
- return self.__content
47
-
48
- @property
49
- def _format(self) -> typing.Literal['png']:
50
- return c.PNG
51
-
52
- def _repr_png_(self):
53
- return self._content
54
-
55
-
56
- class HtmlDashboard(Dashboard):
57
- """
58
- Instantiate a Dashboard from an HTML string
59
- """
60
-
61
- def __init__(self, content: io.StringIO | str) -> None:
62
- """
63
- Constructor for HtmlDashboard
64
-
65
- Arguments:
66
- content: The content of the dashboard as HTML string
67
- """
68
- if isinstance(content, io.StringIO):
69
- content = content.getvalue()
70
-
71
- self.__content = content
72
-
73
- @property
74
- def _content(self) -> str:
75
- return self.__content
76
-
77
- @property
78
- def _format(self) -> typing.Literal['html']:
79
- return c.HTML
80
-
81
- def _repr_html_(self):
82
- return self._content
@@ -1,79 +0,0 @@
1
- from typing import Type, TypeVar, Callable, Coroutine, Any
2
- from enum import Enum
3
- from dataclasses import dataclass
4
- from pydantic import BaseModel, Field
5
- import os, time
6
-
7
- from ._arguments._run_time_args import DashboardArgs
8
- from ._py_module import PyModule
9
- from ._manifest import AnalyticsOutputConfig
10
- from ._exceptions import InvalidInputError, ConfigurationError, FileExecutionError
11
- from . import _constants as c, _dashboard_types as d, _utils as u
12
-
13
- T = TypeVar('T', bound=d.Dashboard)
14
-
15
-
16
- class DashboardFormat(Enum):
17
- PNG = "png"
18
- HTML = "html"
19
-
20
- class DashboardDependencies(BaseModel):
21
- name: str
22
- dataset: str
23
- fixed_parameters: list[dict[str, str]] = Field(default_factory=list)
24
-
25
- class DashboardConfig(AnalyticsOutputConfig):
26
- format: DashboardFormat = Field(default=DashboardFormat.PNG)
27
- depends_on: list[DashboardDependencies] = Field(default_factory=list)
28
-
29
-
30
- @dataclass
31
- class DashboardDefinition:
32
- dashboard_name: str
33
- filepath: str
34
- config: DashboardConfig
35
-
36
- @property
37
- def dashboard_func(self) -> Callable[[DashboardArgs], Coroutine[Any, Any, d.Dashboard]]:
38
- if not hasattr(self, '_dashboard_func'):
39
- module = PyModule(self.filepath)
40
- self._dashboard_func = module.get_func_or_class(c.MAIN_FUNC)
41
- return self._dashboard_func
42
-
43
- def get_dashboard_format(self) -> str:
44
- return self.config.format.value
45
-
46
- async def get_dashboard(self, args: DashboardArgs, *, dashboard_type: Type[T] = d.Dashboard) -> T:
47
- try:
48
- dashboard = await self.dashboard_func(args)
49
- assert isinstance(dashboard, dashboard_type), f"Function does not return expected Dashboard type: {dashboard_type}"
50
- except (InvalidInputError, ConfigurationError, FileExecutionError) as e:
51
- raise e
52
- except Exception as e:
53
- raise FileExecutionError(f'Failed to run "{c.MAIN_FUNC}" function for dashboard "{self.dashboard_name}"', e) from e
54
-
55
- return dashboard
56
-
57
-
58
- class DashboardsIO:
59
-
60
- @classmethod
61
- def load_files(cls, logger: u.Logger, base_path: str) -> dict[str, DashboardDefinition]:
62
- start = time.time()
63
-
64
- dashboards_by_name = {}
65
- for dp, _, filenames in os.walk(u.Path(base_path, c.DASHBOARDS_FOLDER)):
66
- for file in filenames:
67
- filepath = os.path.join(dp, file)
68
- file_stem, extension = os.path.splitext(file)
69
- if not extension == '.py':
70
- continue
71
-
72
- # Check for corresponding .yml file
73
- yml_path = os.path.join(dp, file_stem + '.yml')
74
- config_dict = u.load_yaml_config(yml_path) if os.path.exists(yml_path) else {}
75
- config = DashboardConfig(name=file_stem, **config_dict)
76
- dashboards_by_name[file_stem] = DashboardDefinition(file_stem, filepath, config)
77
-
78
- logger.log_activity_time("loading files for dashboards", start)
79
- return dashboards_by_name