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.
Files changed (79) hide show
  1. dateutils/__init__.py +6 -0
  2. dateutils/_enums.py +25 -0
  3. squirrels/dateutils.py → dateutils/_implementation.py +58 -111
  4. dateutils/types.py +6 -0
  5. squirrels/__init__.py +7 -13
  6. squirrels/_api_server.py +5 -5
  7. squirrels/{arguments/init_time_args.py → _arguments/_init_time_args.py} +2 -2
  8. squirrels/{arguments/run_time_args.py → _arguments/_run_time_args.py} +4 -26
  9. squirrels/_auth.py +2 -2
  10. squirrels/_command_line.py +13 -9
  11. squirrels/_connection_set.py +5 -5
  12. squirrels/_constants.py +1 -1
  13. squirrels/_dashboard_types.py +82 -0
  14. squirrels/_dashboards_io.py +2 -2
  15. squirrels/_data_sources.py +564 -0
  16. squirrels/_exceptions.py +1 -1
  17. squirrels/_initializer.py +82 -58
  18. squirrels/_manifest.py +5 -5
  19. squirrels/_model_builder.py +2 -0
  20. squirrels/_model_configs.py +3 -3
  21. squirrels/_model_queries.py +1 -1
  22. squirrels/_models.py +28 -14
  23. squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.py +4 -4
  24. squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.yml +2 -2
  25. squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  26. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.py +2 -2
  27. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.sql +1 -1
  28. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.yml +2 -0
  29. squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.sql +1 -1
  30. squirrels/_package_data/base_project/models/federates/federate_example.py +41 -0
  31. squirrels/_package_data/base_project/models/federates/federate_example.sql +25 -0
  32. squirrels/{package_data → _package_data}/base_project/models/federates/federate_example.yml +6 -6
  33. squirrels/{package_data → _package_data}/base_project/parameters.yml +9 -8
  34. squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
  35. squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +14 -16
  36. squirrels/{package_data → _package_data}/base_project/pyconfigs/parameters.py +13 -8
  37. squirrels/{package_data → _package_data}/base_project/pyconfigs/user.py +2 -2
  38. squirrels/_parameter_configs.py +34 -34
  39. squirrels/_parameter_options.py +348 -0
  40. squirrels/_parameter_sets.py +18 -18
  41. squirrels/_parameters.py +1266 -0
  42. squirrels/_project.py +37 -12
  43. squirrels/_utils.py +5 -3
  44. squirrels/arguments.py +2 -0
  45. squirrels/connections.py +1 -0
  46. squirrels/dashboards.py +1 -82
  47. squirrels/data_sources.py +8 -563
  48. squirrels/parameter_options.py +8 -348
  49. squirrels/parameters.py +9 -1266
  50. squirrels/types.py +11 -0
  51. {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/METADATA +11 -17
  52. squirrels-0.5.0b3.dist-info/RECORD +80 -0
  53. squirrels/package_data/base_project/macros/macros_example.sql +0 -15
  54. squirrels/package_data/base_project/models/federates/federate_example.py +0 -44
  55. squirrels/package_data/base_project/models/federates/federate_example.sql +0 -17
  56. squirrels/package_data/base_project/pyconfigs/connections.py +0 -14
  57. squirrels-0.5.0b1.dist-info/RECORD +0 -70
  58. /squirrels/{dataset_result.py → _dataset_types.py} +0 -0
  59. /squirrels/{package_data → _package_data}/base_project/.env +0 -0
  60. /squirrels/{package_data → _package_data}/base_project/.env.example +0 -0
  61. /squirrels/{package_data → _package_data}/base_project/assets/expenses.db +0 -0
  62. /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
  63. /squirrels/{package_data → _package_data}/base_project/connections.yml +0 -0
  64. /squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +0 -0
  65. /squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +0 -0
  66. /squirrels/{package_data → _package_data}/base_project/docker/compose.yml +0 -0
  67. /squirrels/{package_data → _package_data}/base_project/duckdb_init.sql +0 -0
  68. /squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +0 -0
  69. /squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.yml +0 -0
  70. /squirrels/{package_data → _package_data}/base_project/models/sources.yml +0 -0
  71. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
  72. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.yml +0 -0
  73. /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.csv +0 -0
  74. /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.yml +0 -0
  75. /squirrels/{package_data → _package_data}/base_project/squirrels.yml.j2 +0 -0
  76. /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
  77. {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/WHEEL +0 -0
  78. {squirrels-0.5.0b1.dist-info → squirrels-0.5.0b3.dist-info}/entry_points.txt +0 -0
  79. {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
- 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]
dateutils/types.py ADDED
@@ -0,0 +1,6 @@
1
+ from ._implementation import (
2
+ DateModifier,
3
+ DayIdxOfCalendarUnit,
4
+ OffsetUnits,
5
+ DateRepresentationModifier
6
+ )
squirrels/__init__.py CHANGED
@@ -1,23 +1,17 @@
1
1
  from ._version import __version__
2
2
 
3
- from .arguments.init_time_args import ConnectionsArgs, ParametersArgs, BuildModelArgs
4
- from .arguments.run_time_args import AuthLoginArgs, AuthTokenArgs, ContextArgs, ModelArgs, DashboardArgs
3
+ from .arguments import *
5
4
 
6
- from .parameter_options import SelectParameterOption, DateParameterOption, DateRangeParameterOption
7
- from .parameter_options import NumberParameterOption, NumberRangeParameterOption, TextParameterOption
5
+ from .connections import *
8
6
 
9
- from .parameters import SingleSelectParameter, MultiSelectParameter, DateParameter, DateRangeParameter
10
- from .parameters import NumberParameter, NumberRangeParameter, TextParameter, TextValue
7
+ from .parameter_options import *
11
8
 
12
- from .data_sources import SelectDataSource, DateDataSource, DateRangeDataSource
13
- from .data_sources import NumberDataSource, NumberRangeDataSource, TextDataSource
9
+ from .parameters import *
14
10
 
15
- from .dashboards import PngDashboard, HtmlDashboard
11
+ from .data_sources import *
16
12
 
17
- from ._auth import BaseUser
13
+ from .dashboards import *
18
14
 
19
- from ._manifest import ConnectionProperties, ConnectionType
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 .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}
@@ -1,31 +1,9 @@
1
1
  from typing import Callable, Any, Coroutine
2
2
  import polars as pl
3
3
 
4
- from .init_time_args import _WithConnectionDictArgs, ParametersArgs, BuildModelArgs, ConnectionsArgs
4
+ from ._init_time_args import ParametersArgs, BuildModelArgs
5
5
  from .._auth import BaseUser
6
- from ..parameters import Parameter, TextValue
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 placeholders(self) -> dict[str, Any]:
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.placeholders
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, "Changing the admin user is not permitted")
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:
@@ -1,5 +1,5 @@
1
1
  from argparse import ArgumentParser, _SubParsersAction
2
- import sys, asyncio, traceback, io, os, subprocess
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('-o', '--overwrite', action='store_true', help="Overwrite files that already exist")
53
- init_parser.add_argument('--core', action='store_true', help='Include all core files')
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', action='store_true', help=f'Include a sample dashboard file')
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 prefixed with a timestamp."
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, overwrite=args.overwrite).init_project(args)
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)