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.
@@ -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="Vehicle name associated with the tag record",
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="Begin datetime of the target range associated with the tag record",
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 datetime of the target range associated with the tag record",
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
- validate_snake_case(v)
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 datetime of the tag record",
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 datetime of the tag record",
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
- def clear(self):
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 count_records(self) -> int:
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 == self.tag_target.sqn)
941
+ .filter(TagRecordTable.target_sqn == target_sqn)
1000
942
  .scalar()
1001
943
  )
1002
944
 
1003
- def iter_records(
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 == self.tag_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 add_ranged_record(
1007
+ def target_add_record(
1064
1008
  self,
1065
- begin_dt: datetime.datetime | None,
1066
- end_dt: datetime.datetime | None,
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. If ``begin_dt`` or ``end_dt`` is None, it will
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=self.tag_target.sqn,
1091
- begin_dt=begin_dt or self.tag_target.begin_dt,
1092
- end_dt=end_dt or self.tag_target.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 add_record(
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 = session.query(TagRecordTable).filter(TagRecordTable.sqn == sqn).one_or_none()
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 remove_records(
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 == self.tag_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
- count_tags = count_records
1238
- iter_tags = iter_records
1239
- add_ranged_tag = add_ranged_record
1240
- add_tag = add_record
1241
- update_tag = update_record
1242
- remove_tags = remove_records
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.69
3
+ Version: 1.0.71
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -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=OaJ_iXbYPRRqq1-GwkqnZ-6UUj4jhhd8Myfu92VKg8Y,55419
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.69.dist-info/METADATA,sha256=kiAxXNij0yyE_PgMAVoCDuXjD_FPGgxA1abUHMLaB44,1481
27
- plexus_python_common-1.0.69.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
28
- plexus_python_common-1.0.69.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
29
- plexus_python_common-1.0.69.dist-info/RECORD,,
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,,