labfreed 0.2.1__tar.gz → 0.2.3__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.
- {labfreed-0.2.1 → labfreed-0.2.3}/.github/workflows/pypi-publish.yml +1 -1
- {labfreed-0.2.1 → labfreed-0.2.3}/CHANGELOG.md +7 -6
- {labfreed-0.2.1 → labfreed-0.2.3}/PKG-INFO +65 -40
- {labfreed-0.2.1 → labfreed-0.2.3}/README.md +63 -39
- labfreed-0.2.3/labfreed/__init__.py +11 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/labfreed_infrastructure.py +50 -71
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_cat/__init__.py +4 -1
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_cat/pac_cat.py +4 -13
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_cat/predefined_categories.py +11 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id/extension.py +3 -3
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/table_segment.py +21 -4
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/trex_base_models.py +10 -136
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/value_segments.py +2 -12
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_extensions/display_name_extension.py +1 -1
- labfreed-0.2.1/labfreed/__init__.py +0 -8
- {labfreed-0.2.1 → labfreed-0.2.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/.github/workflows/run-tests.yml +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/LICENSE +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_cat/category_base.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id/id_segment.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id/pac_id.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id/url_parser.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id/url_serializer.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id_resolver/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id_resolver/cit_v1.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id_resolver/cit_v2.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id_resolver/resolver.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/pac_id_resolver/services.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/qr/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/qr/generate_qr.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/python_convenience/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/python_convenience/data_table.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/python_convenience/pyTREX.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/python_convenience/quantity.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/trex/trex.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/utilities/base36.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_extensions/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_extensions/default_extension_interpreters.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_extensions/trex_extension.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/gs1/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/gs1/gs1.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/labfreed/well_known_keys.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/unece/UneceUnits.json +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/unece/__init__.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_keys/unece/unece_units.py +0 -0
- {labfreed-0.2.1 → labfreed-0.2.3}/pyproject.toml +0 -0
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
## Change Log
|
|
2
|
+
### v0.2.3
|
|
3
|
+
- improvements in formatting of validation messages
|
|
4
|
+
- bugfix in DisplayNameExtension
|
|
5
|
+
|
|
6
|
+
### v0.2.2
|
|
7
|
+
- minor changes for better access of subfunctions. No change in existing API
|
|
8
|
+
|
|
2
9
|
### v0.2.1
|
|
3
10
|
- improved docu. no code changes
|
|
4
11
|
|
|
@@ -22,11 +29,5 @@
|
|
|
22
29
|
- QR generation
|
|
23
30
|
- ok-ish test coverage
|
|
24
31
|
|
|
25
|
-
# Attributions
|
|
26
|
-
The following tools were used:
|
|
27
|
-
- [pdoc](https://pdoc.dev/) was a great help with generating documentation
|
|
28
|
-
- [Pydantic](https://docs.pydantic.dev/latest/)
|
|
29
|
-
- json with UNECE units from (https://github.com/quadient/unece-units/blob/main/python/src/unece_excel_parser/parsedUneceUnits.json)
|
|
30
|
-
- json with GS1 codes from (https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld)
|
|
31
32
|
|
|
32
33
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labfreed
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
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
|
|
@@ -98,30 +98,28 @@ There is a nice function to highlight problems
|
|
|
98
98
|
pac.print_validation_messages()
|
|
99
99
|
```
|
|
100
100
|
```text
|
|
101
|
-
>> Validation Results
|
|
102
|
-
>>
|
|
103
|
-
>> │ RECOMMENDATION
|
|
104
|
-
>> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and │
|
|
105
|
-
>> │
|
|
106
|
-
>> │
|
|
107
|
-
>>
|
|
108
|
-
>>
|
|
109
|
-
>> │
|
|
110
|
-
>> │
|
|
111
|
-
>> │
|
|
112
|
-
>>
|
|
113
|
-
>>
|
|
114
|
-
>> │
|
|
115
|
-
>> │
|
|
116
|
-
>> │
|
|
117
|
-
>>
|
|
118
|
-
>> │
|
|
119
|
-
>>
|
|
120
|
-
>> │
|
|
121
|
-
>> │
|
|
122
|
-
>>
|
|
123
|
-
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:👉@👈1234 │
|
|
124
|
-
>> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
101
|
+
>> Validation Results
|
|
102
|
+
>> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
103
|
+
>> │ **RECOMMENDATION** in id segment value bal500 │
|
|
104
|
+
>> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
|
|
105
|
+
>> │ │
|
|
106
|
+
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:👉bal👈500/21:@1234 │
|
|
107
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
108
|
+
>> │ **RECOMMENDATION** in id segment value @1234 │
|
|
109
|
+
>> │ Characters '@' 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/240:bal500/21:👉@👈1234 │
|
|
112
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
113
|
+
>> │ **RECOMMENDATION** in id segment value bal500 │
|
|
114
|
+
>> │ Characters 'a','l','b' 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/240:👉bal👈500/21:@1234 │
|
|
117
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
118
|
+
>> │ **RECOMMENDATION** in id segment value @1234 │
|
|
119
|
+
>> │ Characters '@' 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/240:bal500/21:👉@👈1234 │
|
|
122
|
+
>> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
125
123
|
```
|
|
126
124
|
### Save as QR Code
|
|
127
125
|
|
|
@@ -141,7 +139,7 @@ PAC-CAT defines a (optional) way how the identifier is structured.
|
|
|
141
139
|
PAC_ID.from_url() automatically converts to PAC-CAT if possible.
|
|
142
140
|
|
|
143
141
|
```python
|
|
144
|
-
from labfreed
|
|
142
|
+
from labfreed import PAC_CAT
|
|
145
143
|
pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
|
|
146
144
|
pac = PAC_ID.from_url(pac_str)
|
|
147
145
|
if isinstance(pac, PAC_CAT):
|
|
@@ -197,7 +195,7 @@ print(f'WEIGHT = {v.value}')
|
|
|
197
195
|
#### Create PAC-ID
|
|
198
196
|
|
|
199
197
|
```python
|
|
200
|
-
from labfreed
|
|
198
|
+
from labfreed import PAC_ID, IDSegment
|
|
201
199
|
from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys
|
|
202
200
|
|
|
203
201
|
pac = PAC_ID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
|
|
@@ -213,9 +211,9 @@ Note that utility types for Quantity (number with unit) and table are needed
|
|
|
213
211
|
|
|
214
212
|
```python
|
|
215
213
|
from datetime import datetime
|
|
216
|
-
from labfreed.trex.python_convenience
|
|
217
|
-
from labfreed.trex.python_convenience
|
|
218
|
-
from labfreed.trex.python_convenience
|
|
214
|
+
from labfreed.trex.python_convenience import pyTREX
|
|
215
|
+
from labfreed.trex.python_convenience import DataTable
|
|
216
|
+
from labfreed.trex.python_convenience import Quantity
|
|
219
217
|
|
|
220
218
|
# Value segments of different type
|
|
221
219
|
segments = {
|
|
@@ -245,18 +243,18 @@ trex.print_validation_messages()
|
|
|
245
243
|
```text
|
|
246
244
|
>> Validation Results
|
|
247
245
|
>> ┌────────────────────────────────────────────────────────────┐
|
|
248
|
-
>> │ ERROR
|
|
249
|
-
>> │ Column header key contains invalid characters: 'a','
|
|
246
|
+
>> │ **ERROR** in TREX table column Date │
|
|
247
|
+
>> │ Column header key contains invalid characters: 'a','t','e' │
|
|
250
248
|
>> │ │
|
|
251
249
|
>> │ STOP$T.D:20240505T1306 │
|
|
252
250
|
>> │ +TEMP$KEL:10.15 │
|
|
253
251
|
>> │ +OK$T.B:F │
|
|
254
252
|
>> │ +COMMENT$T.A:FOO │
|
|
255
|
-
>> │ +COMMENT2$T.T:
|
|
253
|
+
>> │ +COMMENT2$T.T:12G3 │
|
|
256
254
|
>> │ +TABLE$$DURATION$HUR:D👉ate👈$T.D:OK$T.B:COMMENT$T.A:: │
|
|
257
|
-
>> │ 1:
|
|
258
|
-
>> │ 1.1:
|
|
259
|
-
>> │ 1.3:
|
|
255
|
+
>> │ 1:20250425T081731.192:T:FOO:: │
|
|
256
|
+
>> │ 1.1:20250425T081731.192:T:BAR:: │
|
|
257
|
+
>> │ 1.3:20250425T081731.192:F:BLUBB │
|
|
260
258
|
>> └────────────────────────────────────────────────────────────┘
|
|
261
259
|
```
|
|
262
260
|
#### Combine PAC-ID and TREX and serialize
|
|
@@ -268,12 +266,12 @@ pac_str = pac.to_url()
|
|
|
268
266
|
print(pac_str)
|
|
269
267
|
```
|
|
270
268
|
```text
|
|
271
|
-
>> 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:
|
|
269
|
+
>> 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:20250425T081731.192:T:FOO::1.1:20250425T081731.192:T:BAR::1.3:20250425T081731.192:F:BLUBB
|
|
272
270
|
```
|
|
273
271
|
## PAC-ID Resolver
|
|
274
272
|
|
|
275
273
|
```python
|
|
276
|
-
from labfreed
|
|
274
|
+
from labfreed import PAC_ID_Resolver, load_cit
|
|
277
275
|
# Get a CIT
|
|
278
276
|
dir = os.path.join(os.getcwd(), 'examples')
|
|
279
277
|
p = os.path.join(dir, 'cit_mine.yaml')
|
|
@@ -299,7 +297,26 @@ for sg in service_groups:
|
|
|
299
297
|
|
|
300
298
|
```
|
|
301
299
|
```text
|
|
302
|
-
>>
|
|
300
|
+
>> Services from origin 'PERSONAL
|
|
301
|
+
>> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
302
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
303
|
+
>> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
304
|
+
>> │ CAS Search │ https://pubchem.ncbi.nlm.nih.gov/#query=7732-18-5 │ ACTIVE │
|
|
305
|
+
>> └──────────────┴───────────────────────────────────────────────────┴───────────┘
|
|
306
|
+
>> Services from origin 'MY_COMPANY
|
|
307
|
+
>> ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
308
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
309
|
+
>> ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
310
|
+
>> │ Chemical Management │ https://chem-manager.com/METTORIUS.COM/-MS/240:X3511/CAS:7732-18-5 │ INACTIVE │
|
|
311
|
+
>> └─────────────────────┴────────────────────────────────────────────────────────────────────┴───────────┘
|
|
312
|
+
>> Services from origin 'METTORIUS.COM
|
|
313
|
+
>> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
314
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
315
|
+
>> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
316
|
+
>> │ CoA │ https://mettorius.com/CoA.pdf │ ACTIVE │
|
|
317
|
+
>> │ MSDS │ https://mettorius.com/MSDS.pdf │ ACTIVE │
|
|
318
|
+
>> │ Shop │ https://mettorius.com/shop.html │ ACTIVE │
|
|
319
|
+
>> └──────────────┴─────────────────────────────────┴───────────┘
|
|
303
320
|
```
|
|
304
321
|
<!-- END EXAMPLES -->
|
|
305
322
|
|
|
@@ -307,6 +324,13 @@ for sg in service_groups:
|
|
|
307
324
|
|
|
308
325
|
<!-- BEGIN CHANGELOG -->
|
|
309
326
|
## Change Log
|
|
327
|
+
### v0.2.3
|
|
328
|
+
- improvements in formatting of validation messages
|
|
329
|
+
- bugfix in DisplayNameExtension
|
|
330
|
+
|
|
331
|
+
### v0.2.2
|
|
332
|
+
- minor changes for better access of subfunctions. No change in existing API
|
|
333
|
+
|
|
310
334
|
### v0.2.1
|
|
311
335
|
- improved docu. no code changes
|
|
312
336
|
|
|
@@ -329,6 +353,7 @@ for sg in service_groups:
|
|
|
329
353
|
- supports PAC-ID, PAC-CAT, TREX and DisplayName
|
|
330
354
|
- QR generation
|
|
331
355
|
- ok-ish test coverage
|
|
356
|
+
<!-- END CHANGELOG -->
|
|
332
357
|
|
|
333
358
|
# Attributions
|
|
334
359
|
The following tools were used:
|
|
@@ -336,4 +361,4 @@ The following tools were used:
|
|
|
336
361
|
- [Pydantic](https://docs.pydantic.dev/latest/)
|
|
337
362
|
- json with UNECE units from (https://github.com/quadient/unece-units/blob/main/python/src/unece_excel_parser/parsedUneceUnits.json)
|
|
338
363
|
- json with GS1 codes from (https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld)
|
|
339
|
-
|
|
364
|
+
|
|
@@ -67,30 +67,28 @@ There is a nice function to highlight problems
|
|
|
67
67
|
pac.print_validation_messages()
|
|
68
68
|
```
|
|
69
69
|
```text
|
|
70
|
-
>> Validation Results
|
|
71
|
-
>>
|
|
72
|
-
>> │ RECOMMENDATION
|
|
73
|
-
>> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and │
|
|
74
|
-
>> │
|
|
75
|
-
>> │
|
|
76
|
-
>>
|
|
77
|
-
>>
|
|
78
|
-
>> │
|
|
79
|
-
>> │
|
|
80
|
-
>> │
|
|
81
|
-
>>
|
|
82
|
-
>>
|
|
83
|
-
>> │
|
|
84
|
-
>> │
|
|
85
|
-
>> │
|
|
86
|
-
>>
|
|
87
|
-
>> │
|
|
88
|
-
>>
|
|
89
|
-
>> │
|
|
90
|
-
>> │
|
|
91
|
-
>>
|
|
92
|
-
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:👉@👈1234 │
|
|
93
|
-
>> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
70
|
+
>> Validation Results
|
|
71
|
+
>> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
72
|
+
>> │ **RECOMMENDATION** in id segment value bal500 │
|
|
73
|
+
>> │ Characters 'a','l','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/240:👉bal👈500/21:@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/240:bal500/21:👉@👈1234 │
|
|
81
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
82
|
+
>> │ **RECOMMENDATION** in id segment value bal500 │
|
|
83
|
+
>> │ Characters 'a','l','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/240:👉bal👈500/21:@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/240:bal500/21:👉@👈1234 │
|
|
91
|
+
>> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
94
92
|
```
|
|
95
93
|
### Save as QR Code
|
|
96
94
|
|
|
@@ -110,7 +108,7 @@ PAC-CAT defines a (optional) way how the identifier is structured.
|
|
|
110
108
|
PAC_ID.from_url() automatically converts to PAC-CAT if possible.
|
|
111
109
|
|
|
112
110
|
```python
|
|
113
|
-
from labfreed
|
|
111
|
+
from labfreed import PAC_CAT
|
|
114
112
|
pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
|
|
115
113
|
pac = PAC_ID.from_url(pac_str)
|
|
116
114
|
if isinstance(pac, PAC_CAT):
|
|
@@ -166,7 +164,7 @@ print(f'WEIGHT = {v.value}')
|
|
|
166
164
|
#### Create PAC-ID
|
|
167
165
|
|
|
168
166
|
```python
|
|
169
|
-
from labfreed
|
|
167
|
+
from labfreed import PAC_ID, IDSegment
|
|
170
168
|
from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys
|
|
171
169
|
|
|
172
170
|
pac = PAC_ID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
|
|
@@ -182,9 +180,9 @@ Note that utility types for Quantity (number with unit) and table are needed
|
|
|
182
180
|
|
|
183
181
|
```python
|
|
184
182
|
from datetime import datetime
|
|
185
|
-
from labfreed.trex.python_convenience
|
|
186
|
-
from labfreed.trex.python_convenience
|
|
187
|
-
from labfreed.trex.python_convenience
|
|
183
|
+
from labfreed.trex.python_convenience import pyTREX
|
|
184
|
+
from labfreed.trex.python_convenience import DataTable
|
|
185
|
+
from labfreed.trex.python_convenience import Quantity
|
|
188
186
|
|
|
189
187
|
# Value segments of different type
|
|
190
188
|
segments = {
|
|
@@ -214,18 +212,18 @@ trex.print_validation_messages()
|
|
|
214
212
|
```text
|
|
215
213
|
>> Validation Results
|
|
216
214
|
>> ┌────────────────────────────────────────────────────────────┐
|
|
217
|
-
>> │ ERROR
|
|
218
|
-
>> │ Column header key contains invalid characters: 'a','
|
|
215
|
+
>> │ **ERROR** in TREX table column Date │
|
|
216
|
+
>> │ Column header key contains invalid characters: 'a','t','e' │
|
|
219
217
|
>> │ │
|
|
220
218
|
>> │ STOP$T.D:20240505T1306 │
|
|
221
219
|
>> │ +TEMP$KEL:10.15 │
|
|
222
220
|
>> │ +OK$T.B:F │
|
|
223
221
|
>> │ +COMMENT$T.A:FOO │
|
|
224
|
-
>> │ +COMMENT2$T.T:
|
|
222
|
+
>> │ +COMMENT2$T.T:12G3 │
|
|
225
223
|
>> │ +TABLE$$DURATION$HUR:D👉ate👈$T.D:OK$T.B:COMMENT$T.A:: │
|
|
226
|
-
>> │ 1:
|
|
227
|
-
>> │ 1.1:
|
|
228
|
-
>> │ 1.3:
|
|
224
|
+
>> │ 1:20250425T081731.192:T:FOO:: │
|
|
225
|
+
>> │ 1.1:20250425T081731.192:T:BAR:: │
|
|
226
|
+
>> │ 1.3:20250425T081731.192:F:BLUBB │
|
|
229
227
|
>> └────────────────────────────────────────────────────────────┘
|
|
230
228
|
```
|
|
231
229
|
#### Combine PAC-ID and TREX and serialize
|
|
@@ -237,12 +235,12 @@ pac_str = pac.to_url()
|
|
|
237
235
|
print(pac_str)
|
|
238
236
|
```
|
|
239
237
|
```text
|
|
240
|
-
>> 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:
|
|
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:20250425T081731.192:T:FOO::1.1:20250425T081731.192:T:BAR::1.3:20250425T081731.192:F:BLUBB
|
|
241
239
|
```
|
|
242
240
|
## PAC-ID Resolver
|
|
243
241
|
|
|
244
242
|
```python
|
|
245
|
-
from labfreed
|
|
243
|
+
from labfreed import PAC_ID_Resolver, load_cit
|
|
246
244
|
# Get a CIT
|
|
247
245
|
dir = os.path.join(os.getcwd(), 'examples')
|
|
248
246
|
p = os.path.join(dir, 'cit_mine.yaml')
|
|
@@ -268,7 +266,26 @@ for sg in service_groups:
|
|
|
268
266
|
|
|
269
267
|
```
|
|
270
268
|
```text
|
|
271
|
-
>>
|
|
269
|
+
>> Services from origin 'PERSONAL
|
|
270
|
+
>> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
271
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
272
|
+
>> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
273
|
+
>> │ CAS Search │ https://pubchem.ncbi.nlm.nih.gov/#query=7732-18-5 │ ACTIVE │
|
|
274
|
+
>> └──────────────┴───────────────────────────────────────────────────┴───────────┘
|
|
275
|
+
>> Services from origin 'MY_COMPANY
|
|
276
|
+
>> ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
277
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
278
|
+
>> ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
279
|
+
>> │ Chemical Management │ https://chem-manager.com/METTORIUS.COM/-MS/240:X3511/CAS:7732-18-5 │ INACTIVE │
|
|
280
|
+
>> └─────────────────────┴────────────────────────────────────────────────────────────────────┴───────────┘
|
|
281
|
+
>> Services from origin 'METTORIUS.COM
|
|
282
|
+
>> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
283
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
284
|
+
>> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
285
|
+
>> │ CoA │ https://mettorius.com/CoA.pdf │ ACTIVE │
|
|
286
|
+
>> │ MSDS │ https://mettorius.com/MSDS.pdf │ ACTIVE │
|
|
287
|
+
>> │ Shop │ https://mettorius.com/shop.html │ ACTIVE │
|
|
288
|
+
>> └──────────────┴─────────────────────────────────┴───────────┘
|
|
272
289
|
```
|
|
273
290
|
<!-- END EXAMPLES -->
|
|
274
291
|
|
|
@@ -276,6 +293,13 @@ for sg in service_groups:
|
|
|
276
293
|
|
|
277
294
|
<!-- BEGIN CHANGELOG -->
|
|
278
295
|
## Change Log
|
|
296
|
+
### v0.2.3
|
|
297
|
+
- improvements in formatting of validation messages
|
|
298
|
+
- bugfix in DisplayNameExtension
|
|
299
|
+
|
|
300
|
+
### v0.2.2
|
|
301
|
+
- minor changes for better access of subfunctions. No change in existing API
|
|
302
|
+
|
|
279
303
|
### v0.2.1
|
|
280
304
|
- improved docu. no code changes
|
|
281
305
|
|
|
@@ -298,6 +322,7 @@ for sg in service_groups:
|
|
|
298
322
|
- supports PAC-ID, PAC-CAT, TREX and DisplayName
|
|
299
323
|
- QR generation
|
|
300
324
|
- ok-ish test coverage
|
|
325
|
+
<!-- END CHANGELOG -->
|
|
301
326
|
|
|
302
327
|
# Attributions
|
|
303
328
|
The following tools were used:
|
|
@@ -305,4 +330,3 @@ The following tools were used:
|
|
|
305
330
|
- [Pydantic](https://docs.pydantic.dev/latest/)
|
|
306
331
|
- json with UNECE units from (https://github.com/quadient/unece-units/blob/main/python/src/unece_excel_parser/parsedUneceUnits.json)
|
|
307
332
|
- json with GS1 codes from (https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld)
|
|
308
|
-
<!-- END CHANGELOG -->
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Python implementation of LabFREED building blocks
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
__version__ = "0.2.3"
|
|
6
|
+
|
|
7
|
+
from labfreed.pac_id import * # noqa: F403
|
|
8
|
+
from labfreed.pac_cat import * # noqa: F403
|
|
9
|
+
from labfreed.pac_id_resolver import * # noqa: F403
|
|
10
|
+
from labfreed.trex import * # noqa: F403
|
|
11
|
+
from labfreed.labfreed_infrastructure import * # noqa: F403
|
|
@@ -159,25 +159,9 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
|
|
|
159
159
|
return warnings_list
|
|
160
160
|
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
def _emphasize_in(self, validation_msg, validation_node_str:str, fmt, color='black'):
|
|
164
|
-
if validation_msg.highlight_sub_patterns:
|
|
165
|
-
replacements = validation_msg.highlight_sub_patterns
|
|
166
|
-
else:
|
|
167
|
-
replacements = [validation_msg.highlight]
|
|
168
|
-
# Sort patterns by length descending to avoid subpattern clobbering
|
|
169
|
-
sorted_patterns = sorted(replacements, key=len, reverse=True)
|
|
170
|
-
# Escape the patterns for regex safety
|
|
171
|
-
escaped_patterns = [re.escape(p) for p in sorted_patterns]
|
|
172
|
-
# Create one regex pattern with alternation (longest first)
|
|
173
|
-
pattern = re.compile("|".join(escaped_patterns))
|
|
174
|
-
|
|
175
|
-
out = pattern.sub(lambda m: fmt(m.group(0)), validation_node_str)
|
|
176
|
-
return out
|
|
177
|
-
|
|
178
162
|
|
|
179
163
|
def print_validation_messages(self, target='console'):
|
|
180
|
-
msgs = self.
|
|
164
|
+
msgs = self.format_validation_messages(target=target)
|
|
181
165
|
|
|
182
166
|
table = Table(title="Validation Results", show_header=False, title_justify='left')
|
|
183
167
|
|
|
@@ -185,12 +169,29 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
|
|
|
185
169
|
return table.add_column(s, vertical='top')
|
|
186
170
|
col("-")
|
|
187
171
|
|
|
188
|
-
|
|
189
172
|
if not msgs:
|
|
190
173
|
table.add_row('All clear!', end_section=True)
|
|
191
174
|
return
|
|
192
175
|
|
|
193
176
|
for m in msgs:
|
|
177
|
+
table.add_row(m)
|
|
178
|
+
table.add_section()
|
|
179
|
+
|
|
180
|
+
logging.info(table)
|
|
181
|
+
print(table)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def format_validation_messages(self, target='console') -> list[str]:
|
|
185
|
+
"""Format validation messages
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
target (str, optional): Target format: 'markdown', 'console', 'html', 'html_styled'.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
list[str]: formated messages
|
|
192
|
+
"""
|
|
193
|
+
formatted_msg = list()
|
|
194
|
+
for m in self.validation_messages():
|
|
194
195
|
if m.level == ValidationMsgLevel.ERROR:
|
|
195
196
|
color = 'red'
|
|
196
197
|
else:
|
|
@@ -199,70 +200,48 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
|
|
|
199
200
|
match target:
|
|
200
201
|
case 'markdown':
|
|
201
202
|
fmt = lambda s: f'👉{s}👈' # noqa: E731
|
|
203
|
+
fmt_title = lambda s: f'**{s}**' # noqa: E731
|
|
204
|
+
br = '\n'
|
|
202
205
|
case 'console':
|
|
203
206
|
fmt = lambda s: f'[{color} bold]{s}[/{color} bold]' # noqa: E731
|
|
207
|
+
fmt_title = fmt
|
|
208
|
+
br = '\n'
|
|
204
209
|
case 'html':
|
|
205
|
-
fmt = lambda s: f'<span class="
|
|
210
|
+
fmt = lambda s: f'<span class="validation-{m.level.name.lower()}">{s}</span>' # noqa: E731
|
|
211
|
+
fmt_title = lambda s: f'<span class="validation-title">{s}</span>' # noqa: E731
|
|
212
|
+
br = '<br>'
|
|
206
213
|
case 'html_styled':
|
|
207
|
-
fmt = lambda s: f'<b style="color:{color}>{s}</b>' # noqa: E731
|
|
214
|
+
fmt = lambda s: f'<b style="color:{color}">{s}</b>' # noqa: E731
|
|
215
|
+
fmt_title = fmt
|
|
216
|
+
br = '<br>'
|
|
208
217
|
|
|
209
218
|
serialized = str(self)
|
|
210
|
-
emphazised_highlight = self._emphasize_in(m, serialized, fmt=fmt
|
|
219
|
+
emphazised_highlight = self._emphasize_in(m, serialized, fmt=fmt)
|
|
211
220
|
emphazised_highlight = emphazised_highlight.replace('👈👉','') # removes two consecutive markers, to make it cleaner
|
|
212
221
|
|
|
213
|
-
txt =
|
|
214
|
-
txt +=
|
|
215
|
-
txt +=
|
|
216
|
-
|
|
217
|
-
table.add_row( txt)
|
|
218
|
-
table.add_section()
|
|
219
|
-
|
|
220
|
-
logging.info(table)
|
|
221
|
-
print(table)
|
|
222
|
+
txt = f'{fmt_title(m.level.name)} in {m.source}'
|
|
223
|
+
txt += br + f'{m.msg}'
|
|
224
|
+
txt += br+br + emphazised_highlight
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
formatted_msg.append(txt)
|
|
227
|
+
return formatted_msg
|
|
226
228
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
# if not msgs:
|
|
240
|
-
# print('All clear!')
|
|
241
|
-
# return
|
|
242
|
-
|
|
243
|
-
# for m in msgs:
|
|
244
|
-
# if m.level.casefold() == "error":
|
|
245
|
-
# color = 'red'
|
|
246
|
-
# else:
|
|
247
|
-
# color = 'yellow'
|
|
248
|
-
|
|
249
|
-
# text = Text.from_markup(f'\n [bold {color}]{m.level} [/bold {color}] in \t {m.source}' )
|
|
250
|
-
# print(text)
|
|
251
|
-
# match target:
|
|
252
|
-
# case 'markdown':
|
|
253
|
-
# formatted_highlight = m.emphazised_highlight.replace('emph', f'🔸').replace('[/', '').replace('[', '').replace(']', '')
|
|
254
|
-
# case 'console':
|
|
255
|
-
# formatted_highlight = m.emphazised_highlight.replace('emph', f'bold {color}')
|
|
256
|
-
# case 'html':
|
|
257
|
-
# formatted_highlight = m.emphazised_highlight.replace('emph', f'b').replace('[', '<').replace(']', '>')
|
|
258
|
-
# fmtd = str_to_highlight_in.replace(m.highlight, formatted_highlight)
|
|
259
|
-
# fmtd = Text.from_markup(fmtd)
|
|
260
|
-
# print(fmtd)
|
|
261
|
-
# print(Text.from_markup(f'{m.problem_msg}'))
|
|
229
|
+
def _emphasize_in(self, validation_msg, validation_node_str:str, fmt):
|
|
230
|
+
if validation_msg.highlight_sub_patterns:
|
|
231
|
+
replacements = validation_msg.highlight_sub_patterns
|
|
232
|
+
else:
|
|
233
|
+
replacements = [validation_msg.highlight]
|
|
234
|
+
# Sort patterns by length descending to avoid subpattern clobbering
|
|
235
|
+
sorted_patterns = sorted(replacements, key=len, reverse=True)
|
|
236
|
+
# Escape the patterns for regex safety
|
|
237
|
+
escaped_patterns = [re.escape(p) for p in sorted_patterns]
|
|
238
|
+
# Create one regex pattern with alternation (longest first)
|
|
239
|
+
pattern = re.compile("|".join(escaped_patterns))
|
|
262
240
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
241
|
+
out = pattern.sub(lambda m: fmt(m.group(0)), validation_node_str)
|
|
242
|
+
return out
|
|
243
|
+
|
|
244
|
+
|
|
266
245
|
|
|
267
246
|
def _filter_errors(val_msg:list[ValidationMessage]) -> list[ValidationMessage]:
|
|
268
247
|
return [ m for m in val_msg if m.level == ValidationMsgLevel.ERROR ]
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from .pac_cat import PAC_CAT
|
|
2
2
|
from .category_base import Category
|
|
3
|
-
from .predefined_categories import
|
|
3
|
+
from .predefined_categories import (
|
|
4
|
+
Material_Device, Material_Substance, Material_Consumable, Material_Misc, Data_Method, Data_Result, Data_Progress,
|
|
5
|
+
Data_Calibration, Data_Abstract, category_key_to_class_map
|
|
6
|
+
)
|
|
4
7
|
|
|
5
8
|
__all__ = [
|
|
6
9
|
"PAC_CAT",
|
|
@@ -10,7 +10,7 @@ from rich.table import Table
|
|
|
10
10
|
from labfreed.labfreed_infrastructure import ValidationMsgLevel
|
|
11
11
|
|
|
12
12
|
from labfreed.pac_cat.category_base import Category
|
|
13
|
-
from labfreed.pac_cat.predefined_categories import
|
|
13
|
+
from labfreed.pac_cat.predefined_categories import category_key_to_class_map
|
|
14
14
|
from labfreed.pac_id.id_segment import IDSegment
|
|
15
15
|
from labfreed.pac_id.pac_id import PAC_ID
|
|
16
16
|
|
|
@@ -68,18 +68,7 @@ class PAC_CAT(PAC_ID):
|
|
|
68
68
|
category_key = segments[0].value
|
|
69
69
|
segments.pop(0)
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
'-MD': Material_Device,
|
|
73
|
-
'-MS': Material_Substance,
|
|
74
|
-
'-MC': Material_Consumable,
|
|
75
|
-
'-MM': Material_Misc,
|
|
76
|
-
'-DM': Data_Method,
|
|
77
|
-
'-DR': Data_Result,
|
|
78
|
-
'-DC': Data_Calibration,
|
|
79
|
-
'-DP': Data_Progress,
|
|
80
|
-
'-DS': Data_Static
|
|
81
|
-
}
|
|
82
|
-
known_cat = mapping.get(category_key)
|
|
71
|
+
known_cat = category_key_to_class_map.get(category_key)
|
|
83
72
|
|
|
84
73
|
if not known_cat:
|
|
85
74
|
return Category(key=category_key, segments=segments)
|
|
@@ -157,3 +146,5 @@ class PAC_CAT(PAC_ID):
|
|
|
157
146
|
table.add_section()
|
|
158
147
|
print(table)
|
|
159
148
|
|
|
149
|
+
|
|
150
|
+
|
|
@@ -188,3 +188,14 @@ class Data_Static(Data_Abstract):
|
|
|
188
188
|
''' Category segments, which are not defined in the specification'''
|
|
189
189
|
|
|
190
190
|
|
|
191
|
+
category_key_to_class_map = {
|
|
192
|
+
'-MD': Material_Device,
|
|
193
|
+
'-MS': Material_Substance,
|
|
194
|
+
'-MC': Material_Consumable,
|
|
195
|
+
'-MM': Material_Misc,
|
|
196
|
+
'-DM': Data_Method,
|
|
197
|
+
'-DR': Data_Result,
|
|
198
|
+
'-DC': Data_Calibration,
|
|
199
|
+
'-DP': Data_Progress,
|
|
200
|
+
'-DS': Data_Static
|
|
201
|
+
}
|
|
@@ -15,6 +15,8 @@ class ExtensionBase(ABC):
|
|
|
15
15
|
def data(self) -> str:
|
|
16
16
|
raise NotImplementedError("Subclasses must implement 'data'")
|
|
17
17
|
|
|
18
|
+
def __str__(self):
|
|
19
|
+
return f'{self.name}${self.type}/{self.data}'
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
|
|
@@ -43,6 +45,4 @@ class Extension(LabFREED_BaseModel,ExtensionBase):
|
|
|
43
45
|
"extra": "allow", # Allow extra keys during pre-validation
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
return f'{self.name}${self.type}/{self.data}'
|
|
48
|
-
|
|
48
|
+
|
|
@@ -8,7 +8,7 @@ from pydantic import RootModel, model_validator
|
|
|
8
8
|
from labfreed.trex.trex_base_models import Value
|
|
9
9
|
from labfreed.well_known_keys.unece.unece_units import unece_unit_codes
|
|
10
10
|
from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
|
|
11
|
-
from labfreed.trex.trex_base_models import AlphanumericValue, BinaryValue, BoolValue, DateValue, ErrorValue, NumericValue, TREX_Segment, TextValue
|
|
11
|
+
from labfreed.trex.trex_base_models import AlphanumericValue, BinaryValue, BoolValue, DateValue, ErrorValue, NumericValue, TREX_Segment, TextValue
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class ColumnHeader(LabFREED_BaseModel):
|
|
@@ -217,11 +217,28 @@ def _deserialize_table_segment_from_trex_segment_str(trex_segment_str) -> TableS
|
|
|
217
217
|
headers.append(ColumnHeader(key=col_key, type=col_type))
|
|
218
218
|
|
|
219
219
|
data = [row.split(':') for row in body.split('::') ]
|
|
220
|
-
col_types = [h.type for h in headers]
|
|
221
220
|
# convert to correct value types
|
|
222
|
-
data_with_types = [[
|
|
221
|
+
data_with_types = [[_str_to_value_type(h.type, cv) for cv, h in zip(r, headers)] for r in data]
|
|
223
222
|
data = [ TableRow(r) for r in data_with_types]
|
|
224
223
|
|
|
225
|
-
out = TableSegment(column_headers=headers, data=
|
|
224
|
+
out = TableSegment(column_headers=headers, data=data, key=name)
|
|
225
|
+
return out
|
|
226
|
+
|
|
227
|
+
def _str_to_value_type(type_, s):
|
|
228
|
+
match type_:
|
|
229
|
+
case 'T.D':
|
|
230
|
+
out = DateValue(value=s)
|
|
231
|
+
case 'T.B':
|
|
232
|
+
out = BoolValue(value=s)
|
|
233
|
+
case 'T.A':
|
|
234
|
+
out = AlphanumericValue(value=s)
|
|
235
|
+
case 'T.T':
|
|
236
|
+
out = TextValue(value=s)
|
|
237
|
+
case 'T.X':
|
|
238
|
+
out = BinaryValue(value=s)
|
|
239
|
+
case 'E':
|
|
240
|
+
out = ErrorValue(value=s)
|
|
241
|
+
case _:
|
|
242
|
+
out = NumericValue(value=s)
|
|
226
243
|
return out
|
|
227
244
|
|
|
@@ -4,11 +4,10 @@ import re
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
from pydantic import PrivateAttr,
|
|
7
|
+
from pydantic import PrivateAttr, model_validator
|
|
8
8
|
from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
|
|
9
|
-
from abc import ABC,
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
10
|
|
|
11
|
-
from labfreed.utilities.base36 import base36, to_base36, from_base36
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
''' Configure pdoc'''
|
|
@@ -32,22 +31,9 @@ class Value(LabFREED_BaseModel, ABC):
|
|
|
32
31
|
def serialize(self):
|
|
33
32
|
return self.value
|
|
34
33
|
|
|
35
|
-
@abstractclassmethod
|
|
36
|
-
def _from_python_type(cls, v):
|
|
37
|
-
...
|
|
38
|
-
|
|
39
|
-
@abstractmethod
|
|
40
|
-
def _value_to_python_type(self):
|
|
41
|
-
...
|
|
42
34
|
|
|
43
35
|
|
|
44
36
|
class NumericValue(Value):
|
|
45
|
-
@field_validator('value', mode='before')
|
|
46
|
-
@classmethod
|
|
47
|
-
def _from_python_type(cls, v:str| int|float):
|
|
48
|
-
if isinstance(v, str):
|
|
49
|
-
return v
|
|
50
|
-
return str(v)
|
|
51
37
|
|
|
52
38
|
@model_validator(mode='after')
|
|
53
39
|
def _validate(self):
|
|
@@ -56,7 +42,7 @@ class NumericValue(Value):
|
|
|
56
42
|
self._add_validation_message(
|
|
57
43
|
source=f"TREX numeric value {value}",
|
|
58
44
|
level=ValidationMsgLevel.ERROR,
|
|
59
|
-
msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in quantity segment.
|
|
45
|
+
msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in quantity segment. Must be a number.",
|
|
60
46
|
highlight_pattern = f'{value}',
|
|
61
47
|
highlight_sub=not_allowed_chars
|
|
62
48
|
)
|
|
@@ -68,36 +54,9 @@ class NumericValue(Value):
|
|
|
68
54
|
highlight_pattern = f'{value}'
|
|
69
55
|
)
|
|
70
56
|
return self
|
|
71
|
-
|
|
72
|
-
def _value_to_python_type(self) -> str:
|
|
73
|
-
v = float(self.value)
|
|
74
|
-
if '.' not in self.value and 'E' not in self.value:
|
|
75
|
-
return int(v)
|
|
76
|
-
else:
|
|
77
|
-
return v
|
|
78
|
-
|
|
79
57
|
|
|
80
58
|
class DateValue(Value):
|
|
81
59
|
_date_time_dict:dict|None = PrivateAttr(default=None)
|
|
82
|
-
@field_validator('value', mode='before')
|
|
83
|
-
@classmethod
|
|
84
|
-
def _from_python_type(cls, v:str| date|time|datetime):
|
|
85
|
-
if isinstance(v, str):
|
|
86
|
-
return v
|
|
87
|
-
|
|
88
|
-
sd = ""
|
|
89
|
-
st = ""
|
|
90
|
-
if isinstance(v, date) or isinstance(v, datetime):
|
|
91
|
-
sd = v.strftime('%Y%m%d')
|
|
92
|
-
if isinstance(v, time) or isinstance(v, datetime):
|
|
93
|
-
if v.microsecond:
|
|
94
|
-
st = v.strftime("T%H%M%S.") + f"{v.microsecond // 1000:03d}"
|
|
95
|
-
elif v.second:
|
|
96
|
-
st = v.strftime("T%H%M%S")
|
|
97
|
-
else:
|
|
98
|
-
st = v.strftime("T%H%M")
|
|
99
|
-
|
|
100
|
-
return sd + st
|
|
101
60
|
|
|
102
61
|
@model_validator(mode='after')
|
|
103
62
|
def _validate(self):
|
|
@@ -134,24 +93,8 @@ class DateValue(Value):
|
|
|
134
93
|
self._date_time_dict = d
|
|
135
94
|
return self
|
|
136
95
|
|
|
137
|
-
|
|
138
|
-
d = self._date_time_dict
|
|
139
|
-
if d.get('year') and d.get('hour'): # input is only a time
|
|
140
|
-
return datetime(**d)
|
|
141
|
-
elif d.get('year'):
|
|
142
|
-
return date(**d)
|
|
143
|
-
else:
|
|
144
|
-
return time(**d)
|
|
145
|
-
|
|
146
|
-
|
|
96
|
+
|
|
147
97
|
class BoolValue(Value):
|
|
148
|
-
@field_validator('value', mode='before')
|
|
149
|
-
@classmethod
|
|
150
|
-
def _from_python_type(cls, v:str| bool):
|
|
151
|
-
if isinstance(v, str):
|
|
152
|
-
return v
|
|
153
|
-
|
|
154
|
-
return 'T' if v else 'F'
|
|
155
98
|
|
|
156
99
|
@model_validator(mode='after')
|
|
157
100
|
def _validate(self):
|
|
@@ -164,21 +107,9 @@ class BoolValue(Value):
|
|
|
164
107
|
highlight_sub=[c for c in self.value]
|
|
165
108
|
)
|
|
166
109
|
return self
|
|
167
|
-
|
|
168
|
-
def _value_to_python_type(self) -> str:
|
|
169
|
-
if self.value == 'T':
|
|
170
|
-
return True
|
|
171
|
-
elif self.value == 'F':
|
|
172
|
-
return False
|
|
173
|
-
else:
|
|
174
|
-
Exception(f'{self} is not valid boolean. That really should not have been possible -- Contact the maintainers of the library')
|
|
175
|
-
|
|
110
|
+
|
|
176
111
|
|
|
177
112
|
class AlphanumericValue(Value):
|
|
178
|
-
@field_validator('value', mode='before')
|
|
179
|
-
@classmethod
|
|
180
|
-
def _from_python_type(cls, v:str):
|
|
181
|
-
return v
|
|
182
113
|
|
|
183
114
|
@model_validator(mode='after')
|
|
184
115
|
def _validate(self):
|
|
@@ -199,20 +130,9 @@ class AlphanumericValue(Value):
|
|
|
199
130
|
highlight_sub=not_allowed_chars
|
|
200
131
|
)
|
|
201
132
|
return self
|
|
202
|
-
|
|
203
|
-
def _value_to_python_type(self) -> str:
|
|
204
|
-
return self.value
|
|
205
|
-
|
|
133
|
+
|
|
206
134
|
|
|
207
135
|
class TextValue(Value):
|
|
208
|
-
@field_validator('value', mode='before')
|
|
209
|
-
@classmethod
|
|
210
|
-
def _from_python_type(cls, v:base36|str):
|
|
211
|
-
if isinstance(v, str):
|
|
212
|
-
logging.info('Got str for text value > converting to base36')
|
|
213
|
-
return to_base36(v).root
|
|
214
|
-
else:
|
|
215
|
-
return v.root
|
|
216
136
|
|
|
217
137
|
@model_validator(mode='after')
|
|
218
138
|
def _validate(self):
|
|
@@ -225,20 +145,9 @@ class TextValue(Value):
|
|
|
225
145
|
highlight_sub=not_allowed_chars
|
|
226
146
|
)
|
|
227
147
|
return self
|
|
228
|
-
|
|
229
|
-
def _value_to_python_type(self) -> str:
|
|
230
|
-
decoded = from_base36(self.value)
|
|
231
|
-
return decoded
|
|
232
|
-
|
|
148
|
+
|
|
233
149
|
|
|
234
150
|
class BinaryValue(Value):
|
|
235
|
-
@field_validator('value', mode='before')
|
|
236
|
-
@classmethod
|
|
237
|
-
def _from_python_type(cls, v:base36|str):
|
|
238
|
-
if isinstance(v, str):
|
|
239
|
-
return v
|
|
240
|
-
else:
|
|
241
|
-
return v.root
|
|
242
151
|
|
|
243
152
|
@model_validator(mode='after')
|
|
244
153
|
def _validate(self):
|
|
@@ -251,17 +160,9 @@ class BinaryValue(Value):
|
|
|
251
160
|
highlight_sub=not_allowed_chars
|
|
252
161
|
)
|
|
253
162
|
return self
|
|
254
|
-
|
|
255
|
-
def _value_to_python_type(self) -> bytes:
|
|
256
|
-
decoded = bytes(from_base36(self))
|
|
257
|
-
return decoded
|
|
258
|
-
|
|
163
|
+
|
|
259
164
|
|
|
260
165
|
class ErrorValue(Value):
|
|
261
|
-
@field_validator('value', mode='before')
|
|
262
|
-
@classmethod
|
|
263
|
-
def _from_python_type(cls, v:str):
|
|
264
|
-
return v
|
|
265
166
|
|
|
266
167
|
@model_validator(mode='after')
|
|
267
168
|
def _validate(self):
|
|
@@ -274,10 +175,6 @@ class ErrorValue(Value):
|
|
|
274
175
|
highlight_sub=not_allowed_chars
|
|
275
176
|
)
|
|
276
177
|
return self
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
def _value_to_python_type(self) -> str:
|
|
280
|
-
return self.value
|
|
281
178
|
|
|
282
179
|
|
|
283
180
|
class TREX_Segment(LabFREED_BaseModel, ABC):
|
|
@@ -305,31 +202,8 @@ class TREX_Segment(LabFREED_BaseModel, ABC):
|
|
|
305
202
|
|
|
306
203
|
|
|
307
204
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def str_to_value_type(s:str, t:str):
|
|
311
|
-
match t:
|
|
312
|
-
case 'T.D':
|
|
313
|
-
v = DateValue(value=s)
|
|
314
|
-
case 'T.B':
|
|
315
|
-
v = BoolValue(value=s)
|
|
316
|
-
case 'T.A':
|
|
317
|
-
v = AlphanumericValue(value=s)
|
|
318
|
-
case 'T.T':
|
|
319
|
-
try:
|
|
320
|
-
value = base36(s)
|
|
321
|
-
except ValueError:
|
|
322
|
-
logging.error('String given as T.T contains characters which base36 should not')
|
|
323
|
-
value = s
|
|
324
|
-
v = TextValue(value=value)
|
|
325
|
-
case 'T.X':
|
|
326
|
-
v = BinaryValue(value=s)
|
|
327
|
-
case 'E' :
|
|
328
|
-
v = ErrorValue(value=s)
|
|
329
|
-
case _ :
|
|
330
|
-
v = NumericValue(value=s)
|
|
331
|
-
return v
|
|
332
|
-
|
|
205
|
+
|
|
206
|
+
|
|
333
207
|
|
|
334
208
|
|
|
335
209
|
|
|
@@ -89,19 +89,9 @@ def _deserialize_value_segment_from_trex_segment_str(trex_segment_str) -> ValueS
|
|
|
89
89
|
case 'T.A':
|
|
90
90
|
out = AlphanumericSegment(key=key, value=value, type=type_)
|
|
91
91
|
case 'T.T':
|
|
92
|
-
|
|
93
|
-
value = base36(value)
|
|
94
|
-
except ValueError:
|
|
95
|
-
logging.error('String given as T.T contains characters which base36 should not')
|
|
96
|
-
value = value
|
|
97
|
-
out = TextSegment(key=key, value=value, type=type_) # prevent repeated conversion from str to base36 and make explict that when parsing we assume the string tpo be base36 already
|
|
92
|
+
out = TextSegment(key=key, value=value, type=type_)
|
|
98
93
|
case 'T.X':
|
|
99
|
-
|
|
100
|
-
value = base36(value)
|
|
101
|
-
except ValueError:
|
|
102
|
-
logging.error('String given as T.X contains characters which base36 should not')
|
|
103
|
-
value = value
|
|
104
|
-
out = BinarySegment(key=key, value=value, type=type_) # prevent repeated conversion from str to base36 and make explict that when parsing we assume the string tpo be base36 already
|
|
94
|
+
out = BinarySegment(key=key, value=value, type=type_)
|
|
105
95
|
case 'E':
|
|
106
96
|
out = ErrorSegment(key=key, value=value, type=type_)
|
|
107
97
|
case _:
|
|
@@ -15,7 +15,7 @@ class DisplayNameExtension(ExtensionBase, LabFREED_BaseModel):
|
|
|
15
15
|
@property
|
|
16
16
|
def data(self)->str:
|
|
17
17
|
# return '/'.join([to_base36(dn) for dn in self.display_name])
|
|
18
|
-
return to_base36(self.display_name)
|
|
18
|
+
return to_base36(self.display_name).root
|
|
19
19
|
|
|
20
20
|
@staticmethod
|
|
21
21
|
def from_extension(ext:ExtensionBase) -> Self:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labfreed-0.2.1 → labfreed-0.2.3}/labfreed/well_known_extensions/default_extension_interpreters.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
|