squirrels 0.5.0b2__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 (77) hide show
  1. dateutils/__init__.py +6 -460
  2. dateutils/_enums.py +25 -0
  3. dateutils/_implementation.py +409 -0
  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/_connection_set.py +5 -5
  11. squirrels/_constants.py +1 -1
  12. squirrels/_dashboard_types.py +82 -0
  13. squirrels/_dashboards_io.py +2 -2
  14. squirrels/_data_sources.py +564 -0
  15. squirrels/_exceptions.py +1 -1
  16. squirrels/_initializer.py +31 -26
  17. squirrels/_manifest.py +5 -5
  18. squirrels/_model_configs.py +2 -2
  19. squirrels/_model_queries.py +1 -1
  20. squirrels/_models.py +28 -16
  21. squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.py +4 -4
  22. squirrels/{package_data → _package_data}/base_project/dashboards/dashboard_example.yml +2 -2
  23. squirrels/_package_data/base_project/macros/macros_example.sql +17 -0
  24. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.py +2 -2
  25. squirrels/{package_data → _package_data}/base_project/models/builds/build_example.sql +1 -1
  26. squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.sql +1 -1
  27. squirrels/_package_data/base_project/models/federates/federate_example.py +41 -0
  28. squirrels/_package_data/base_project/models/federates/federate_example.sql +25 -0
  29. squirrels/{package_data → _package_data}/base_project/models/federates/federate_example.yml +6 -6
  30. squirrels/{package_data → _package_data}/base_project/parameters.yml +9 -8
  31. squirrels/_package_data/base_project/pyconfigs/connections.py +14 -0
  32. squirrels/{package_data → _package_data}/base_project/pyconfigs/context.py +14 -16
  33. squirrels/{package_data → _package_data}/base_project/pyconfigs/parameters.py +13 -8
  34. squirrels/{package_data → _package_data}/base_project/pyconfigs/user.py +2 -2
  35. squirrels/_parameter_configs.py +34 -34
  36. squirrels/_parameter_options.py +348 -0
  37. squirrels/_parameter_sets.py +18 -18
  38. squirrels/_parameters.py +1266 -0
  39. squirrels/_project.py +37 -12
  40. squirrels/_utils.py +4 -2
  41. squirrels/arguments.py +2 -0
  42. squirrels/connections.py +1 -0
  43. squirrels/dashboards.py +1 -82
  44. squirrels/data_sources.py +8 -563
  45. squirrels/parameter_options.py +8 -348
  46. squirrels/parameters.py +9 -1266
  47. squirrels/types.py +11 -0
  48. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/METADATA +1 -1
  49. squirrels-0.5.0b3.dist-info/RECORD +80 -0
  50. squirrels/package_data/base_project/macros/macros_example.sql +0 -15
  51. squirrels/package_data/base_project/models/federates/federate_example.py +0 -44
  52. squirrels/package_data/base_project/models/federates/federate_example.sql +0 -17
  53. squirrels/package_data/base_project/pyconfigs/connections.py +0 -14
  54. squirrels-0.5.0b2.dist-info/RECORD +0 -70
  55. /squirrels/{dataset_result.py → _dataset_types.py} +0 -0
  56. /squirrels/{package_data → _package_data}/base_project/.env +0 -0
  57. /squirrels/{package_data → _package_data}/base_project/.env.example +0 -0
  58. /squirrels/{package_data → _package_data}/base_project/assets/expenses.db +0 -0
  59. /squirrels/{package_data → _package_data}/base_project/assets/weather.db +0 -0
  60. /squirrels/{package_data → _package_data}/base_project/connections.yml +0 -0
  61. /squirrels/{package_data → _package_data}/base_project/docker/.dockerignore +0 -0
  62. /squirrels/{package_data → _package_data}/base_project/docker/Dockerfile +0 -0
  63. /squirrels/{package_data → _package_data}/base_project/docker/compose.yml +0 -0
  64. /squirrels/{package_data → _package_data}/base_project/duckdb_init.sql +0 -0
  65. /squirrels/{package_data/base_project/.gitignore → _package_data/base_project/gitignore} +0 -0
  66. /squirrels/{package_data → _package_data}/base_project/models/builds/build_example.yml +0 -0
  67. /squirrels/{package_data → _package_data}/base_project/models/dbviews/dbview_example.yml +0 -0
  68. /squirrels/{package_data → _package_data}/base_project/models/sources.yml +0 -0
  69. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.csv +0 -0
  70. /squirrels/{package_data → _package_data}/base_project/seeds/seed_categories.yml +0 -0
  71. /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.csv +0 -0
  72. /squirrels/{package_data → _package_data}/base_project/seeds/seed_subcategories.yml +0 -0
  73. /squirrels/{package_data → _package_data}/base_project/squirrels.yml.j2 +0 -0
  74. /squirrels/{package_data → _package_data}/base_project/tmp/.gitignore +0 -0
  75. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/WHEEL +0 -0
  76. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/entry_points.txt +0 -0
  77. {squirrels-0.5.0b2.dist-info → squirrels-0.5.0b3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,409 @@
1
+ from typing import Sequence, Type
2
+ from dataclasses import dataclass
3
+ from datetime import date as Date, datetime
4
+ from dateutil.relativedelta import relativedelta
5
+ from abc import ABCMeta, abstractmethod
6
+
7
+ from ._enums import DayOfWeekEnum, MonthEnum
8
+
9
+
10
+ class DateModifier(metaclass=ABCMeta):
11
+ """
12
+ Interface for all Date modification classes, and declares a "modify" method
13
+ """
14
+
15
+ @abstractmethod
16
+ def modify(self, date: Date) -> Date:
17
+ """
18
+ Method to be overwritten, modifies the input date
19
+
20
+ Arguments:
21
+ date: The input date to modify.
22
+
23
+ Returns:
24
+ The modified date.
25
+ """
26
+ pass
27
+
28
+ def _get_date(self, datetype: Type, year: int, month: int, day: int) -> Date:
29
+ return datetype(year, month, day)
30
+
31
+
32
+ @dataclass
33
+ class DayIdxOfCalendarUnit(DateModifier):
34
+ """
35
+ Interface for adjusting a date to some day of calendar unit
36
+ """
37
+ idx: int
38
+
39
+ def __post_init__(self) -> None:
40
+ if self.idx == 0:
41
+ raise ValueError(f"For constructors of class names that start with DayIdxOf_, idx cannot be zero")
42
+ self.incr = self.idx - 1 if self.idx > 0 else self.idx
43
+
44
+
45
+ @dataclass
46
+ class DayIdxOfMonthsCycle(DayIdxOfCalendarUnit):
47
+ """
48
+ DateModifier class to get the idx-th day of a cycle of months for an input date
49
+
50
+ Attributes:
51
+ idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
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
53
+ first_month_of_cycle: The first month of months cycle of year. Default is January
54
+ """
55
+ num_months_in_cycle: int
56
+ first_month_of_cycle: MonthEnum = MonthEnum.January
57
+
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
63
+
64
+ def modify(self, date: Date) -> Date:
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
67
+ year = date.year if date.month >= first_month_of_curr_cycle else date.year - 1
68
+ first_day = self._get_date(type(date), year, first_month_of_curr_cycle, 1)
69
+ ref_date = first_day if self.idx > 0 else first_day + relativedelta(months=self.num_months_in_cycle)
70
+ return ref_date + relativedelta(days=self.incr)
71
+
72
+
73
+ @dataclass
74
+ class DayIdxOfYear(DayIdxOfMonthsCycle):
75
+ """
76
+ DateModifier class to get the idx-th day of year of an input date
77
+
78
+ Attributes:
79
+ idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
80
+ first_month_of_year: The first month of year. Default is January
81
+ """
82
+
83
+ def __init__(self, idx: int, first_month_of_year: MonthEnum = MonthEnum.January):
84
+ super().__init__(idx, num_months_in_cycle=12, first_month_of_cycle=first_month_of_year)
85
+
86
+
87
+ @dataclass
88
+ class DayIdxOfQuarter(DayIdxOfMonthsCycle):
89
+ """
90
+ DateModifier class to get the idx-th day of quarter of an input date
91
+
92
+ Attributes:
93
+ idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
94
+ first_month_of_quarter: The first month of first quarter. Default is January
95
+ """
96
+
97
+ def __init__(self, idx: int, first_month_of_quarter: MonthEnum = MonthEnum.January):
98
+ super().__init__(idx, num_months_in_cycle=3, first_month_of_cycle=first_month_of_quarter)
99
+
100
+
101
+ @dataclass
102
+ class DayIdxOfMonth(DayIdxOfCalendarUnit):
103
+ """
104
+ DateModifier class to get the idx-th day of month of an input date
105
+
106
+ Attributes:
107
+ idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
108
+ """
109
+
110
+ def modify(self, date: Date) -> Date:
111
+ first_day = self._get_date(type(date), date.year, date.month, 1)
112
+ ref_date = first_day if self.idx > 0 else first_day + relativedelta(months=1)
113
+ return ref_date + relativedelta(days=self.incr)
114
+
115
+
116
+ @dataclass
117
+ class DayIdxOfWeek(DayIdxOfCalendarUnit):
118
+ """
119
+ DateModifier class to get the idx-th day of week of an input date
120
+
121
+ Attributes:
122
+ idx: 1 for first, 2 for second, etc. Or, -1 for last, -2 for second last, etc. Must not be 0
123
+ first_day_of_week: The day of week identified as the "first". Default is Monday
124
+ """
125
+ first_day_of_week: DayOfWeekEnum = DayOfWeekEnum.Monday
126
+
127
+ def __post_init__(self) -> None:
128
+ super().__post_init__()
129
+ self.first_dow_num = self.first_day_of_week.value
130
+
131
+ def modify(self, date: Date) -> Date:
132
+ distance_from_first_day = (1 + date.weekday() - self.first_dow_num) % 7
133
+ total_incr = -distance_from_first_day + (7 if self.idx < 0 else 0) + self.incr
134
+ return date + relativedelta(days=total_incr)
135
+
136
+
137
+ @dataclass
138
+ class OffsetUnits(DateModifier):
139
+ """
140
+ Abstract DateModifier class to offset an input date by some number of some calendar unit
141
+ """
142
+ offset: int
143
+
144
+
145
+ @dataclass
146
+ class OffsetYears(OffsetUnits):
147
+ """
148
+ DateModifier class to offset an input date by some number of years
149
+
150
+ Attributes:
151
+ offset: The number of years to offset the input date.
152
+ """
153
+
154
+ def modify(self, date: Date) -> Date:
155
+ return date + relativedelta(years=self.offset)
156
+
157
+
158
+ @dataclass
159
+ class OffsetMonths(OffsetUnits):
160
+ """
161
+ DateModifier class to offset an input date by some number of months
162
+
163
+ Attributes:
164
+ offset: The number of months to offset the input date.
165
+ """
166
+
167
+ def modify(self, date: Date) -> Date:
168
+ return date + relativedelta(months=self.offset)
169
+
170
+
171
+ @dataclass
172
+ class OffsetWeeks(OffsetUnits):
173
+ """
174
+ DateModifier class to offset an input date by some number of weeks
175
+
176
+ Attributes:
177
+ offset: The number of weeks to offset the input date.
178
+ """
179
+
180
+ def modify(self, date: Date) -> Date:
181
+ return date + relativedelta(weeks=self.offset)
182
+
183
+
184
+ @dataclass
185
+ class OffsetDays(OffsetUnits):
186
+ """
187
+ DateModifier class to offset an input date by some number of days
188
+
189
+ Attributes:
190
+ offset: The number of days to offset the input date.
191
+ """
192
+
193
+ def modify(self, date: Date) -> Date:
194
+ return date + relativedelta(days=self.offset)
195
+
196
+
197
+ @dataclass
198
+ class DateModPipeline(DateModifier):
199
+ """
200
+ DateModifier class to apply a list of date modifiers to an input date
201
+
202
+ Attributes:
203
+ modifiers: The list of DateModifier's to apply in sequence.
204
+ """
205
+ date_modifiers: Sequence[DateModifier]
206
+
207
+ def modify(self, date: Date) -> Date:
208
+ for modifier in self.date_modifiers:
209
+ date = modifier.modify(date)
210
+ return date
211
+
212
+ def get_joined_modifiers(self, date_modifiers: Sequence[DateModifier]) -> Sequence[DateModifier]:
213
+ """
214
+ Create a new sequence of DateModifier by joining the date modifiers in this class
215
+ with the input date_modifiers
216
+
217
+ Arguments:
218
+ date_modifiers: The new date modifier sequence to join
219
+
220
+ Returns:
221
+ A new sequence of DateModifier
222
+ """
223
+ joined_modifiers = tuple(self.date_modifiers) + tuple(date_modifiers)
224
+ return joined_modifiers
225
+
226
+ def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
227
+ """
228
+ Create a new DateModPipeline with more date modifiers
229
+
230
+ Arguments:
231
+ date_modifiers: The additional date modifiers to add
232
+
233
+ Returns:
234
+ A new DateModPipeline
235
+ """
236
+ joined_modifiers = self.get_joined_modifiers(date_modifiers)
237
+ return DateModPipeline(joined_modifiers)
238
+
239
+ def get_date_list(self, start_date: Date, step: DateModifier) -> Sequence[Date]:
240
+ """
241
+ This method modifies the input date, and returns all dates from the input date to the modified date,
242
+ incremented by a DateModifier step.
243
+
244
+ If the step is positive and start date is less than end date, then it'll return an increasing list of
245
+ dates starting from the start date. If the step is negative and start date is greater than end date,
246
+ then it'll return a decreasing list of dates starting from the start date. Otherwise, an empty list
247
+ is returned.
248
+
249
+ Arguments:
250
+ start_date: The input date (it's the first date in the output list if step moves towards end date)
251
+ step: The increment to take (specified as an offset DateModifier). Offset cannot be zero
252
+
253
+ Returns:
254
+ A list of datetime objects
255
+ """
256
+ assert isinstance(step, OffsetUnits)
257
+ if step.offset == 0:
258
+ raise ValueError(f"The length of 'step' must not be zero")
259
+
260
+ output: Sequence[Date] = []
261
+ end_date = self.modify(start_date)
262
+ curr_date = start_date
263
+ is_not_done_positive_step = lambda: curr_date <= end_date and step.offset > 0
264
+ is_not_done_negative_step = lambda: curr_date >= end_date and step.offset < 0
265
+ while is_not_done_positive_step() or is_not_done_negative_step():
266
+ output.append(curr_date)
267
+ curr_date = step.modify(curr_date)
268
+ return output
269
+
270
+
271
+ @dataclass
272
+ class DateRepresentationModifier(metaclass=ABCMeta):
273
+ """
274
+ Abstract class for modifying other representations of dates (such as string or unix timestemp)
275
+ """
276
+ date_modifiers: Sequence[DateModifier]
277
+
278
+ def __post_init__(self) -> None:
279
+ self.date_mod_pipeline = DateModPipeline(self.date_modifiers)
280
+
281
+ @abstractmethod
282
+ def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
283
+ pass
284
+
285
+
286
+ @dataclass
287
+ class DateStringModifier(DateRepresentationModifier):
288
+ """
289
+ Class to modify a string representation of a date given a DateModifier
290
+
291
+ Attributes:
292
+ date_modifier: The DateModifier to apply on datetime objects
293
+ date_format: Format of the output date string. Default is '%Y-%m-%d'
294
+ """
295
+ date_format: str = '%Y-%m-%d'
296
+
297
+ def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
298
+ """
299
+ Create a new DateStringModifier with more date modifiers
300
+
301
+ Arguments:
302
+ date_modifiers: The additional date modifiers to add
303
+
304
+ Returns:
305
+ A new DateStringModifier
306
+ """
307
+ joined_modifiers = self.date_mod_pipeline.get_joined_modifiers(date_modifiers)
308
+ return DateStringModifier(joined_modifiers, self.date_format)
309
+
310
+ def _get_input_date_obj(self, date_str: str, input_format: str | None = None) -> Date:
311
+ input_format = self.date_format if input_format is None else input_format
312
+ return datetime.strptime(date_str, input_format).date()
313
+
314
+ def modify(self, date_str: str, input_format: str | None = None) -> str:
315
+ """
316
+ Modifies the input date string with the date modifiers
317
+
318
+ Arguments:
319
+ date_str: The input date string
320
+ input_format: The input date format. Defaults to the same as output date format
321
+
322
+ Returns:
323
+ The resulting date string
324
+ """
325
+ date_obj = self._get_input_date_obj(date_str, input_format)
326
+ return self.date_mod_pipeline.modify(date_obj).strftime(self.date_format)
327
+
328
+ def get_date_list(self, start_date_str: str, step: DateModifier, input_format: str | None = None) -> Sequence[str]:
329
+ """
330
+ This method modifies the input date string, and returns all dates as strings from the input date
331
+ to the modified date, incremented by a DateModifier step.
332
+
333
+ If the step is positive and start date is less than end date, then it'll return an increasing list of
334
+ dates starting from the start date. If the step is negative and start date is greater than end date,
335
+ then it'll return a decreasing list of dates starting from the start date. Otherwise, an empty list
336
+ is returned.
337
+
338
+ Arguments:
339
+ start_date_str: The input date string (it's the first date in the output list if step moves towards end date)
340
+ step: The increment to take (specified as an offset DateModifier). Offset cannot be zero
341
+ input_format: The input date format. Defaults to the same as output date format
342
+
343
+ Returns:
344
+ A list of date strings
345
+ """
346
+ assert isinstance(step, OffsetUnits)
347
+ curr_date = self._get_input_date_obj(start_date_str, input_format)
348
+ output = self.date_mod_pipeline.get_date_list(curr_date, step)
349
+ return [x.strftime(self.date_format) for x in output]
350
+
351
+
352
+ @dataclass
353
+ class TimestampModifier(DateRepresentationModifier):
354
+ """
355
+ Class to modify a numeric representation of a date (as Unix/Epoch/POSIX timestamp) given a DateModifier
356
+
357
+ Attributes:
358
+ date_modifier: The DateModifier to apply on datetime objects
359
+ date_format: Format of the date string. Default is '%Y-%m-%d'
360
+ """
361
+
362
+ def with_more_modifiers(self, date_modifiers: Sequence[DateModifier]):
363
+ """
364
+ Create a new TimestampModifier with more date modifiers
365
+
366
+ Arguments:
367
+ date_modifiers: The additional date modifiers to add
368
+
369
+ Returns:
370
+ A new TimestampModifier
371
+ """
372
+ joined_modifiers = self.date_mod_pipeline.get_joined_modifiers(date_modifiers)
373
+ return TimestampModifier(joined_modifiers)
374
+
375
+ def modify(self, timestamp: float) -> float:
376
+ """
377
+ Modifies the input timestamp with the date modifiers
378
+
379
+ Arguments:
380
+ timestamp: The input timestamp as float
381
+
382
+ Returns:
383
+ The resulting timestamp
384
+ """
385
+ date_obj = datetime.fromtimestamp(timestamp).date()
386
+ modified_date = self.date_mod_pipeline.modify(date_obj)
387
+ modified_datetime = datetime.combine(modified_date, datetime.min.time())
388
+ return modified_datetime.timestamp()
389
+
390
+ def get_date_list(self, start_timestamp: float, step: DateModifier) -> Sequence[float]:
391
+ """
392
+ This method modifies the input timestamp, and returns all dates as timestampes/floats from the input date
393
+ to the modified date, incremented by a DateModifier step.
394
+
395
+ If the step is positive and start date is less than end date, then it'll return an increasing list of
396
+ dates starting from the start date. If the step is negative and start date is greater than end date,
397
+ then it'll return a decreasing list of dates starting from the start date. Otherwise, an empty list
398
+ is returned.
399
+
400
+ Arguments:
401
+ start_timestamp: The input timestamp as float (it's the first date in the output list if step moves towards end date)
402
+ step: The increment to take (specified as an offset DateModifier). Offset cannot be zero
403
+
404
+ Returns:
405
+ A list of timestamp as floats
406
+ """
407
+ curr_date = datetime.fromtimestamp(start_timestamp).date()
408
+ output = self.date_mod_pipeline.get_date_list(curr_date, step)
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:
@@ -4,8 +4,8 @@ from sqlalchemy import Engine
4
4
  import time, polars as pl
5
5
 
6
6
  from . import _utils as u, _constants as c, _py_module as pm
7
- from .arguments.init_time_args import ConnectionsArgs
8
- from ._manifest import ManifestConfig, ConnectionProperties, ConnectionType
7
+ from ._arguments._init_time_args import ConnectionsArgs
8
+ from ._manifest import ManifestConfig, ConnectionProperties, ConnectionTypeEnum
9
9
 
10
10
 
11
11
  @dataclass
@@ -31,11 +31,11 @@ class ConnectionSet:
31
31
  def run_sql_query_from_conn_name(self, query: str, conn_name: str, placeholders: dict = {}) -> pl.DataFrame:
32
32
  conn = self.get_connection(conn_name)
33
33
  try:
34
- if isinstance(conn, ConnectionProperties) and (conn.type == ConnectionType.CONNECTORX or conn.type == ConnectionType.ADBC):
34
+ if isinstance(conn, ConnectionProperties) and (conn.type == ConnectionTypeEnum.CONNECTORX or conn.type == ConnectionTypeEnum.ADBC):
35
35
  if len(placeholders) > 0:
36
36
  raise u.ConfigurationError(f"Connection '{conn_name}' is a ConnectorX or ADBC connection, which does not support placeholders")
37
37
  df = pl.read_database_uri(query, conn.uri, engine=conn.type.value)
38
- elif isinstance(conn, ConnectionProperties) and conn.type == ConnectionType.SQLALCHEMY:
38
+ elif isinstance(conn, ConnectionProperties) and conn.type == ConnectionTypeEnum.SQLALCHEMY:
39
39
  with conn.engine.connect() as connection:
40
40
  df = pl.read_database(query, connection, execute_options={"parameters": placeholders})
41
41
  else:
@@ -52,7 +52,7 @@ class ConnectionSet:
52
52
  if isinstance(conn, Engine):
53
53
  conn.dispose()
54
54
  elif isinstance(conn, ConnectionProperties):
55
- if conn.type == ConnectionType.SQLALCHEMY:
55
+ if conn.type == ConnectionTypeEnum.SQLALCHEMY:
56
56
  conn.engine.dispose()
57
57
  elif hasattr(conn, 'close'):
58
58
  conn.close()
squirrels/_constants.py CHANGED
@@ -33,7 +33,7 @@ SQRL_CONNECTIONS_DEFAULT_NAME_USED = 'SQRL_CONNECTIONS__DEFAULT_NAME_USED'
33
33
  SQRL_DUCKDB_VENV_DB_FILE_PATH = 'SQRL_DUCKDB_VENV__DB_FILE_PATH'
34
34
 
35
35
  # Folder/File names
36
- PACKAGE_DATA_FOLDER = 'package_data'
36
+ PACKAGE_DATA_FOLDER = '_package_data'
37
37
  BASE_PROJECT_FOLDER = 'base_project'
38
38
 
39
39
  GLOBAL_ENV_FOLDER = '.squirrels'