plexus-python-common 1.0.53__tar.gz → 1.0.55__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.53 → plexus_python_common-1.0.55}/PKG-INFO +1 -1
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/datautils.py +3 -1
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/ormutils.py +50 -4
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/tagutils.py +87 -75
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/tagutils_test.py +152 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/.editorconfig +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/.github/workflows/pr.yml +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/.github/workflows/push.yml +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/.gitignore +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/MANIFEST.in +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/README.md +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/VERSION +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/pyproject.toml +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/0-dummy +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/1-dummy +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/2-dummy +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.0.0.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.0.0.vol-0.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.1.1.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.1.1.vol-1.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.2.2.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.2.2.vol-2.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.csv.part0 +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.csv.part1 +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.csv.part2 +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/dummy.txt +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/setup.cfg +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/setup.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMFile.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMNode.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMTags.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMWay.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/pose.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/proj.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/resources/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/resources/tags/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/resources/tags/universal.tagset.yaml +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/apiutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/bagutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/config.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/dockerutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/jsonutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/pathutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/s3utils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/sqlutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/strutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/testutils.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus_python_common.egg-info/requires.txt +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus_python_common.egg-info/top_level.txt +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/pose_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/proj_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/datautils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/dockerutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/pathutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/s3utils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/strutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/utils/testutils_test.py +0 -0
- {plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/testenv.py +0 -0
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/datautils.py
RENAMED
|
@@ -118,7 +118,9 @@ validate_vehicle_name = make_validate_parse_string(parse_vehicle_name)
|
|
|
118
118
|
validate_bag_name = make_validate_parse_string(parse_bag_name)
|
|
119
119
|
|
|
120
120
|
|
|
121
|
-
def validate_dt_timezone(dt: datetime.datetime):
|
|
121
|
+
def validate_dt_timezone(dt: datetime.datetime, *, allow_naive: bool = False):
|
|
122
|
+
if allow_naive and dt.tzinfo is None:
|
|
123
|
+
return
|
|
122
124
|
if dt.tzinfo != datetime.timezone.utc:
|
|
123
125
|
raise ValueError(f"dt '{dt}' is not in UTC")
|
|
124
126
|
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/ormutils.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
from collections.abc import Iterable, Sequence
|
|
2
3
|
from typing import Protocol, Self
|
|
3
4
|
|
|
4
5
|
import pydantic as pdt
|
|
@@ -7,7 +8,8 @@ import sqlalchemy.dialects.postgresql as sa_pg
|
|
|
7
8
|
import sqlalchemy.dialects.sqlite as sa_sqlite
|
|
8
9
|
import sqlalchemy.exc as sa_exc
|
|
9
10
|
import sqlalchemy.orm as sa_orm
|
|
10
|
-
from iker.common.utils.dbutils import dialects
|
|
11
|
+
from iker.common.utils.dbutils import dialects, orm_to_dict
|
|
12
|
+
from iker.common.utils.iterutils import head
|
|
11
13
|
from sqlmodel import Field, SQLModel
|
|
12
14
|
|
|
13
15
|
from plexus.common.utils.datautils import validate_dt_timezone
|
|
@@ -17,6 +19,7 @@ __all__ = [
|
|
|
17
19
|
"compare_postgresql_types",
|
|
18
20
|
"compare_sqlite_types",
|
|
19
21
|
"model_name_of",
|
|
22
|
+
"print_model_results",
|
|
20
23
|
"validate_model_extended",
|
|
21
24
|
"collect_model_tables",
|
|
22
25
|
"model_copy_from",
|
|
@@ -137,13 +140,56 @@ def compare_sqlite_types(type_a, type_b) -> bool:
|
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
|
|
140
|
-
def model_name_of(model: type[SQLModel], fallback_classname: bool = True) -> str | None:
|
|
143
|
+
def model_name_of(model: type[SQLModel] | SQLModel, fallback_classname: bool = True) -> str | None:
|
|
141
144
|
table_name = getattr(model, "__tablename__")
|
|
142
|
-
if not table_name:
|
|
143
|
-
return model
|
|
145
|
+
if not table_name and fallback_classname:
|
|
146
|
+
return getattr(model, "__name__") if isinstance(model, type) else getattr(model.__class__, "__name__")
|
|
144
147
|
return table_name
|
|
145
148
|
|
|
146
149
|
|
|
150
|
+
def print_model_results(results: Iterable[SQLModel | Sequence[SQLModel]]):
|
|
151
|
+
"""
|
|
152
|
+
Pretty-print an iterable of ``SQLModel`` instances or sequences of ``SQLModel`` instances in a tabular format
|
|
153
|
+
using the ``rich`` library.
|
|
154
|
+
|
|
155
|
+
:param results: An iterable of ``SQLModel`` instances or sequences of ``SQLModel`` instances to print.
|
|
156
|
+
"""
|
|
157
|
+
results = list(results)
|
|
158
|
+
if not results:
|
|
159
|
+
print("Query results: (empty)")
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
first_result = head(results)
|
|
163
|
+
|
|
164
|
+
cols = [f"{model_name_of(db_model)}.{key}"
|
|
165
|
+
for db_model in (first_result if isinstance(first_result, Sequence) else [first_result])
|
|
166
|
+
for key in orm_to_dict(db_model).keys()]
|
|
167
|
+
|
|
168
|
+
rows = [
|
|
169
|
+
{f"{model_name_of(db_model)}.{key}": str(value)
|
|
170
|
+
for db_model in (result if isinstance(result, Sequence) else (result,))
|
|
171
|
+
for key, value in orm_to_dict(db_model).items()}
|
|
172
|
+
for result in results
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
col_widths = [len(col) for col in cols]
|
|
176
|
+
for row in rows:
|
|
177
|
+
for i, col in enumerate(cols):
|
|
178
|
+
col_widths[i] = max(col_widths[i], len(str(row.get(col, ""))))
|
|
179
|
+
|
|
180
|
+
from rich.console import Console
|
|
181
|
+
from rich.table import Table
|
|
182
|
+
|
|
183
|
+
console = Console()
|
|
184
|
+
table = Table(show_header=True)
|
|
185
|
+
for col, col_width in zip(cols, col_widths):
|
|
186
|
+
table.add_column(col, min_width=col_width)
|
|
187
|
+
for row in rows:
|
|
188
|
+
table.add_row(*[str(row.get(col)) for col in cols])
|
|
189
|
+
|
|
190
|
+
console.print(table, crop=False)
|
|
191
|
+
|
|
192
|
+
|
|
147
193
|
def validate_model_extended(model_base: type[SQLModel], model_extended: type[SQLModel]) -> bool:
|
|
148
194
|
"""
|
|
149
195
|
Validates if ``model_extended`` is an extension of ``model_base`` by checking if all fields in ``model_base``
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/tagutils.py
RENAMED
|
@@ -40,9 +40,9 @@ __all__ = [
|
|
|
40
40
|
"populate_tagset",
|
|
41
41
|
"predefined_tagsets",
|
|
42
42
|
"render_tagset_markdown_readme",
|
|
43
|
-
"
|
|
43
|
+
"TagTarget",
|
|
44
44
|
"TagRecord",
|
|
45
|
-
"
|
|
45
|
+
"TagTargetTable",
|
|
46
46
|
"TagRecordTable",
|
|
47
47
|
"tag_cache_file_path",
|
|
48
48
|
"TagCache",
|
|
@@ -295,10 +295,10 @@ def render_tagset_markdown_readme(tagset: Tagset) -> str:
|
|
|
295
295
|
BaseModel = make_base_model()
|
|
296
296
|
|
|
297
297
|
|
|
298
|
-
class
|
|
299
|
-
|
|
298
|
+
class TagTarget(BaseModel):
|
|
299
|
+
identifier: str = Field(
|
|
300
300
|
sa_column=sa.Column(sa_sqlite.VARCHAR(256), nullable=False, unique=True),
|
|
301
|
-
description="
|
|
301
|
+
description="Identifier of the tag target",
|
|
302
302
|
)
|
|
303
303
|
tagger_name: str = Field(
|
|
304
304
|
sa_column=sa.Column(sa_sqlite.VARCHAR(128), nullable=False),
|
|
@@ -321,9 +321,9 @@ class TagTargetInfo(BaseModel):
|
|
|
321
321
|
description="End datetime of the target range associated with the tag record",
|
|
322
322
|
)
|
|
323
323
|
|
|
324
|
-
@pdt.field_validator("
|
|
324
|
+
@pdt.field_validator("identifier", mode="after")
|
|
325
325
|
@classmethod
|
|
326
|
-
def
|
|
326
|
+
def validate_identifier(cls, v: str) -> str:
|
|
327
327
|
validate_slash_tag(v)
|
|
328
328
|
return v
|
|
329
329
|
|
|
@@ -348,13 +348,13 @@ class TagTargetInfo(BaseModel):
|
|
|
348
348
|
@pdt.field_validator("begin_dt", mode="after")
|
|
349
349
|
@classmethod
|
|
350
350
|
def validate_begin_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
351
|
-
validate_dt_timezone(v)
|
|
351
|
+
validate_dt_timezone(v, allow_naive=True)
|
|
352
352
|
return v
|
|
353
353
|
|
|
354
354
|
@pdt.field_validator("end_dt", mode="after")
|
|
355
355
|
@classmethod
|
|
356
356
|
def validate_end_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
357
|
-
validate_dt_timezone(v)
|
|
357
|
+
validate_dt_timezone(v, allow_naive=True)
|
|
358
358
|
return v
|
|
359
359
|
|
|
360
360
|
@pdt.model_validator(mode="after")
|
|
@@ -390,13 +390,13 @@ class TagRecord(BaseModel):
|
|
|
390
390
|
@pdt.field_validator("begin_dt", mode="after")
|
|
391
391
|
@classmethod
|
|
392
392
|
def validate_begin_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
393
|
-
validate_dt_timezone(v)
|
|
393
|
+
validate_dt_timezone(v, allow_naive=True)
|
|
394
394
|
return v
|
|
395
395
|
|
|
396
396
|
@pdt.field_validator("end_dt", mode="after")
|
|
397
397
|
@classmethod
|
|
398
398
|
def validate_end_dt(cls, v: datetime.datetime) -> datetime.datetime:
|
|
399
|
-
validate_dt_timezone(v)
|
|
399
|
+
validate_dt_timezone(v, allow_naive=True)
|
|
400
400
|
return v
|
|
401
401
|
|
|
402
402
|
@pdt.model_validator(mode="after")
|
|
@@ -412,7 +412,7 @@ class TagRecord(BaseModel):
|
|
|
412
412
|
return v
|
|
413
413
|
|
|
414
414
|
|
|
415
|
-
class
|
|
415
|
+
class TagTargetTable(TagTarget, make_sequence_model_mixin("sqlite"), table=True):
|
|
416
416
|
__tablename__ = "tag_target_info"
|
|
417
417
|
|
|
418
418
|
|
|
@@ -421,8 +421,8 @@ class TagRecordTable(TagRecord, make_sequence_model_mixin("sqlite"), table=True)
|
|
|
421
421
|
|
|
422
422
|
|
|
423
423
|
if typing.TYPE_CHECKING:
|
|
424
|
-
class
|
|
425
|
-
|
|
424
|
+
class TagTargetTable(SQLModel, SequenceModelMixinProtocol):
|
|
425
|
+
identifier: sa_orm.Mapped[str] = ...
|
|
426
426
|
tagger_name: sa_orm.Mapped[str] = ...
|
|
427
427
|
tagger_version: sa_orm.Mapped[str] = ...
|
|
428
428
|
vehicle_name: sa_orm.Mapped[str] = ...
|
|
@@ -468,63 +468,63 @@ class TagCache(object):
|
|
|
468
468
|
with self.conn_maker.make_session() as session:
|
|
469
469
|
yield session
|
|
470
470
|
|
|
471
|
-
def get_target(self,
|
|
471
|
+
def get_target(self, identifier: str) -> TagTargetTable | None:
|
|
472
472
|
with self.make_session() as session:
|
|
473
|
-
return session.query(
|
|
473
|
+
return session.query(TagTargetTable).filter(TagTargetTable.identifier == identifier).one_or_none()
|
|
474
474
|
|
|
475
475
|
def query_targets(
|
|
476
476
|
self,
|
|
477
|
-
|
|
477
|
+
identifier: str | None = None,
|
|
478
478
|
tagger_name: str | None = None,
|
|
479
479
|
tagger_version: str | None = None,
|
|
480
480
|
vehicle_name: str | None = None,
|
|
481
481
|
begin_dt: datetime.datetime | None = None,
|
|
482
482
|
end_dt: datetime.datetime | None = None,
|
|
483
|
-
) ->
|
|
483
|
+
) -> list[TagTargetTable]:
|
|
484
484
|
with self.make_session() as session:
|
|
485
|
-
query = session.query(
|
|
486
|
-
if
|
|
487
|
-
query = query.filter(
|
|
485
|
+
query = session.query(TagTargetTable)
|
|
486
|
+
if identifier:
|
|
487
|
+
query = query.filter(TagTargetTable.identifier == identifier)
|
|
488
488
|
if tagger_name:
|
|
489
|
-
query = query.filter(
|
|
489
|
+
query = query.filter(TagTargetTable.tagger_name == tagger_name)
|
|
490
490
|
if tagger_version:
|
|
491
|
-
query = query.filter(
|
|
491
|
+
query = query.filter(TagTargetTable.tagger_version == tagger_version)
|
|
492
492
|
if vehicle_name:
|
|
493
|
-
query = query.filter(
|
|
493
|
+
query = query.filter(TagTargetTable.vehicle_name == vehicle_name)
|
|
494
494
|
if begin_dt:
|
|
495
|
-
query = query.filter(
|
|
495
|
+
query = query.filter(TagTargetTable.end_dt >= begin_dt)
|
|
496
496
|
if end_dt:
|
|
497
|
-
query = query.filter(
|
|
497
|
+
query = query.filter(TagTargetTable.begin_dt <= end_dt)
|
|
498
498
|
|
|
499
|
-
|
|
499
|
+
return query.all()
|
|
500
500
|
|
|
501
501
|
def add_target(
|
|
502
502
|
self,
|
|
503
|
-
|
|
503
|
+
identifier: str,
|
|
504
504
|
tagger_name: str,
|
|
505
505
|
tagger_version: str,
|
|
506
506
|
vehicle_name: str,
|
|
507
507
|
begin_dt: datetime.datetime,
|
|
508
508
|
end_dt: datetime.datetime,
|
|
509
|
-
) ->
|
|
509
|
+
) -> TagTargetTable:
|
|
510
510
|
with self.make_session() as session:
|
|
511
|
-
target_info =
|
|
512
|
-
|
|
511
|
+
target_info = TagTarget(
|
|
512
|
+
identifier=identifier,
|
|
513
513
|
tagger_name=tagger_name,
|
|
514
514
|
tagger_version=tagger_version,
|
|
515
515
|
vehicle_name=vehicle_name,
|
|
516
516
|
begin_dt=begin_dt,
|
|
517
517
|
end_dt=end_dt,
|
|
518
518
|
)
|
|
519
|
-
db_target_info = clone_sequence_model_instance(
|
|
519
|
+
db_target_info = clone_sequence_model_instance(TagTargetTable, target_info)
|
|
520
520
|
session.add(db_target_info)
|
|
521
521
|
session.commit()
|
|
522
522
|
|
|
523
|
-
return self.get_target(
|
|
523
|
+
return self.get_target(identifier)
|
|
524
524
|
|
|
525
525
|
def remove_targets(
|
|
526
526
|
self,
|
|
527
|
-
|
|
527
|
+
identifier: str | None = None,
|
|
528
528
|
tagger_name: str | None = None,
|
|
529
529
|
tagger_version: str | None = None,
|
|
530
530
|
vehicle_name: str | None = None,
|
|
@@ -532,19 +532,19 @@ class TagCache(object):
|
|
|
532
532
|
end_dt: datetime.datetime | None = None,
|
|
533
533
|
):
|
|
534
534
|
with self.make_session() as session:
|
|
535
|
-
query = session.query(
|
|
536
|
-
if
|
|
537
|
-
query = query.filter(
|
|
535
|
+
query = session.query(TagTargetTable)
|
|
536
|
+
if identifier:
|
|
537
|
+
query = query.filter(TagTargetTable.identifier == identifier)
|
|
538
538
|
if tagger_name:
|
|
539
|
-
query = query.filter(
|
|
539
|
+
query = query.filter(TagTargetTable.tagger_name == tagger_name)
|
|
540
540
|
if tagger_version:
|
|
541
|
-
query = query.filter(
|
|
541
|
+
query = query.filter(TagTargetTable.tagger_version == tagger_version)
|
|
542
542
|
if vehicle_name:
|
|
543
|
-
query = query.filter(
|
|
543
|
+
query = query.filter(TagTargetTable.vehicle_name == vehicle_name)
|
|
544
544
|
if begin_dt:
|
|
545
|
-
query = query.filter(
|
|
545
|
+
query = query.filter(TagTargetTable.end_dt >= begin_dt)
|
|
546
546
|
if end_dt:
|
|
547
|
-
query = query.filter(
|
|
547
|
+
query = query.filter(TagTargetTable.begin_dt <= end_dt)
|
|
548
548
|
|
|
549
549
|
query.delete()
|
|
550
550
|
session.commit()
|
|
@@ -552,7 +552,7 @@ class TagCache(object):
|
|
|
552
552
|
(
|
|
553
553
|
session
|
|
554
554
|
.query(TagRecordTable)
|
|
555
|
-
.filter(TagRecordTable.target_sqn.notin_(session.query(
|
|
555
|
+
.filter(TagRecordTable.target_sqn.notin_(session.query(TagTargetTable.sqn)))
|
|
556
556
|
.delete()
|
|
557
557
|
)
|
|
558
558
|
session.commit()
|
|
@@ -611,7 +611,7 @@ class TagCache(object):
|
|
|
611
611
|
end_dt: datetime.datetime | None = None,
|
|
612
612
|
tag_pattern: str | None = None,
|
|
613
613
|
*,
|
|
614
|
-
|
|
614
|
+
target_identifier: str | None = None,
|
|
615
615
|
target_tagger_name: str | None = None,
|
|
616
616
|
target_tagger_version: str | None = None,
|
|
617
617
|
target_vehicle_name: str | None = None,
|
|
@@ -620,7 +620,7 @@ class TagCache(object):
|
|
|
620
620
|
tagsets: Sequence[Tagset] | None = None,
|
|
621
621
|
tagset_inverted: bool = False,
|
|
622
622
|
batch_size: int = 1000,
|
|
623
|
-
) -> Generator[tuple[TagRecordTable,
|
|
623
|
+
) -> Generator[tuple[TagRecordTable, TagTargetTable], None, None]:
|
|
624
624
|
"""
|
|
625
625
|
Query tag records along with their target info in the cache with optional filters.
|
|
626
626
|
|
|
@@ -628,7 +628,7 @@ class TagCache(object):
|
|
|
628
628
|
:param end_dt: Filter by end time (inclusive)
|
|
629
629
|
:param tag_pattern: Filter by tag name pattern (SQL LIKE syntax, e.g. "dummy_tag:%" to match all tags starting
|
|
630
630
|
with "dummy_tag:")
|
|
631
|
-
:param
|
|
631
|
+
:param target_identifier: Filter by target identifier (exact match)
|
|
632
632
|
:param target_tagger_name: Filter by target tagger name (exact match)
|
|
633
633
|
:param target_tagger_version: Filter by target tagger version (exact match)
|
|
634
634
|
:param target_vehicle_name: Filter by target vehicle name (exact match)
|
|
@@ -643,8 +643,8 @@ class TagCache(object):
|
|
|
643
643
|
with self.make_session() as session:
|
|
644
644
|
query = (
|
|
645
645
|
session
|
|
646
|
-
.query(TagRecordTable,
|
|
647
|
-
.join(
|
|
646
|
+
.query(TagRecordTable, TagTargetTable)
|
|
647
|
+
.join(TagTargetTable, TagRecordTable.target_sqn == TagTargetTable.sqn)
|
|
648
648
|
)
|
|
649
649
|
if begin_dt:
|
|
650
650
|
query = query.filter(TagRecordTable.end_dt >= begin_dt)
|
|
@@ -652,18 +652,18 @@ class TagCache(object):
|
|
|
652
652
|
query = query.filter(TagRecordTable.begin_dt <= end_dt)
|
|
653
653
|
if tag_pattern:
|
|
654
654
|
query = query.filter(TagRecordTable.tag.like(f"{escape_sql_like(tag_pattern)}%", escape="\\"))
|
|
655
|
-
if
|
|
656
|
-
query = query.filter(
|
|
655
|
+
if target_identifier:
|
|
656
|
+
query = query.filter(TagTargetTable.identifier == target_identifier)
|
|
657
657
|
if target_tagger_name:
|
|
658
|
-
query = query.filter(
|
|
658
|
+
query = query.filter(TagTargetTable.tagger_name == target_tagger_name)
|
|
659
659
|
if target_tagger_version:
|
|
660
|
-
query = query.filter(
|
|
660
|
+
query = query.filter(TagTargetTable.tagger_version == target_tagger_version)
|
|
661
661
|
if target_vehicle_name:
|
|
662
|
-
query = query.filter(
|
|
662
|
+
query = query.filter(TagTargetTable.vehicle_name == target_vehicle_name)
|
|
663
663
|
if target_begin_dt:
|
|
664
|
-
query = query.filter(
|
|
664
|
+
query = query.filter(TagTargetTable.end_dt >= target_begin_dt)
|
|
665
665
|
if target_end_dt:
|
|
666
|
-
query = query.filter(
|
|
666
|
+
query = query.filter(TagTargetTable.begin_dt <= target_end_dt)
|
|
667
667
|
if tagsets:
|
|
668
668
|
if tagset_inverted:
|
|
669
669
|
query = query.filter(
|
|
@@ -717,49 +717,61 @@ class TagCache(object):
|
|
|
717
717
|
def clear(self):
|
|
718
718
|
with self.make_session() as session:
|
|
719
719
|
session.execute(sa.delete(TagRecordTable))
|
|
720
|
-
session.execute(sa.delete(
|
|
720
|
+
session.execute(sa.delete(TagTargetTable))
|
|
721
721
|
session.commit()
|
|
722
722
|
|
|
723
723
|
def append_to(self, target_file_path: str, *, overwrite: bool = False):
|
|
724
724
|
target_tag_cache = TagCache(file_path=target_file_path)
|
|
725
725
|
if overwrite:
|
|
726
726
|
target_tag_cache.clear()
|
|
727
|
-
TagCache.
|
|
727
|
+
TagCache.copy_to(self, target_tag_cache)
|
|
728
728
|
|
|
729
729
|
def merge_from(self, source_file_path: str, *, overwrite: bool = False):
|
|
730
730
|
source_tag_cache = TagCache(file_path=source_file_path)
|
|
731
731
|
if overwrite:
|
|
732
732
|
self.clear()
|
|
733
|
-
TagCache.
|
|
733
|
+
TagCache.copy_to(source_tag_cache, self)
|
|
734
734
|
|
|
735
735
|
@staticmethod
|
|
736
|
-
def
|
|
737
|
-
|
|
736
|
+
def copy_to(src: "TagCache", dst: "TagCache"):
|
|
737
|
+
# If src and dst are the same instance or point to the same file path,
|
|
738
|
+
# do nothing to avoid accidentally clearing the cache
|
|
739
|
+
if src == dst or src.file_path == dst.file_path:
|
|
738
740
|
return
|
|
739
741
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
742
|
+
with src.make_session() as src_session, dst.make_session() as dst_session:
|
|
743
|
+
src_targets = src_session.query(TagTargetTable).all()
|
|
744
|
+
dst_targets = [
|
|
745
|
+
clone_sequence_model_instance(TagTargetTable, db_tag_target_info, clear_meta_fields=True)
|
|
746
|
+
for db_tag_target_info in src_targets
|
|
747
|
+
]
|
|
748
|
+
dst_session.add_all(dst_targets)
|
|
749
|
+
dst_session.flush() # ensure new sqn values are assigned
|
|
750
|
+
|
|
751
|
+
sqn_map = {src_target.sqn: dst_target.sqn for src_target, dst_target in zip(src_targets, dst_targets)}
|
|
752
|
+
|
|
753
|
+
for results in batched(src_session.query(TagRecordTable).yield_per(1000), 1000):
|
|
754
|
+
clones = []
|
|
755
|
+
for db_tag_record in results:
|
|
756
|
+
cloned = clone_sequence_model_instance(TagRecordTable, db_tag_record, clear_meta_fields=True)
|
|
757
|
+
try:
|
|
758
|
+
cloned.target_sqn = sqn_map[db_tag_record.target_sqn]
|
|
759
|
+
except KeyError as e:
|
|
760
|
+
raise ValueError(f"no cloned target for target_sqn '{db_tag_record.target_sqn}'") from e
|
|
761
|
+
clones.append(cloned)
|
|
762
|
+
dst_session.add_all(clones)
|
|
763
|
+
dst_session.commit()
|
|
752
764
|
|
|
753
765
|
|
|
754
766
|
class TargetedTagCache(object):
|
|
755
|
-
def __init__(self, cache:
|
|
767
|
+
def __init__(self, cache: TagCache, target_info: TagTargetTable):
|
|
756
768
|
self.target_info = target_info
|
|
757
769
|
self.cache = cache
|
|
758
770
|
|
|
759
771
|
@contextlib.contextmanager
|
|
760
772
|
def make_session(self) -> Generator[sa_orm.Session, None, None]:
|
|
761
773
|
with self.cache.make_session() as session:
|
|
762
|
-
if not session.query(
|
|
774
|
+
if not session.query(TagTargetTable).filter(TagTargetTable.sqn == self.target_info.sqn).first():
|
|
763
775
|
raise ValueError(f"target info with sqn '{self.target_info.sqn}' is no longer present in cache")
|
|
764
776
|
yield session
|
|
765
777
|
|
|
@@ -844,7 +856,7 @@ class TargetedTagCache(object):
|
|
|
844
856
|
:param props: Additional properties of the tag record in JSON format (optional)
|
|
845
857
|
:return: Self instance for chaining
|
|
846
858
|
"""
|
|
847
|
-
return self.
|
|
859
|
+
return self.add_ranged_tag(
|
|
848
860
|
begin_dt=self.target_info.begin_dt,
|
|
849
861
|
end_dt=self.target_info.end_dt,
|
|
850
862
|
tag=tag,
|
|
@@ -196,3 +196,155 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
196
196
|
self.assertEqual(len(list(cache.iter_tag_and_targets())), total_tasks_count)
|
|
197
197
|
self.assertEqual(len(list(cache.iter_tag_and_targets(tag_pattern="dummy:bar"))),
|
|
198
198
|
total_tasks_count // len(tags))
|
|
199
|
+
|
|
200
|
+
def test_tag_cache__clone(self):
|
|
201
|
+
tagset = MutableTagset(namespace="tagset", desc="A dummy tagset for testing")
|
|
202
|
+
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
203
|
+
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
204
|
+
|
|
205
|
+
tags = [
|
|
206
|
+
"dummy:foo",
|
|
207
|
+
"dummy:bar",
|
|
208
|
+
"dummy:baz",
|
|
209
|
+
"dummy:qux",
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
with tempfile.TemporaryDirectory() as temp_directory:
|
|
213
|
+
temp_directory = pathlib.Path(temp_directory)
|
|
214
|
+
|
|
215
|
+
src_cache = TagCache(file_path=temp_directory / "src_tag_cache.db")
|
|
216
|
+
|
|
217
|
+
src_cache.add_target("awesome_tagger/20200101_000000/dummy_vehicle/0",
|
|
218
|
+
"awesome_tagger",
|
|
219
|
+
"1.0.0",
|
|
220
|
+
"dummy_vehicle",
|
|
221
|
+
dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
222
|
+
dt_parse_iso("2020-01-01T01:00:00+00:00"))
|
|
223
|
+
|
|
224
|
+
src_target_cache = src_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
225
|
+
|
|
226
|
+
tags_count = 1000
|
|
227
|
+
|
|
228
|
+
for i in range(tags_count):
|
|
229
|
+
src_target_cache.add_tag(tags[i % len(tags)])
|
|
230
|
+
|
|
231
|
+
dst_cache = TagCache(file_path=temp_directory / "dst_tag_cache.db")
|
|
232
|
+
|
|
233
|
+
TagCache.copy_to(src_cache, dst_cache)
|
|
234
|
+
|
|
235
|
+
for i in range(tags_count):
|
|
236
|
+
src_target_cache.add_tag(tags[i % len(tags)])
|
|
237
|
+
|
|
238
|
+
dst_target_cache = dst_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
239
|
+
|
|
240
|
+
self.assertEqual(len(list(src_target_cache.iter_tags())), tags_count * 2)
|
|
241
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))), tags_count * 2 // 2)
|
|
242
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
243
|
+
tags_count * 2 // 2)
|
|
244
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
245
|
+
tags_count * 2 // 2)
|
|
246
|
+
|
|
247
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags())), tags_count)
|
|
248
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))), tags_count // 2)
|
|
249
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
250
|
+
tags_count // 2)
|
|
251
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
252
|
+
tags_count // 2)
|
|
253
|
+
|
|
254
|
+
def test_tag_cache__clone_same_file(self):
|
|
255
|
+
tagset = MutableTagset(namespace="tagset", desc="A dummy tagset for testing")
|
|
256
|
+
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
257
|
+
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
258
|
+
|
|
259
|
+
tags = [
|
|
260
|
+
"dummy:foo",
|
|
261
|
+
"dummy:bar",
|
|
262
|
+
"dummy:baz",
|
|
263
|
+
"dummy:qux",
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
# Clone to the same file path should not cause any issue, and the cloned cache should be able to read
|
|
267
|
+
# the tags added to the source cache after cloning.
|
|
268
|
+
with tempfile.TemporaryDirectory() as temp_directory:
|
|
269
|
+
temp_directory = pathlib.Path(temp_directory)
|
|
270
|
+
|
|
271
|
+
src_cache = TagCache(file_path=temp_directory / "tag_cache.db")
|
|
272
|
+
|
|
273
|
+
src_cache.add_target("awesome_tagger/20200101_000000/dummy_vehicle/0",
|
|
274
|
+
"awesome_tagger",
|
|
275
|
+
"1.0.0",
|
|
276
|
+
"dummy_vehicle",
|
|
277
|
+
dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
278
|
+
dt_parse_iso("2020-01-01T01:00:00+00:00"))
|
|
279
|
+
|
|
280
|
+
src_target_cache = src_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
281
|
+
|
|
282
|
+
tags_count = 1000
|
|
283
|
+
|
|
284
|
+
for i in range(tags_count):
|
|
285
|
+
src_target_cache.add_tag(tags[i % len(tags)])
|
|
286
|
+
|
|
287
|
+
dst_cache = TagCache(file_path=temp_directory / "tag_cache.db")
|
|
288
|
+
|
|
289
|
+
TagCache.copy_to(src_cache, dst_cache)
|
|
290
|
+
|
|
291
|
+
for i in range(tags_count):
|
|
292
|
+
src_target_cache.add_tag(tags[i % len(tags)])
|
|
293
|
+
|
|
294
|
+
dst_target_cache = dst_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
295
|
+
|
|
296
|
+
self.assertEqual(len(list(src_target_cache.iter_tags())), tags_count * 2)
|
|
297
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))), tags_count * 2 // 2)
|
|
298
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
299
|
+
tags_count * 2 // 2)
|
|
300
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
301
|
+
tags_count * 2 // 2)
|
|
302
|
+
|
|
303
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags())), tags_count * 2)
|
|
304
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))), tags_count * 2 // 2)
|
|
305
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
306
|
+
tags_count * 2 // 2)
|
|
307
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
308
|
+
tags_count * 2 // 2)
|
|
309
|
+
|
|
310
|
+
with tempfile.TemporaryDirectory() as temp_directory:
|
|
311
|
+
temp_directory = pathlib.Path(temp_directory)
|
|
312
|
+
|
|
313
|
+
src_cache = TagCache(file_path=temp_directory / "tag_cache.db")
|
|
314
|
+
|
|
315
|
+
src_cache.add_target("awesome_tagger/20200101_000000/dummy_vehicle/0",
|
|
316
|
+
"awesome_tagger",
|
|
317
|
+
"1.0.0",
|
|
318
|
+
"dummy_vehicle",
|
|
319
|
+
dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
320
|
+
dt_parse_iso("2020-01-01T01:00:00+00:00"))
|
|
321
|
+
|
|
322
|
+
src_target_cache = src_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
323
|
+
|
|
324
|
+
tags_count = 1000
|
|
325
|
+
|
|
326
|
+
for i in range(tags_count):
|
|
327
|
+
src_target_cache.add_tag(tags[i % len(tags)])
|
|
328
|
+
|
|
329
|
+
dst_cache = src_cache
|
|
330
|
+
|
|
331
|
+
TagCache.copy_to(src_cache, dst_cache)
|
|
332
|
+
|
|
333
|
+
for i in range(tags_count):
|
|
334
|
+
src_target_cache.add_tag(tags[i % len(tags)])
|
|
335
|
+
|
|
336
|
+
dst_target_cache = dst_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
337
|
+
|
|
338
|
+
self.assertEqual(len(list(src_target_cache.iter_tags())), tags_count * 2)
|
|
339
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))), tags_count * 2 // 2)
|
|
340
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
341
|
+
tags_count * 2 // 2)
|
|
342
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
343
|
+
tags_count * 2 // 2)
|
|
344
|
+
|
|
345
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags())), tags_count * 2)
|
|
346
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))), tags_count * 2 // 2)
|
|
347
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
348
|
+
tags_count * 2 // 2)
|
|
349
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
350
|
+
tags_count * 2 // 2)
|
|
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.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/0-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/resources/unittest/pathutils/1-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/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.53 → plexus_python_common-1.0.55}/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.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMFile.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMNode.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMTags.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/OSMWay.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/carto/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/resources/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/apiutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/bagutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/config.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/dockerutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/jsonutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/pathutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/s3utils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/sqlutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/src/plexus/common/utils/strutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/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.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/pose_test.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.53 → plexus_python_common-1.0.55}/test/plexus_tests/common/proj_test.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
|