plexus-python-common 1.0.69__py3-none-any.whl → 1.0.71__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.
- plexus/common/utils/tagutils.py +284 -133
- {plexus_python_common-1.0.69.dist-info → plexus_python_common-1.0.71.dist-info}/METADATA +1 -1
- {plexus_python_common-1.0.69.dist-info → plexus_python_common-1.0.71.dist-info}/RECORD +5 -5
- {plexus_python_common-1.0.69.dist-info → plexus_python_common-1.0.71.dist-info}/WHEEL +0 -0
- {plexus_python_common-1.0.69.dist-info → plexus_python_common-1.0.71.dist-info}/top_level.txt +0 -0
plexus/common/utils/tagutils.py
CHANGED
|
@@ -33,14 +33,14 @@ from plexus.common.resources.tags import predefined_tagset_specs
|
|
|
33
33
|
from plexus.common.utils.datautils import validate_colon_tag, validate_snake_case, validate_vehicle_name
|
|
34
34
|
from plexus.common.utils.datautils import validate_dt_timezone, validate_semver, validate_slash_tag
|
|
35
35
|
from plexus.common.utils.jsonutils import json_datetime_encoder
|
|
36
|
-
from plexus.common.utils.ormutils import SQLiteDateTime
|
|
37
|
-
from plexus.common.utils.ormutils import SequenceModelMixinProtocol
|
|
36
|
+
from plexus.common.utils.ormutils import SQLiteDateTime, SequenceModelMixinProtocol
|
|
38
37
|
from plexus.common.utils.ormutils import clone_sequence_model_instance, make_base_model, make_sequence_model_mixin
|
|
39
38
|
from plexus.common.utils.sqlutils import escape_sql_like
|
|
40
39
|
|
|
41
40
|
__all__ = [
|
|
42
41
|
"RichDesc",
|
|
43
42
|
"Tag",
|
|
43
|
+
"BoundTag",
|
|
44
44
|
"Tagset",
|
|
45
45
|
"MutableTagset",
|
|
46
46
|
"populate_tagset",
|
|
@@ -394,15 +394,20 @@ def make_tag_target_model_mixin() -> type[pdt.BaseModel]:
|
|
|
394
394
|
)
|
|
395
395
|
vehicle_name: str = Field(
|
|
396
396
|
sa_column=sa.Column(sa_sqlite.VARCHAR(128), nullable=False),
|
|
397
|
-
description="
|
|
397
|
+
description="Name of the tagger's applicability vehicle",
|
|
398
398
|
)
|
|
399
399
|
begin_dt: datetime.datetime = Field(
|
|
400
400
|
sa_column=sa.Column(SQLiteDateTime, nullable=False),
|
|
401
|
-
description="
|
|
401
|
+
description="Beginning of the tagger's applicability time range",
|
|
402
402
|
)
|
|
403
403
|
end_dt: datetime.datetime = Field(
|
|
404
404
|
sa_column=sa.Column(SQLiteDateTime, nullable=False),
|
|
405
|
-
description="End
|
|
405
|
+
description="End of the tagger's applicability time range",
|
|
406
|
+
)
|
|
407
|
+
props: JsonType | None = Field(
|
|
408
|
+
sa_column=sa.Column(sa_sqlite.JSON, nullable=True),
|
|
409
|
+
default=None,
|
|
410
|
+
description="Additional properties of the tag target in JSON format",
|
|
406
411
|
)
|
|
407
412
|
|
|
408
413
|
@pdt.field_validator("identifier", mode="after")
|
|
@@ -414,7 +419,7 @@ def make_tag_target_model_mixin() -> type[pdt.BaseModel]:
|
|
|
414
419
|
@pdt.field_validator("tagger_name", mode="after")
|
|
415
420
|
@classmethod
|
|
416
421
|
def validate_tagger_name(cls, v: str) -> str:
|
|
417
|
-
|
|
422
|
+
validate_slash_tag(v)
|
|
418
423
|
return v
|
|
419
424
|
|
|
420
425
|
@pdt.field_validator("tagger_version", mode="after")
|
|
@@ -466,21 +471,21 @@ def make_tag_record_model_mixin() -> type[pdt.BaseModel]:
|
|
|
466
471
|
)
|
|
467
472
|
begin_dt: datetime.datetime = Field(
|
|
468
473
|
sa_column=sa.Column(SQLiteDateTime, nullable=False),
|
|
469
|
-
description="Begin
|
|
474
|
+
description="Begin of the tag record time range",
|
|
470
475
|
)
|
|
471
476
|
end_dt: datetime.datetime = Field(
|
|
472
477
|
sa_column=sa.Column(SQLiteDateTime, nullable=False),
|
|
473
|
-
description="End
|
|
478
|
+
description="End of the tag record time range",
|
|
474
479
|
)
|
|
475
480
|
tagset_namespace: str | None = Field(
|
|
476
481
|
sa_column=sa.Column(sa_sqlite.VARCHAR(64), nullable=True),
|
|
477
482
|
default=None,
|
|
478
|
-
description="Namespace of the tagset that the tag belongs to",
|
|
483
|
+
description="Namespace of the tagset that the tag in this record belongs to",
|
|
479
484
|
)
|
|
480
485
|
tagset_version: str | None = Field(
|
|
481
486
|
sa_column=sa.Column(sa_sqlite.VARCHAR(32), nullable=True),
|
|
482
487
|
default=None,
|
|
483
|
-
description="Version of the tagset that the tag belongs to",
|
|
488
|
+
description="Version of the tagset that the tag in this record belongs to",
|
|
484
489
|
)
|
|
485
490
|
tag: str = Field(
|
|
486
491
|
sa_column=sa.Column(sa_sqlite.VARCHAR(256), nullable=False),
|
|
@@ -573,6 +578,7 @@ if typing.TYPE_CHECKING:
|
|
|
573
578
|
vehicle_name: sa_orm.Mapped[str] = ...
|
|
574
579
|
begin_dt: sa_orm.Mapped[datetime.datetime] = ...
|
|
575
580
|
end_dt: sa_orm.Mapped[datetime.datetime] = ...
|
|
581
|
+
props: sa_orm.Mapped[JsonType | None] = ...
|
|
576
582
|
|
|
577
583
|
|
|
578
584
|
class TagTargetTable(TagTarget, SequenceModelMixinProtocol):
|
|
@@ -669,6 +675,7 @@ class TagCache(object):
|
|
|
669
675
|
vehicle_name: str,
|
|
670
676
|
begin_dt: datetime.datetime,
|
|
671
677
|
end_dt: datetime.datetime,
|
|
678
|
+
props: JsonType | None = None,
|
|
672
679
|
) -> TagTargetTable:
|
|
673
680
|
with self.make_session() as session:
|
|
674
681
|
tag_target = TagTarget(
|
|
@@ -678,6 +685,7 @@ class TagCache(object):
|
|
|
678
685
|
vehicle_name=vehicle_name,
|
|
679
686
|
begin_dt=begin_dt,
|
|
680
687
|
end_dt=end_dt,
|
|
688
|
+
props=props,
|
|
681
689
|
)
|
|
682
690
|
db_tag_target = clone_sequence_model_instance(TagTargetTable, tag_target)
|
|
683
691
|
session.add(db_tag_target)
|
|
@@ -886,7 +894,7 @@ class TagCache(object):
|
|
|
886
894
|
*,
|
|
887
895
|
tagsets: Sequence[Tagset] | None = None,
|
|
888
896
|
tagset_inverted: bool = False,
|
|
889
|
-
):
|
|
897
|
+
) -> Self:
|
|
890
898
|
"""
|
|
891
899
|
Remove tag records from the cache that match the specified filters.
|
|
892
900
|
|
|
@@ -898,6 +906,7 @@ class TagCache(object):
|
|
|
898
906
|
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
899
907
|
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
900
908
|
tagsets)
|
|
909
|
+
:return: Self for chaining
|
|
901
910
|
"""
|
|
902
911
|
with self.make_session() as session:
|
|
903
912
|
query_stmt = session.query(TagRecordTable)
|
|
@@ -922,86 +931,20 @@ class TagCache(object):
|
|
|
922
931
|
query_stmt.delete()
|
|
923
932
|
session.commit()
|
|
924
933
|
|
|
925
|
-
|
|
926
|
-
with self.make_session() as session:
|
|
927
|
-
session.execute(sa.delete(TagRecordTable))
|
|
928
|
-
session.execute(sa.delete(TagTargetTable))
|
|
929
|
-
session.commit()
|
|
930
|
-
|
|
931
|
-
def append_to(self, target_file_path: str, *, overwrite: bool = False):
|
|
932
|
-
target_tag_cache = TagCache(file_path=target_file_path)
|
|
933
|
-
if overwrite:
|
|
934
|
-
target_tag_cache.clear()
|
|
935
|
-
TagCache.copy_to(self, target_tag_cache)
|
|
936
|
-
|
|
937
|
-
def merge_from(self, source_file_path: str, *, overwrite: bool = False):
|
|
938
|
-
source_tag_cache = TagCache(file_path=source_file_path)
|
|
939
|
-
if overwrite:
|
|
940
|
-
self.clear()
|
|
941
|
-
TagCache.copy_to(source_tag_cache, self)
|
|
942
|
-
|
|
943
|
-
@staticmethod
|
|
944
|
-
def copy_to(src: "TagCache", dst: "TagCache"):
|
|
945
|
-
# If src and dst are the same instance or point to the same file path,
|
|
946
|
-
# do nothing to avoid accidentally clearing the cache
|
|
947
|
-
if src == dst or src.file_path == dst.file_path:
|
|
948
|
-
return
|
|
949
|
-
|
|
950
|
-
with src.make_session() as src_session, dst.make_session() as dst_session:
|
|
951
|
-
src_db_tag_targets = src_session.query(TagTargetTable).all()
|
|
952
|
-
dst_db_tag_targets = [
|
|
953
|
-
clone_sequence_model_instance(TagTargetTable, db_tag_target, clear_meta_fields=True)
|
|
954
|
-
for db_tag_target in src_db_tag_targets
|
|
955
|
-
]
|
|
956
|
-
dst_session.add_all(dst_db_tag_targets)
|
|
957
|
-
dst_session.flush() # ensure new sqn values are assigned
|
|
958
|
-
|
|
959
|
-
sqn_map = {src_db_tag_target.sqn: dst_db_tag_target.sqn
|
|
960
|
-
for src_db_tag_target, dst_db_tag_target in zip(src_db_tag_targets, dst_db_tag_targets)}
|
|
961
|
-
|
|
962
|
-
for db_tag_records in batched(src_session.query(TagRecordTable).yield_per(1000), 1000):
|
|
963
|
-
cloned_db_tag_records = []
|
|
964
|
-
for db_tag_record in db_tag_records:
|
|
965
|
-
cloned_db_tag_record = clone_sequence_model_instance(TagRecordTable,
|
|
966
|
-
db_tag_record,
|
|
967
|
-
clear_meta_fields=True)
|
|
968
|
-
try:
|
|
969
|
-
cloned_db_tag_record.target_sqn = sqn_map[db_tag_record.target_sqn]
|
|
970
|
-
except KeyError as e:
|
|
971
|
-
raise ValueError(f"no cloned tag target for target_sqn '{db_tag_record.target_sqn}'") from e
|
|
972
|
-
cloned_db_tag_records.append(cloned_db_tag_record)
|
|
973
|
-
dst_session.add_all(cloned_db_tag_records)
|
|
974
|
-
dst_session.commit()
|
|
975
|
-
|
|
976
|
-
count_tags = count_records
|
|
977
|
-
iter_tags = iter_records
|
|
978
|
-
iter_tag_and_targets = iter_record_and_targets
|
|
979
|
-
remove_tags = remove_records
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
class TargetedTagCache(object):
|
|
983
|
-
def __init__(self, cache: TagCache, tag_target: TagTargetTable):
|
|
984
|
-
self.cache = cache
|
|
985
|
-
self.tag_target = tag_target
|
|
986
|
-
|
|
987
|
-
@contextlib.contextmanager
|
|
988
|
-
def make_session(self) -> Generator[sa_orm.Session, None, None]:
|
|
989
|
-
with self.cache.make_session() as session:
|
|
990
|
-
if not session.query(TagTargetTable).filter(TagTargetTable.sqn == self.tag_target.sqn).first():
|
|
991
|
-
raise ValueError(f"tag target with sqn '{self.tag_target.sqn}' is no longer present in cache")
|
|
992
|
-
yield session
|
|
934
|
+
return self
|
|
993
935
|
|
|
994
|
-
def
|
|
936
|
+
def target_count_records(self, target_sqn: int) -> int:
|
|
995
937
|
with self.make_session() as session:
|
|
996
938
|
return (
|
|
997
939
|
session
|
|
998
940
|
.query(sa.func.count(TagRecordTable.sqn))
|
|
999
|
-
.filter(TagRecordTable.target_sqn ==
|
|
941
|
+
.filter(TagRecordTable.target_sqn == target_sqn)
|
|
1000
942
|
.scalar()
|
|
1001
943
|
)
|
|
1002
944
|
|
|
1003
|
-
def
|
|
945
|
+
def target_iter_records(
|
|
1004
946
|
self,
|
|
947
|
+
target_sqn: int,
|
|
1005
948
|
begin_dt: datetime.datetime | None = None,
|
|
1006
949
|
end_dt: datetime.datetime | None = None,
|
|
1007
950
|
tagset_namespace: str | None = None,
|
|
@@ -1017,6 +960,7 @@ class TargetedTagCache(object):
|
|
|
1017
960
|
"""
|
|
1018
961
|
Query tag records in the cache with optional filters.
|
|
1019
962
|
|
|
963
|
+
:param target_sqn: Filter by target sequence number (exact match)
|
|
1020
964
|
:param begin_dt: Filter by begin time (inclusive)
|
|
1021
965
|
:param end_dt: Filter by end time (inclusive)
|
|
1022
966
|
:param tagset_namespace: Filter by tagset namespace (exact match)
|
|
@@ -1032,7 +976,7 @@ class TargetedTagCache(object):
|
|
|
1032
976
|
"""
|
|
1033
977
|
|
|
1034
978
|
with self.make_session() as session:
|
|
1035
|
-
query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn ==
|
|
979
|
+
query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn == target_sqn)
|
|
1036
980
|
if begin_dt:
|
|
1037
981
|
query_stmt = query_stmt.filter(TagRecordTable.end_dt >= begin_dt)
|
|
1038
982
|
if end_dt:
|
|
@@ -1060,19 +1004,20 @@ class TargetedTagCache(object):
|
|
|
1060
1004
|
for result in query_stmt.yield_per(batch_size):
|
|
1061
1005
|
yield result
|
|
1062
1006
|
|
|
1063
|
-
def
|
|
1007
|
+
def target_add_record(
|
|
1064
1008
|
self,
|
|
1065
|
-
|
|
1066
|
-
|
|
1009
|
+
target_sqn: int,
|
|
1010
|
+
begin_dt: datetime.datetime,
|
|
1011
|
+
end_dt: datetime.datetime,
|
|
1067
1012
|
tag: str | Tag | BoundTag,
|
|
1068
1013
|
props: JsonType | None = None,
|
|
1069
1014
|
tagset_namespace: str | None = None,
|
|
1070
1015
|
tagset_version: str | None = None,
|
|
1071
1016
|
) -> Chainable[Self, TagRecordTable]:
|
|
1072
1017
|
"""
|
|
1073
|
-
Add a tag record to the cache for the specified time range.
|
|
1074
|
-
default to the target's ``begin_dt`` or ``end_dt`` respectively.
|
|
1018
|
+
Add a tag record to the cache for the specified time range.
|
|
1075
1019
|
|
|
1020
|
+
:param target_sqn: Sequence number of the tag record's target
|
|
1076
1021
|
:param begin_dt: Begin datetime of the tag record
|
|
1077
1022
|
:param end_dt: End datetime of the tag record
|
|
1078
1023
|
:param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
|
|
@@ -1083,13 +1028,13 @@ class TargetedTagCache(object):
|
|
|
1083
1028
|
instance will be used.
|
|
1084
1029
|
:param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a ``BoundTag``
|
|
1085
1030
|
instance, this parameter will be ignored and the version from the instance will be used.
|
|
1086
|
-
:return: Self instance for chaining
|
|
1031
|
+
:return: Self instance for chaining along with the added ``TagRecordTable`` instance
|
|
1087
1032
|
"""
|
|
1088
1033
|
with self.make_session() as session:
|
|
1089
1034
|
tag_record = TagRecord(
|
|
1090
|
-
target_sqn=
|
|
1091
|
-
begin_dt=begin_dt
|
|
1092
|
-
end_dt=end_dt
|
|
1035
|
+
target_sqn=target_sqn,
|
|
1036
|
+
begin_dt=begin_dt,
|
|
1037
|
+
end_dt=end_dt,
|
|
1093
1038
|
tagset_namespace=tag.namespace if isinstance(tag, BoundTag) else tagset_namespace,
|
|
1094
1039
|
tagset_version=tag.version if isinstance(tag, BoundTag) else tagset_version,
|
|
1095
1040
|
tag=tag.name if isinstance(tag, Tag) else tag,
|
|
@@ -1101,38 +1046,9 @@ class TargetedTagCache(object):
|
|
|
1101
1046
|
|
|
1102
1047
|
return chainable(self, db_tag_record)
|
|
1103
1048
|
|
|
1104
|
-
def
|
|
1105
|
-
self,
|
|
1106
|
-
tag: str | Tag | BoundTag,
|
|
1107
|
-
props: JsonType | None = None,
|
|
1108
|
-
tagset_namespace: str | None = None,
|
|
1109
|
-
tagset_version: str | None = None,
|
|
1110
|
-
) -> Chainable[Self, TagRecordTable]:
|
|
1111
|
-
"""
|
|
1112
|
-
Add a tag record to the cache for the entire target range.
|
|
1113
|
-
|
|
1114
|
-
:param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
|
|
1115
|
-
its name will be used.
|
|
1116
|
-
:param props: Additional properties of the tag record in JSON format (optional)
|
|
1117
|
-
:param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1118
|
-
``BoundTag`` instance, this parameter will be ignored and the namespace from the
|
|
1119
|
-
instance will be used.
|
|
1120
|
-
:param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1121
|
-
``BoundTag`` instance, this parameter will be ignored and the version from the instance
|
|
1122
|
-
will be used.
|
|
1123
|
-
:return: Self instance for chaining
|
|
1124
|
-
"""
|
|
1125
|
-
return self.add_ranged_record(
|
|
1126
|
-
begin_dt=self.tag_target.begin_dt,
|
|
1127
|
-
end_dt=self.tag_target.end_dt,
|
|
1128
|
-
tag=tag,
|
|
1129
|
-
tagset_namespace=tagset_namespace,
|
|
1130
|
-
tagset_version=tagset_version,
|
|
1131
|
-
props=props,
|
|
1132
|
-
)
|
|
1133
|
-
|
|
1134
|
-
def update_record(
|
|
1049
|
+
def target_update_record(
|
|
1135
1050
|
self,
|
|
1051
|
+
target_sqn: int,
|
|
1136
1052
|
sqn: int,
|
|
1137
1053
|
*,
|
|
1138
1054
|
begin_dt: datetime.datetime | None = None,
|
|
@@ -1146,6 +1062,7 @@ class TargetedTagCache(object):
|
|
|
1146
1062
|
"""
|
|
1147
1063
|
Update a tag record in the cache by its sequence number.
|
|
1148
1064
|
|
|
1065
|
+
:param target_sqn: Sequence number of the tag record's target
|
|
1149
1066
|
:param sqn: Sequence number of the tag record to be updated
|
|
1150
1067
|
:param begin_dt: New begin datetime of the tag record (optional)
|
|
1151
1068
|
:param end_dt: New end datetime of the tag record (optional)
|
|
@@ -1159,10 +1076,15 @@ class TargetedTagCache(object):
|
|
|
1159
1076
|
is a ``BoundTag`` instance, this parameter will be ignored and the version from the
|
|
1160
1077
|
instance will be used.
|
|
1161
1078
|
:param flags: New integer bitmask storing status or metadata flags for this tag record (optional)
|
|
1162
|
-
:return: Self instance for chaining
|
|
1079
|
+
:return: Self instance for chaining along with the updated ``TagRecordTable`` instance
|
|
1163
1080
|
"""
|
|
1164
1081
|
with self.make_session() as session:
|
|
1165
|
-
db_tag_record =
|
|
1082
|
+
db_tag_record = (
|
|
1083
|
+
session
|
|
1084
|
+
.query(TagRecordTable)
|
|
1085
|
+
.filter(TagTargetTable.sqn == target_sqn, TagRecordTable.sqn == sqn)
|
|
1086
|
+
.one_or_none()
|
|
1087
|
+
)
|
|
1166
1088
|
if not db_tag_record:
|
|
1167
1089
|
raise ValueError(f"tag record with sqn '{sqn}' not found in cache")
|
|
1168
1090
|
|
|
@@ -1185,8 +1107,9 @@ class TargetedTagCache(object):
|
|
|
1185
1107
|
|
|
1186
1108
|
return chainable(self, db_tag_record)
|
|
1187
1109
|
|
|
1188
|
-
def
|
|
1110
|
+
def target_remove_records(
|
|
1189
1111
|
self,
|
|
1112
|
+
target_sqn: int,
|
|
1190
1113
|
begin_dt: datetime.datetime | None = None,
|
|
1191
1114
|
end_dt: datetime.datetime | None = None,
|
|
1192
1115
|
tagset_namespace: str | None = None,
|
|
@@ -1199,6 +1122,7 @@ class TargetedTagCache(object):
|
|
|
1199
1122
|
"""
|
|
1200
1123
|
Remove tag records from the cache that match the specified filters.
|
|
1201
1124
|
|
|
1125
|
+
:param target_sqn: Filter by target sequence number (exact match)
|
|
1202
1126
|
:param begin_dt: Filter by begin time (inclusive)
|
|
1203
1127
|
:param end_dt: Filter by end time (inclusive)
|
|
1204
1128
|
:param tagset_namespace: Filter by tagset namespace (exact match)
|
|
@@ -1210,7 +1134,7 @@ class TargetedTagCache(object):
|
|
|
1210
1134
|
:return: Self instance for chaining
|
|
1211
1135
|
"""
|
|
1212
1136
|
with self.make_session() as session:
|
|
1213
|
-
query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn ==
|
|
1137
|
+
query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn == target_sqn)
|
|
1214
1138
|
if begin_dt:
|
|
1215
1139
|
query_stmt = query_stmt.filter(TagRecordTable.end_dt >= begin_dt)
|
|
1216
1140
|
if end_dt:
|
|
@@ -1234,12 +1158,239 @@ class TargetedTagCache(object):
|
|
|
1234
1158
|
|
|
1235
1159
|
return self
|
|
1236
1160
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1161
|
+
def clear(self):
|
|
1162
|
+
with self.make_session() as session:
|
|
1163
|
+
session.execute(sa.delete(TagRecordTable))
|
|
1164
|
+
session.execute(sa.delete(TagTargetTable))
|
|
1165
|
+
session.commit()
|
|
1166
|
+
|
|
1167
|
+
def append_to(self, target_file_path: str, *, overwrite: bool = False):
|
|
1168
|
+
target_tag_cache = TagCache(file_path=target_file_path)
|
|
1169
|
+
if overwrite:
|
|
1170
|
+
target_tag_cache.clear()
|
|
1171
|
+
TagCache.copy_to(self, target_tag_cache)
|
|
1172
|
+
|
|
1173
|
+
def merge_from(self, source_file_path: str, *, overwrite: bool = False):
|
|
1174
|
+
source_tag_cache = TagCache(file_path=source_file_path)
|
|
1175
|
+
if overwrite:
|
|
1176
|
+
self.clear()
|
|
1177
|
+
TagCache.copy_to(source_tag_cache, self)
|
|
1178
|
+
|
|
1179
|
+
@staticmethod
|
|
1180
|
+
def copy_to(src: "TagCache", dst: "TagCache"):
|
|
1181
|
+
# If src and dst are the same instance or point to the same file path,
|
|
1182
|
+
# do nothing to avoid accidentally clearing the cache
|
|
1183
|
+
if src == dst or src.file_path == dst.file_path:
|
|
1184
|
+
return
|
|
1185
|
+
|
|
1186
|
+
with src.make_session() as src_session, dst.make_session() as dst_session:
|
|
1187
|
+
src_db_tag_targets = src_session.query(TagTargetTable).all()
|
|
1188
|
+
dst_db_tag_targets = [
|
|
1189
|
+
clone_sequence_model_instance(TagTargetTable, db_tag_target, clear_meta_fields=True)
|
|
1190
|
+
for db_tag_target in src_db_tag_targets
|
|
1191
|
+
]
|
|
1192
|
+
dst_session.add_all(dst_db_tag_targets)
|
|
1193
|
+
dst_session.flush() # ensure new sqn values are assigned
|
|
1194
|
+
|
|
1195
|
+
sqn_map = {src_db_tag_target.sqn: dst_db_tag_target.sqn
|
|
1196
|
+
for src_db_tag_target, dst_db_tag_target in zip(src_db_tag_targets, dst_db_tag_targets)}
|
|
1197
|
+
|
|
1198
|
+
for db_tag_records in batched(src_session.query(TagRecordTable).yield_per(1000), 1000):
|
|
1199
|
+
cloned_db_tag_records = []
|
|
1200
|
+
for db_tag_record in db_tag_records:
|
|
1201
|
+
cloned_db_tag_record = clone_sequence_model_instance(TagRecordTable,
|
|
1202
|
+
db_tag_record,
|
|
1203
|
+
clear_meta_fields=True)
|
|
1204
|
+
try:
|
|
1205
|
+
cloned_db_tag_record.target_sqn = sqn_map[db_tag_record.target_sqn]
|
|
1206
|
+
except KeyError as e:
|
|
1207
|
+
raise ValueError(f"no cloned tag target for target_sqn '{db_tag_record.target_sqn}'") from e
|
|
1208
|
+
cloned_db_tag_records.append(cloned_db_tag_record)
|
|
1209
|
+
dst_session.add_all(cloned_db_tag_records)
|
|
1210
|
+
dst_session.commit()
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
class TargetedTagCache(object):
|
|
1214
|
+
def __init__(self, cache: TagCache, tag_target: TagTargetTable):
|
|
1215
|
+
self.cache = cache
|
|
1216
|
+
self.tag_target = tag_target
|
|
1217
|
+
|
|
1218
|
+
def count_records(self) -> int:
|
|
1219
|
+
return self.cache.target_count_records(self.tag_target.sqn)
|
|
1220
|
+
|
|
1221
|
+
def iter_records(
|
|
1222
|
+
self,
|
|
1223
|
+
begin_dt: datetime.datetime | None = None,
|
|
1224
|
+
end_dt: datetime.datetime | None = None,
|
|
1225
|
+
tagset_namespace: str | None = None,
|
|
1226
|
+
tagset_version: str | None = None,
|
|
1227
|
+
tag_prefix: str | None = None,
|
|
1228
|
+
*,
|
|
1229
|
+
tagsets: Sequence[Tagset] | None = None,
|
|
1230
|
+
tagset_inverted: bool = False,
|
|
1231
|
+
skip: int | None = None,
|
|
1232
|
+
limit: int | None = None,
|
|
1233
|
+
batch_size: int = 1000,
|
|
1234
|
+
) -> Generator[TagRecordTable, None, None]:
|
|
1235
|
+
return self.cache.target_iter_records(
|
|
1236
|
+
target_sqn=self.tag_target.sqn,
|
|
1237
|
+
begin_dt=begin_dt,
|
|
1238
|
+
end_dt=end_dt,
|
|
1239
|
+
tagset_namespace=tagset_namespace,
|
|
1240
|
+
tagset_version=tagset_version,
|
|
1241
|
+
tag_prefix=tag_prefix,
|
|
1242
|
+
tagsets=tagsets,
|
|
1243
|
+
tagset_inverted=tagset_inverted,
|
|
1244
|
+
skip=skip,
|
|
1245
|
+
limit=limit,
|
|
1246
|
+
batch_size=batch_size,
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
def add_ranged_record(
|
|
1250
|
+
self,
|
|
1251
|
+
begin_dt: datetime.datetime | None,
|
|
1252
|
+
end_dt: datetime.datetime | None,
|
|
1253
|
+
tag: str | Tag | BoundTag,
|
|
1254
|
+
props: JsonType | None = None,
|
|
1255
|
+
tagset_namespace: str | None = None,
|
|
1256
|
+
tagset_version: str | None = None,
|
|
1257
|
+
) -> Chainable[Self, TagRecordTable]:
|
|
1258
|
+
"""
|
|
1259
|
+
Add a tag record to the cache for the specified time range. If ``begin_dt`` or ``end_dt`` is None, it will
|
|
1260
|
+
default to the target's ``begin_dt`` or ``end_dt`` respectively.
|
|
1261
|
+
|
|
1262
|
+
:param begin_dt: Begin datetime of the tag record
|
|
1263
|
+
:param end_dt: End datetime of the tag record
|
|
1264
|
+
:param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
|
|
1265
|
+
its name will be used.
|
|
1266
|
+
:param props: Additional properties of the tag record in JSON format (optional)
|
|
1267
|
+
:param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1268
|
+
``BoundTag`` instance, this parameter will be ignored and the namespace from the
|
|
1269
|
+
instance will be used.
|
|
1270
|
+
:param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a ``BoundTag``
|
|
1271
|
+
instance, this parameter will be ignored and the version from the instance will be used.
|
|
1272
|
+
:return: Self instance for chaining along with the added ``TagRecordTable`` instance
|
|
1273
|
+
"""
|
|
1274
|
+
result = self.cache.target_add_record(
|
|
1275
|
+
target_sqn=self.tag_target.sqn,
|
|
1276
|
+
begin_dt=begin_dt or self.tag_target.begin_dt,
|
|
1277
|
+
end_dt=end_dt or self.tag_target.end_dt,
|
|
1278
|
+
tag=tag,
|
|
1279
|
+
props=props,
|
|
1280
|
+
tagset_namespace=tagset_namespace,
|
|
1281
|
+
tagset_version=tagset_version,
|
|
1282
|
+
)
|
|
1283
|
+
return result.retarget(self)
|
|
1284
|
+
|
|
1285
|
+
def add_record(
|
|
1286
|
+
self,
|
|
1287
|
+
tag: str | Tag | BoundTag,
|
|
1288
|
+
props: JsonType | None = None,
|
|
1289
|
+
tagset_namespace: str | None = None,
|
|
1290
|
+
tagset_version: str | None = None,
|
|
1291
|
+
) -> Chainable[Self, TagRecordTable]:
|
|
1292
|
+
"""
|
|
1293
|
+
Add a tag record to the cache for the entire target range.
|
|
1294
|
+
|
|
1295
|
+
:param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
|
|
1296
|
+
its name will be used.
|
|
1297
|
+
:param props: Additional properties of the tag record in JSON format (optional)
|
|
1298
|
+
:param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1299
|
+
``BoundTag`` instance, this parameter will be ignored and the namespace from the
|
|
1300
|
+
instance will be used.
|
|
1301
|
+
:param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1302
|
+
``BoundTag`` instance, this parameter will be ignored and the version from the instance
|
|
1303
|
+
will be used.
|
|
1304
|
+
:return: Self instance for chaining along with the added ``TagRecordTable`` instance
|
|
1305
|
+
"""
|
|
1306
|
+
result = self.add_ranged_record(
|
|
1307
|
+
begin_dt=self.tag_target.begin_dt,
|
|
1308
|
+
end_dt=self.tag_target.end_dt,
|
|
1309
|
+
tag=tag,
|
|
1310
|
+
tagset_namespace=tagset_namespace,
|
|
1311
|
+
tagset_version=tagset_version,
|
|
1312
|
+
props=props,
|
|
1313
|
+
)
|
|
1314
|
+
return result.retarget(self)
|
|
1315
|
+
|
|
1316
|
+
def update_record(
|
|
1317
|
+
self,
|
|
1318
|
+
sqn: int,
|
|
1319
|
+
*,
|
|
1320
|
+
begin_dt: datetime.datetime | None = None,
|
|
1321
|
+
end_dt: datetime.datetime | None = None,
|
|
1322
|
+
tag: str | Tag | BoundTag | None = None,
|
|
1323
|
+
props: JsonType | None = None,
|
|
1324
|
+
tagset_namespace: str | None = None,
|
|
1325
|
+
tagset_version: str | None = None,
|
|
1326
|
+
flags: int | None = None,
|
|
1327
|
+
) -> Chainable[Self, TagRecordTable]:
|
|
1328
|
+
"""
|
|
1329
|
+
Update a tag record in the cache by its sequence number.
|
|
1330
|
+
|
|
1331
|
+
:param sqn: Sequence number of the tag record to be updated
|
|
1332
|
+
:param begin_dt: New begin datetime of the tag record (optional)
|
|
1333
|
+
:param end_dt: New end datetime of the tag record (optional)
|
|
1334
|
+
:param tag: New tag name or ``Tag``/``BoundTag`` instance to be updated (optional). If ``Tag``/``BoundTag``
|
|
1335
|
+
instance is provided, its name will be used.
|
|
1336
|
+
:param props: New additional properties of the tag record in JSON format (optional)
|
|
1337
|
+
:param tagset_namespace: New namespace of the tagset that the tag belongs to (optional). If the ``tag``
|
|
1338
|
+
parameter is a ``BoundTag`` instance, this parameter will be ignored and the namespace
|
|
1339
|
+
from the instance will be used.
|
|
1340
|
+
:param tagset_version: New version of the tagset that the tag belongs to (optional). If the ``tag`` parameter
|
|
1341
|
+
is a ``BoundTag`` instance, this parameter will be ignored and the version from the
|
|
1342
|
+
instance will be used.
|
|
1343
|
+
:param flags: New integer bitmask storing status or metadata flags for this tag record (optional)
|
|
1344
|
+
:return: Self instance for chaining along with the updated ``TagRecordTable`` instance
|
|
1345
|
+
"""
|
|
1346
|
+
result = self.cache.target_update_record(
|
|
1347
|
+
self.tag_target.sqn,
|
|
1348
|
+
sqn,
|
|
1349
|
+
begin_dt=begin_dt,
|
|
1350
|
+
end_dt=end_dt,
|
|
1351
|
+
tag=tag,
|
|
1352
|
+
props=props,
|
|
1353
|
+
tagset_namespace=tagset_namespace,
|
|
1354
|
+
tagset_version=tagset_version,
|
|
1355
|
+
flags=flags,
|
|
1356
|
+
)
|
|
1357
|
+
return result.retarget(self)
|
|
1358
|
+
|
|
1359
|
+
def remove_records(
|
|
1360
|
+
self,
|
|
1361
|
+
begin_dt: datetime.datetime | None = None,
|
|
1362
|
+
end_dt: datetime.datetime | None = None,
|
|
1363
|
+
tagset_namespace: str | None = None,
|
|
1364
|
+
tagset_version: str | None = None,
|
|
1365
|
+
tag_prefix: str | None = None,
|
|
1366
|
+
*,
|
|
1367
|
+
tagsets: Sequence[Tagset] | None = None,
|
|
1368
|
+
tagset_inverted: bool = False,
|
|
1369
|
+
) -> Self:
|
|
1370
|
+
"""
|
|
1371
|
+
Remove tag records from the cache that match the specified filters.
|
|
1372
|
+
|
|
1373
|
+
:param begin_dt: Filter by begin time (inclusive)
|
|
1374
|
+
:param end_dt: Filter by end time (inclusive)
|
|
1375
|
+
:param tagset_namespace: Filter by tagset namespace (exact match)
|
|
1376
|
+
:param tagset_version: Filter by tagset version (exact match)
|
|
1377
|
+
:param tag_prefix: Filter by tag name prefix, e.g. "dummy_tag:" to match all tags starting with "dummy_tag:"
|
|
1378
|
+
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
1379
|
+
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
1380
|
+
tagsets)
|
|
1381
|
+
:return: Self instance for chaining
|
|
1382
|
+
"""
|
|
1383
|
+
self.cache.target_remove_records(
|
|
1384
|
+
self.tag_target.sqn,
|
|
1385
|
+
begin_dt=begin_dt,
|
|
1386
|
+
end_dt=end_dt,
|
|
1387
|
+
tagset_namespace=tagset_namespace,
|
|
1388
|
+
tagset_version=tagset_version,
|
|
1389
|
+
tag_prefix=tag_prefix,
|
|
1390
|
+
tagsets=tagsets,
|
|
1391
|
+
tagset_inverted=tagset_inverted,
|
|
1392
|
+
)
|
|
1393
|
+
return self
|
|
1243
1394
|
|
|
1244
1395
|
|
|
1245
1396
|
@memorized
|
|
@@ -21,9 +21,9 @@ plexus/common/utils/pathutils.py,sha256=hGJqSLj08tuOeZ7WeC5d4BtjnPI732BuntVQBQsq
|
|
|
21
21
|
plexus/common/utils/s3utils.py,sha256=zlO4kGs-c2gUeOfPfiKIE5liQZsbYxqAZYCwA8kL0Lo,36017
|
|
22
22
|
plexus/common/utils/sqlutils.py,sha256=D6kTBjhO5YlNRt3uFlPt6z3uH61m9ajEzPYmsI6NoFc,231
|
|
23
23
|
plexus/common/utils/strutils.py,sha256=O9Inv4ffUTf6Xjc5ftoZwbIua1NeG7itCT9S3zjZxBc,16436
|
|
24
|
-
plexus/common/utils/tagutils.py,sha256=
|
|
24
|
+
plexus/common/utils/tagutils.py,sha256=a49OLhIG8-3cR04DX4Cs3fyasbX551mBqg4V3bUsZNk,62206
|
|
25
25
|
plexus/common/utils/testutils.py,sha256=N8ijLu7X-hlQlHzvv0TtSsQpIF4T1hbr-AjkILoV2Ac,6152
|
|
26
|
-
plexus_python_common-1.0.
|
|
27
|
-
plexus_python_common-1.0.
|
|
28
|
-
plexus_python_common-1.0.
|
|
29
|
-
plexus_python_common-1.0.
|
|
26
|
+
plexus_python_common-1.0.71.dist-info/METADATA,sha256=SbTPXbTTk_V68ThFajwP1hBuj1LvM0pyzrpsw4WclLM,1481
|
|
27
|
+
plexus_python_common-1.0.71.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
28
|
+
plexus_python_common-1.0.71.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
|
|
29
|
+
plexus_python_common-1.0.71.dist-info/RECORD,,
|
|
File without changes
|
{plexus_python_common-1.0.69.dist-info → plexus_python_common-1.0.71.dist-info}/top_level.txt
RENAMED
|
File without changes
|