squirrels 0.5.0b1__tar.gz → 0.5.0b3__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.
Files changed (113) hide show
  1. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/PKG-INFO +11 -17
  2. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/README.md +10 -16
  3. squirrels-0.5.0b3/dateutils/__init__.py +6 -0
  4. squirrels-0.5.0b3/dateutils/_enums.py +25 -0
  5. squirrels-0.5.0b1/squirrels/dateutils.py → squirrels-0.5.0b3/dateutils/_implementation.py +58 -111
  6. squirrels-0.5.0b3/dateutils/types.py +6 -0
  7. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/pyproject.toml +7 -1
  8. squirrels-0.5.0b3/squirrels/__init__.py +17 -0
  9. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_api_server.py +5 -5
  10. squirrels-0.5.0b1/squirrels/arguments/init_time_args.py → squirrels-0.5.0b3/squirrels/_arguments/_init_time_args.py +2 -2
  11. squirrels-0.5.0b1/squirrels/arguments/run_time_args.py → squirrels-0.5.0b3/squirrels/_arguments/_run_time_args.py +4 -26
  12. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_auth.py +2 -2
  13. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_command_line.py +13 -9
  14. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_connection_set.py +5 -5
  15. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_constants.py +1 -1
  16. squirrels-0.5.0b1/squirrels/dashboards.py → squirrels-0.5.0b3/squirrels/_dashboard_types.py +12 -12
  17. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_dashboards_io.py +2 -2
  18. squirrels-0.5.0b1/squirrels/data_sources.py → squirrels-0.5.0b3/squirrels/_data_sources.py +56 -55
  19. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_exceptions.py +1 -1
  20. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_initializer.py +82 -58
  21. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_manifest.py +5 -5
  22. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_model_builder.py +2 -0
  23. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_model_configs.py +3 -3
  24. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_model_queries.py +1 -1
  25. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_models.py +28 -14
  26. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/dashboards/dashboard_example.py +4 -4
  27. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/dashboards/dashboard_example.yml +2 -2
  28. squirrels-0.5.0b3/squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  29. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/builds/build_example.py +2 -2
  30. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/builds/build_example.sql +1 -1
  31. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/builds/build_example.yml +2 -0
  32. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/dbviews/dbview_example.sql +1 -1
  33. squirrels-0.5.0b3/squirrels/_package_data/base_project/models/federates/federate_example.py +41 -0
  34. squirrels-0.5.0b3/squirrels/_package_data/base_project/models/federates/federate_example.sql +25 -0
  35. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/federates/federate_example.yml +6 -6
  36. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/parameters.yml +9 -8
  37. squirrels-0.5.0b3/squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
  38. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/pyconfigs/context.py +14 -16
  39. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/pyconfigs/parameters.py +13 -8
  40. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/pyconfigs/user.py +2 -2
  41. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_parameter_configs.py +34 -34
  42. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_parameter_sets.py +18 -18
  43. squirrels-0.5.0b1/squirrels/parameters.py → squirrels-0.5.0b3/squirrels/_parameters.py +54 -54
  44. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_project.py +37 -12
  45. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_utils.py +5 -3
  46. squirrels-0.5.0b3/squirrels/arguments.py +2 -0
  47. squirrels-0.5.0b3/squirrels/connections.py +1 -0
  48. squirrels-0.5.0b3/squirrels/dashboards.py +1 -0
  49. squirrels-0.5.0b3/squirrels/data_sources.py +8 -0
  50. squirrels-0.5.0b3/squirrels/parameter_options.py +8 -0
  51. squirrels-0.5.0b3/squirrels/parameters.py +9 -0
  52. squirrels-0.5.0b3/squirrels/types.py +11 -0
  53. squirrels-0.5.0b1/.cursorignore +0 -2
  54. squirrels-0.5.0b1/.github/workflows/python-publish.yml +0 -45
  55. squirrels-0.5.0b1/database_elt/expenses/.gitignore +0 -1
  56. squirrels-0.5.0b1/database_elt/expenses/create-expenses.py +0 -90
  57. squirrels-0.5.0b1/database_elt/expenses/create-lookups.py +0 -55
  58. squirrels-0.5.0b1/database_elt/seattle_weather/create_db.py +0 -13
  59. squirrels-0.5.0b1/database_elt/seattle_weather/seattle-weather.csv +0 -1462
  60. squirrels-0.5.0b1/squirrels/__init__.py +0 -23
  61. squirrels-0.5.0b1/squirrels/package_data/base_project/macros/macros_example.sql +0 -15
  62. squirrels-0.5.0b1/squirrels/package_data/base_project/models/federates/federate_example.py +0 -44
  63. squirrels-0.5.0b1/squirrels/package_data/base_project/models/federates/federate_example.sql +0 -17
  64. squirrels-0.5.0b1/squirrels/package_data/base_project/pyconfigs/connections.py +0 -14
  65. squirrels-0.5.0b1/tests/__init__.py +0 -0
  66. squirrels-0.5.0b1/tests/_auth_test.py +0 -166
  67. squirrels-0.5.0b1/tests/_connection_set_test.py +0 -46
  68. squirrels-0.5.0b1/tests/_manifest_test.py +0 -290
  69. squirrels-0.5.0b1/tests/_model_builder_test.py +0 -168
  70. squirrels-0.5.0b1/tests/_model_configs_test.py +0 -16
  71. squirrels-0.5.0b1/tests/_models_basic_test.py +0 -152
  72. squirrels-0.5.0b1/tests/_models_test.py +0 -167
  73. squirrels-0.5.0b1/tests/_seeds_test.py +0 -124
  74. squirrels-0.5.0b1/tests/_sources_test.py +0 -151
  75. squirrels-0.5.0b1/tests/_utils_test.py +0 -22
  76. squirrels-0.5.0b1/tests/arguments/run_time_args_test.py +0 -16
  77. squirrels-0.5.0b1/tests/conftest.py +0 -19
  78. squirrels-0.5.0b1/tests/data_sources_test.py +0 -206
  79. squirrels-0.5.0b1/tests/dateutils_test.py +0 -132
  80. squirrels-0.5.0b1/tests/parameter_configs_tests/_parameter_configs_test.py +0 -184
  81. squirrels-0.5.0b1/tests/parameter_configs_tests/_parameter_sets_test.py +0 -237
  82. squirrels-0.5.0b1/tests/parameter_configs_tests/conftest.py +0 -111
  83. squirrels-0.5.0b1/tests/parameter_options_test.py +0 -122
  84. squirrels-0.5.0b1/tests/parameters_test.py +0 -386
  85. squirrels-0.5.0b1/uv.lock +0 -2164
  86. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/.gitignore +0 -0
  87. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/LICENSE +0 -0
  88. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_api_response_models.py +0 -0
  89. /squirrels-0.5.0b1/squirrels/dataset_result.py → /squirrels-0.5.0b3/squirrels/_dataset_types.py +0 -0
  90. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/.env +0 -0
  91. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/.env.example +0 -0
  92. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/assets/expenses.db +0 -0
  93. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/assets/weather.db +0 -0
  94. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/connections.yml +0 -0
  95. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/docker/.dockerignore +0 -0
  96. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/docker/Dockerfile +0 -0
  97. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/docker/compose.yml +0 -0
  98. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/duckdb_init.sql +0 -0
  99. /squirrels-0.5.0b1/squirrels/package_data/base_project/.gitignore → /squirrels-0.5.0b3/squirrels/_package_data/base_project/gitignore +0 -0
  100. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/dbviews/dbview_example.yml +0 -0
  101. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/models/sources.yml +0 -0
  102. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/seeds/seed_categories.csv +0 -0
  103. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/seeds/seed_categories.yml +0 -0
  104. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/seeds/seed_subcategories.csv +0 -0
  105. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/seeds/seed_subcategories.yml +0 -0
  106. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/squirrels.yml.j2 +0 -0
  107. {squirrels-0.5.0b1/squirrels/package_data → squirrels-0.5.0b3/squirrels/_package_data}/base_project/tmp/.gitignore +0 -0
  108. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_package_loader.py +0 -0
  109. /squirrels-0.5.0b1/squirrels/parameter_options.py → /squirrels-0.5.0b3/squirrels/_parameter_options.py +0 -0
  110. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_py_module.py +0 -0
  111. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_seeds.py +0 -0
  112. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_sources.py +0 -0
  113. {squirrels-0.5.0b1 → squirrels-0.5.0b3}/squirrels/_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: squirrels
