encommon 0.22.9__py3-none-any.whl → 0.22.11__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.
encommon/times/windows.py CHANGED
@@ -8,8 +8,13 @@ is permitted, for more information consult the project license file.
8
8
 
9
9
 
10
10
  from copy import deepcopy
11
+ from threading import Lock
12
+ from typing import Annotated
11
13
  from typing import Optional
12
14
  from typing import TYPE_CHECKING
15
+ from typing import Type
16
+
17
+ from pydantic import Field
13
18
 
14
19
  from sqlalchemy import Column
15
20
  from sqlalchemy import String
@@ -23,6 +28,9 @@ from .common import PARSABLE
23
28
  from .params import WindowsParams
24
29
  from .time import Time
25
30
  from .window import Window
31
+ from ..types import BaseModel
32
+ from ..types import DictStrAny
33
+ from ..types import LDictStrAny
26
34
 
27
35
  if TYPE_CHECKING:
28
36
  from .params import WindowParams
@@ -33,17 +41,7 @@ WINDOWS = dict[str, Window]
33
41
 
34
42
 
35
43
 
36
- class SQLBase(DeclarativeBase):
37
- """
38
- Some additional class that SQLAlchemy requires to work.
39
-
40
- .. note::
41
- Input parameters are not defined, check parent class.
42
- """
43
-
44
-
45
-
46
- class WindowsTable(SQLBase):
44
+ class WindowsTable(DeclarativeBase):
47
45
  """
48
46
  Schematic for the database operations using SQLAlchemy.
49
47
 
@@ -73,7 +71,83 @@ class WindowsTable(SQLBase):
73
71
  String,
74
72
  nullable=False)
75
73
 
76
- __tablename__ = 'windows'
74
+
75
+
76
+ class WindowsRecord(BaseModel):
77
+ """
78
+ Contain the information of the record from the database.
79
+
80
+ :param record: Record from the SQLAlchemy query routine.
81
+ """
82
+
83
+ group: Annotated[
84
+ str,
85
+ Field(...,
86
+ description='Group the children by category',
87
+ min_length=1)]
88
+
89
+ unique: Annotated[
90
+ str,
91
+ Field(...,
92
+ description='Unique identifier for the child',
93
+ min_length=1)]
94
+
95
+ last: Annotated[
96
+ str,
97
+ Field(...,
98
+ description='Last schedule for the period',
99
+ min_length=20,
100
+ max_length=32)]
101
+
102
+ next: Annotated[
103
+ str,
104
+ Field(...,
105
+ description='Next schedule for the period',
106
+ min_length=20,
107
+ max_length=32)]
108
+
109
+ update: Annotated[
110
+ str,
111
+ Field(...,
112
+ description='When the record was updated',
113
+ min_length=20,
114
+ max_length=32)]
115
+
116
+
117
+ def __init__(
118
+ self,
119
+ record: Optional[DeclarativeBase] = None,
120
+ ) -> None:
121
+ """
122
+ Initialize instance for class using provided parameters.
123
+ """
124
+
125
+ fields = [
126
+ 'group',
127
+ 'unique',
128
+ 'last',
129
+ 'next',
130
+ 'update']
131
+
132
+ params = {
133
+ x: getattr(record, x)
134
+ for x in fields}
135
+
136
+
137
+ timefs = [
138
+ 'last',
139
+ 'next',
140
+ 'update']
141
+
142
+ params |= {
143
+ k: (Time(v).simple
144
+ if v else None)
145
+ for k, v in
146
+ params.items()
147
+ if k in timefs}
148
+
149
+
150
+ super().__init__(**params)
77
151
 
78
152
 
79
153
 
@@ -108,6 +182,8 @@ class Windows:
108
182
 
109
183
  __store: str
110
184
  __group: str
185
+ __table: Type[WindowsTable]
186
+ __locker: Lock
111
187
 
112
188
  __sengine: Engine
113
189
  __session: (
@@ -117,16 +193,17 @@ class Windows:
117
193
  __start: Time
118
194
  __stop: Time
119
195
 
120
- __windows: WINDOWS
196
+ __childs: WINDOWS
121
197
 
122
198
 
123
- def __init__(
199
+ def __init__( # noqa: CFQ002
124
200
  self,
125
201
  params: Optional['WindowsParams'] = None,
126
202
  start: PARSABLE = 'now',
127
203
  stop: PARSABLE = '3000-01-01',
128
204
  *,
129
205
  store: str = 'sqlite:///:memory:',
206
+ table: str = 'windows',
130
207
  group: str = 'default',
131
208
  ) -> None:
132
209
  """
