plexus-python-common 1.0.18__tar.gz → 1.0.19__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.
Files changed (65) hide show
  1. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/PKG-INFO +1 -1
  2. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/ormutils.py +31 -24
  3. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/strutils.py +76 -23
  4. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
  5. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/utils/strutils_test.py +47 -2
  6. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/.editorconfig +0 -0
  7. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/.github/workflows/pr.yml +0 -0
  8. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/.github/workflows/push.yml +0 -0
  9. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/.gitignore +0 -0
  10. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/MANIFEST.in +0 -0
  11. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/README.md +0 -0
  12. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/VERSION +0 -0
  13. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/pyproject.toml +0 -0
  14. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
  15. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
  16. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
  17. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/0-dummy +0 -0
  18. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/1-dummy +0 -0
  19. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/2-dummy +0 -0
  20. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.0.0.jsonl +0 -0
  21. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.0.0.vol-0.jsonl +0 -0
  22. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.0.jsonl +0 -0
  23. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.1.1.jsonl +0 -0
  24. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.1.1.vol-1.jsonl +0 -0
  25. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.1.jsonl +0 -0
  26. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.2.2.jsonl +0 -0
  27. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.2.2.vol-2.jsonl +0 -0
  28. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.2.jsonl +0 -0
  29. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.csv.part0 +0 -0
  30. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.csv.part1 +0 -0
  31. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.csv.part2 +0 -0
  32. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/resources/unittest/shutils/dummy.txt +0 -0
  33. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/setup.cfg +0 -0
  34. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/setup.py +0 -0
  35. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/__init__.py +0 -0
  36. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/carto/OSMFile.py +0 -0
  37. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/carto/OSMNode.py +0 -0
  38. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/carto/OSMTags.py +0 -0
  39. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/carto/OSMWay.py +0 -0
  40. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/carto/__init__.py +0 -0
  41. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/config.py +0 -0
  42. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/pose.py +0 -0
  43. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/proj.py +0 -0
  44. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/__init__.py +0 -0
  45. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/bagutils.py +0 -0
  46. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/datautils.py +0 -0
  47. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/jsonutils.py +0 -0
  48. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/s3utils.py +0 -0
  49. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus/common/utils/shutils.py +0 -0
  50. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
  51. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
  52. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
  53. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus_python_common.egg-info/requires.txt +0 -0
  54. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/src/plexus_python_common.egg-info/top_level.txt +0 -0
  55. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_test.py +0 -0
  56. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/__init__.py +0 -0
  57. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
  58. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
  59. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/pose_test.py +0 -0
  60. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/proj_test.py +0 -0
  61. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
  62. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/utils/datautils_test.py +0 -0
  63. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
  64. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
  65. {plexus_python_common-1.0.18 → plexus_python_common-1.0.19}/test/plexus_tests/common/utils/shutils_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.18
3
+ Version: 1.0.19
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -496,7 +496,10 @@ def make_snapshot_model_trigger(engine: sa.Engine, model: type[SQLModel]):
496
496
  conn.execute(sa.text(create_snapshot_auto_update_trigger_sql))
497
497
 
498
498
 
499
- def db_make_order_by_clause[SerialModelT: SerialModelMixin](model: type[SerialModelT], order_by: list[str] = None):
499
+ def db_make_order_by_clause[SerialModelT: SerialModelMixin](
500
+ model: type[SerialModelT],
501
+ order_by: list[str] | None = None,
502
+ ):
500
503
  order_criteria = []
501
504
  if order_by:
502
505
  for field in order_by:
@@ -548,11 +551,16 @@ def db_read_serial_model[SerialModelT: SerialModelMixin](
548
551
  def db_read_serial_models[SerialModelT: SerialModelMixin](
549
552
  db: sa_orm.Session,
550
553
  model: type[SerialModelT],
551
- skip: int,
552
- limit: int,
553
- order_by: list[str] = None,
554
+ skip: int | None = None,
555
+ limit: int | None = None,
556
+ order_by: list[str] | None = None,
554
557
  ) -> list[SerialModelT]:
555
- return db.query(model).order_by(*db_make_order_by_clause(model, order_by)).offset(skip).limit(limit).all()
558
+ query = db.query(model).order_by(*db_make_order_by_clause(model, order_by))
559
+ if skip is not None:
560
+ query = query.offset(skip)
561
+ if limit is not None:
562
+ query = query.limit(limit)
563
+ return query.all()
556
564
 
557
565
 
558
566
  def db_update_serial_model[SerialModelT: SerialModelMixin](
@@ -736,9 +744,9 @@ def db_read_expired_snapshot_models_of_record[SnapshotModelT: SnapshotModelMixin
736
744
  def db_read_latest_snapshot_models[SnapshotModelT: SnapshotModelMixin](
737
745
  db: sa_orm.Session,
738
746
  model: type[SnapshotModelT],
739
- skip: int,
740
- limit: int,
741
- order_by: list[str] = None,
747
+ skip: int | None = None,
748
+ limit: int | None = None,
749
+ order_by: list[str] | None = None,
742
750
  ) -> list[SnapshotModelT]:
743
751
  subquery = (
744
752
  db
@@ -748,34 +756,33 @@ def db_read_latest_snapshot_models[SnapshotModelT: SnapshotModelMixin](
748
756
  .subquery()
749
757
  )
750
758
 
751
- return (
759
+ query = (
752
760
  db
753
761
  .query(model)
754
762
  .join(subquery,
755
763
  sa.and_(model.record_sid == subquery.c.record_sid, model.created_at == subquery.c.max_created_at))
756
764
  .order_by(*db_make_order_by_clause(model, order_by))
757
- .offset(skip)
758
- .limit(limit)
759
- .all()
760
765
  )
766
+ if skip is not None:
767
+ query = query.offset(skip)
768
+ if limit is not None:
769
+ query = query.limit(limit)
770
+ return query.all()
761
771
 
762
772
 
763
773
  def db_read_active_snapshot_models[SnapshotModelT: SnapshotModelMixin](
764
774
  db: sa_orm.Session,
765
775
  model: type[SnapshotModelT],
766
- skip: int,
767
- limit: int,
768
- order_by: list[str] = None,
776
+ skip: int | None = None,
777
+ limit: int | None = None,
778
+ order_by: list[str] | None = None,
769
779
  ) -> list[SnapshotModelT]:
770
- return (
771
- db
772
- .query(model)
773
- .where(model.expired_at.is_(None))
774
- .order_by(*db_make_order_by_clause(model, order_by))
775
- .offset(skip)
776
- .limit(limit)
777
- .all()
778
- )
780
+ query = db.query(model).where(model.expired_at.is_(None)).order_by(*db_make_order_by_clause(model, order_by))
781
+ if skip is not None:
782
+ query = query.offset(skip)
783
+ if limit is not None:
784
+ query = query.limit(limit)
785
+ return query.all()
779
786
 
780
787
 
781
788
  def db_update_snapshot_model[SnapshotModelT: SnapshotModelMixin](
@@ -33,10 +33,14 @@ __all__ = [
33
33
  "vin_code_chars",
34
34
  "vin_code_pattern",
35
35
  "vin_code_parser",
36
+ "to_colon_tag",
37
+ "to_slash_tag",
36
38
  "UserName",
39
+ "UserEmail",
37
40
  "VehicleName",
38
41
  "BagName",
39
42
  "parse_user_name",
43
+ "parse_user_email",
40
44
  "parse_vehicle_name",
41
45
  "parse_bag_name",
42
46
  ]
@@ -193,6 +197,14 @@ vin_code_pattern = make_string_pattern(vin_code_regexp)
193
197
  vin_code_parser = make_string_parser(vin_code_element)
194
198
 
195
199
 
200
+ def to_colon_tag(tag: str) -> str:
201
+ return tag.replace("/", ":")
202
+
203
+
204
+ def to_slash_tag(tag: str) -> str:
205
+ return tag.replace(":", "/")
206
+
207
+
196
208
  @dataclasses.dataclass(frozen=True, eq=True)
197
209
  class UserName(object):
198
210
  first_name: str
@@ -205,6 +217,15 @@ class UserName(object):
205
217
  return f"{self.first_name}{self.sn}.{self.last_name}"
206
218
 
207
219
 
220
+ @dataclasses.dataclass(frozen=True, eq=True)
221
+ class UserEmail(object):
222
+ user_name: UserName
223
+ domain: str
224
+
225
+ def __str__(self) -> str:
226
+ return f"{str(self.user_name)}@{self.domain}"
227
+
228
+
208
229
  @dataclasses.dataclass(frozen=True, eq=True)
209
230
  class VehicleName(object):
210
231
  brand: str
@@ -232,39 +253,58 @@ class BagName(object):
232
253
  return f"{dt_format(self.record_dt, basic_format())}-{self.vehicle_name}-{self.record_sn}.bag"
233
254
 
234
255
 
256
+ @singleton
257
+ def get_user_name_element() -> pp.ParserElement:
258
+ return pp.Combine(lowers_element("first_name") +
259
+ positive_number_element("sn")[0, 1] +
260
+ period_token +
261
+ lowers_element("last_name"))
262
+
263
+
235
264
  @singleton
236
265
  def get_user_name_parser() -> pp.ParserElement:
237
- element = pp.Combine(lowers_element("first_name") +
238
- positive_number_element("sn")[0, 1] +
239
- period_token +
240
- lowers_element("last_name"))
241
- return make_string_parser(element)
266
+ return make_string_parser(get_user_name_element())
267
+
268
+
269
+ @singleton
270
+ def get_user_email_element() -> pp.ParserElement:
271
+ return pp.Combine(get_user_name_element()("user_name") +
272
+ pp.Char("@") +
273
+ pp.Combine((kebab_case_element + period_token)[1, ...] + lowers_element)("domain"))
274
+
275
+
276
+ @singleton
277
+ def get_user_email_parser() -> pp.ParserElement:
278
+ return make_string_parser(get_user_email_element())
279
+
280
+
281
+ @singleton
282
+ def get_vehicle_name_element() -> pp.ParserElement:
283
+ return pp.Combine(lower_identifier_element("brand") +
284
+ (underscore_token +
285
+ pp.Combine(lower_identifier_element +
286
+ (underscore_token + lower_identifier_element)[...])("alias")) +
287
+ (underscore_token + digits_element("code"))[0, 1] +
288
+ (underscore_token + pp.Char("V") + vin_code_element("vin"))[0, 1])
242
289
 
243
290
 
244
291
  @singleton
245
292
  def get_vehicle_name_parser() -> pp.ParserElement:
246
- element = pp.Combine(lower_identifier_element("brand") +
247
- (underscore_token +
248
- pp.Combine(lower_identifier_element +
249
- (underscore_token + lower_identifier_element)[...])("alias")) +
250
- (underscore_token + digits_element("code"))[0, 1] +
251
- (underscore_token + pp.Char("V") + vin_code_element("vin"))[0, 1])
252
- return make_string_parser(element)
293
+ return make_string_parser(get_vehicle_name_element())
294
+
295
+
296
+ @singleton
297
+ def get_bag_name_element() -> pp.ParserElement:
298
+ return pp.Combine(basic_datetime_element("record_dt") +
299
+ (hyphen_token +
300
+ get_vehicle_name_element()("vehicle_name")) +
301
+ (hyphen_token + number_element("record_sn")) +
302
+ (period_token + pp.Literal("bag"))[0, 1])
253
303
 
254
304
 
255
305
  @singleton
256
306
  def get_bag_name_parser() -> pp.ParserElement:
257
- element = pp.Combine(basic_datetime_element("record_dt") +
258
- (hyphen_token +
259
- pp.Group(lower_identifier_element("brand") +
260
- (underscore_token +
261
- pp.Combine(lower_identifier_element +
262
- (underscore_token + lower_identifier_element)[...])("alias")) +
263
- (underscore_token + digits_element("code"))[0, 1] +
264
- (underscore_token + pp.Char("V") + vin_code_element("vin"))[0, 1])("vehicle_name")) +
265
- (hyphen_token + number_element("record_sn")) +
266
- (period_token + pp.Literal("bag"))[0, 1])
267
- return make_string_parser(element)
307
+ return make_string_parser(get_bag_name_element())
268
308
 
269
309
 
270
310
  def parse_user_name(s: str) -> UserName | None:
@@ -277,6 +317,19 @@ def parse_user_name(s: str) -> UserName | None:
277
317
  )
278
318
 
279
319
 
320
+ def parse_user_email(s: str) -> UserEmail | None:
321
+ user_email_match = get_user_email_parser().parse_string(s, parse_all=True)
322
+
323
+ return UserEmail(
324
+ UserName(
325
+ user_email_match.get("user_name").get("first_name"),
326
+ user_email_match.get("user_name").get("last_name"),
327
+ int(user_email_match.get("user_name").get("sn", 0)),
328
+ ),
329
+ user_email_match.get("domain"),
330
+ )
331
+
332
+
280
333
  def parse_vehicle_name(s: str) -> VehicleName | None:
281
334
  vehicle_name_match = get_vehicle_name_parser().parse_string(s, parse_all=True)
282
335
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.18
3
+ Version: 1.0.19
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -4,12 +4,12 @@ import ddt
4
4
  import pyparsing as pp
5
5
  from iker.common.utils.dtutils import dt_parse_iso
6
6
 
7
- from plexus.common.utils.strutils import BagName, UserName, VehicleName
7
+ from plexus.common.utils.strutils import BagName, UserEmail, UserName, VehicleName
8
8
  from plexus.common.utils.strutils import colon_tag_parser, colon_tag_pattern, slash_tag_parser, slash_tag_pattern
9
9
  from plexus.common.utils.strutils import dot_case_parser, dot_case_pattern
10
10
  from plexus.common.utils.strutils import hex_string_parser, hex_string_pattern
11
11
  from plexus.common.utils.strutils import kebab_case_parser, kebab_case_pattern
12
- from plexus.common.utils.strutils import parse_bag_name, parse_user_name, parse_vehicle_name
12
+ from plexus.common.utils.strutils import parse_bag_name, parse_user_email, parse_user_name, parse_vehicle_name
13
13
  from plexus.common.utils.strutils import semver_parser, semver_pattern
14
14
  from plexus.common.utils.strutils import snake_case_parser, snake_case_pattern
15
15
  from plexus.common.utils.strutils import strict_abspath_parser, strict_abspath_pattern
@@ -568,6 +568,51 @@ class StrUtilsTest(unittest.TestCase):
568
568
  with self.assertRaises(pp.ParseException):
569
569
  parse_user_name(data)
570
570
 
571
+ data_parse_user_email = [
572
+ ("dummy.person@some.company", UserEmail(UserName("dummy", "person", 0), "some.company")),
573
+ ("dummy1.person@another.company", UserEmail(UserName("dummy", "person", 1), "another.company")),
574
+ ("dummy1.person@yet-another.company", UserEmail(UserName("dummy", "person", 1), "yet-another.company")),
575
+ ("dummy1.person@yet-yet.another.company", UserEmail(UserName("dummy", "person", 1), "yet-yet.another.company")),
576
+ ("dummy999999.person@good.company", UserEmail(UserName("dummy", "person", 999999), "good.company")),
577
+ ("dummy.person@better.or.even-better.company",
578
+ UserEmail(UserName("dummy", "person", 0), "better.or.even-better.company")),
579
+ ]
580
+
581
+ @ddt.idata(data_parse_user_email)
582
+ @ddt.unpack
583
+ def test_parse_user_email(self, data, expect):
584
+ self.assertEqual(parse_user_email(data), expect)
585
+ self.assertEqual(data, str(expect))
586
+
587
+ data_parse_user_email__bad_cases = [
588
+ ("dummy",),
589
+ ("0.dummy",),
590
+ ("dummy.0",),
591
+ ("Dummy.Dummy",),
592
+ ("dummy-dummy.dummy",),
593
+ ("0dummy.dummy",),
594
+ ("0dummy0.dummy",),
595
+ ("dummy01.dummy",),
596
+ ("dummy@some.company",),
597
+ ("0.dummy@some.company",),
598
+ ("dummy.0@some.company",),
599
+ ("Dummy.Dummy@some.company",),
600
+ ("dummy-dummy.dummy@some.company",),
601
+ ("0dummy.dummy@some.company",),
602
+ ("0dummy0.dummy@some.company",),
603
+ ("dummy01.dummy@some.company",),
604
+ ("dummy.person@BAD.COMPANY",),
605
+ ("dummy1.person@worse.company.",),
606
+ ("dummy1.person@even.worse-company",),
607
+ ("dummy1.person#worst.company",),
608
+ ]
609
+
610
+ @ddt.idata(data_parse_user_email__bad_cases)
611
+ @ddt.unpack
612
+ def test_parse_user_email__bad_cases(self, data):
613
+ with self.assertRaises(pp.ParseException):
614
+ parse_user_email(data)
615
+
571
616
  data_parse_vehicle_name = [
572
617
  ("brand_alias",
573
618
  VehicleName("brand", "alias", None, None)),