3
- Version: 0.5.0b1
3
+ Version: 0.5.0b3
4
4
  Summary: Squirrels - API Framework for Data Analytics
5
5
  Project-URL: Homepage, https://squirrels-analytics.github.io
6
6
  Project-URL: Repository, https://github.com/squirrels-analytics/squirrels
@@ -62,8 +62,8 @@ Here are a few of the things that squirrels can do:
62
62
  - Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
63
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 an interactive UI or by a command line that generates rendered sql queries and results (for a given set of parameter selections).
66
- - Define authentication logic (in `auth.py`) and authorize privacy scope per dataset (in `squirrels.yml`). The user's attributes can even be used in your query logic!
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 build tool `poetry`. Information on setting up poetry can be found at: https://python-poetry.org/docs/.
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
- poetry install
84
+ ```bash
85
+ uv sync
86
86
  ```
87
87
 
88
- And activate the virtual environment created by poetry with:
88
+ And activate the virtual environment with:
89
89
 
90
- ```
91
- poetry shell
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
- In poetry's virtual environment, run `pytest`.
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,7 +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.
@@ -24,8 +24,8 @@ Here are a few of the things that squirrels can do:
24
24
  - Configure parameter widgets (types include single-select, multi-select, date, number, etc.) for your datasets (in `parameters.py`).
25
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 an interactive UI or by a command line that generates rendered sql queries and results (for a given set of parameter selections).
28
- - 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!
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 build tool `poetry`. Information on setting up poetry can be found at: https://python-poetry.org/docs/.
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
- poetry install
46
+ ```bash
47
+ uv sync
48
48
  ```
49
49
 
50
- And activate the virtual environment created by poetry with:
50
+ And activate the virtual environment with:
51
51
 
52
- ```
53
- poetry shell
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
- In poetry's virtual environment, run `pytest`.
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.
@@ -0,0 +1,6 @@
1
+ from ._enums import DayOfWeekEnum, MonthEnum
2
+ from ._implementation import (
3
+ DayIdxOfMonthsCycle, DayIdxOfYear, DayIdxOfQuarter, DayIdxOfMonth, DayIdxOfWeek,
4
+ OffsetYears, OffsetMonths, OffsetWeeks, OffsetDays,
5
+ DateModPipeline, DateStringModifier, TimestampModifier
6
+ )
@@ -0,0 +1,25 @@
1
+ from enum import Enum
2
+
3
+
4
+ class DayOfWeekEnum(Enum):
5
+ Sunday = 0
6
+ Monday = 1
7
+ Tuesday = 2
8
+ Wednesday = 3
9
+ Thursday = 4
10
+ Friday = 5
11
+ Saturday = 6
12
+
13
+ class MonthEnum(Enum):
14
+ January = 1
15
+ February = 2
16
+ March = 3
17
+ April = 4
18
+ May = 5
19
+ June = 6
20
+ July = 7
21
+ August = 8
22
+ September = 9
23
+ October = 10
24
+ November = 11
25
+ December = 12
@@ -3,33 +3,8 @@ from dataclasses import dataclass
3
3
  from datetime import date as Date, datetime
