plexus-python-common 1.0.63__tar.gz → 1.0.65__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.63 → plexus_python_common-1.0.65}/PKG-INFO +1 -1
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/tagutils.py +145 -10
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/tagutils_test.py +175 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/.editorconfig +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/.github/workflows/pr.yml +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/.github/workflows/push.yml +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/.gitignore +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/MANIFEST.in +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/README.md +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/VERSION +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/pyproject.toml +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/0-dummy +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/1-dummy +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/2-dummy +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.0.0.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.0.0.vol-0.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.1.1.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.1.1.vol-1.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.2.2.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.2.2.vol-2.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.csv.part0 +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.csv.part1 +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.csv.part2 +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.txt +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/setup.cfg +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/setup.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMFile.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMNode.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMTags.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMWay.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/resources/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/resources/tags/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/resources/tags/unittest-1.0.0.tagset.yaml +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/resources/tags/universal-1.0.0.tagset.yaml +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/apiutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/bagutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/config.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/datautils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/dockerutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/gisutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/jsonutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/ormutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/pathutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/s3utils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/sqlutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/strutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/testutils.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus_python_common.egg-info/requires.txt +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus_python_common.egg-info/top_level.txt +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/datautils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/dockerutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/gisutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/pathutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/s3utils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/strutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/utils/testutils_test.py +0 -0
- {plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/testenv.py +0 -0
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/tagutils.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import dataclasses
|
|
3
3
|
import datetime
|
|
4
|
+
import math
|
|
4
5
|
import os
|
|
5
6
|
import pathlib
|
|
6
7
|
import textwrap
|
|
@@ -16,7 +17,9 @@ import sqlalchemy as sa
|
|
|
16
17
|
import sqlalchemy.dialects.sqlite as sa_sqlite
|
|
17
18
|
import sqlalchemy.orm as sa_orm
|
|
18
19
|
from iker.common.utils.dbutils import ConnectionMaker
|
|
19
|
-
from iker.common.utils.
|
|
20
|
+
from iker.common.utils.dtutils import dt_from_ts_us, dt_to_ts_us
|
|
21
|
+
from iker.common.utils.funcutils import Chainable
|
|
22
|
+
from iker.common.utils.funcutils import chainable, memorized, singleton
|
|
20
23
|
from iker.common.utils.iterutils import batched, head_or_none
|
|
21
24
|
from iker.common.utils.iterutils import dicttree
|
|
22
25
|
from iker.common.utils.iterutils import dicttree_add, dicttree_remove
|
|
@@ -29,6 +32,7 @@ from sqlmodel import Field, SQLModel
|
|
|
29
32
|
from plexus.common.resources.tags import predefined_tagset_specs
|
|
30
33
|
from plexus.common.utils.datautils import validate_colon_tag, validate_snake_case, validate_vehicle_name
|
|
31
34
|
from plexus.common.utils.datautils import validate_dt_timezone, validate_semver, validate_slash_tag
|
|
35
|
+
from plexus.common.utils.jsonutils import json_datetime_encoder
|
|
32
36
|
from plexus.common.utils.ormutils import SequenceModelMixinProtocol
|
|
33
37
|
from plexus.common.utils.ormutils import clone_sequence_model_instance, make_base_model, make_sequence_model_mixin
|
|
34
38
|
from plexus.common.utils.sqlutils import escape_sql_like
|
|
@@ -48,6 +52,8 @@ __all__ = [
|
|
|
48
52
|
"tag_cache_file_path",
|
|
49
53
|
"TagCache",
|
|
50
54
|
"tag_cache",
|
|
55
|
+
"standard_clip_duration_us",
|
|
56
|
+
"populate_clip_ranges",
|
|
51
57
|
]
|
|
52
58
|
|
|
53
59
|
|
|
@@ -440,6 +446,14 @@ class TagTarget(BaseModel):
|
|
|
440
446
|
raise ValueError(f"begin_dt '{self.begin_dt}' is greater than end_dt '{self.end_dt}'")
|
|
441
447
|
return self
|
|
442
448
|
|
|
449
|
+
@pdt.field_serializer("begin_dt", mode="plain")
|
|
450
|
+
def serialize_begin_dt(self, v: datetime.datetime) -> str:
|
|
451
|
+
return json_datetime_encoder(v)
|
|
452
|
+
|
|
453
|
+
@pdt.field_serializer("end_dt", mode="plain")
|
|
454
|
+
def serialize_end_dt(self, v: datetime.datetime) -> str:
|
|
455
|
+
return json_datetime_encoder(v)
|
|
456
|
+
|
|
443
457
|
|
|
444
458
|
class TagRecord(BaseModel):
|
|
445
459
|
target_sqn: int = Field(
|
|
@@ -473,6 +487,11 @@ class TagRecord(BaseModel):
|
|
|
473
487
|
default=None,
|
|
474
488
|
description="Additional properties of the tag record in JSON format",
|
|
475
489
|
)
|
|
490
|
+
flags: int = Field(
|
|
491
|
+
sa_column=sa.Column(sa_sqlite.INTEGER),
|
|
492
|
+
default=0,
|
|
493
|
+
description="Integer bitmask storing status or metadata flags for this tag record",
|
|
494
|
+
)
|
|
476
495
|
|
|
477
496
|
@pdt.field_validator("begin_dt", mode="after")
|
|
478
497
|
@classmethod
|
|
@@ -512,6 +531,14 @@ class TagRecord(BaseModel):
|
|
|
512
531
|
validate_colon_tag(v)
|
|
513
532
|
return v
|
|
514
533
|
|
|
534
|
+
@pdt.field_serializer("begin_dt", mode="plain")
|
|
535
|
+
def serialize_begin_dt(self, v: datetime.datetime) -> str:
|
|
536
|
+
return json_datetime_encoder(v)
|
|
537
|
+
|
|
538
|
+
@pdt.field_serializer("end_dt", mode="plain")
|
|
539
|
+
def serialize_end_dt(self, v: datetime.datetime) -> str:
|
|
540
|
+
return json_datetime_encoder(v)
|
|
541
|
+
|
|
515
542
|
|
|
516
543
|
class TagTargetTable(TagTarget, make_sequence_model_mixin("sqlite"), table=True):
|
|
517
544
|
__tablename__ = "tag_target_info"
|
|
@@ -539,6 +566,7 @@ if typing.TYPE_CHECKING:
|
|
|
539
566
|
tagset_version: sa_orm.Mapped[str | None] = ...
|
|
540
567
|
tag: sa_orm.Mapped[str] = ...
|
|
541
568
|
props: sa_orm.Mapped[JsonType | None] = ...
|
|
569
|
+
flags: sa_orm.Mapped[int] = ...
|
|
542
570
|
|
|
543
571
|
|
|
544
572
|
@singleton
|
|
@@ -571,9 +599,12 @@ class TagCache(object):
|
|
|
571
599
|
with self.conn_maker.make_session() as session:
|
|
572
600
|
yield session
|
|
573
601
|
|
|
574
|
-
def get_target(self,
|
|
602
|
+
def get_target(self, target: int | str) -> TagTargetTable | None:
|
|
575
603
|
with self.make_session() as session:
|
|
576
|
-
|
|
604
|
+
if isinstance(target, int):
|
|
605
|
+
return session.get(TagTargetTable, target)
|
|
606
|
+
else:
|
|
607
|
+
return session.query(TagTargetTable).filter(TagTargetTable.identifier == target).one_or_none()
|
|
577
608
|
|
|
578
609
|
def query_targets(
|
|
579
610
|
self,
|
|
@@ -660,10 +691,10 @@ class TagCache(object):
|
|
|
660
691
|
)
|
|
661
692
|
session.commit()
|
|
662
693
|
|
|
663
|
-
def with_target(self,
|
|
664
|
-
target_info = self.get_target(
|
|
694
|
+
def with_target(self, target: int | str) -> "TargetedTagCache":
|
|
695
|
+
target_info = self.get_target(target)
|
|
665
696
|
if target_info is None:
|
|
666
|
-
raise ValueError(f"target
|
|
697
|
+
raise ValueError(f"target '{target}' not found in cache")
|
|
667
698
|
return TargetedTagCache(cache=self, target_info=target_info)
|
|
668
699
|
|
|
669
700
|
def iter_tags(
|
|
@@ -957,7 +988,7 @@ class TargetedTagCache(object):
|
|
|
957
988
|
props: JsonType | None = None,
|
|
958
989
|
tagset_namespace: str | None = None,
|
|
959
990
|
tagset_version: str | None = None,
|
|
960
|
-
) -> Self:
|
|
991
|
+
) -> Chainable[Self, TagRecordTable]:
|
|
961
992
|
"""
|
|
962
993
|
Add a tag record to the cache for the specified time range. If ``begin_dt`` or ``end_dt`` is None, it will
|
|
963
994
|
default to the target's ``begin_dt`` or ``end_dt`` respectively.
|
|
@@ -984,10 +1015,12 @@ class TargetedTagCache(object):
|
|
|
984
1015
|
tag=tag.name if isinstance(tag, Tag) else tag,
|
|
985
1016
|
props=props,
|
|
986
1017
|
)
|
|
987
|
-
|
|
1018
|
+
db_tag_record = clone_sequence_model_instance(TagRecordTable, tag_record)
|
|
1019
|
+
session.add(db_tag_record)
|
|
988
1020
|
session.commit()
|
|
989
1021
|
|
|
990
|
-
|
|
1022
|
+
session.refresh(db_tag_record)
|
|
1023
|
+
return chainable(self, db_tag_record)
|
|
991
1024
|
|
|
992
1025
|
def add_tag(
|
|
993
1026
|
self,
|
|
@@ -995,7 +1028,7 @@ class TargetedTagCache(object):
|
|
|
995
1028
|
props: JsonType | None = None,
|
|
996
1029
|
tagset_namespace: str | None = None,
|
|
997
1030
|
tagset_version: str | None = None,
|
|
998
|
-
) -> Self:
|
|
1031
|
+
) -> Chainable[Self, TagRecordTable]:
|
|
999
1032
|
"""
|
|
1000
1033
|
Add a tag record to the cache for the entire target range.
|
|
1001
1034
|
|
|
@@ -1019,6 +1052,61 @@ class TargetedTagCache(object):
|
|
|
1019
1052
|
props=props,
|
|
1020
1053
|
)
|
|
1021
1054
|
|
|
1055
|
+
def update_tag(
|
|
1056
|
+
self,
|
|
1057
|
+
sqn: int,
|
|
1058
|
+
*,
|
|
1059
|
+
begin_dt: datetime.datetime | None = None,
|
|
1060
|
+
end_dt: datetime.datetime | None = None,
|
|
1061
|
+
tag: str | Tag | BoundTag | None = None,
|
|
1062
|
+
props: JsonType | None = None,
|
|
1063
|
+
tagset_namespace: str | None = None,
|
|
1064
|
+
tagset_version: str | None = None,
|
|
1065
|
+
flags: int | None = None,
|
|
1066
|
+
) -> Chainable[Self, TagRecordTable]:
|
|
1067
|
+
"""
|
|
1068
|
+
Update a tag record in the cache by its sequence number.
|
|
1069
|
+
|
|
1070
|
+
:param sqn: Sequence number of the tag record to be updated
|
|
1071
|
+
:param begin_dt: New begin datetime of the tag record (optional)
|
|
1072
|
+
:param end_dt: New end datetime of the tag record (optional)
|
|
1073
|
+
:param tag: New tag name or ``Tag``/``BoundTag`` instance to be updated (optional). If ``Tag``/``BoundTag``
|
|
1074
|
+
instance is provided, its name will be used.
|
|
1075
|
+
:param props: New additional properties of the tag record in JSON format (optional)
|
|
1076
|
+
:param tagset_namespace: New namespace of the tagset that the tag belongs to (optional). If the ``tag``
|
|
1077
|
+
parameter is a ``BoundTag`` instance, this parameter will be ignored and the namespace
|
|
1078
|
+
from the instance will be used.
|
|
1079
|
+
:param tagset_version: New version of the tagset that the tag belongs to (optional). If the ``tag`` parameter
|
|
1080
|
+
is a ``BoundTag`` instance, this parameter will be ignored and the version from the
|
|
1081
|
+
instance will be used.
|
|
1082
|
+
:param flags: New integer bitmask storing status or metadata flags for this tag record (optional)
|
|
1083
|
+
:return: Self instance for chaining
|
|
1084
|
+
"""
|
|
1085
|
+
with self.make_session() as session:
|
|
1086
|
+
db_tag_record = session.query(TagRecordTable).filter(TagRecordTable.sqn == sqn).one_or_none()
|
|
1087
|
+
if not db_tag_record:
|
|
1088
|
+
raise ValueError(f"tag record with sqn '{sqn}' not found in cache")
|
|
1089
|
+
|
|
1090
|
+
if begin_dt is not None:
|
|
1091
|
+
db_tag_record.begin_dt = begin_dt
|
|
1092
|
+
if end_dt is not None:
|
|
1093
|
+
db_tag_record.end_dt = end_dt
|
|
1094
|
+
if tagset_namespace is not None:
|
|
1095
|
+
db_tag_record.tagset_namespace = tag.namespace if isinstance(tag, BoundTag) else tagset_namespace
|
|
1096
|
+
if tagset_version is not None:
|
|
1097
|
+
db_tag_record.tagset_version = tag.version if isinstance(tag, BoundTag) else tagset_version
|
|
1098
|
+
if tag is not None:
|
|
1099
|
+
db_tag_record.tag = tag.name if isinstance(tag, Tag) else tag
|
|
1100
|
+
if props is not None:
|
|
1101
|
+
db_tag_record.props = props
|
|
1102
|
+
if flags is not None:
|
|
1103
|
+
db_tag_record.flags = flags
|
|
1104
|
+
|
|
1105
|
+
session.commit()
|
|
1106
|
+
|
|
1107
|
+
session.refresh(db_tag_record)
|
|
1108
|
+
return chainable(self, db_tag_record)
|
|
1109
|
+
|
|
1022
1110
|
def remove_tags(
|
|
1023
1111
|
self,
|
|
1024
1112
|
begin_dt: datetime.datetime | None = None,
|
|
@@ -1093,3 +1181,50 @@ def tag_cache(*, identifier: str | None = None, file_path: str | None = None) ->
|
|
|
1093
1181
|
if file_path is not None:
|
|
1094
1182
|
return TagCache(file_path=file_path)
|
|
1095
1183
|
return TagCache(file_path=tag_cache_file_path())
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
@singleton
|
|
1187
|
+
def standard_clip_duration_us() -> int:
|
|
1188
|
+
"""Duration of clips to split samples into, in microseconds, which is fixed to 20 seconds for now."""
|
|
1189
|
+
return 20 * 1_000_000 # 20 seconds
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def populate_clip_ranges(
|
|
1193
|
+
data_begin_dt: datetime.datetime,
|
|
1194
|
+
data_end_dt: datetime.datetime,
|
|
1195
|
+
*,
|
|
1196
|
+
clip_duration_us: int = None
|
|
1197
|
+
) -> Generator[tuple[datetime.datetime, datetime.datetime, datetime.datetime, datetime.datetime]]:
|
|
1198
|
+
"""
|
|
1199
|
+
Generate clip begin datetime, clip data begin datetime, and clip data end datetime for each clip slot that overlaps
|
|
1200
|
+
with the given data begin and end datetimes.
|
|
1201
|
+
The clip begin datetime is the beginning of the clip slot, the clip data begin datetime is the maximum of the clip
|
|
1202
|
+
begin datetime and the data begin datetime, and the clip data end datetime is the minimum of the clip end datetime
|
|
1203
|
+
and the data end datetime.
|
|
1204
|
+
|
|
1205
|
+
:param data_begin_dt: The beginning datetime of the data.
|
|
1206
|
+
:param data_end_dt: The end datetime of the data.
|
|
1207
|
+
:param clip_duration_us: The duration of each clip in microseconds. If not provided,
|
|
1208
|
+
the standard clip duration will be used.
|
|
1209
|
+
:return: A generator of tuples containing the clip begin datetime, clip data begin datetime,
|
|
1210
|
+
and clip data end datetime
|
|
1211
|
+
"""
|
|
1212
|
+
clip_duration_us = clip_duration_us or standard_clip_duration_us()
|
|
1213
|
+
|
|
1214
|
+
if data_begin_dt == data_end_dt:
|
|
1215
|
+
clip_slot = math.floor(dt_to_ts_us(data_begin_dt) / clip_duration_us)
|
|
1216
|
+
clip_begin_dt = dt_from_ts_us(clip_slot * clip_duration_us)
|
|
1217
|
+
clip_end_dt = dt_from_ts_us((clip_slot + 1) * clip_duration_us)
|
|
1218
|
+
yield clip_begin_dt, clip_end_dt, data_begin_dt, data_end_dt
|
|
1219
|
+
return
|
|
1220
|
+
|
|
1221
|
+
begin_clip_slot = math.floor(dt_to_ts_us(data_begin_dt) / clip_duration_us)
|
|
1222
|
+
end_clip_slot = math.ceil(dt_to_ts_us(data_end_dt) / clip_duration_us)
|
|
1223
|
+
|
|
1224
|
+
for clip_slot in range(begin_clip_slot, end_clip_slot):
|
|
1225
|
+
clip_begin_dt = dt_from_ts_us(clip_slot * clip_duration_us)
|
|
1226
|
+
clip_end_dt = dt_from_ts_us((clip_slot + 1) * clip_duration_us)
|
|
1227
|
+
clip_data_begin_dt = max(clip_begin_dt, data_begin_dt)
|
|
1228
|
+
clip_data_end_dt = min(clip_end_dt, data_end_dt)
|
|
1229
|
+
|
|
1230
|
+
yield clip_begin_dt, clip_end_dt, clip_data_begin_dt, clip_data_end_dt
|
|
@@ -10,6 +10,7 @@ from iker.common.utils.iterutils import last
|
|
|
10
10
|
from iker.common.utils.jsonutils import JsonObject
|
|
11
11
|
|
|
12
12
|
from plexus.common.utils.tagutils import MutableTagset, Tag, TagCache, Tagset
|
|
13
|
+
from plexus.common.utils.tagutils import populate_clip_ranges
|
|
13
14
|
from plexus.common.utils.tagutils import predefined_tagsets, render_tagset_markdown_readme
|
|
14
15
|
from plexus.common.utils.tagutils import tag_cache_file_path
|
|
15
16
|
|
|
@@ -497,3 +498,177 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
497
498
|
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
498
499
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
499
500
|
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
501
|
+
|
|
502
|
+
data_populate_clip_ranges = [
|
|
503
|
+
(
|
|
504
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
505
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
506
|
+
[
|
|
507
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
508
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
509
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
510
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
511
|
+
],
|
|
512
|
+
),
|
|
513
|
+
(
|
|
514
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
515
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00"),
|
|
516
|
+
[
|
|
517
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
518
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
519
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
520
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
521
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
522
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
523
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
524
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00")),
|
|
525
|
+
(dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
526
|
+
dt_parse_iso("2023-01-01T00:01:00.000000+00:00"),
|
|
527
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
528
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00")),
|
|
529
|
+
],
|
|
530
|
+
),
|
|
531
|
+
(
|
|
532
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
533
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00"),
|
|
534
|
+
[
|
|
535
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
536
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
537
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
538
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00")),
|
|
539
|
+
],
|
|
540
|
+
),
|
|
541
|
+
(
|
|
542
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
543
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
544
|
+
[
|
|
545
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
546
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
547
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
548
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
549
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
550
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
551
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
552
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00")),
|
|
553
|
+
],
|
|
554
|
+
),
|
|
555
|
+
(
|
|
556
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
557
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
558
|
+
[
|
|
559
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
560
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
561
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
562
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00")),
|
|
563
|
+
],
|
|
564
|
+
),
|
|
565
|
+
(
|
|
566
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
567
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
568
|
+
[
|
|
569
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
570
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
571
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
572
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00")),
|
|
573
|
+
],
|
|
574
|
+
)
|
|
575
|
+
]
|
|
576
|
+
|
|
577
|
+
@ddt.idata(data_populate_clip_ranges)
|
|
578
|
+
@ddt.unpack
|
|
579
|
+
def test_populate_clip_ranges(self, data_begin_dt, data_end_dt, expected):
|
|
580
|
+
result = list(populate_clip_ranges(data_begin_dt, data_end_dt))
|
|
581
|
+
self.assertEqual(result, expected)
|
|
582
|
+
|
|
583
|
+
data_populate_clip_ranges__customized_duration = [
|
|
584
|
+
(
|
|
585
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
586
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
587
|
+
[
|
|
588
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
589
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
590
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
591
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00")),
|
|
592
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
593
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
594
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
595
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
596
|
+
],
|
|
597
|
+
),
|
|
598
|
+
(
|
|
599
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
600
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00"),
|
|
601
|
+
[
|
|
602
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
603
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
604
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
605
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
606
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
607
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
608
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
609
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00")),
|
|
610
|
+
(dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
611
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
612
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
613
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00")),
|
|
614
|
+
(dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
615
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00"),
|
|
616
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
617
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00")),
|
|
618
|
+
],
|
|
619
|
+
),
|
|
620
|
+
(
|
|
621
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
622
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00"),
|
|
623
|
+
[
|
|
624
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
625
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
626
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
627
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00")),
|
|
628
|
+
],
|
|
629
|
+
),
|
|
630
|
+
(
|
|
631
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
632
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
633
|
+
[
|
|
634
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
635
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
636
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
637
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00")),
|
|
638
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
639
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
640
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
641
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
642
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
643
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
644
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
645
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00")),
|
|
646
|
+
],
|
|
647
|
+
),
|
|
648
|
+
(
|
|
649
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
650
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
651
|
+
[
|
|
652
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
653
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
654
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
655
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00")),
|
|
656
|
+
],
|
|
657
|
+
),
|
|
658
|
+
(
|
|
659
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
660
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
661
|
+
[
|
|
662
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
663
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
664
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
665
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00")),
|
|
666
|
+
],
|
|
667
|
+
)
|
|
668
|
+
]
|
|
669
|
+
|
|
670
|
+
@ddt.idata(data_populate_clip_ranges__customized_duration)
|
|
671
|
+
@ddt.unpack
|
|
672
|
+
def test_populate_clip_ranges__customized_duration(self, data_begin_dt, data_end_dt, expected):
|
|
673
|
+
result = list(populate_clip_ranges(data_begin_dt, data_end_dt, clip_duration_us=10_000_000))
|
|
674
|
+
self.assertEqual(result, expected)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/0-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/1-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/2-dummy
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/resources/unittest/pathutils/dummy.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMFile.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMNode.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMTags.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/OSMWay.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/carto/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/resources/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/apiutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/bagutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/config.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/datautils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/dockerutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/gisutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/jsonutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/ormutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/pathutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/s3utils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/sqlutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/strutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/src/plexus/common/utils/testutils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.63 → plexus_python_common-1.0.65}/test/plexus_tests/common/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|