plexus-python-common 1.0.13__tar.gz → 1.0.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/PKG-INFO +1 -1
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/ormutils.py +419 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
- plexus_python_common-1.0.14/test/plexus_tests/common/utils/ormutils_test.py +572 -0
- plexus_python_common-1.0.13/test/plexus_tests/common/utils/ormutils_test.py +0 -112
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/.editorconfig +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/.github/workflows/pr.yml +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/.github/workflows/push.yml +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/.gitignore +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/MANIFEST.in +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/README.md +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/VERSION +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/pyproject.toml +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/0-dummy +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/1-dummy +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/2-dummy +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.0.0.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.0.0.vol-0.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.1.1.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.1.1.vol-1.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.2.2.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.2.2.vol-2.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.csv.part0 +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.csv.part1 +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.csv.part2 +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/resources/unittest/shutils/dummy.txt +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/setup.cfg +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/setup.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/__init__.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/carto/OSMFile.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/carto/OSMNode.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/carto/OSMTags.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/carto/OSMWay.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/config.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/pose.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/proj.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/bagutils.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/datautils.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/jsonutils.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/s3utils.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/shutils.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/strutils.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus_python_common.egg-info/requires.txt +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus_python_common.egg-info/top_level.txt +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/__init__.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/pose_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/proj_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/utils/datautils_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/utils/shutils_test.py +0 -0
- {plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/test/plexus_tests/common/utils/strutils_test.py +0 -0
{plexus_python_common-1.0.13 → plexus_python_common-1.0.14}/src/plexus/common/utils/ormutils.py
RENAMED
|
@@ -4,6 +4,8 @@ from typing import Protocol, Self, TypeVar
|
|
|
4
4
|
import pydantic as pdt
|
|
5
5
|
import sqlalchemy as sa
|
|
6
6
|
import sqlalchemy.dialects.postgresql as sa_pg
|
|
7
|
+
import sqlalchemy.exc as sa_exc
|
|
8
|
+
import sqlalchemy.orm as sa_orm
|
|
7
9
|
from sqlmodel import Field, SQLModel
|
|
8
10
|
|
|
9
11
|
from plexus.common.utils.datautils import validate_dt_timezone
|
|
@@ -30,7 +32,30 @@ __all__ = [
|
|
|
30
32
|
"SerialModel",
|
|
31
33
|
"RecordModel",
|
|
32
34
|
"SnapshotModel",
|
|
35
|
+
"clone_serial_model_instance",
|
|
36
|
+
"clone_record_model_instance",
|
|
37
|
+
"clone_snapshot_model_instance",
|
|
33
38
|
"make_snapshot_model_trigger",
|
|
39
|
+
"db_create_serial_model",
|
|
40
|
+
"db_create_serial_models",
|
|
41
|
+
"db_read_serial_model",
|
|
42
|
+
"db_read_serial_models",
|
|
43
|
+
"db_update_serial_model",
|
|
44
|
+
"db_delete_serial_model",
|
|
45
|
+
"db_create_record_model",
|
|
46
|
+
"db_create_record_models",
|
|
47
|
+
"db_update_record_model",
|
|
48
|
+
"db_create_snapshot_model",
|
|
49
|
+
"db_create_snapshot_models",
|
|
50
|
+
"db_read_snapshot_models_of_record",
|
|
51
|
+
"db_read_latest_snapshot_model_of_record",
|
|
52
|
+
"db_read_active_snapshot_model_of_record",
|
|
53
|
+
"db_read_expired_snapshot_models_of_record",
|
|
54
|
+
"db_read_latest_snapshot_models",
|
|
55
|
+
"db_read_active_snapshot_models",
|
|
56
|
+
"db_update_snapshot_model",
|
|
57
|
+
"db_expire_snapshot_model",
|
|
58
|
+
"db_activate_snapshot_model",
|
|
34
59
|
]
|
|
35
60
|
|
|
36
61
|
ModelT = TypeVar("ModelT", bound=SQLModel)
|
|
@@ -363,7 +388,62 @@ class SnapshotModel(make_base_model(), make_snapshot_model_mixin(), table=True):
|
|
|
363
388
|
pass
|
|
364
389
|
|
|
365
390
|
|
|
391
|
+
def clone_serial_model_instance(
|
|
392
|
+
model: type[SerialModelMixin],
|
|
393
|
+
instance: SerialModelMixin,
|
|
394
|
+
*,
|
|
395
|
+
clear_meta_fields: bool = True,
|
|
396
|
+
inplace: bool = False,
|
|
397
|
+
) -> SerialModelMixin:
|
|
398
|
+
result = model.model_validate(instance)
|
|
399
|
+
result = instance if inplace else result
|
|
400
|
+
if clear_meta_fields:
|
|
401
|
+
result.sid = None
|
|
402
|
+
return result
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def clone_record_model_instance(
|
|
406
|
+
model: type[RecordModelMixin],
|
|
407
|
+
instance: RecordModelMixin,
|
|
408
|
+
*,
|
|
409
|
+
clear_meta_fields: bool = True,
|
|
410
|
+
inplace: bool = False,
|
|
411
|
+
) -> RecordModelMixin:
|
|
412
|
+
result = model.model_validate(instance)
|
|
413
|
+
result = instance if inplace else result
|
|
414
|
+
if clear_meta_fields:
|
|
415
|
+
result.sid = None
|
|
416
|
+
result.created_at = None
|
|
417
|
+
result.updated_at = None
|
|
418
|
+
return result
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def clone_snapshot_model_instance(
|
|
422
|
+
model: type[SnapshotModelMixin],
|
|
423
|
+
instance: SnapshotModelMixin,
|
|
424
|
+
*,
|
|
425
|
+
clear_meta_fields: bool = True,
|
|
426
|
+
inplace: bool = False,
|
|
427
|
+
) -> SnapshotModelMixin:
|
|
428
|
+
result = model.model_validate(instance)
|
|
429
|
+
result = instance if inplace else result
|
|
430
|
+
if clear_meta_fields:
|
|
431
|
+
result.sid = None
|
|
432
|
+
result.created_at = None
|
|
433
|
+
result.expired_at = None
|
|
434
|
+
result.record_sid = None
|
|
435
|
+
return result
|
|
436
|
+
|
|
437
|
+
|
|
366
438
|
def make_snapshot_model_trigger(engine: sa.Engine, model: type[SQLModel]):
|
|
439
|
+
"""
|
|
440
|
+
Creates the necessary database objects (sequence, function, trigger) to support automatic snapshot management
|
|
441
|
+
for the given snapshot model. This includes a sequence for `record_sid`, a function to handle snapshot updates,
|
|
442
|
+
and a trigger to invoke the function before inserts. The model must extend `SnapshotModel`.
|
|
443
|
+
|
|
444
|
+
:param engine: SQLAlchemy engine connected to the target database.
|
|
445
|
+
:param model: The snapshot model class extending `SnapshotModel`.
|
|
446
|
+
"""
|
|
367
447
|
table_name = model.__tablename__
|
|
368
448
|
if not table_name:
|
|
369
449
|
raise ValueError("missing '__tablename__' attribute")
|
|
@@ -418,3 +498,342 @@ def make_snapshot_model_trigger(engine: sa.Engine, model: type[SQLModel]):
|
|
|
418
498
|
conn.execute(sa.text(create_snapshot_auto_update_function_sql))
|
|
419
499
|
conn.execute(sa.text(create_snapshot_auto_update_trigger_sql))
|
|
420
500
|
conn.commit()
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def db_create_serial_model(
|
|
504
|
+
db: sa_orm.Session,
|
|
505
|
+
model: type[SerialModelMixin],
|
|
506
|
+
instance: SerialModelMixin,
|
|
507
|
+
) -> SerialModelMixin:
|
|
508
|
+
db_instance = model.model_validate(instance)
|
|
509
|
+
db.add(db_instance)
|
|
510
|
+
db.commit()
|
|
511
|
+
|
|
512
|
+
return db_instance
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def db_create_serial_models(
|
|
516
|
+
db: sa_orm.Session,
|
|
517
|
+
model: type[SerialModelMixin],
|
|
518
|
+
instances: list[SerialModelMixin],
|
|
519
|
+
) -> list[SerialModelMixin]:
|
|
520
|
+
db_instances = [model.model_validate(instance) for instance in instances]
|
|
521
|
+
db.bulk_save_objects(db_instances, return_defaults=True)
|
|
522
|
+
db.commit()
|
|
523
|
+
|
|
524
|
+
return db_instances
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def db_read_serial_model(
|
|
528
|
+
db: sa_orm.Session,
|
|
529
|
+
model: type[SerialModelMixin],
|
|
530
|
+
sid: int,
|
|
531
|
+
) -> SerialModelMixin:
|
|
532
|
+
db_instance = db.query(model).where(model.sid == sid).one_or_none()
|
|
533
|
+
if db_instance is None:
|
|
534
|
+
raise sa_exc.NoResultFound(f"'{model}' of specified sid '{sid}' not found")
|
|
535
|
+
|
|
536
|
+
return db_instance
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def db_read_serial_models(
|
|
540
|
+
db: sa_orm.Session,
|
|
541
|
+
model: type[SerialModelMixin],
|
|
542
|
+
skip: int,
|
|
543
|
+
limit: int,
|
|
544
|
+
) -> list[SerialModelMixin]:
|
|
545
|
+
return db.query(model).order_by(model.sid).offset(skip).limit(limit).all()
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def db_update_serial_model(
|
|
549
|
+
db: sa_orm.Session,
|
|
550
|
+
model: type[SerialModelMixin],
|
|
551
|
+
instance: SerialModelMixin,
|
|
552
|
+
) -> SerialModelMixin:
|
|
553
|
+
db_instance = db.query(model).where(model.sid == instance.sid).one_or_none()
|
|
554
|
+
if db_instance is None:
|
|
555
|
+
raise sa_exc.NoResultFound(f"'{model}' of specified sid '{instance.sid}' not found")
|
|
556
|
+
|
|
557
|
+
db_instance = model_copy_from(db_instance, instance, exclude_none=True)
|
|
558
|
+
model.model_validate(db_instance)
|
|
559
|
+
db.commit()
|
|
560
|
+
|
|
561
|
+
return db_instance
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def db_delete_serial_model(
|
|
565
|
+
db: sa_orm.Session,
|
|
566
|
+
model: type[SerialModelMixin],
|
|
567
|
+
sid: int,
|
|
568
|
+
) -> SerialModelMixin:
|
|
569
|
+
db_instance = db.query(model).where(model.sid == sid).one_or_none()
|
|
570
|
+
if db_instance is None:
|
|
571
|
+
raise sa_exc.NoResultFound(f"'{model}' of specified sid '{sid}' not found")
|
|
572
|
+
|
|
573
|
+
db.delete(db_instance)
|
|
574
|
+
db.commit()
|
|
575
|
+
|
|
576
|
+
return db_instance
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def db_create_record_model(
|
|
580
|
+
db: sa_orm.Session,
|
|
581
|
+
model: type[RecordModelMixin],
|
|
582
|
+
instance: RecordModelMixin,
|
|
583
|
+
created_at: datetime.datetime | None = None,
|
|
584
|
+
) -> RecordModelMixin:
|
|
585
|
+
db_instance = clone_serial_model_instance(model, instance)
|
|
586
|
+
db_instance.created_at = created_at
|
|
587
|
+
db_instance.updated_at = created_at
|
|
588
|
+
db.add(db_instance)
|
|
589
|
+
db.commit()
|
|
590
|
+
|
|
591
|
+
return db_instance
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def db_create_record_models(
|
|
595
|
+
db: sa_orm.Session,
|
|
596
|
+
model: type[RecordModelMixin],
|
|
597
|
+
instances: list[RecordModelMixin],
|
|
598
|
+
created_at: datetime.datetime | None = None,
|
|
599
|
+
) -> list[RecordModelMixin]:
|
|
600
|
+
db_instances = [clone_serial_model_instance(model, instance) for instance in instances]
|
|
601
|
+
for db_instance in db_instances:
|
|
602
|
+
db_instance.created_at = created_at
|
|
603
|
+
db_instance.updated_at = created_at
|
|
604
|
+
db.bulk_save_objects(db_instances, return_defaults=True)
|
|
605
|
+
db.commit()
|
|
606
|
+
|
|
607
|
+
return db_instances
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def db_update_record_model(
|
|
611
|
+
db: sa_orm.Session,
|
|
612
|
+
model: type[RecordModelMixin],
|
|
613
|
+
instance: RecordModelMixin,
|
|
614
|
+
sid: int,
|
|
615
|
+
updated_at: datetime.datetime,
|
|
616
|
+
) -> RecordModelMixin:
|
|
617
|
+
db_instance = db.query(model).where(model.sid == sid).one_or_none()
|
|
618
|
+
if db_instance is None:
|
|
619
|
+
raise sa_exc.NoResultFound(f"'{model}' of specified sid '{sid}' not found")
|
|
620
|
+
|
|
621
|
+
db_instance = model_copy_from(db_instance, instance, exclude_none=True)
|
|
622
|
+
db_instance.updated_at = updated_at
|
|
623
|
+
db_instance = clone_serial_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
|
|
624
|
+
db.commit()
|
|
625
|
+
|
|
626
|
+
return db_instance
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def db_create_snapshot_model(
|
|
630
|
+
db: sa_orm.Session,
|
|
631
|
+
model: type[SnapshotModelMixin],
|
|
632
|
+
instance: SnapshotModelMixin,
|
|
633
|
+
created_at: datetime.datetime,
|
|
634
|
+
) -> SnapshotModelMixin:
|
|
635
|
+
db_instance = clone_snapshot_model_instance(model, instance)
|
|
636
|
+
|
|
637
|
+
db_instance.created_at = created_at
|
|
638
|
+
db_instance.expired_at = None
|
|
639
|
+
db.add(db_instance)
|
|
640
|
+
db.flush()
|
|
641
|
+
|
|
642
|
+
db_instance.record_sid = db_instance.sid
|
|
643
|
+
db.commit()
|
|
644
|
+
|
|
645
|
+
return db_instance
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def db_create_snapshot_models(
|
|
649
|
+
db: sa_orm.Session,
|
|
650
|
+
model: type[SnapshotModelMixin],
|
|
651
|
+
instances: list[SnapshotModelMixin],
|
|
652
|
+
created_at: datetime.datetime,
|
|
653
|
+
) -> list[SnapshotModelMixin]:
|
|
654
|
+
db_instances = [clone_snapshot_model_instance(model, instance) for instance in instances]
|
|
655
|
+
for db_instance in db_instances:
|
|
656
|
+
db_instance.created_at = created_at
|
|
657
|
+
db_instance.expired_at = None
|
|
658
|
+
db.bulk_save_objects(db_instances, return_defaults=True)
|
|
659
|
+
db.flush()
|
|
660
|
+
|
|
661
|
+
for db_instance in db_instances:
|
|
662
|
+
db_instance.record_sid = db_instance.sid
|
|
663
|
+
db.commit()
|
|
664
|
+
|
|
665
|
+
return db_instances
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def db_read_snapshot_models_of_record(
|
|
669
|
+
db: sa_orm.Session,
|
|
670
|
+
model: type[SnapshotModelMixin],
|
|
671
|
+
record_sid: int,
|
|
672
|
+
) -> list[SnapshotModelMixin]:
|
|
673
|
+
return (
|
|
674
|
+
db
|
|
675
|
+
.query(model)
|
|
676
|
+
.where(model.record_sid == record_sid)
|
|
677
|
+
.order_by(model.created_at.desc())
|
|
678
|
+
.all()
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def db_read_latest_snapshot_model_of_record(
|
|
683
|
+
db: sa_orm.Session,
|
|
684
|
+
model: type[SnapshotModelMixin],
|
|
685
|
+
record_sid: int,
|
|
686
|
+
) -> SnapshotModelMixin:
|
|
687
|
+
db_instance = (
|
|
688
|
+
db
|
|
689
|
+
.query(model)
|
|
690
|
+
.where(model.record_sid == record_sid)
|
|
691
|
+
.order_by(model.created_at.desc())
|
|
692
|
+
.first()
|
|
693
|
+
)
|
|
694
|
+
if db_instance is None:
|
|
695
|
+
raise sa_exc.NoResultFound(f"'{model}' of specified record_sid '{record_sid}' not found")
|
|
696
|
+
|
|
697
|
+
return db_instance
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def db_read_active_snapshot_model_of_record(
|
|
701
|
+
db: sa_orm.Session,
|
|
702
|
+
model: type[SnapshotModelMixin],
|
|
703
|
+
record_sid: int,
|
|
704
|
+
) -> SnapshotModelMixin:
|
|
705
|
+
db_instance = db.query(model).where(model.record_sid == record_sid, model.expired_at.is_(None)).one_or_none()
|
|
706
|
+
if db_instance is None:
|
|
707
|
+
raise sa_exc.NoResultFound(f"Active '{model}' of specified record_sid '{record_sid}' not found")
|
|
708
|
+
|
|
709
|
+
return db_instance
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
def db_read_expired_snapshot_models_of_record(
|
|
713
|
+
db: sa_orm.Session,
|
|
714
|
+
model: type[SnapshotModelMixin],
|
|
715
|
+
record_sid: int,
|
|
716
|
+
) -> list[SnapshotModelMixin]:
|
|
717
|
+
return (
|
|
718
|
+
db
|
|
719
|
+
.query(model)
|
|
720
|
+
.where(model.record_sid == record_sid, model.expired_at.is_not(None))
|
|
721
|
+
.order_by(model.expired_at.desc())
|
|
722
|
+
.all()
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def db_read_latest_snapshot_models(
|
|
727
|
+
db: sa_orm.Session,
|
|
728
|
+
model: type[SnapshotModelMixin],
|
|
729
|
+
skip: int,
|
|
730
|
+
limit: int,
|
|
731
|
+
) -> list[SnapshotModelMixin]:
|
|
732
|
+
subquery = (
|
|
733
|
+
db
|
|
734
|
+
.query(model.record_sid,
|
|
735
|
+
sa.func.max(model.created_at).label("max_created_at"))
|
|
736
|
+
.group_by(model.record_sid)
|
|
737
|
+
.subquery()
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
return (
|
|
741
|
+
db
|
|
742
|
+
.query(model)
|
|
743
|
+
.join(subquery,
|
|
744
|
+
sa.and_(model.record_sid == subquery.c.record_sid, model.created_at == subquery.c.max_created_at))
|
|
745
|
+
.order_by(model.record_sid)
|
|
746
|
+
.offset(skip)
|
|
747
|
+
.limit(limit)
|
|
748
|
+
.all()
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def db_read_active_snapshot_models(
|
|
753
|
+
db: sa_orm.Session,
|
|
754
|
+
model: type[SnapshotModelMixin],
|
|
755
|
+
skip: int,
|
|
756
|
+
limit: int,
|
|
757
|
+
) -> list[SnapshotModelMixin]:
|
|
758
|
+
return db.query(model).where(model.expired_at.is_(None)).offset(skip).limit(limit).all()
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def db_update_snapshot_model(
|
|
762
|
+
db: sa_orm.Session,
|
|
763
|
+
model: type[SnapshotModelMixin],
|
|
764
|
+
instance: SnapshotModelMixin,
|
|
765
|
+
record_sid: int,
|
|
766
|
+
updated_at: datetime.datetime,
|
|
767
|
+
) -> SnapshotModelMixin:
|
|
768
|
+
db_instance = db.query(model).where(model.record_sid == record_sid, model.expired_at.is_(None)).one_or_none()
|
|
769
|
+
if db_instance is None:
|
|
770
|
+
raise sa_exc.NoResultFound(f"Active '{model}' of specified record_sid '{record_sid}' not found")
|
|
771
|
+
|
|
772
|
+
db_instance.expired_at = updated_at
|
|
773
|
+
db_instance = clone_snapshot_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
|
|
774
|
+
db.flush()
|
|
775
|
+
|
|
776
|
+
db_new_instance = clone_snapshot_model_instance(model, instance)
|
|
777
|
+
db_new_instance.record_sid = record_sid
|
|
778
|
+
db_new_instance.created_at = updated_at
|
|
779
|
+
db_new_instance.expired_at = None
|
|
780
|
+
db.add(db_new_instance)
|
|
781
|
+
db.commit()
|
|
782
|
+
|
|
783
|
+
return db_new_instance
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
def db_expire_snapshot_model(
|
|
787
|
+
db: sa_orm.Session,
|
|
788
|
+
model: type[SnapshotModelMixin],
|
|
789
|
+
record_sid: int,
|
|
790
|
+
updated_at: datetime.datetime,
|
|
791
|
+
) -> SnapshotModelMixin:
|
|
792
|
+
db_instance = (
|
|
793
|
+
db
|
|
794
|
+
.query(model)
|
|
795
|
+
.where(model.record_sid == record_sid, model.expired_at.is_(None))
|
|
796
|
+
.one_or_none()
|
|
797
|
+
)
|
|
798
|
+
if db_instance is None:
|
|
799
|
+
raise sa_exc.NoResultFound(f"Active '{model}' of specified record_sid '{record_sid}' not found")
|
|
800
|
+
|
|
801
|
+
db_instance.expired_at = updated_at
|
|
802
|
+
db_instance = clone_snapshot_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
|
|
803
|
+
db.commit()
|
|
804
|
+
|
|
805
|
+
return db_instance
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def db_activate_snapshot_model(
|
|
809
|
+
db: sa_orm.Session,
|
|
810
|
+
model: type[SnapshotModelMixin],
|
|
811
|
+
record_sid: int,
|
|
812
|
+
updated_at: datetime.datetime,
|
|
813
|
+
) -> SnapshotModelMixin:
|
|
814
|
+
db_instance = db.query(model).where(model.record_sid == record_sid, model.expired_at.is_(None)).one_or_none()
|
|
815
|
+
if db_instance is not None:
|
|
816
|
+
raise sa_exc.MultipleResultsFound(f"Active '{model}' of specified record_sid '{record_sid}' already exists")
|
|
817
|
+
|
|
818
|
+
db_instance = (
|
|
819
|
+
db
|
|
820
|
+
.query(model)
|
|
821
|
+
.where(model.record_sid == record_sid, model.expired_at.is_not(None))
|
|
822
|
+
.order_by(model.expired_at.desc())
|
|
823
|
+
.first()
|
|
824
|
+
)
|
|
825
|
+
if db_instance is None:
|
|
826
|
+
raise sa_exc.NoResultFound(f"Expired '{model}' of specified record_sid '{record_sid}' not found")
|
|
827
|
+
|
|
828
|
+
db_new_instance = clone_snapshot_model_instance(model, db_instance)
|
|
829
|
+
db_new_instance.record_sid = record_sid
|
|
830
|
+
db_new_instance.created_at = db_instance.expired_at
|
|
831
|
+
db_new_instance.expired_at = updated_at
|
|
832
|
+
|
|
833
|
+
db_new_instance = clone_snapshot_model_instance(model, db_new_instance, clear_meta_fields=False, inplace=True)
|
|
834
|
+
db_new_instance.created_at = updated_at
|
|
835
|
+
db_new_instance.expired_at = None
|
|
836
|
+
db.add(db_new_instance)
|
|
837
|
+
db.commit()
|
|
838
|
+
|
|
839
|
+
return db_new_instance
|