4
4
  from dateutil.relativedelta import relativedelta
5
5
  from abc import ABCMeta, abstractmethod
6
- from enum import Enum
7
-
8
- from . import _utils as u
9
-
10
-
11
- class DayOfWeek(Enum):
12
- Sunday = 0
13
- Monday = 1
14
- Tuesday = 2
15
- Wednesday = 3
16
- Thursday = 4
17
- Friday = 5
18
- Saturday = 6
19
-
20
- class Month(Enum):
21
- January = 1
22
- February = 2
23
- March = 3
24
- April = 4
25
- May = 5
26
- June = 6
27
- July = 7
28
- August = 8
29
- September = 9
30
- October = 10
31
- November = 11
32
- December = 12
6
+
7
+ from ._enums import DayOfWeekEnum, MonthEnum
33
8
 
34
9
 
35
10
  class DateModifier(metaclass=ABCMeta):
@@ -54,20 +29,21 @@ class DateModifier(metaclass=ABCMeta):
54
29
  return datetype(year, month, day)
55
30
 
56
31
 
57
- class _DayIdxOfCalendarUnit(DateModifier):
32
+ @dataclass
33
+ class DayIdxOfCalendarUnit(DateModifier):
58
34
  """
59
35
  Interface for adjusting a date to some day of calendar unit
60
36
  """