@@ -144,7 +221,17 @@ class Windows:
144
221
  self.__store = store
145
222
  self.__group = group
146
223
 
147
- self.__make_engine()
224
+ class SQLBase(DeclarativeBase):
225
+ pass
226
+
227
+ self.__table = type(
228
+ f'encommon_windows_{table}',
229
+ (SQLBase, WindowsTable),
230
+ {'__tablename__': table})
231
+
232
+ self.__locker = Lock()
233
+
234
+ self.__build_engine()
148
235
 
149
236
 
150
237
  start = Time(start)
@@ -156,12 +243,12 @@ class Windows:
156
243
  self.__stop = stop
157
244
 
158
245
 
159
- self.__windows = {}
246
+ self.__childs = {}
160
247
 
161
- self.load_children()
248
+ self.__build_objects()
162
249
 
163
250
 
164
- def __make_engine(
251
+ def __build_engine(
165
252
  self,
166
253
  ) -> None:
167
254
  """
@@ -172,7 +259,7 @@ class Windows:
172
259
  self.__store,
173
260
  pool_pre_ping=True)
174
261
 
175
- (SQLBase.metadata
262
+ (self.__table.metadata
176
263
  .create_all(sengine))
177
264
 
178
265
  session = (
@@ -182,6 +269,23 @@ class Windows:
182
269
  self.__session = session
183
270
 
184
271
 
272
+ def __build_objects(
273
+ self,
274
+ ) -> None:
275
+ """
276
+ Construct instances using the configuration parameters.
277
+ """
278
+
279
+ params = self.__params
280
+
281
+ items = (
282
+ params.windows
283
+ .items())
284
+
285
+ for name, item in items:
286
+ self.create(name, item)
287
+
288
+
185
289
  @property
186
290
  def params(
187
291
  self,
@@ -221,6 +325,19 @@ class Windows:
221
325
  return self.__group
222
326
 
223
327
 
328
+ @property
329
+ def store_table(
330
+ self,
331
+ ) -> Type[WindowsTable]:
332
+ """
333
+ Return the value for the attribute from class instance.
334
+
335
+ :returns: Value for the attribute from class instance.
336
+ """
337
+
338
+ return self.__table
339
+
340
+
224
341
  @property
225
342
  def store_engine(
226
343
  self,
@@ -283,124 +400,55 @@ class Windows:
283
400
  :returns: Value for the attribute from class instance.
284
401
  """
285
402
 
286
- return dict(self.__windows)
403
+ return dict(self.__childs)
287
404
 
288
405
 
289
- def load_children(
406
+ def save_children(
290
407
  self,
291
408
  ) -> None:
292
409
  """
293
- Construct the children instances for the primary class.
410
+ Save the child caches from the attribute into database.
294
411
  """
295
412
 
296
- params = self.__params
297
- windows = self.__windows
298
-
299
- start = self.__start
300
- stop = self.__stop
413
+ sess = self.__session()
414
+ lock = self.__locker
301
415
 
416
+ table = self.__table
302
417
  group = self.__group
418
+ childs = self.__childs
303
419
 
304
- session = self.store_session
305
-
306
- config = params.windows
307
-
308
-
309
- _table = WindowsTable
310
- _group = _table.group
311
- _unique = _table.unique
312
-
313
- query = (
314
- session.query(_table)
315
- .filter(_group == group)
316
- .order_by(_unique))
317
-
318
- for record in query.all():
319
-
320
- unique = str(record.unique)
321
- next = str(record.next)
322
-
323
- if unique not in config:
324
- continue
325
420
 
326
- _config = config[unique]
421
+ inserts: LDictStrAny = []
327
422
 
328
- _config.start = next
329
- _config.anchor = next
423
+ update = Time('now')
330
424
 
331
425
 
332
- items = config.items()
426
+ items = childs.items()
333
427
 
334
- for key, value in items:
428
+ for unique, cache in items:
335
429
 
336
- if key in windows:
430
+ _last = cache.last.subsec
431
+ _next = cache.next.subsec
432
+ _update = update.subsec
337
433
 
338
- window = windows[key]
434
+ inputs: DictStrAny = {
435
+ 'group': group,
436
+ 'unique': unique,
437
+ 'last': _last,
438
+ 'next': _next,
439
+ 'update': _update}
339
440
 
340
- window.update(value.start)
441
+ inserts.append(inputs)
341
442
 
342
- continue
343
443
 
344
- _start = (
345
- value.start or start)
444
+ with lock, sess as session:
346
445
 
347
- _stop = (
348
- value.stop or stop)
446
+ for insert in inserts:
349
447
 
350
- if _start < start:
351
- _start = start
448
+ session.merge(
449
+ table(**insert))
352
450
 
353
- if _stop > stop:
354
- _stop = stop
355
-
356
- _anchor = (
357
- value.anchor or _start)
358
-
359
- window = Window(
360
- value.window,
361
- start=_start,
362
- stop=_stop,
363
- anchor=_anchor,
364
- delay=value.delay)
365
-
366
- windows[key] = window
367
-
368
-
369
- self.__windows = windows
370
-
371
-
372
- def save_children(
373
- self,
374
- ) -> None:
375
- """
376
- Save the child caches from the attribute into database.
377
- """
378
-
379
- windows = self.__windows
380
-
381
- group = self.__group
382
-
383
- session = self.store_session
384
-
385
-
386
- items = windows.items()
387
-
388
- for unique, window in items:
389
-
390
- update = Time('now')
391
-
392
- append = WindowsTable(
393
- group=group,
394
- unique=unique,
395
- last=window.last.subsec,
396
- next=window.next.subsec,
397
- update=update.subsec)
398
-
399
- session.merge(append)
400
-
401
-
402
- session.commit()
403
- session.close()
451
+ session.commit()
404
452
 
405
453
 
406
454
  def ready(
@@ -416,14 +464,14 @@ class Windows:
416
464
  :returns: Boolean indicating whether enough time passed.
417
465
  """
418
466
 
419
- windows = self.__windows
467
+ childs = self.__childs
420
468
 
421
- if unique not in windows:
469
+ if unique not in childs:
422
470
  raise ValueError('unique')
423
471
 
424
- window = windows[unique]
472
+ child = childs[unique]
425
473
 
426
- ready = window.ready(update)
474
+ ready = child.ready(update)
427
475
 
428
476
  if ready is True:
429
477
  self.save_children()
@@ -448,6 +496,52 @@ class Windows:
448
496
  unique, update)
449
497
 
450
498
 
499
+ def select(
500
+ self,
501
+ unique: str,
502
+ ) -> Optional[WindowsRecord]:
503
+ """
504
+ Return the record from within the table in the database.
505
+
506
+ :returns: Record from within the table in the database.
507
+ """
508
+
509
+ sess = self.__session()
510
+ lock = self.__locker
511
+
512
+ table = self.__table
513
+ model = WindowsRecord
514
+
515
+ table = self.__table
516
+ group = self.__group
517
+
518
+ _unique = table.unique
519
+ _group = table.group
520
+
521
+
522
+ with lock, sess as session:
523
+
524
+ query = (
525
+ session.query(table)
526
+ .filter(_unique == unique)
527
+ .filter(_group == group))
528
+
529
+ _records = list(query.all())
530
+
531
+
532
+ records = [
533
+ model(x)
534
+ for x in _records]
535
+
536
+
537
+ if len(records) == 0:
538
+ return None
539
+
540
+ assert len(records) == 1
541
+
542
+ return records[0]
543
+
544
+
451
545
  def create(
452
546
  self,
453
547
  unique: str,
@@ -461,20 +555,58 @@ class Windows:
461
555
  :returns: Newly constructed instance of related class.
462
556
  """
463
557
 
464
- config = (
465
- self.params
466
- .windows)
558
+ childs = self.__childs
559
+ _start = self.__start
560
+ _stop = self.__stop
467
561
 
468
- if unique in config:
562
+ if unique in childs:
469
563
  raise ValueError('unique')
470
564
 
471
- config[unique] = params
565
+ window = params.window
566
+ start = params.start
567
+ stop = params.stop
568
+ anchor = params.anchor
569
+ delay = params.delay
570
+
571
+
572
+ select = self.select(unique)
573
+
574
+ if select is not None:
575
+ start = select.next
576
+ anchor = select.next
577
+
578
+
579
+ child_start = Time(
580
+ start if start
581
+ else _start)
582
+
583
+ child_stop = Time(
584
+ stop if stop
585
+ else _stop)
586
+
587
+ anchor = anchor or start
588
+
589
+
590
+ if child_start < _start:
591
+ child_start = _start
592
+
593
+ if child_stop > _stop:
594
+ child_stop = _stop
595
+
596
+
597
+ child = Window(
598
+ window=window,
599
+ start=child_start,
600
+ stop=child_stop,
601
+ anchor=anchor,
602
+ delay=delay)
603
+
604
+ childs[unique] = child
472
605
 
473
- self.load_children()
474
606
 
475
607
  self.save_children()
476
608
 
477
- return self.children[unique]
609
+ return child
478
610
 
479
611
 
480
612
  def update(
@@ -489,14 +621,14 @@ class Windows:
489
621
  :param value: Override the time updated for window value.
490
622
  """
491
623
 
492
- windows = self.__windows
624
+ childs = self.__childs
493
625
 
494
- if unique not in windows:
626
+ if unique not in childs:
495
627
  raise ValueError('unique')
496
628
 
497
- window = windows[unique]
629
+ child = childs[unique]
498
630
 
499
- window.update(value)
631
+ child.update(value)
500
632
 
501
633
  self.save_children()
502
634
 
@@ -515,32 +647,26 @@ class Windows:
515
647
  :param unique: Unique identifier for the related child.
516
648
  """
517
649
 
518
- params = self.__params
519
- windows = self.__windows
650
+ sess = self.__session()
651
+ lock = self.__locker
652
+ childs = self.__childs
520
653
 
654
+ table = self.__table
521
655
  group = self.__group
522
656
 
523
- session = self.store_session
524
-
525
- config = params.windows
526
-
527
-
528
- if unique in config:
529
- del config[unique]
657
+ _unique = table.unique
658
+ _group = table.group
530
659
 
531
- if unique in windows:
532
- del windows[unique]
533
660
 
661
+ with lock, sess as session:
534
662
 
535
- _table = WindowsTable
536
- _group = _table.group
537
- _unique = _table.unique
663
+ (session.query(table)
664
+ .filter(_unique == unique)
665
+ .filter(_group == group)
666
+ .delete())
538
667
 
539
- (session.query(_table)
540
- .filter(_unique == unique)
541
- .filter(_group == group)
542
- .delete())
668
+ session.commit()
543
669
 
544
670
 
545
- session.commit()
546
- session.close()
671
+ if unique in childs:
672
+ del childs[unique]
@@ -28,6 +28,7 @@ from .strings import instr
28
28
  from .strings import rplstr
29
29
  from .strings import strplwr
30
30
  from .types import DictStrAny
31
+ from .types import LDictStrAny
31
32
  from .types import NCFalse
32
33
  from .types import NCNone
33
34
  from .types import NCTrue
@@ -51,6 +52,7 @@ __all__ = [
51
52
  'inrepr',
52
53
  'instr',
53
54
  'lattrs',
55
+ 'LDictStrAny',
54
56
  'merge_dicts',
55
57
  'rplstr',
56
58
  'setate',
encommon/types/notate.py CHANGED
@@ -16,6 +16,7 @@ from typing import Union
16
16
 
17
17
  from .empty import Empty
18
18
  from .types import DictStrAny
19
+ from .types import LDictStrAny
19
20
 
20
21
 
21
22
 
@@ -194,13 +195,13 @@ def delate(
194
195
 
195
196
 
196
197
  def impate( # noqa: CFQ001,CFQ004
197
- source: DictStrAny | list[DictStrAny],
198
+ source: DictStrAny | LDictStrAny,
198
199
  delim: str = '/',
199
200
  parent: Optional[str] = None,
200
201
  *,
201
202
  implode_list: bool = True,
202
203
  recurse_list: bool = True,
203
- ) -> DictStrAny | list[DictStrAny]:
204
+ ) -> DictStrAny | LDictStrAny:
204
205
  """
205
206
  Implode the dictionary into a single depth of notation.
206
207
 
encommon/types/types.py CHANGED
@@ -12,6 +12,7 @@ from typing import Any
12
12
 
13
13
 
14
14
  DictStrAny = dict[str, Any]
15
+ LDictStrAny = list[DictStrAny]
15
16
 
16
17
 
17
18
 
encommon/version.txt CHANGED
@@ -1 +1 @@
1
- 0.22.9
1
+ 0.22.11
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: encommon
3
- Version: 0.22.9
3
+ Version: 0.22.11
4
4
  Summary: Enasis Network Common Library
5
5
  License: MIT
6
6
  Project-URL: Source, https://github.com/enasisnetwork/encommon