labfreed 0.2.11__tar.gz → 0.3.0a0__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.

Potentially problematic release.


This version of labfreed might be problematic. Click here for more details.

Files changed (70) hide show
  1. {labfreed-0.2.11 → labfreed-0.3.0a0}/CHANGELOG.md +3 -0
  2. {labfreed-0.2.11 → labfreed-0.3.0a0}/PKG-INFO +43 -29
  3. {labfreed-0.2.11 → labfreed-0.3.0a0}/README.md +39 -28
  4. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/__init__.py +1 -1
  5. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/labfreed_infrastructure.py +3 -3
  6. labfreed-0.3.0a0/labfreed/pac_attributes/api_data_models/request.py +56 -0
  7. labfreed-0.3.0a0/labfreed/pac_attributes/api_data_models/response.py +184 -0
  8. labfreed-0.3.0a0/labfreed/pac_attributes/api_data_models/server_capabilities_response.py +7 -0
  9. labfreed-0.3.0a0/labfreed/pac_attributes/client/__init__.py +1 -0
  10. labfreed-0.3.0a0/labfreed/pac_attributes/client/attribute_cache.py +78 -0
  11. labfreed-0.3.0a0/labfreed/pac_attributes/client/client.py +148 -0
  12. labfreed-0.3.0a0/labfreed/pac_attributes/server/attribute_data_sources.py +69 -0
  13. labfreed-0.3.0a0/labfreed/pac_attributes/server/server.py +237 -0
  14. labfreed-0.3.0a0/labfreed/pac_attributes/server/translation_data_sources.py +60 -0
  15. labfreed-0.3.0a0/labfreed/pac_attributes/well_knonw_attribute_keys.py +11 -0
  16. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_cat/category_base.py +19 -0
  17. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_cat/pac_cat.py +8 -1
  18. labfreed-0.3.0a0/labfreed/pac_id/extension.py +73 -0
  19. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id/pac_id.py +2 -2
  20. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id/url_parser.py +4 -4
  21. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id/url_serializer.py +11 -5
  22. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id_resolver/cit_common.py +1 -1
  23. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id_resolver/cit_v1.py +0 -3
  24. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id_resolver/cit_v2.py +1 -6
  25. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id_resolver/resolver.py +12 -7
  26. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id_resolver/services.py +0 -1
  27. labfreed-0.3.0a0/labfreed/trex/python_convenience/quantity.py +147 -0
  28. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/value_segments.py +1 -1
  29. labfreed-0.3.0a0/labfreed/utilities/ensure_utc_time.py +6 -0
  30. labfreed-0.3.0a0/labfreed/utilities/translations.py +60 -0
  31. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_extensions/display_name_extension.py +3 -3
  32. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py +4 -0
  33. labfreed-0.3.0a0/labfreed/well_known_keys/labfreed/well_known_keys.py +28 -0
  34. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_keys/unece/unece_units.py +2 -1
  35. labfreed-0.3.0a0/labfreed_extended/app/app_infrastructure.py +104 -0
  36. labfreed-0.3.0a0/labfreed_extended/app/pac_info.py +79 -0
  37. labfreed-0.3.0a0/labfreed_extended/pac_attributes/py_attributes.py +123 -0
  38. labfreed-0.3.0a0/labfreed_extended/pac_attributes/server/attribute_server_factory.py +91 -0
  39. labfreed-0.3.0a0/labfreed_extended/pac_attributes/server/excel_attribute_data_source.py +128 -0
  40. labfreed-0.3.0a0/labfreed_extended/utilities/formatted_print.py +64 -0
  41. {labfreed-0.2.11 → labfreed-0.3.0a0}/pyproject.toml +5 -0
  42. labfreed-0.2.11/labfreed/pac_id/extension.py +0 -48
  43. labfreed-0.2.11/labfreed/trex/python_convenience/quantity.py +0 -66
  44. labfreed-0.2.11/labfreed/well_known_keys/gs1/gs1.py +0 -4
  45. labfreed-0.2.11/labfreed/well_known_keys/labfreed/well_known_keys.py +0 -18
  46. {labfreed-0.2.11 → labfreed-0.3.0a0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  47. {labfreed-0.2.11 → labfreed-0.3.0a0}/.github/workflows/pypi-publish.yml +0 -0
  48. {labfreed-0.2.11 → labfreed-0.3.0a0}/.github/workflows/run-tests.yml +0 -0
  49. {labfreed-0.2.11 → labfreed-0.3.0a0}/LICENSE +0 -0
  50. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_cat/__init__.py +0 -0
  51. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_cat/predefined_categories.py +0 -0
  52. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id/__init__.py +0 -0
  53. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id/id_segment.py +0 -0
  54. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/pac_id_resolver/__init__.py +0 -0
  55. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/qr/__init__.py +0 -0
  56. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/qr/generate_qr.py +0 -0
  57. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/__init__.py +0 -0
  58. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/python_convenience/__init__.py +0 -0
  59. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/python_convenience/data_table.py +0 -0
  60. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/python_convenience/pyTREX.py +0 -0
  61. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/table_segment.py +0 -0
  62. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/trex.py +0 -0
  63. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/trex/trex_base_models.py +0 -0
  64. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/utilities/base36.py +0 -0
  65. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_extensions/__init__.py +0 -0
  66. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_extensions/default_extension_interpreters.py +0 -0
  67. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_extensions/trex_extension.py +0 -0
  68. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_keys/gs1/__init__.py +0 -0
  69. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_keys/unece/UneceUnits.json +0 -0
  70. {labfreed-0.2.11 → labfreed-0.3.0a0}/labfreed/well_known_keys/unece/__init__.py +0 -0
@@ -1,4 +1,7 @@
1
1
  ## Change Log
2
+ ### v0.2.12
3
+ - bugfix:no warning message if PAC-CAT has same segment key in two segments
4
+
2
5
  ### v0.2.11
3
6
  - bugfix:added missing well known segment key '250'
4
7
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labfreed
3
- Version: 0.2.11
3
+ Version: 0.3.0a0
4
4
  Summary: Python implementation of LabFREED building blocks
5
5
  Author-email: Reto Thürer <thuerer.r@buchi.com>
6
6
  Requires-Python: >=3.11
@@ -28,16 +28,23 @@ Requires-Dist: pytest>=8.3.5 ; extra == "dev"
28
28
  Requires-Dist: pdoc>=15.0.1 ; extra == "dev"
29
29
  Requires-Dist: flit>=3.12.0 ; extra == "dev"
30
30
  Requires-Dist: ruff>=0.11.5 ; extra == "dev"
31
+ Requires-Dist: Flask>=3.1.1 ; extra == "extended"
32
+ Requires-Dist: openpyxl>=3.1.5 ; extra == "extended"
31
33
  Project-URL: Documentation, https://github.com/retothuerer/LabFREED?tab=readme-ov-file#readme
32
34
  Project-URL: Homepage, https://github.com/retothuerer/LabFREED
33
35
  Project-URL: Source, https://github.com/retothuerer/LabFREED
34
36
  Project-URL: Tracker, https://github.com/retothuerer/LabFREED/issues
35
37
  Provides-Extra: dev
38
+ Provides-Extra: extended
36
39
 
37
40
  # LabFREED for Python
38
41
 
39
42
  [![PyPI](https://img.shields.io/pypi/v/labfreed.svg)](https://pypi.org/project/labfreed/) ![Python Version](https://img.shields.io/pypi/pyversions/labfreed) [![Test Labfreed](https://github.com/retothuerer/LabFREED/actions/workflows/run-tests.yml/badge.svg)](https://github.com/retothuerer/LabFREED/actions/workflows/run-tests.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
40
43
 
44
+ <!--
45
+ [![Ruff](https://img.shields.io/badge/style-Ruff-black?logo=ruff&labelColor=gray)](https://github.com/astral-sh/ruff)
46
+ -->
47
+
41
48
 
42
49
  This is a Python implementation of [LabFREED](https://labfreed.org/) building blocks.
43
50
 
@@ -103,28 +110,32 @@ There is a nice function to highlight problems
103
110
  pac.print_validation_messages()
104
111
  ```
105
112
  ```text
106
- >> Validation Results
107
- >> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
108
- >> │ **RECOMMENDATION** in id segment value bal500
109
- >> │ Characters 'l','a','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
110
- >> │
111
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234
112
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
113
- >> │ **RECOMMENDATION** in id segment value @1234 │
114
- >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
115
- >> │
116
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234
117
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
118
- >> │ **RECOMMENDATION** in id segment value bal500
119
- >> │ Characters 'l','a','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
120
- >> │
121
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234
122
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
123
- >> │ **RECOMMENDATION** in id segment value @1234
124
- >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
125
- >> │ │
126
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234
127
- >> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
113
+ >> Validation Results
114
+ >> ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
115
+ >> │ **RECOMMENDATION** in id segment value bal500
116
+ >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers
117
+ >> │ (0-9), '-' and '+'
118
+ >> │
119
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234 │
120
+ >> ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
121
+ >> │ **RECOMMENDATION** in id segment value @1234
122
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-'
123
+ >> │ and '+'
124
+ >> │ │
125
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234
126
+ >> ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
127
+ >> │ **RECOMMENDATION** in id segment value bal500
128
+ >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers
129
+ >> │ (0-9), '-' and '+' │
130
+ >> │
131
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234
132
+ >> ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
133
+ >> │ **RECOMMENDATION** in id segment value @1234
134
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' │
135
+ >> │ and '+' │
136
+ >> │ │
137
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234 │
138
+ >> └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
128
139
  ```
129
140
  ### Save as QR Code
130
141
 
@@ -249,7 +260,7 @@ trex.print_validation_messages()
249
260
  >> Validation Results
250
261
  >> ┌────────────────────────────────────────────────────────────┐
251
262
  >> │ **ERROR** in TREX table column Date │
252
- >> │ Column header key contains invalid characters: 'e','t','a' │
263
+ >> │ Column header key contains invalid characters: 'a','t','e' │
253
264
  >> │ │
254
265
  >> │ STOP$T.D:20240505T1306 │
255
266
  >> │ +TEMP$KEL:10.15 │
@@ -257,9 +268,9 @@ trex.print_validation_messages()
257
268
  >> │ +COMMENT$T.A:FOO │
258
269
  >> │ +COMMENT2$T.T:12G3 │
259
270
  >> │ +TABLE$$DURATION$HUR:D👉ate👈$T.D:OK$T.B:COMMENT$T.A:: │
260
- >> │ 1:20250522T163256.043:T:FOO:: │
261
- >> │ 1.1:20250522T163256.043:T:BAR:: │
262
- >> │ 1.3:20250522T163256.043:F:BLUBB │
271
+ >> │ 1:20250522T180101.575:T:FOO:: │
272
+ >> │ 1.1:20250522T180101.575:T:BAR:: │
273
+ >> │ 1.3:20250522T180101.575:F:BLUBB │
263
274
  >> └────────────────────────────────────────────────────────────┘
264
275
  ```
265
276
  #### Combine PAC-ID and TREX and serialize
@@ -271,7 +282,7 @@ pac_str = pac.to_url()
271
282
  print(pac_str)
272
283
  ```
273
284
  ```text
274
- >> HTTPS://PAC.METTORIUS.COM/21:1234*MYTREX$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250522T163256.043:T:FOO::1.1:20250522T163256.043:T:BAR::1.3:20250522T163256.043:F:BLUBB
285
+ >> HTTPS://PAC.METTORIUS.COM/21:1234*MYTREX$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250522T180101.575:T:FOO::1.1:20250522T180101.575:T:BAR::1.3:20250522T180101.575:F:BLUBB
275
286
  ```
276
287
  ## PAC-ID Resolver
277
288
 
@@ -332,9 +343,12 @@ for sg in service_groups:
332
343
 
333
344
  <!-- BEGIN CHANGELOG -->
334
345
  ## Change Log
346
+ ### v0.2.12
347
+ - bugfix:no warning message if PAC-CAT has same segment key in two segments
348
+
335
349
  ### v0.2.11
336
350
  - bugfix:added missing well known segment key '250'
337
-
351
+
338
352
  ### v0.2.10
339
353
  - bugfix:added missing well known segment key '20'
340
354
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/labfreed.svg)](https://pypi.org/project/labfreed/) ![Python Version](https://img.shields.io/pypi/pyversions/labfreed) [![Test Labfreed](https://github.com/retothuerer/LabFREED/actions/workflows/run-tests.yml/badge.svg)](https://github.com/retothuerer/LabFREED/actions/workflows/run-tests.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
4
 
5
+ <!--
6
+ [![Ruff](https://img.shields.io/badge/style-Ruff-black?logo=ruff&labelColor=gray)](https://github.com/astral-sh/ruff)
7
+ -->
8
+
5
9
 
6
10
  This is a Python implementation of [LabFREED](https://labfreed.org/) building blocks.
7
11
 
@@ -67,28 +71,32 @@ There is a nice function to highlight problems
67
71
  pac.print_validation_messages()
68
72
  ```
69
73
  ```text
70
- >> Validation Results
71
- >> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
72
- >> │ **RECOMMENDATION** in id segment value bal500
73
- >> │ Characters 'l','a','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
74
- >> │
75
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234
76
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
77
- >> │ **RECOMMENDATION** in id segment value @1234 │
78
- >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
79
- >> │
80
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234
81
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
82
- >> │ **RECOMMENDATION** in id segment value bal500
83
- >> │ Characters 'l','a','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
84
- >> │
85
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234
86
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
87
- >> │ **RECOMMENDATION** in id segment value @1234
88
- >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
89
- >> │ │
90
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234
91
- >> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
74
+ >> Validation Results
75
+ >> ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
76
+ >> │ **RECOMMENDATION** in id segment value bal500
77
+ >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers
78
+ >> │ (0-9), '-' and '+'
79
+ >> │
80
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234 │
81
+ >> ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
82
+ >> │ **RECOMMENDATION** in id segment value @1234
83
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-'
84
+ >> │ and '+'
85
+ >> │ │
86
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234
87
+ >> ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
88
+ >> │ **RECOMMENDATION** in id segment value bal500
89
+ >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers
90
+ >> │ (0-9), '-' and '+' │
91
+ >> │
92
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/👉bal👈500/@1234
93
+ >> ├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
94
+ >> │ **RECOMMENDATION** in id segment value @1234
95
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' │
96
+ >> │ and '+' │
97
+ >> │ │
98
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/bal500/👉@👈1234 │
99
+ >> └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
92
100
  ```
93
101
  ### Save as QR Code
94
102
 
@@ -213,7 +221,7 @@ trex.print_validation_messages()
213
221
  >> Validation Results
214
222
  >> ┌────────────────────────────────────────────────────────────┐
215
223
  >> │ **ERROR** in TREX table column Date │
216
- >> │ Column header key contains invalid characters: 'e','t','a' │
224
+ >> │ Column header key contains invalid characters: 'a','t','e' │
217
225
  >> │ │
218
226
  >> │ STOP$T.D:20240505T1306 │
219
227
  >> │ +TEMP$KEL:10.15 │
@@ -221,9 +229,9 @@ trex.print_validation_messages()
221
229
  >> │ +COMMENT$T.A:FOO │
222
230
  >> │ +COMMENT2$T.T:12G3 │
223
231
  >> │ +TABLE$$DURATION$HUR:D👉ate👈$T.D:OK$T.B:COMMENT$T.A:: │
224
- >> │ 1:20250522T163256.043:T:FOO:: │
225
- >> │ 1.1:20250522T163256.043:T:BAR:: │
226
- >> │ 1.3:20250522T163256.043:F:BLUBB │
232
+ >> │ 1:20250522T180101.575:T:FOO:: │
233
+ >> │ 1.1:20250522T180101.575:T:BAR:: │
234
+ >> │ 1.3:20250522T180101.575:F:BLUBB │
227
235
  >> └────────────────────────────────────────────────────────────┘
228
236
  ```
229
237
  #### Combine PAC-ID and TREX and serialize
@@ -235,7 +243,7 @@ pac_str = pac.to_url()
235
243
  print(pac_str)
236
244
  ```
237
245
  ```text
238
- >> HTTPS://PAC.METTORIUS.COM/21:1234*MYTREX$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250522T163256.043:T:FOO::1.1:20250522T163256.043:T:BAR::1.3:20250522T163256.043:F:BLUBB
246
+ >> HTTPS://PAC.METTORIUS.COM/21:1234*MYTREX$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250522T180101.575:T:FOO::1.1:20250522T180101.575:T:BAR::1.3:20250522T180101.575:F:BLUBB
239
247
  ```
240
248
  ## PAC-ID Resolver
241
249
 
@@ -296,9 +304,12 @@ for sg in service_groups:
296
304
 
297
305
  <!-- BEGIN CHANGELOG -->
298
306
  ## Change Log
307
+ ### v0.2.12
308
+ - bugfix:no warning message if PAC-CAT has same segment key in two segments
309
+
299
310
  ### v0.2.11
300
311
  - bugfix:added missing well known segment key '250'
301
-
312
+
302
313
  ### v0.2.10
303
314
  - bugfix:added missing well known segment key '20'
304
315
 
@@ -2,7 +2,7 @@
2
2
  Python implementation of LabFREED building blocks
3
3
  '''
4
4
 
5
- __version__ = "0.2.11"
5
+ __version__ = "0.3.0a"
6
6
 
7
7
  from labfreed.pac_id import * # noqa: F403
8
8
  from labfreed.pac_cat import * # noqa: F403
@@ -108,7 +108,7 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
108
108
  self._validation_messages.append(w)
109
109
 
110
110
  # Function to extract warnings from a model and its nested models
111
- def _get_nested_validation_messages(self, parent_name: str = "", visited: Set[int] = None) -> List['ValidationMessage']:
111
+ def _get_nested_validation_messages(self, parent_name: str = "", visited: Set[int]|None = None) -> List['ValidationMessage']:
112
112
  """
113
113
  Recursively extract warnings from a Pydantic model and its nested fields, including computed fields.
114
114
 
@@ -140,7 +140,8 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
140
140
  warnings_list.extend(item._get_nested_validation_messages(list_path, visited))
141
141
 
142
142
  # Traverse computed fields
143
- computed_fields = getattr(self, '__pydantic_decorators__', {}).computed_fields or {}
143
+ mdl:BaseModel = getattr(self, '__pydantic_decorators__', {})
144
+ computed_fields = mdl.computed_fields or {}
144
145
  for field_name in computed_fields:
145
146
  full_path = f"{parent_name}.{field_name}" if parent_name else field_name
146
147
  try:
@@ -255,4 +256,3 @@ def _filter_warnings(val_msg:list[ValidationMessage]) -> list[ValidationMessage]
255
256
  def _quote_texts(texts:list[str]):
256
257
  return ','.join([f"'{t}'" for t in texts])
257
258
 
258
-
@@ -0,0 +1,56 @@
1
+ from typing import Self
2
+ from pydantic import ConfigDict, model_validator
3
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel, LabFREED_ValidationError, ValidationMsgLevel
4
+ from labfreed.pac_id.pac_id import PAC_ID
5
+
6
+
7
+ class AttributeRequestPayload(LabFREED_BaseModel):
8
+ model_config = ConfigDict(frozen=True)
9
+
10
+ pac_urls: list[str]
11
+ language_preferences: list[str]
12
+ restrict_to_attribute_groups: list[str]|None = None
13
+ suppress_forward_lookup: bool = False
14
+
15
+ def as_json(self):
16
+ return self.model_dump_json()
17
+
18
+ @classmethod
19
+ def from_json(cls, json):
20
+ return cls.model_validate_json(json)
21
+
22
+ @model_validator(mode="before")
23
+ @classmethod
24
+ def _handle_single_pac_url(cls, data):
25
+ p = data.get('pac_urls')
26
+ if isinstance(p, str):
27
+ data['pac_urls'] = [p]
28
+ return data
29
+
30
+ @model_validator(mode="after")
31
+ def _validate_pacs(self) -> Self:
32
+ if len(self.pac_urls) > 100:
33
+ self._add_validation_message(
34
+ source="pacs",
35
+ level = ValidationMsgLevel.ERROR,
36
+ msg='The number of pac-ids must be limited to 100'
37
+ )
38
+
39
+ for pac_url in self.pac_urls:
40
+ try:
41
+ PAC_ID.from_url(pac_url)
42
+ except LabFREED_ValidationError:
43
+ self._add_validation_message(
44
+ source="pacs",
45
+ level = ValidationMsgLevel.ERROR,
46
+ msg='{pac_url} is not a valid PAC-ID'
47
+ )
48
+
49
+ if not self.is_valid:
50
+ raise LabFREED_ValidationError(message='Invalid request', validation_msgs=self.validation_messages())
51
+
52
+ return self
53
+
54
+
55
+
56
+
@@ -0,0 +1,184 @@
1
+
2
+ from abc import ABC
3
+ from datetime import datetime
4
+ import re
5
+ from typing import Annotated, Any, Literal, Union, get_args
6
+ from labfreed.utilities.ensure_utc_time import ensure_utc
7
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
8
+ from pydantic import Field, field_validator, model_validator
9
+
10
+
11
+ class AttributeBase(LabFREED_BaseModel, ABC):
12
+ key: str
13
+ value: Any
14
+ label: str = ""
15
+
16
+ observed_at: datetime | None = None
17
+
18
+ def __init__(self, **data):
19
+ # Automatically inject the Literal value for `type`
20
+ discriminator_value = self._get_discriminator_value()
21
+ data["type"] = discriminator_value
22
+ super().__init__(**data)
23
+
24
+ @field_validator('observed_at', mode='before')
25
+ def set_utc_observed_at_if_naive(cls, value):
26
+ if isinstance(value, datetime):
27
+ return ensure_utc(value)
28
+ else:
29
+ return value
30
+
31
+ @classmethod
32
+ def _get_discriminator_value(cls) -> str:
33
+ """Extract the Literal value from the 'type' annotation."""
34
+ try:
35
+ type_annotation = cls.__annotations__["type"]
36
+ literal_value = get_args(type_annotation)[0]
37
+ return literal_value
38
+ except Exception as e:
39
+ raise TypeError(
40
+ f"{cls.__name__} must define `type: Literal[<value>]` annotation"
41
+ ) from e
42
+
43
+
44
+
45
+
46
+ class ReferenceAttribute(AttributeBase):
47
+ type: Literal["reference"]
48
+ value: str
49
+
50
+ class DateTimeAttribute(AttributeBase):
51
+ type: Literal["datetime"]
52
+ value: datetime
53
+
54
+ @field_validator('value', mode='before')
55
+ def set_utc__if_naive(cls, value):
56
+ if isinstance(value, datetime):
57
+ return ensure_utc(value)
58
+ else:
59
+ return value
60
+
61
+ class BoolAttribute(AttributeBase):
62
+ type: Literal["bool"]
63
+ value: bool
64
+
65
+ class TextAttribute(AttributeBase):
66
+ type: Literal["text"]
67
+ value: str
68
+
69
+
70
+
71
+
72
+ class NumericValue(LabFREED_BaseModel):
73
+ magnitude: str
74
+ unit: str
75
+
76
+ @model_validator(mode='after')
77
+ def _validate_value(self):
78
+ value = self.magnitude
79
+ if not_allowed_chars := set(re.sub(r'[0-9\.\-\+Ee]', '', value)):
80
+ self._add_validation_message(
81
+ source="Numeric Attribute",
82
+ level=ValidationMsgLevel.ERROR, # noqa: F821
83
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in quantity segment. Must be a number.",
84
+ highlight_pattern = f'{value}',
85
+ highlight_sub=not_allowed_chars
86
+ )
87
+ if not re.fullmatch(r'-?\d+(\.\d+)?([Ee][\+-]?\d+)?', value):
88
+ self._add_validation_message(
89
+ source="Numeric Attribute",
90
+ level=ValidationMsgLevel.ERROR,
91
+ msg=f"{value} cannot be converted to number",
92
+ highlight_pattern = f'{value}'
93
+ )
94
+ return self
95
+
96
+ @model_validator(mode="after")
97
+ def _validate_units(self):
98
+ '''A sanity check on unit complying with UCUM. NOTE: It is not a complete validation
99
+ - I check for blankspaces and ^, which are often used for units, but are invalid.
100
+ - the general structure of a ucum unit is validated, but 1)parentheses are not matched 2) units are not validated 3)prefixes are not checked
101
+ '''
102
+ if ' ' in self.unit or '^' in self.unit:
103
+ self._add_validation_message(
104
+ source="Numeric Attribute",
105
+ level= ValidationMsgLevel.ERROR,
106
+ msg=f"Unit {self.unit} is invalid. Must not contain blankspace or '^'.",
107
+ highlight_pattern = self.unit
108
+ )
109
+ elif not re.fullmatch(r"^(((?P<unit>[\w\[\]]+?)(?P<exponent>\-?\d+)?|(?P<annotation>)\{\w+?\})(?P<operator>[\./]?)?)+", self.unit):
110
+ self._add_validation_message(
111
+ source="Numeric Attribute",
112
+ level= ValidationMsgLevel.WARNING,
113
+ msg=f"Unit {self.unit} is probably invalid. Ensure it complies with UCUM specifications.",
114
+ highlight_pattern = self.unit
115
+ )
116
+ return self
117
+
118
+
119
+
120
+
121
+ class NumericAttribute(AttributeBase):
122
+ type: Literal["numeric"]
123
+ value: NumericValue
124
+
125
+ class ObjectAttribute(AttributeBase):
126
+ type: Literal["object"]
127
+ value: dict[str, Any]
128
+
129
+
130
+
131
+
132
+ Attribute = Annotated[
133
+ Union[
134
+ ReferenceAttribute,
135
+ DateTimeAttribute,
136
+ BoolAttribute,
137
+ TextAttribute,
138
+ NumericAttribute,
139
+ ObjectAttribute
140
+ ],
141
+ Field(discriminator="type")
142
+ ]
143
+
144
+ VALID_FOREVER = "forever"
145
+
146
+ class AttributeGroup(LabFREED_BaseModel):
147
+ key: str
148
+ label: str = ""
149
+ attributes: list[Attribute]
150
+
151
+ state_of: datetime
152
+ valid_until: datetime | Literal["forever"] | None = None
153
+
154
+ @field_validator('valid_until', mode='before')
155
+ def set_utc_valid_until_if_naive(cls, value):
156
+ if isinstance(value, datetime):
157
+ return ensure_utc(value)
158
+ else:
159
+ return value
160
+
161
+
162
+ class AttributesOfPACID(LabFREED_BaseModel):
163
+ pac_url: str
164
+ attribute_groups: list[AttributeGroup]
165
+
166
+
167
+
168
+ class AttributeResponsePayload(LabFREED_BaseModel):
169
+ schema_version: str = Field(default='1.0')
170
+ language:str
171
+ pac_attributes: list[AttributesOfPACID]
172
+
173
+ def to_json(self):
174
+ return self.model_dump_json(exclude_none=True)
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
@@ -0,0 +1,7 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class ServerCapabilities(BaseModel):
5
+ supported_languages: list[str]
6
+ default_language: str
7
+ available_attribute_groups: list[str]
@@ -0,0 +1 @@
1
+ import labfreed.utilities.translations
@@ -0,0 +1,78 @@
1
+
2
+
3
+ from datetime import datetime
4
+ from typing import Literal, Protocol
5
+
6
+
7
+ from labfreed.pac_attributes.api_data_models.response import AttributeGroup
8
+ from labfreed.pac_id.pac_id import PAC_ID
9
+
10
+
11
+
12
+ class CacheableAttributeGroup(AttributeGroup):
13
+ origin:str
14
+ language:str
15
+ valid_until: Literal['forever'] | datetime | None = None
16
+
17
+ # @model_validator(mode='after')
18
+ # def set_valid_until(self) -> 'CacheableAttributeGroup':
19
+ # vals = [a.valid_until for a in self.attributes]
20
+ # if all(e == 'forever' for e in vals):
21
+ # self.valid_until = 'forever'
22
+ # elif any(e is None for e in vals):
23
+ # self.valid_until = None
24
+ # else:
25
+ # self.valid_until = min(v for v in vals if isinstance(v, datetime))
26
+ # return self
27
+
28
+
29
+ @property
30
+ def still_valid(self):
31
+ if self.valid_until is None:
32
+ return False
33
+ if self.valid_until == 'forever':
34
+ return True
35
+
36
+ return self.valid_until > datetime.now()
37
+
38
+
39
+
40
+
41
+ class AttributeCache(Protocol):
42
+ def get_all(self, service_url:str, pac:PAC_ID) -> list[CacheableAttributeGroup]:
43
+ pass
44
+
45
+ def get_attribute_groups(self, service_url:str, pac:PAC_ID, attribute_groups:list[str]):
46
+ pass
47
+
48
+ def update(self, service_url:str, pac:PAC_ID, attribute_groups:list[CacheableAttributeGroup]):
49
+ pass
50
+
51
+
52
+
53
+ class MemoryAttributeCache(AttributeCache):
54
+ '''simple in-memory implementation of AttributeCache'''
55
+ def __init__(self):
56
+ self._store = dict()
57
+
58
+ def get_all(self, service_url:str, pac:PAC_ID|str) -> list[CacheableAttributeGroup]:
59
+ if isinstance(pac, str):
60
+ pac = PAC_ID.from_url(pac)
61
+ k = self._generate_dict_key(service_url=service_url, pac=pac)
62
+
63
+ ags = [CacheableAttributeGroup.model_validate(e) for e in self._store.get(k, [])]
64
+ return ags
65
+
66
+ def get_attribute_groups(self, service_url:str, pac:PAC_ID, attribute_groups:list[str]):
67
+ all_ags = self.get_all(service_url=service_url, pac=pac)
68
+ selected_ags = [ag for ag in all_ags if ag.key in attribute_groups]
69
+ return selected_ags
70
+
71
+ def update(self, service_url:str, pac:PAC_ID, attribute_groups: list[CacheableAttributeGroup] ):
72
+ k = self._generate_dict_key(service_url=service_url, pac=pac)
73
+ self._store.update({k: [e.model_dump() for e in attribute_groups]})
74
+
75
+ @staticmethod
76
+ def _generate_dict_key(service_url:str, pac:PAC_ID):
77
+ key = service_url +";"+ pac.to_url(include_extensions=False)
78
+ return key