61
- def __init__(self, idx: int) -> None:
62
- super().__init__()
63
- self.idx = idx
37
+ idx: int
38
+
39
+ def __post_init__(self) -> None:
64
40
  if self.idx == 0:
65
- raise u.ConfigurationError(f"For constructors of class names that start with DayIdxOf_, idx cannot be zero")
41
+ raise ValueError(f"For constructors of class names that start with DayIdxOf_, idx cannot be zero")
66
42
  self.incr = self.idx - 1 if self.idx > 0 else self.idx
67
43
 
68
44
 
69
45
  @dataclass
70
- class DayIdxOfMonthsCycle(_DayIdxOfCalendarUnit):
46
+ class DayIdxOfMonthsCycle(DayIdxOfCalendarUnit):
71
47
  """
72
48
  DateModifier class to get the idx-th day of a cycle of months for an input date
73
49
 
@@ -76,23 +52,21 @@ class DayIdxOfMonthsCycle(_DayIdxOfCalendarUnit):
76
52
  num_months_in_cycle: 2 for one 6th of year, 3 for Quarter, 4 for one 3rd of year, 6 for half year, 12 for full year. Must fit evenly in 12
77
53
  first_month_of_cycle: The first month of months cycle of year. Default is January
78
54
  """
79
- _num_months_in_cycle: int
80
- _first_month_of_cycle: Month
55
+ num_months_in_cycle: int
56
+ first_month_of_cycle: MonthEnum = MonthEnum.January
81
57
 
82
- def __init__(self, idx: int, num_months_in_cycle: int, first_month_of_cycle: Month = Month.January) -> None:
83
- super().__init__(idx)
84
- self._num_months_in_cycle = num_months_in_cycle
85
- self._first_month_of_cycle = first_month_of_cycle
86
- if 12 % self._num_months_in_cycle != 0:
87
- raise u.ConfigurationError(f"Value X must fit evenly in 12")
88
- self.first_month_of_first_cycle = (self._first_month_of_cycle.value - 1) % self._num_months_in_cycle + 1
58
+ def __post_init__(self) -> None:
59
+ super().__post_init__()
60
+ if 12 % self.num_months_in_cycle != 0:
61
+ raise ValueError(f"Argument 'num_months_in_cycle' must fit evenly in 12")
62
+ self.first_month_of_first_cycle = (self.first_month_of_cycle.value - 1) % self.num_months_in_cycle + 1
89
63
 
90
64
  def modify(self, date: Date) -> Date:
91
- current_cycle = (date.month - self.first_month_of_first_cycle) % 12 // self._num_months_in_cycle
92
- first_month_of_curr_cycle = current_cycle * self._num_months_in_cycle + self.first_month_of_first_cycle
65
+ current_cycle = (date.month - self.first_month_of_first_cycle) % 12 // self.num_months_in_cycle
66
+ first_month_of_curr_cycle = current_cycle * self.num_months_in_cycle + self.first_month_of_first_cycle
93
67
  year = date.year if date.month >= first_month_of_curr_cycle else date.year - 1
94
68
  first_day = self._get_date(type(date), year, first_month_of_curr_cycle, 1)
95
- ref_date = first_day if self.idx > 0 else first_day + relativedelta(months=self._num_months_in_cycle)
69
+ ref_date = first_day if self.idx > 0 else first_day + relativedelta(months=self.num_months_in_cycle)
96
70
  return ref_date + relativedelta(days=self.incr)
97
71
 
98
72
 
@@ -106,7 +80,7 @@ class DayIdxOfYear(DayIdxOfMonthsCycle):
106
80
  first_month_of_year: The first month of year. Default is January
107
81
  """
108
82
 
109
- def __init__(self, idx: int, first_month_of_year: Month = Month.January):
83
+ def __init__(self, idx: int, first_month_of_year: MonthEnum = MonthEnum.January):
110
84
  super().__init__(idx, num_months_in_cycle=12, first_month_of_cycle=first_month_of_year)
111
85
 
112
86
 
@@ -120,12 +94,12 @@ class DayIdxOfQuarter(DayIdxOfMonthsCycle):
120
94
  first_month_of_quarter: The first month of first quarter. Default is January
121
95
  """
122
96
 
123
- def __init__(self, idx: int, first_month_of_quarter: Month = Month.January):
97
+ def __init__(self, idx: int, first_month_of_quarter: MonthEnum = MonthEnum.January):
124
98
  super().__init__(idx, num_months_in_cycle=3, first_month_of_cycle=first_month_of_quarter)
