squirrels 0.5.0b1__py3-none-any.whl → 0.5.0b3__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.
- dateutils/__init__.py +6 -0
- dateutils/_enums.py +25 -0
- squirrels/dateutils.py → dateutils/_implementation.py +58 -111
- dateutils/types.py +6 -0
- squirrels/__init__.py +7 -13
- squirrels/_api_server.py +5 -5
- squirrels/{arguments/init_time_args.py → _arguments/_init_time_args.py} +2 -2
- squirrels/{arguments/run_time_args.py → _arguments/_run_time_args.py} +4 -26
- squirrels/_auth.py +2 -2
- squirrels/_command_line.py +13 -9
- squirrels/_connection_set.py +5 -5
- squirrels/_constants.py +1 -1
- squirrels/_dashboard_types.py +82 -0
- squirrels/_dashboards_io.py +2 -2
- squirrels/_data_sources.py +564 -0
- squirrels/_exceptions.py +1 -1
- squirrels/_initializer.py +82 -58
- squirrels/_manifest.py +5 -5
- squirrels/_model_builder.py +2 -0
- squirrels/_model_configs.py +3 -3
- squirrels/_model_queries.py +1 -1
- squirrels/_models.py +28 -14
- squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.py +4 -4
- squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.yml +2 -2
- squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
- squirrels/{package_data → _package_data}/base_project/models/builds/build_example.py +2 -2
- squirrels/{package_data → _package_data}/base_project/models/builds/build_example.sql +1 -1
- squirrels/{package_data → _package_data}/base_project/models/builds/build_example.yml +2 -0
- squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.sql +1 -1
- squirrels/_package_data/base_project/models/federates/federate_example.py +41 -0
- squirrels/_package_data/base_project/models/federates/federate_example.sql +25 -0
- squirrels/{package_data → _package_data}/base_project/models/federates/federate_example.yml +6 -6
- squirrels/{package_data → _package_data}/base_project/parameters.yml +9 -8
- squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
- squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +14 -16
- squirrels/{package_data → _package_data}/base_project/pyconfigs/parameters.py +13 -8
- squirrels/{package_data → _package_data}/base_project/pyconfigs/user.py +2 -2
- squirrels/_parameter_configs.py +34 -34
- squirrels/_parameter_options.py +348 -0
- squirrels/_parameter_sets.py +18 -18
- squirrels/_parameters.py +1266 -0
- squirrels/_project.py +37 -12
- squirrels/_utils.py +5 -3
- squirrels/arguments.py +2 -0
- squirrels/connections.py +1 -0
- squirrels/dashboards.py +1 -82
- squirrels/data_sources.py +8 -563
- squirrels/parameter_options.py +8 -348
- squirrels/parameters.py +9 -1266
- squirrels/types.py +11 -0
- {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/METADATA +11 -17
- squirrels-0.5.0b3.dist-info/RECORD +80 -0
- squirrels/package_data/base_project/macros/macros_example.sql +0 -15
- squirrels/package_data/base_project/models/federates/federate_example.py +0 -44
- squirrels/package_data/base_project/models/federates/federate_example.sql +0 -17
- squirrels/package_data/base_project/pyconfigs/connections.py +0 -14
- squirrels-0.5.0b1.dist-info/RECORD +0 -70
- /squirrels/{dataset_result.py → _dataset_types.py} +0 -0
- /squirrels/{package_data → _package_data}/base_project/.env +0 -0
- /squirrels/{package_data → _package_data}/base_project/.env.example +0 -0
- /squirrels/{package_data → _package_data}/base_project/assets/expenses.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
- /squirrels/{package_data → _package_data}/base_project/connections.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +0 -0
- /squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +0 -0
- /squirrels/{package_data → _package_data}/base_project/docker/compose.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/duckdb_init.sql +0 -0
- /squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +0 -0
- /squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/models/sources.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.csv +0 -0
- /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.yml +0 -0
- /squirrels/{package_data → _package_data}/base_project/squirrels.yml.j2 +0 -0
- /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
- {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/WHEEL +0 -0
- {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/entry_points.txt +0 -0
- {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/licenses/LICENSE +0 -0
dateutils/__init__.py
ADDED
|
@@ -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
|
+
)
|
dateutils/_enums.py
ADDED
|
@@ -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
|
-
|
|
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
|
-
|
|
32
|
+
@dataclass
|
|
33
|
+
class DayIdxOfCalendarUnit(DateModifier):
|
|
58
34
|
"""
|
|
59
35
|
Interface for adjusting a date to some day of calendar unit
|
|
60
36
|
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
37
|
+
idx: int
|
|
38
|
+
|
|
39
|
+
def __post_init__(self) -> None:
|
|
64
40
|
if self.idx == 0:
|
|
65
|
-
raise
|
|
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(
|
|
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
|
-
|
|
80
|
-
|
|
55
|
+
num_months_in_cycle: int
|
|
56
|
+
first_month_of_cycle: MonthEnum = MonthEnum.January
|
|
81
57
|
|
|
82
|
-
def
|
|
83
|
-
super().
|
|
84
|
-
self.
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
92
|
-
first_month_of_curr_cycle = current_cycle * self.
|
|
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.
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
-
|
|
125
|
+
first_day_of_week: DayOfWeekEnum = DayOfWeekEnum.Monday
|
|
155
126
|
|
|
156
|
-
def
|
|
157
|
-
super().
|
|
158
|
-
self.
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
super().__init__()
|
|
173
|
-
self.offset = offset
|
|
142
|
+
offset: int
|
|
174
143
|
|
|
175
144
|
|
|
176
145
|
@dataclass
|
|
177
|
-
class OffsetYears(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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,
|
|
256
|
+
assert isinstance(step, OffsetUnits)
|
|
304
257
|
if step.offset == 0:
|
|
305
|
-
raise
|
|
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
|
-
|
|
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
|
-
|
|
323
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
357
|
-
return DateStringModifier(joined_modifiers, self.
|
|
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.
|
|
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.
|
|
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,
|
|
346
|
+
assert isinstance(step, OffsetUnits)
|
|
396
347
|
curr_date = self._get_input_date_obj(start_date_str, input_format)
|
|
397
|
-
output = self.
|
|
398
|
-
return [x.strftime(self.
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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]
|
dateutils/types.py
ADDED
squirrels/__init__.py
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
from ._version import __version__
|
|
2
2
|
|
|
3
|
-
from .arguments
|
|
4
|
-
from .arguments.run_time_args import AuthLoginArgs, AuthTokenArgs, ContextArgs, ModelArgs, DashboardArgs
|
|
3
|
+
from .arguments import *
|
|
5
4
|
|
|
6
|
-
from .
|
|
7
|
-
from .parameter_options import NumberParameterOption, NumberRangeParameterOption, TextParameterOption
|
|
5
|
+
from .connections import *
|
|
8
6
|
|
|
9
|
-
from .
|
|
10
|
-
from .parameters import NumberParameter, NumberRangeParameter, TextParameter, TextValue
|
|
7
|
+
from .parameter_options import *
|
|
11
8
|
|
|
12
|
-
from .
|
|
13
|
-
from .data_sources import NumberDataSource, NumberRangeDataSource, TextDataSource
|
|
9
|
+
from .parameters import *
|
|
14
10
|
|
|
15
|
-
from .
|
|
11
|
+
from .data_sources import *
|
|
16
12
|
|
|
17
|
-
from .
|
|
13
|
+
from .dashboards import *
|
|
18
14
|
|
|
19
|
-
from .
|
|
15
|
+
from .types import *
|
|
20
16
|
|
|
21
17
|
from ._project import SquirrelsProject
|
|
22
|
-
|
|
23
|
-
from .dataset_result import DatasetResult
|
squirrels/_api_server.py
CHANGED
|
@@ -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 .
|
|
20
|
+
from ._dashboard_types import Dashboard
|
|
21
21
|
from ._project import SquirrelsProject
|
|
22
|
-
from .
|
|
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
|
|
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
|
|
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}
|
|
@@ -1,31 +1,9 @@
|
|
|
1
1
|
from typing import Callable, Any, Coroutine
|
|
2
2
|
import polars as pl
|
|
3
3
|
|
|
4
|
-
from .
|
|
4
|
+
from ._init_time_args import ParametersArgs, BuildModelArgs
|
|
5
5
|
from .._auth import BaseUser
|
|
6
|
-
from ..
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class AuthLoginArgs(_WithConnectionDictArgs):
|
|
10
|
-
|
|
11
|
-
def __init__(
|
|
12
|
-
self, conn_args: ConnectionsArgs, _connections: dict[str, Any],
|
|
13
|
-
username: str,
|
|
14
|
-
password: str
|
|
15
|
-
):
|
|
16
|
-
super().__init__(conn_args.project_path, conn_args.proj_vars, conn_args.env_vars, _connections)
|
|
17
|
-
self.username = username
|
|
18
|
-
self.password = password
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class AuthTokenArgs(_WithConnectionDictArgs):
|
|
22
|
-
|
|
23
|
-
def __init__(
|
|
24
|
-
self, conn_args: ConnectionsArgs, _connections: dict[str, Any],
|
|
25
|
-
token: str
|
|
26
|
-
):
|
|
27
|
-
super().__init__(conn_args.project_path, conn_args.proj_vars, conn_args.env_vars, _connections)
|
|
28
|
-
self.token = token
|
|
6
|
+
from .._parameters import Parameter, TextValue
|
|
29
7
|
|
|
30
8
|
|
|
31
9
|
class ContextArgs(ParametersArgs):
|
|
@@ -57,7 +35,7 @@ class ContextArgs(ParametersArgs):
|
|
|
57
35
|
return self._traits.copy()
|
|
58
36
|
|
|
59
37
|
@property
|
|
60
|
-
def
|
|
38
|
+
def _placeholders_copy(self) -> dict[str, Any]:
|
|
61
39
|
"""
|
|
62
40
|
A dictionary of placeholder name to placeholder value
|
|
63
41
|
"""
|
|
@@ -103,7 +81,7 @@ class ModelArgs(BuildModelArgs, ContextArgs):
|
|
|
103
81
|
self.user = ctx_args.user
|
|
104
82
|
self._prms = ctx_args.prms
|
|
105
83
|
self._traits = ctx_args.traits
|
|
106
|
-
self._placeholders = ctx_args.
|
|
84
|
+
self._placeholders = ctx_args._placeholders_copy
|
|
107
85
|
self._connections = build_model_args.connections
|
|
108
86
|
self._dependencies = build_model_args.dependencies
|
|
109
87
|
self._ref = build_model_args.ref
|
squirrels/_auth.py
CHANGED
|
@@ -282,8 +282,8 @@ class Authenticator(_t.Generic[User]):
|
|
|
282
282
|
if not update_user:
|
|
283
283
|
raise InvalidInputError(101, f"User '{username}' already exists")
|
|
284
284
|
|
|
285
|
-
if username == c.ADMIN_USERNAME:
|
|
286
|
-
raise InvalidInputError(24, "
|
|
285
|
+
if username == c.ADMIN_USERNAME and user_data.get("is_admin") is False:
|
|
286
|
+
raise InvalidInputError(24, "Setting the admin user to non-admin is not permitted")
|
|
287
287
|
new_user = self.DbUser(password_hash=existing_user.password_hash, **user_data)
|
|
288
288
|
session.delete(existing_user)
|
|
289
289
|
else:
|
squirrels/_command_line.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from argparse import ArgumentParser, _SubParsersAction
|
|
2
|
-
import sys, asyncio, traceback, io,
|
|
2
|
+
import sys, asyncio, traceback, io, subprocess
|
|
3
3
|
|
|
4
4
|
sys.path.append('.')
|
|
5
5
|
|
|
@@ -11,10 +11,12 @@ from ._project import SquirrelsProject
|
|
|
11
11
|
from . import _constants as c, _utils as u
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def _run_duckdb_cli(project: SquirrelsProject):
|
|
14
|
+
def _run_duckdb_cli(project: SquirrelsProject, ui: bool):
|
|
15
15
|
_, target_init_path = u._read_duckdb_init_sql()
|
|
16
16
|
init_args = f"-init {target_init_path}" if target_init_path else ""
|
|
17
17
|
command = ['duckdb']
|
|
18
|
+
if ui:
|
|
19
|
+
command.append('-ui')
|
|
18
20
|
if init_args:
|
|
19
21
|
command.extend(init_args.split())
|
|
20
22
|
command.extend(['-readonly', project._duckdb_venv_path])
|
|
@@ -48,21 +50,22 @@ def main():
|
|
|
48
50
|
|
|
49
51
|
init_parser = add_subparser(subparsers, c.INIT_CMD, 'Create a new squirrels project')
|
|
50
52
|
|
|
51
|
-
init_parser.add_argument('name', nargs='?', type=str, help='The name of the project')
|
|
52
|
-
init_parser.add_argument('-
|
|
53
|
-
init_parser.add_argument('--
|
|
53
|
+
init_parser.add_argument('name', nargs='?', type=str, help='The name of the project folder to create. Ignored if --curr-dir is used')
|
|
54
|
+
init_parser.add_argument('--curr-dir', action='store_true', help='Create the project in the current directory')
|
|
55
|
+
init_parser.add_argument('--use-defaults', action='store_true', help='Use default values for unspecified options (except project folder name) instead of prompting for input')
|
|
54
56
|
init_parser.add_argument('--connections', type=str, choices=c.CONF_FORMAT_CHOICES, help=f'Configure database connections as yaml (default) or python')
|
|
55
57
|
init_parser.add_argument('--parameters', type=str, choices=c.CONF_FORMAT_CHOICES, help=f'Configure parameters as python (default) or yaml')
|
|
56
58
|
init_parser.add_argument('--build', type=str, choices=c.FILE_TYPE_CHOICES, help='Create build model as sql (default) or python file')
|
|
57
59
|
init_parser.add_argument('--federate', type=str, choices=c.FILE_TYPE_CHOICES, help='Create federated model as sql (default) or python file')
|
|
58
|
-
init_parser.add_argument('--dashboard',
|
|
60
|
+
init_parser.add_argument('--dashboard', type=str, choices=['y', 'n'], help=f'Include (y) or exclude (n, default) a sample dashboard file')
|
|
61
|
+
init_parser.add_argument('--admin-password', type=str, help='The password for the admin user. If --use-defaults is used, then a random password is generated')
|
|
59
62
|
|
|
60
63
|
def with_file_format_options(parser: ArgumentParser):
|
|
61
64
|
help_text = "Create model as sql (default) or python file"
|
|
62
65
|
parser.add_argument('--format', type=str, choices=c.FILE_TYPE_CHOICES, default=c.SQL_FILE_TYPE, help=help_text)
|
|
63
66
|
return parser
|
|
64
67
|
|
|
65
|
-
get_file_help_text = "Get a sample file for the squirrels project. If the file name already exists, it will be
|
|
68
|
+
get_file_help_text = "Get a sample file for the squirrels project. If the file name already exists, it will be suffixed with a timestamp."
|
|
66
69
|
get_file_parser = add_subparser(subparsers, c.GET_FILE_CMD, get_file_help_text)
|
|
67
70
|
get_file_subparsers = get_file_parser.add_subparsers(title='file_name', dest='file_name')
|
|
68
71
|
add_subparser(get_file_subparsers, c.DOTENV_FILE, f'Get sample {c.DOTENV_FILE} and {c.DOTENV_FILE}.example files')
|
|
@@ -104,6 +107,7 @@ def main():
|
|
|
104
107
|
build_parser.add_argument('--stage', type=str, help='If the venv file is in use, stage the duckdb file to replace the venv later')
|
|
105
108
|
|
|
106
109
|
duckdb_parser = add_subparser(subparsers, c.DUCKDB_CMD, 'Run the duckdb command line tool')
|
|
110
|
+
duckdb_parser.add_argument('--ui', action='store_true', help='Run the duckdb local UI')
|
|
107
111
|
|
|
108
112
|
run_parser = add_subparser(subparsers, c.RUN_CMD, 'Run the API server')
|
|
109
113
|
run_parser.add_argument('--build', action='store_true', help='Build the virtual data environment (with duckdb) first before running the API server')
|
|
@@ -116,7 +120,7 @@ def main():
|
|
|
116
120
|
if args.version:
|
|
117
121
|
print(__version__)
|
|
118
122
|
elif args.command == c.INIT_CMD:
|
|
119
|
-
Initializer(project_name=args.name,
|
|
123
|
+
Initializer(project_name=args.name, use_curr_dir=args.curr_dir).init_project(args)
|
|
120
124
|
elif args.command == c.GET_FILE_CMD:
|
|
121
125
|
Initializer().get_file(args)
|
|
122
126
|
elif args.command is None:
|
|
@@ -131,7 +135,7 @@ def main():
|
|
|
131
135
|
asyncio.run(task)
|
|
132
136
|
print()
|
|
133
137
|
elif args.command == c.DUCKDB_CMD:
|
|
134
|
-
_run_duckdb_cli(project)
|
|
138
|
+
_run_duckdb_cli(project, args.ui)
|
|
135
139
|
elif args.command == c.RUN_CMD:
|
|
136
140
|
if args.build:
|
|
137
141
|
task = project.build(full_refresh=True)
|