125
99
 
126
100
 
127
101
  @dataclass
128
- class DayIdxOfMonth(_DayIdxOfCalendarUnit):
102
+ class DayIdxOfMonth(DayIdxOfCalendarUnit):
129
103
  """
130
104
  DateModifier class to get the idx-th day of month of an input date
131
105
 
@@ -133,9 +107,6 @@ class DayIdxOfMonth(_DayIdxOfCalendarUnit):
133
107
  idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
134
108
  """
135
109
 
136
- def __init__(self, idx: int) -> None:
137
- super().__init__(idx)
138
-
139
110
  def modify(self, date: Date) -> Date:
140
111
  first_day = self._get_date(type(date), date.year, date.month, 1)
141
112
  ref_date = first_day if self.idx > 0 else first_day + relativedelta(months=1)
@@ -143,7 +114,7 @@ class DayIdxOfMonth(_DayIdxOfCalendarUnit):
143
114
 
144
115
 
145
116
  @dataclass
146
- class DayIdxOfWeek(_DayIdxOfCalendarUnit):
117
+ class DayIdxOfWeek(DayIdxOfCalendarUnit):
147
118
  """
148
119
  DateModifier class to get the idx-th day of week of an input date
149
120
 
@@ -151,12 +122,11 @@ class DayIdxOfWeek(_DayIdxOfCalendarUnit):
151
122
  idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
152
123
  first_day_of_week: The day of week identified as the "first". Default is Monday
153
124
  """
154
- _first_day_of_week: DayOfWeek
125
+ first_day_of_week: DayOfWeekEnum = DayOfWeekEnum.Monday
155
126
 
156
- def __init__(self, idx: int, first_day_of_week: DayOfWeek = DayOfWeek.Monday) -> None:
157
- super().__init__(idx)
158
- self._first_day_of_week = first_day_of_week
159
- self.first_dow_num = self._first_day_of_week.value
127
+ def __post_init__(self) -> None:
128
+ super().__post_init__()
129
+ self.first_dow_num = self.first_day_of_week.value
160
130
 
161
131
  def modify(self, date: Date) -> Date:
162
132
  distance_from_first_day = (1 + date.weekday() - self.first_dow_num) % 7
@@ -164,17 +134,16 @@ class DayIdxOfWeek(_DayIdxOfCalendarUnit):
164
134
  return date + relativedelta(days=total_incr)
165
135
 
166
136
 
167
- class _OffsetUnits(DateModifier):
137
+ @dataclass
138
+ class OffsetUnits(DateModifier):
168
139
  """
169
140
  Abstract DateModifier class to offset an input date by some number of some calendar unit
170
141
  """
171
- def __init__(self, offset: int) -> None:
172
- super().__init__()
173
- self.offset = offset
142
+ offset: int
174
143
 
175
144
 
176
145
  @dataclass
177
- class OffsetYears(_OffsetUnits):
146
+ class OffsetYears(OffsetUnits):
178
147
  """
179
148
  DateModifier class to offset an input date by some number of years
180
149
 
@@ -182,15 +151,12 @@ class OffsetYears(_OffsetUnits):
182
151
  offset: The number of years to offset the input date.
183
152
  """
184
153
 
185
- def __init__(self, offset: int) -> None:
186
- super().__init__(offset)
187
-
188
154
  def modify(self, date: Date) -> Date:
189
155
  return date + relativedelta(years=self.offset)
190
156
 
191
157
 
192
158
  @dataclass
193
- class OffsetMonths(_OffsetUnits):
159
+ class OffsetMonths(OffsetUnits):
194
160
  """
195
161
  DateModifier class to offset an input date by some number of months
196
162
 
@@ -198,15 +164,12 @@ class OffsetMonths(_OffsetUnits):
198
164
  offset: The number of months to offset the input date.
199
165
  """
200
166
 
201
- def __init__(self, offset: int) -> None:
202
- super().__init__(offset)
203
-
204
167
  def modify(self, date: Date) -> Date:
205
168
  return date + relativedelta(months=self.offset)
206
169
 
207
170
 
208
171
  @dataclass
209
- class OffsetWeeks(_OffsetUnits):
172
+ class OffsetWeeks(OffsetUnits):
210
173
  """
211
174
  DateModifier class to offset an input date by some number of weeks
212
175
 
@@ -214,15 +177,12 @@ class OffsetWeeks(_OffsetUnits):
214
177
  offset: The number of weeks to offset the input date.
215
178
  """
216
179
 
217
- def __init__(self, offset: int) -> None:
218
- super().__init__(offset)
219
-
220
180
  def modify(self, date: Date) -> Date:
221
181
  return date + relativedelta(weeks=self.offset)
222
182
 
223
183
 
224
184
  @dataclass
225
- class OffsetDays(_OffsetUnits):
185
+ class OffsetDays(OffsetUnits):
226
186
  """
227
187
  DateModifier class to offset an input date by some number of days
228
188
 
@@ -230,9 +190,6 @@ class OffsetDays(_OffsetUnits):
230
190
  offset: The number of days to offset the input date.
231
191
  """
232
192
 
233
- def __init__(self, offset: int) -> None:
234
- super().__init__(offset)
235
-
236
193
  def modify(self, date: Date) -> Date:
237
194
  return date + relativedelta(days=self.offset)
238
195
 
@@ -245,14 +202,10 @@ class DateModPipeline(DateModifier):
245
202
  Attributes:
246
203
  modifiers: The list of DateModifier's to apply in sequence.
247
204
  """
248
- _date_modifiers: Sequence[DateModifier]
205
+ date_modifiers: Sequence[DateModifier]
249
206
 
250
- def __init__(self, date_modifiers: Sequence[DateModifier]) -> None:
251
- super().__init__()
252
- self._date_modifiers = tuple(date_modifiers)
253
-
254
207
  def modify(self, date: Date) -> Date:
255
- for modifier in self._date_modifiers:
208
+ for modifier in self.date_modifiers:
256
209
  date = modifier.modify(date)
257
210
  return date
258
211
 
@@ -267,7 +220,7 @@ class DateModPipeline(DateModifier):
267
220
  Returns:
268
221
  A new sequence of DateModifier
269
222
  """
270
- joined_modifiers = tuple(self._date_modifiers) + tuple(date_modifiers)
223
+ joined_modifiers = tuple(self.date_modifiers) + tuple(date_modifiers)
271
224
  return joined_modifiers
272
225
 
273
226
  def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
@@ -300,9 +253,9 @@ class DateModPipeline(DateModifier):
300
253
  Returns:
301
254
  A list of datetime objects
302
255
  """
303
- assert isinstance(step, _OffsetUnits)
256
+ assert isinstance(step, OffsetUnits)
304
257
  if step.offset == 0:
305
- raise u.ConfigurationError(f"The length of 'step' must not be zero")
258
+ raise ValueError(f"The length of 'step' must not be zero")
306
259
 
307
260
  output: Sequence[Date] = []
308
261
  end_date = self.modify(start_date)
@@ -315,12 +268,15 @@ class DateModPipeline(DateModifier):
315
268
  return output
316
269
 
317
270
 
318
- class _DateRepresentationModifier(metaclass=ABCMeta):
271
+ @dataclass
272
+ class DateRepresentationModifier(metaclass=ABCMeta):
319
273
  """
320
274
  Abstract class for modifying other representations of dates (such as string or unix timestemp)
321
275
  """
322
- def __init__(self, date_modifiers: Sequence[DateModifier]):
323
- self.date_modifier = DateModPipeline(date_modifiers)
276
+ date_modifiers: Sequence[DateModifier]
277
+
278
+ def __post_init__(self) -> None:
279
+ self.date_mod_pipeline = DateModPipeline(self.date_modifiers)
324
280
 
325
281
  @abstractmethod
326
282
  def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
@@ -328,7 +284,7 @@ class _DateRepresentationModifier(metaclass=ABCMeta):
328
284
 
329
285
 
330
286
  @dataclass
331
- class DateStringModifier(_DateRepresentationModifier):
287
+ class DateStringModifier(DateRepresentationModifier):
332
288
  """
333
289
  Class to modify a string representation of a date given a DateModifier
334
290
 
@@ -336,12 +292,7 @@ class DateStringModifier(_DateRepresentationModifier):
336
292
  date_modifier: The DateModifier to apply on datetime objects
337
293
  date_format: Format of the output date string. Default is '%Y-%m-%d'
338
294
  """
339
- _date_modifiers: Sequence[DateModifier]
340
- _date_format: str
341
-
342
- def __init__(self, date_modifiers: Sequence[DateModifier], date_format: str = '%Y-%m-%d'):
343
- super().__init__(date_modifiers)
344
- self._date_format = date_format
295
+ date_format: str = '%Y-%m-%d'
345
296
 
346
297
  def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
347
298
  """
@@ -353,11 +304,11 @@ class DateStringModifier(_DateRepresentationModifier):
353
304
  Returns:
354
305
  A new DateStringModifier
355
306
  """
356
- joined_modifiers = self.date_modifier.get_joined_modifiers(date_modifiers)
357
- return DateStringModifier(joined_modifiers, self._date_format)
307
+ joined_modifiers = self.date_mod_pipeline.get_joined_modifiers(date_modifiers)
308
+ return DateStringModifier(joined_modifiers, self.date_format)
358
309
 
359
310
  def _get_input_date_obj(self, date_str: str, input_format: str | None = None) -> Date:
360
- input_format = self._date_format if input_format is None else input_format
311
+ input_format = self.date_format if input_format is None else input_format
361
312
  return datetime.strptime(date_str, input_format).date()
362
313
 
363
314
  def modify(self, date_str: str, input_format: str | None = None) -> str:
@@ -372,7 +323,7 @@ class DateStringModifier(_DateRepresentationModifier):
372
323
  The resulting date string
373
324
  """
374
325
  date_obj = self._get_input_date_obj(date_str, input_format)
375
- return self.date_modifier.modify(date_obj).strftime(self._date_format)
326
+ return self.date_mod_pipeline.modify(date_obj).strftime(self.date_format)
376
327
 
377
328
  def get_date_list(self, start_date_str: str, step: DateModifier, input_format: str | None = None) -> Sequence[str]:
378
329
  """
@@ -392,14 +343,14 @@ class DateStringModifier(_DateRepresentationModifier):
392
343
  Returns:
393
344
  A list of date strings
394
345
  """
395
- assert isinstance(step, _OffsetUnits)
346
+ assert isinstance(step, OffsetUnits)
396
347
  curr_date = self._get_input_date_obj(start_date_str, input_format)
397
- output = self.date_modifier.get_date_list(curr_date, step)
398
- return [x.strftime(self._date_format) for x in output]
348
+ output = self.date_mod_pipeline.get_date_list(curr_date, step)
349
+ return [x.strftime(self.date_format) for x in output]
399
350
 
400
351
 
401
352
  @dataclass
402
- class TimestampModifier(_DateRepresentationModifier):
353
+ class TimestampModifier(DateRepresentationModifier):
403
354
  """
404
355
  Class to modify a numeric representation of a date (as Unix/Epoch/POSIX timestamp) given a DateModifier
405
356
 
@@ -407,10 +358,6 @@ class TimestampModifier(_DateRepresentationModifier):
407
358
  date_modifier: The DateModifier to apply on datetime objects
408
359
  date_format: Format of the date string. Default is '%Y-%m-%d'
409
360
  """
410
- _date_modifiers: Sequence[DateModifier]
411
-
412
- def __init__(self, date_modifiers: Sequence[DateModifier]):
413
- super().__init__(date_modifiers)
414
361
 
415
362
  def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
416
363
  """
@@ -422,7 +369,7 @@ class TimestampModifier(_DateRepresentationModifier):
422
369
  Returns:
423
370
  A new TimestampModifier
424
371
  """
425
- joined_modifiers = self.date_modifier.get_joined_modifiers(date_modifiers)
372
+ joined_modifiers = self.date_mod_pipeline.get_joined_modifiers(date_modifiers)
426
373
  return TimestampModifier(joined_modifiers)
427
374
 
428
375
  def modify(self, timestamp: float) -> float:
@@ -436,7 +383,7 @@ class TimestampModifier(_DateRepresentationModifier):
436
383
  The resulting timestamp
437
384
  """
438
385
  date_obj = datetime.fromtimestamp(timestamp).date()
439
- modified_date = self.date_modifier.modify(date_obj)
386
+ modified_date = self.date_mod_pipeline.modify(date_obj)
440
387
  modified_datetime = datetime.combine(modified_date, datetime.min.time())
441
388
  return modified_datetime.timestamp()
442
389
 
@@ -458,5 +405,5 @@ class TimestampModifier(_DateRepresentationModifier):
458
405
  A list of timestamp as floats
459
406
  """
460
407
  curr_date = datetime.fromtimestamp(start_timestamp).date()
461
- output = self.date_modifier.get_date_list(curr_date, step)
408
+ output = self.date_mod_pipeline.get_date_list(curr_date, step)
462
409
  return [datetime.combine(x, datetime.min.time()).timestamp() for x in output]
@@ -0,0 +1,6 @@
1
+ from ._implementation import (
2
+ DateModifier,
3
+ DayIdxOfCalendarUnit,
4
+ OffsetUnits,
5
+ DateRepresentationModifier
6
+ )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "squirrels"
3
- version = "0.5.0b1"
3
+ version = "0.5.0b3"
4
4
  description = "Squirrels - API Framework for Data Analytics"
5
5
  authors = [{ name = "Tim Huang", email = "tim.yuting@hotmail.com" }]
6
6
  requires-python = "~=3.10"
@@ -58,6 +58,12 @@ dev = [
58
58
  "tqdm>=4.67.1,<5",
59
59
  ]
60
60
 
61
+ [tool.hatch.build.targets.sdist]
62
+ include = ["squirrels", "dateutils"]
63
+
64
+ [tool.hatch.build.targets.wheel]
65
+ include = ["squirrels", "dateutils"]
66
+
61
67
  [tool.uv]
62
68
  default-groups = [
63
69
  "test",
@@ -0,0 +1,17 @@
1
+ from ._version import __version__
2
+
3
+ from .arguments import *
4
+
5
+ from .connections import *
6
+
7
+ from .parameter_options import *
8
+
9
+ from .parameters import *
10
+
11
+ from .data_sources import *
12
+
13
+ from .dashboards import *
14
+
15
+ from .types import *
16
+
17
+ from ._project import SquirrelsProject
@@ -17,9 +17,9 @@ from ._version import __version__, sq_major_version
17
17
  from ._manifest import PermissionScope
18
18
  from ._auth import BaseUser, AccessToken, UserField
19
19
  from ._parameter_sets import ParameterSet
20
- from .dashboards import Dashboard
20
+ from ._dashboard_types import Dashboard
21
21
  from ._project import SquirrelsProject
22
- from .dataset_result import DatasetResult
22
+ from ._dataset_types import DatasetResult
23
23
  from ._parameter_configs import APIParamFieldInfo
24
24
 
25
25
  mimetypes.add_type('application/javascript', '.js')
@@ -189,17 +189,17 @@ class ApiServer:
189
189
  traceback.print_exception(exc.error, file=buffer)
190
190
  buffer.write(str(exc))
191
191
  response = JSONResponse(
192
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"An unexpected error occurred", "blame": "Squirrels project"}
192
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"An unexpected server error occurred", "blame": "Squirrels project"}
193
193
  )
194
194
  except ConfigurationError as exc:
195
195
  traceback.print_exc(file=buffer)
196
196
  response = JSONResponse(
197
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"An unexpected error occurred", "blame": "Squirrels project"}
197
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"An unexpected server error occurred", "blame": "Squirrels project"}
198
198
  )
199
199
  except Exception as exc:
200
200
  traceback.print_exc(file=buffer)
201
201
  response = JSONResponse(
202
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"An unexpected error occurred", "blame": "Squirrels framework"}
202
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"An unexpected server error occurred", "blame": "Squirrels framework"}
203
203
  )
204
204
 
205
205
  err_msg = buffer.getvalue()
@@ -88,14 +88,14 @@ class BuildModelArgs(_WithConnectionDictArgs):
88
88
 
89
89
  def run_sql_on_dataframes(self, sql_query: str, *, dataframes: dict[str, pl.LazyFrame] | None = None, **kwargs) -> pl.DataFrame:
90
90
  """
91
- Uses a dictionary of dataframes to execute a SQL query in an embedded in-memory database (sqlite or duckdb based on setting)
91
+ Uses a dictionary of dataframes to execute a SQL query in an embedded in-memory DuckDB database
92
92
 
93
93
  Arguments:
94
94
  sql_query: The SQL query to run
95
95
  dataframes: A dictionary of table names to their polars LazyFrame. If None, uses results of dependent models
96
96
 
97
97
  Returns:
98
- The result as a polars LazyFrame from running the query
98
+ The result as a polars DataFrame from running the query
99
99
  """
100
100
  if dataframes is None:
101
101
  dataframes = {x: self.ref(x) for x in self._dependencies}