labfreed 0.0.19__tar.gz → 0.1.0__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.
- {labfreed-0.0.19 → labfreed-0.1.0}/PKG-INFO +69 -15
- {labfreed-0.0.19 → labfreed-0.1.0}/README.md +68 -15
- labfreed-0.1.0/cit_mine.yaml +22 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/examples.py +32 -7
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/IO/parse_pac.py +1 -1
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/PAC_CAT/data_model.py +2 -2
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/PAC_ID/data_model.py +1 -1
- labfreed-0.1.0/labfreed/PAC_ID_Resolver/cit.yaml +92 -0
- labfreed-0.1.0/labfreed/PAC_ID_Resolver/data_types.py +59 -0
- labfreed-0.1.0/labfreed/PAC_ID_Resolver/resolver.py +221 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/TREX/data_model.py +2 -2
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/__init__.py +1 -1
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/validation.py +13 -3
- {labfreed-0.0.19 → labfreed-0.1.0}/main.py +21 -1
- labfreed-0.0.19/labfreed/IO/generate_label.py +0 -81
- {labfreed-0.0.19 → labfreed-0.1.0}/.vscode/launch.json +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/.vscode/settings.json +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/LICENSE +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/DisplayNameExtension/DisplayNameExtension.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/IO/generate_qr.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/PAC_CAT/__init__.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/PAC_ID/__init__.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/PAC_ID/extensions.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/TREX/UneceUnits.json +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/TREX/parse.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/TREX/unece_units.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/utilities/base36.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/utilities/utility_types.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/labfreed/utilities/well_known_keys.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/publish.ps1 +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/publish.sh +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/publish_commands +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/pyproject.toml +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/pytest.ini +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_(de)_serialization_incl_extension/test__parse.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_(de)_serialization_incl_extension/test__serialize.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_PAC_CAT/test_PAC_CAT_parse.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_PAC_CAT/test_PAC_CAT_serialize.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_PAC_ID/test_PAC_ID_serialize.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_PAC_ID/test_pac_id_parse.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_TREX/test_TREX_parse.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/tests/test_TREX/test_TREX_serialize.py +0 -0
- {labfreed-0.0.19 → labfreed-0.1.0}/update_readme.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labfreed
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: Python implementation of LabFREED building blocks
|
|
5
5
|
Author-email: Reto Thürer <thuerer.r@buchi.com>
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -19,7 +19,7 @@ Requires-Dist: typer>=0.15.2
|
|
|
19
19
|
[](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml)
|
|
20
20
|
-->
|
|
21
21
|
|
|
22
|
-
This is a Python implementation of [LabFREED](
|
|
22
|
+
This is a Python implementation of [LabFREED](https://labfreed.wega-it.com) building blocks.
|
|
23
23
|
|
|
24
24
|
## Supported Building Blocks
|
|
25
25
|
- PAC-ID
|
|
@@ -38,11 +38,16 @@ pip install labfreed
|
|
|
38
38
|
## Usage Examples
|
|
39
39
|
> ⚠️ **Note:** These examples are building on each other. Imports and parsing are not repeated in each example.
|
|
40
40
|
<!-- BEGIN EXAMPLES -->
|
|
41
|
+
```python
|
|
42
|
+
# import built ins
|
|
43
|
+
import os
|
|
44
|
+
```
|
|
41
45
|
### Parse a simple PAC-ID
|
|
42
46
|
|
|
43
47
|
```python
|
|
44
48
|
from labfreed.IO.parse_pac import PAC_Parser
|
|
45
49
|
|
|
50
|
+
|
|
46
51
|
# Parse the PAC-ID
|
|
47
52
|
pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
|
|
48
53
|
pac_id = PAC_Parser().parse(pac_str).pac_id
|
|
@@ -60,7 +65,7 @@ Note that the PAC-ID -- while valid -- uses characters which are not recommended
|
|
|
60
65
|
There is a nice function to highlight problems
|
|
61
66
|
|
|
62
67
|
```python
|
|
63
|
-
pac_id.print_validation_messages()
|
|
68
|
+
pac_id.print_validation_messages(target='markdown')
|
|
64
69
|
```
|
|
65
70
|
```text
|
|
66
71
|
>> =======================================
|
|
@@ -68,15 +73,15 @@ pac_id.print_validation_messages()
|
|
|
68
73
|
>> ---------------------------------------
|
|
69
74
|
>>
|
|
70
75
|
>> Recommendation in id segment value bal500
|
|
71
|
-
>> HTTPS://PAC.METTORIUS.COM/-MD
|
|
72
|
-
>> Characters
|
|
76
|
+
>> HTTPS://PAC.METTORIUS.COM/-MD/🔸b🔸🔸a🔸🔸l🔸500/@1234
|
|
77
|
+
>> Characters a b l should not be used.
|
|
73
78
|
>>
|
|
74
79
|
>> Recommendation in id segment value @1234
|
|
75
|
-
>> HTTPS://PAC.METTORIUS.COM/-MD/bal500
|
|
80
|
+
>> HTTPS://PAC.METTORIUS.COM/-MD/bal500/🔸@🔸1234
|
|
76
81
|
>> Characters @ should not be used.
|
|
77
82
|
>>
|
|
78
83
|
>> Warning in Category -MD
|
|
79
|
-
>> HTTPS://PAC.METTORIUS.COM
|
|
84
|
+
>> HTTPS://PAC.METTORIUS.COM/🔸-MD🔸/bal500/@1234
|
|
80
85
|
>> Category key -MD is not a well known key. It is recommended to use well known keys only
|
|
81
86
|
```
|
|
82
87
|
### Save as QR Code
|
|
@@ -144,12 +149,12 @@ print(f'WEIGHT = {v}')
|
|
|
144
149
|
from labfreed.PAC_ID.data_model import PACID, IDSegment
|
|
145
150
|
from labfreed.utilities.well_known_keys import WellKnownKeys
|
|
146
151
|
|
|
147
|
-
pac_id = PACID(issuer='METTORIUS
|
|
152
|
+
pac_id = PACID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
|
|
148
153
|
pac_str = pac_id.serialize()
|
|
149
154
|
print(pac_str)
|
|
150
155
|
```
|
|
151
156
|
```text
|
|
152
|
-
>> HTTPS://PAC.METTORIUS
|
|
157
|
+
>> HTTPS://PAC.METTORIUS.COM/21:1234
|
|
153
158
|
```
|
|
154
159
|
#### Create a TREX
|
|
155
160
|
TREX can conveniently be created from a python dictionary.
|
|
@@ -174,7 +179,7 @@ trex.update(
|
|
|
174
179
|
)
|
|
175
180
|
|
|
176
181
|
# Create a table
|
|
177
|
-
table = DataTable(['DURATION', '
|
|
182
|
+
table = DataTable(['DURATION', 'Date', 'OK', 'COMMENT'])
|
|
178
183
|
table.append([Quantity(value=1, unit=Unit(symbol='h', name='hour')), datetime.now(), True, 'FOO'])
|
|
179
184
|
table.append([ 1.1, datetime.now(), True, 'BAR'])
|
|
180
185
|
table.append([ 1.3, datetime.now(), False, 'BLUBB'])
|
|
@@ -182,11 +187,24 @@ table.append([ 1.3, datetime.no
|
|
|
182
187
|
trex.update({'TABLE': table})
|
|
183
188
|
|
|
184
189
|
# Validation also works the same way for TREX
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
trex.print_validation_messages(target='markdown')
|
|
191
|
+
```
|
|
192
|
+
```text
|
|
193
|
+
>> =======================================
|
|
194
|
+
>> Validation Results
|
|
195
|
+
>> ---------------------------------------
|
|
196
|
+
>>
|
|
197
|
+
>> Error in TREX table column Date
|
|
198
|
+
>> DEMO$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:D🔸a🔸🔸t🔸🔸e🔸$T.D:OK$T.B:COMMENT$T.A::1:20250410T094130.847:T:FOO::1.1:20250410T094130.847:T:BAR::1.3
|
|
199
|
+
>> :20250410T094130.847:F:BLUBB
|
|
200
|
+
>> Column header key contains invalid characters: t,a,e
|
|
201
|
+
```
|
|
202
|
+
```python
|
|
203
|
+
# there is an error. 'Date' uses lower case. Lets fix it
|
|
189
204
|
d = trex.dict()
|
|
205
|
+
d['TABLE'].col_names[1] = 'DATE'
|
|
206
|
+
trex = TREX(name_='DEMO')
|
|
207
|
+
trex.update(d)
|
|
190
208
|
```
|
|
191
209
|
#### Combine PAC-ID and TREX and serialize
|
|
192
210
|
|
|
@@ -198,7 +216,34 @@ pac_str = pac_with_trex.serialize()
|
|
|
198
216
|
print(pac_str)
|
|
199
217
|
```
|
|
200
218
|
```text
|
|
201
|
-
>> HTTPS://PAC.METTORIUS
|
|
219
|
+
>> HTTPS://PAC.METTORIUS.COM/21:1234*DEMO$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:20250410T094130.847:T:FOO::1.1:20250410T094130.847:T:BAR::1.3:20250410T094130.847:F:BLUBB
|
|
220
|
+
```
|
|
221
|
+
## PAC-ID Resolver
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from labfreed.PAC_ID_Resolver.resolver import PAC_ID_Resolver, load_cit
|
|
225
|
+
# Get a CIT
|
|
226
|
+
dir = os.path.dirname(__file__)
|
|
227
|
+
p = os.path.join(dir, 'cit_mine.yaml')
|
|
228
|
+
cit = load_cit(p)
|
|
229
|
+
|
|
230
|
+
# validate the CIT
|
|
231
|
+
cit.is_valid()
|
|
232
|
+
cit.print_validation_messages()
|
|
233
|
+
```
|
|
234
|
+
```text
|
|
235
|
+
>> [Error during execution: name '__file__' is not defined]
|
|
236
|
+
```
|
|
237
|
+
```python
|
|
238
|
+
# resolve a pac id
|
|
239
|
+
service_groups = PAC_ID_Resolver(cits=[cit]).resolve(pac_with_trex)
|
|
240
|
+
for sg in service_groups:
|
|
241
|
+
sg.print()
|
|
242
|
+
|
|
243
|
+
5
|
|
244
|
+
```
|
|
245
|
+
```text
|
|
246
|
+
>> [Error during execution: name 'cit' is not defined]
|
|
202
247
|
```
|
|
203
248
|
<!-- END EXAMPLES -->
|
|
204
249
|
|
|
@@ -206,7 +251,16 @@ print(pac_str)
|
|
|
206
251
|
|
|
207
252
|
## Change Log
|
|
208
253
|
|
|
254
|
+
### v0.1.0
|
|
255
|
+
- DRAFT Support for PAC-ID Resolver
|
|
256
|
+
|
|
257
|
+
### v0.0.20
|
|
258
|
+
- bugfix in TREX table to dict conversion
|
|
259
|
+
- markdown compatible validation printing
|
|
260
|
+
|
|
209
261
|
### v0.0.19
|
|
210
262
|
- supports PAC-ID, PAC-CAT, TREX and DisplayName
|
|
211
263
|
- QR generation
|
|
212
264
|
- ok-ish test coverage
|
|
265
|
+
|
|
266
|
+
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml)
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
|
-
This is a Python implementation of [LabFREED](
|
|
9
|
+
This is a Python implementation of [LabFREED](https://labfreed.wega-it.com) building blocks.
|
|
10
10
|
|
|
11
11
|
## Supported Building Blocks
|
|
12
12
|
- PAC-ID
|
|
@@ -25,11 +25,16 @@ pip install labfreed
|
|
|
25
25
|
## Usage Examples
|
|
26
26
|
> ⚠️ **Note:** These examples are building on each other. Imports and parsing are not repeated in each example.
|
|
27
27
|
<!-- BEGIN EXAMPLES -->
|
|
28
|
+
```python
|
|
29
|
+
# import built ins
|
|
30
|
+
import os
|
|
31
|
+
```
|
|
28
32
|
### Parse a simple PAC-ID
|
|
29
33
|
|
|
30
34
|
```python
|
|
31
35
|
from labfreed.IO.parse_pac import PAC_Parser
|
|
32
36
|
|
|
37
|
+
|
|
33
38
|
# Parse the PAC-ID
|
|
34
39
|
pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
|
|
35
40
|
pac_id = PAC_Parser().parse(pac_str).pac_id
|
|
@@ -47,7 +52,7 @@ Note that the PAC-ID -- while valid -- uses characters which are not recommended
|
|
|
47
52
|
There is a nice function to highlight problems
|
|
48
53
|
|
|
49
54
|
```python
|
|
50
|
-
pac_id.print_validation_messages()
|
|
55
|
+
pac_id.print_validation_messages(target='markdown')
|
|
51
56
|
```
|
|
52
57
|
```text
|
|
53
58
|
>> =======================================
|
|
@@ -55,15 +60,15 @@ pac_id.print_validation_messages()
|
|
|
55
60
|
>> ---------------------------------------
|
|
56
61
|
>>
|
|
57
62
|
>> Recommendation in id segment value bal500
|
|
58
|
-
>> HTTPS://PAC.METTORIUS.COM/-MD
|
|
59
|
-
>> Characters
|
|
63
|
+
>> HTTPS://PAC.METTORIUS.COM/-MD/🔸b🔸🔸a🔸🔸l🔸500/@1234
|
|
64
|
+
>> Characters a b l should not be used.
|
|
60
65
|
>>
|
|
61
66
|
>> Recommendation in id segment value @1234
|
|
62
|
-
>> HTTPS://PAC.METTORIUS.COM/-MD/bal500
|
|
67
|
+
>> HTTPS://PAC.METTORIUS.COM/-MD/bal500/🔸@🔸1234
|
|
63
68
|
>> Characters @ should not be used.
|
|
64
69
|
>>
|
|
65
70
|
>> Warning in Category -MD
|
|
66
|
-
>> HTTPS://PAC.METTORIUS.COM
|
|
71
|
+
>> HTTPS://PAC.METTORIUS.COM/🔸-MD🔸/bal500/@1234
|
|
67
72
|
>> Category key -MD is not a well known key. It is recommended to use well known keys only
|
|
68
73
|
```
|
|
69
74
|
### Save as QR Code
|
|
@@ -131,12 +136,12 @@ print(f'WEIGHT = {v}')
|
|
|
131
136
|
from labfreed.PAC_ID.data_model import PACID, IDSegment
|
|
132
137
|
from labfreed.utilities.well_known_keys import WellKnownKeys
|
|
133
138
|
|
|
134
|
-
pac_id = PACID(issuer='METTORIUS
|
|
139
|
+
pac_id = PACID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
|
|
135
140
|
pac_str = pac_id.serialize()
|
|
136
141
|
print(pac_str)
|
|
137
142
|
```
|
|
138
143
|
```text
|
|
139
|
-
>> HTTPS://PAC.METTORIUS
|
|
144
|
+
>> HTTPS://PAC.METTORIUS.COM/21:1234
|
|
140
145
|
```
|
|
141
146
|
#### Create a TREX
|
|
142
147
|
TREX can conveniently be created from a python dictionary.
|
|
@@ -161,7 +166,7 @@ trex.update(
|
|
|
161
166
|
)
|
|
162
167
|
|
|
163
168
|
# Create a table
|
|
164
|
-
table = DataTable(['DURATION', '
|
|
169
|
+
table = DataTable(['DURATION', 'Date', 'OK', 'COMMENT'])
|
|
165
170
|
table.append([Quantity(value=1, unit=Unit(symbol='h', name='hour')), datetime.now(), True, 'FOO'])
|
|
166
171
|
table.append([ 1.1, datetime.now(), True, 'BAR'])
|
|
167
172
|
table.append([ 1.3, datetime.now(), False, 'BLUBB'])
|
|
@@ -169,11 +174,24 @@ table.append([ 1.3, datetime.no
|
|
|
169
174
|
trex.update({'TABLE': table})
|
|
170
175
|
|
|
171
176
|
# Validation also works the same way for TREX
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
trex.print_validation_messages(target='markdown')
|
|
178
|
+
```
|
|
179
|
+
```text
|
|
180
|
+
>> =======================================
|
|
181
|
+
>> Validation Results
|
|
182
|
+
>> ---------------------------------------
|
|
183
|
+
>>
|
|
184
|
+
>> Error in TREX table column Date
|
|
185
|
+
>> DEMO$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:D🔸a🔸🔸t🔸🔸e🔸$T.D:OK$T.B:COMMENT$T.A::1:20250410T094130.847:T:FOO::1.1:20250410T094130.847:T:BAR::1.3
|
|
186
|
+
>> :20250410T094130.847:F:BLUBB
|
|
187
|
+
>> Column header key contains invalid characters: t,a,e
|
|
188
|
+
```
|
|
189
|
+
```python
|
|
190
|
+
# there is an error. 'Date' uses lower case. Lets fix it
|
|
176
191
|
d = trex.dict()
|
|
192
|
+
d['TABLE'].col_names[1] = 'DATE'
|
|
193
|
+
trex = TREX(name_='DEMO')
|
|
194
|
+
trex.update(d)
|
|
177
195
|
```
|
|
178
196
|
#### Combine PAC-ID and TREX and serialize
|
|
179
197
|
|
|
@@ -185,7 +203,34 @@ pac_str = pac_with_trex.serialize()
|
|
|
185
203
|
print(pac_str)
|
|
186
204
|
```
|
|
187
205
|
```text
|
|
188
|
-
>> HTTPS://PAC.METTORIUS
|
|
206
|
+
>> HTTPS://PAC.METTORIUS.COM/21:1234*DEMO$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:20250410T094130.847:T:FOO::1.1:20250410T094130.847:T:BAR::1.3:20250410T094130.847:F:BLUBB
|
|
207
|
+
```
|
|
208
|
+
## PAC-ID Resolver
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from labfreed.PAC_ID_Resolver.resolver import PAC_ID_Resolver, load_cit
|
|
212
|
+
# Get a CIT
|
|
213
|
+
dir = os.path.dirname(__file__)
|
|
214
|
+
p = os.path.join(dir, 'cit_mine.yaml')
|
|
215
|
+
cit = load_cit(p)
|
|
216
|
+
|
|
217
|
+
# validate the CIT
|
|
218
|
+
cit.is_valid()
|
|
219
|
+
cit.print_validation_messages()
|
|
220
|
+
```
|
|
221
|
+
```text
|
|
222
|
+
>> [Error during execution: name '__file__' is not defined]
|
|
223
|
+
```
|
|
224
|
+
```python
|
|
225
|
+
# resolve a pac id
|
|
226
|
+
service_groups = PAC_ID_Resolver(cits=[cit]).resolve(pac_with_trex)
|
|
227
|
+
for sg in service_groups:
|
|
228
|
+
sg.print()
|
|
229
|
+
|
|
230
|
+
5
|
|
231
|
+
```
|
|
232
|
+
```text
|
|
233
|
+
>> [Error during execution: name 'cit' is not defined]
|
|
189
234
|
```
|
|
190
235
|
<!-- END EXAMPLES -->
|
|
191
236
|
|
|
@@ -193,7 +238,15 @@ print(pac_str)
|
|
|
193
238
|
|
|
194
239
|
## Change Log
|
|
195
240
|
|
|
241
|
+
### v0.1.0
|
|
242
|
+
- DRAFT Support for PAC-ID Resolver
|
|
243
|
+
|
|
244
|
+
### v0.0.20
|
|
245
|
+
- bugfix in TREX table to dict conversion
|
|
246
|
+
- markdown compatible validation printing
|
|
247
|
+
|
|
196
248
|
### v0.0.19
|
|
197
249
|
- supports PAC-ID, PAC-CAT, TREX and DisplayName
|
|
198
250
|
- QR generation
|
|
199
|
-
- ok-ish test coverage
|
|
251
|
+
- ok-ish test coverage
|
|
252
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
origin: MINE
|
|
2
|
+
|
|
3
|
+
cit:
|
|
4
|
+
# Test
|
|
5
|
+
- if:
|
|
6
|
+
entries:
|
|
7
|
+
- service_type: userhandover-generic
|
|
8
|
+
service_name: AAAAAAAAAAAAAAAAAAhhhhhh
|
|
9
|
+
application_intents:
|
|
10
|
+
- foo
|
|
11
|
+
template_url: https://AAAAAAAAAAAA.{$.pac.issuer}.com/{$.pac.identifier[1].value}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# import built ins
|
|
2
|
+
import os
|
|
1
3
|
'''
|
|
2
4
|
### Parse a simple PAC-ID
|
|
3
5
|
'''
|
|
4
6
|
from labfreed.IO.parse_pac import PAC_Parser
|
|
5
7
|
|
|
8
|
+
|
|
6
9
|
# Parse the PAC-ID
|
|
7
10
|
pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
|
|
8
11
|
pac_id = PAC_Parser().parse(pac_str).pac_id
|
|
@@ -17,7 +20,7 @@ print(f'PAC-ID is valid: {is_valid}')
|
|
|
17
20
|
Note that the PAC-ID -- while valid -- uses characters which are not recommended (results in larger QR code).
|
|
18
21
|
There is a nice function to highlight problems
|
|
19
22
|
'''
|
|
20
|
-
pac_id.print_validation_messages()
|
|
23
|
+
pac_id.print_validation_messages(target='markdown')
|
|
21
24
|
|
|
22
25
|
'''
|
|
23
26
|
### Save as QR Code
|
|
@@ -65,7 +68,7 @@ print(f'WEIGHT = {v}')
|
|
|
65
68
|
from labfreed.PAC_ID.data_model import PACID, IDSegment
|
|
66
69
|
from labfreed.utilities.well_known_keys import WellKnownKeys
|
|
67
70
|
|
|
68
|
-
pac_id = PACID(issuer='METTORIUS
|
|
71
|
+
pac_id = PACID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
|
|
69
72
|
pac_str = pac_id.serialize()
|
|
70
73
|
print(pac_str)
|
|
71
74
|
|
|
@@ -93,7 +96,7 @@ trex.update(
|
|
|
93
96
|
)
|
|
94
97
|
|
|
95
98
|
# Create a table
|
|
96
|
-
table = DataTable(['DURATION', '
|
|
99
|
+
table = DataTable(['DURATION', 'Date', 'OK', 'COMMENT'])
|
|
97
100
|
table.append([Quantity(value=1, unit=Unit(symbol='h', name='hour')), datetime.now(), True, 'FOO'])
|
|
98
101
|
table.append([ 1.1, datetime.now(), True, 'BAR'])
|
|
99
102
|
table.append([ 1.3, datetime.now(), False, 'BLUBB'])
|
|
@@ -101,11 +104,13 @@ table.append([ 1.3, datetime.no
|
|
|
101
104
|
trex.update({'TABLE': table})
|
|
102
105
|
|
|
103
106
|
# Validation also works the same way for TREX
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# Side Note: The TREX can be turned back into a dict
|
|
107
|
+
trex.print_validation_messages(target='markdown')
|
|
108
|
+
''''''
|
|
109
|
+
# there is an error. 'Date' uses lower case. Lets fix it
|
|
108
110
|
d = trex.dict()
|
|
111
|
+
d['TABLE'].col_names[1] = 'DATE'
|
|
112
|
+
trex = TREX(name_='DEMO')
|
|
113
|
+
trex.update(d)
|
|
109
114
|
|
|
110
115
|
'''
|
|
111
116
|
#### Combine PAC-ID and TREX and serialize
|
|
@@ -120,3 +125,23 @@ print(pac_str)
|
|
|
120
125
|
|
|
121
126
|
|
|
122
127
|
|
|
128
|
+
'''
|
|
129
|
+
## PAC-ID Resolver
|
|
130
|
+
'''
|
|
131
|
+
from labfreed.PAC_ID_Resolver.resolver import PAC_ID_Resolver, load_cit
|
|
132
|
+
# Get a CIT
|
|
133
|
+
dir = os.path.dirname(__file__)
|
|
134
|
+
p = os.path.join(dir, 'cit_mine.yaml')
|
|
135
|
+
cit = load_cit(p)
|
|
136
|
+
|
|
137
|
+
# validate the CIT
|
|
138
|
+
cit.is_valid()
|
|
139
|
+
cit.print_validation_messages()
|
|
140
|
+
|
|
141
|
+
''''''
|
|
142
|
+
# resolve a pac id
|
|
143
|
+
service_groups = PAC_ID_Resolver(cits=[cit]).resolve(pac_with_trex)
|
|
144
|
+
for sg in service_groups:
|
|
145
|
+
sg.print()
|
|
146
|
+
|
|
147
|
+
5
|
|
@@ -19,7 +19,7 @@ from ..validation import ValidationMessage, LabFREEDValidationError
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class PACID_With_Extensions(BaseModelWithValidationMessages):
|
|
22
|
-
pac_id: PACID
|
|
22
|
+
pac_id: PACID = Field(serialization_alias='pac')
|
|
23
23
|
extensions: list[Extension] = Field(default_factory=list)
|
|
24
24
|
|
|
25
25
|
def __str__(self):
|
|
@@ -14,7 +14,7 @@ class PAC_CAT(PACID):
|
|
|
14
14
|
'''
|
|
15
15
|
Extends a PAC-ID with interpretation of the identifier as categories
|
|
16
16
|
'''
|
|
17
|
-
categories:list[Category] = Field(default_factory=list
|
|
17
|
+
categories:list[Category] = Field(default_factory=list)
|
|
18
18
|
|
|
19
19
|
@property
|
|
20
20
|
def identifier(self) -> list[IDSegment]:
|
|
@@ -175,7 +175,7 @@ class Category(BaseModelWithValidationMessages):
|
|
|
175
175
|
"populate_by_name": True
|
|
176
176
|
}
|
|
177
177
|
key:str
|
|
178
|
-
additional_segments: list[IDSegment] = Field(default_factory=list)
|
|
178
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
179
179
|
|
|
180
180
|
@computed_field
|
|
181
181
|
@property
|
|
@@ -96,7 +96,7 @@ class IDSegment(BaseModelWithValidationMessages):
|
|
|
96
96
|
|
|
97
97
|
class PACID(BaseModelWithValidationMessages):
|
|
98
98
|
issuer:str
|
|
99
|
-
identifier: conlist(IDSegment, min_length=1) = Field(..., default_factory=list
|
|
99
|
+
identifier: conlist(IDSegment, min_length=1) = Field(..., default_factory=list) # type: ignore # exclude=True prevents this from being serialized by Pydantic
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
@model_validator(mode='after')
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
origin: PAC.METTORIUS.COM
|
|
2
|
+
|
|
3
|
+
macros:
|
|
4
|
+
shop: &shop
|
|
5
|
+
service_type: userhandover-generic
|
|
6
|
+
service_name: Shop
|
|
7
|
+
application_intents:
|
|
8
|
+
- showdevicemanual-apinilabs
|
|
9
|
+
template_url: https://mettorius.com/shop/an={$.pac.identifier.categories[1].segments['240'].value}
|
|
10
|
+
|
|
11
|
+
manual: &manual
|
|
12
|
+
service_type: userhandover-generic
|
|
13
|
+
service_name: Manual
|
|
14
|
+
application_intents:
|
|
15
|
+
- showdevicemanual-apinilabs
|
|
16
|
+
template_url: https://mettorius.com/om/an={$..*['240'].value}
|
|
17
|
+
|
|
18
|
+
CoA: &coa
|
|
19
|
+
service_type: userhandover-generic
|
|
20
|
+
service_name: CoA
|
|
21
|
+
application_intents:
|
|
22
|
+
- showCoA-apinilabs
|
|
23
|
+
template_url: https://mettorius.com/downloads/an={$..*['240'].value}
|
|
24
|
+
|
|
25
|
+
dummy: &dummy
|
|
26
|
+
service_type: userhandover-generic
|
|
27
|
+
application_intents:
|
|
28
|
+
- dummy
|
|
29
|
+
template_url: https://mettorius.com/om/an={$..*['240'].value}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
cit:
|
|
33
|
+
# Test
|
|
34
|
+
- if: mettorius.com == $.pac.issuer
|
|
35
|
+
entries:
|
|
36
|
+
- <<: *shop
|
|
37
|
+
- <<: *manual
|
|
38
|
+
|
|
39
|
+
# Instruments
|
|
40
|
+
- if: mettorius.com == $.pac.issuer AND $.pac.id.categories['-MD']
|
|
41
|
+
entries:
|
|
42
|
+
- <<: *shop
|
|
43
|
+
- <<: *manual
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Consumables
|
|
47
|
+
- if: mettorius.com == $.pac.issuer AND $.pac.id.categories['-MC']
|
|
48
|
+
entries:
|
|
49
|
+
- <<: *shop
|
|
50
|
+
- <<: *manual
|
|
51
|
+
- <<: *coa
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Showcase for logic
|
|
56
|
+
##############################
|
|
57
|
+
|
|
58
|
+
# multiline string
|
|
59
|
+
- if: "($.pac.issuer == mettorius.com)
|
|
60
|
+
AND $.pac.id.categories['-MD']"
|
|
61
|
+
entries:
|
|
62
|
+
- <<: *dummy
|
|
63
|
+
service_name: multiline applicable_if
|
|
64
|
+
|
|
65
|
+
# folded string
|
|
66
|
+
- if: |
|
|
67
|
+
($.pac.issuer == mettorius.com)
|
|
68
|
+
AND $.pac.id.categories['-MD']
|
|
69
|
+
entries:
|
|
70
|
+
- <<: *dummy
|
|
71
|
+
service_name: folded applicable_if
|
|
72
|
+
|
|
73
|
+
# multiline string
|
|
74
|
+
- if: |
|
|
75
|
+
NOT NOT ($.pac.issuer == mettorius.com)
|
|
76
|
+
AND $.pac.id.categories['-MD']
|
|
77
|
+
entries:
|
|
78
|
+
- <<: *dummy
|
|
79
|
+
service_name: multiline applicable_if
|
|
80
|
+
|
|
81
|
+
- if: NOT ($.pac.issuer != METTORIUS.COM)
|
|
82
|
+
entries:
|
|
83
|
+
- <<: *dummy
|
|
84
|
+
service_name: _3
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field, field_validator
|
|
2
|
+
from rich import print
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
|
|
5
|
+
from labfreed.validation import BaseModelWithValidationMessages
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CITEntry(BaseModelWithValidationMessages):
|
|
9
|
+
service_name: str
|
|
10
|
+
application_intents:list[str]
|
|
11
|
+
service_type:str
|
|
12
|
+
template_url:str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CITBlock(BaseModelWithValidationMessages):
|
|
16
|
+
applicable_if: str = Field(default='True', alias='if')
|
|
17
|
+
entries: list[CITEntry]
|
|
18
|
+
|
|
19
|
+
@field_validator('applicable_if', mode='before')
|
|
20
|
+
@classmethod
|
|
21
|
+
def convert_if(cls, v):
|
|
22
|
+
return v if v is not None else 'True'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CIT(BaseModelWithValidationMessages):
|
|
26
|
+
origin: str = ''
|
|
27
|
+
macros:dict = Field(default_factory=dict)
|
|
28
|
+
cit: list[CITBlock] = Field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Service(BaseModelWithValidationMessages):
|
|
34
|
+
service_name: str
|
|
35
|
+
application_intents:list[str]
|
|
36
|
+
service_type:str
|
|
37
|
+
url:str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CITEvaluated(BaseModelWithValidationMessages):
|
|
41
|
+
origin: str = ""
|
|
42
|
+
services: list[Service] = Field(default_factory=list)
|
|
43
|
+
|
|
44
|
+
def __str__(self):
|
|
45
|
+
out = [f'CIT (origin {self.origin})']
|
|
46
|
+
for s in self.services:
|
|
47
|
+
out.append(f'{s.service_name}\t\t\t{s.url}')
|
|
48
|
+
return '\n'.join(out)
|
|
49
|
+
|
|
50
|
+
def print(self):
|
|
51
|
+
table = Table(title=f"Services from origin '{self.origin}")
|
|
52
|
+
|
|
53
|
+
table.add_column("Service Name")
|
|
54
|
+
table.add_column("URL")
|
|
55
|
+
|
|
56
|
+
for s in self.services:
|
|
57
|
+
table.add_row(s.service_name, s.url)
|
|
58
|
+
|
|
59
|
+
print(table)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import yaml
|
|
4
|
+
import json
|
|
5
|
+
import jsonpath_ng.ext as jsonpath
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from labfreed.IO.parse_pac import PAC_Parser, PACID_With_Extensions
|
|
9
|
+
from labfreed.PAC_ID_Resolver.data_types import CIT, CITEntry, CITEvaluated, Service
|
|
10
|
+
|
|
11
|
+
from labfreed.PAC_ID_Resolver.non_needed.query_tools import JSONPathTools
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_cit(path):
|
|
15
|
+
with open(path, 'r') as f:
|
|
16
|
+
cit = yaml.safe_load(f)
|
|
17
|
+
cit = CIT.model_validate(cit)
|
|
18
|
+
return cit
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PAC_ID_Resolver():
|
|
22
|
+
def __init__(self, cits:list[CIT]=None):
|
|
23
|
+
if not cits:
|
|
24
|
+
cits = []
|
|
25
|
+
self.cits = cits
|
|
26
|
+
|
|
27
|
+
# load the default cit
|
|
28
|
+
dir = os.path.dirname(__file__)
|
|
29
|
+
fn ='cit.yaml'
|
|
30
|
+
p = os.path.join(dir, fn)
|
|
31
|
+
with open(p, 'r') as f:
|
|
32
|
+
cit = yaml.safe_load(f)
|
|
33
|
+
cit = CIT.model_validate(cit)
|
|
34
|
+
self.cits.append(cit)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def resolve(self, pac_id:PACID_With_Extensions|str):
|
|
38
|
+
if isinstance(pac_id, str):
|
|
39
|
+
pac_id = PAC_Parser().parse(pac_id)
|
|
40
|
+
|
|
41
|
+
pac_id_json = pac_id.model_dump(by_alias=True)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# dir = os.path.dirname(__file__)
|
|
45
|
+
# p = os.path.join(dir, 'pac-id.json')
|
|
46
|
+
# with open(p , 'r') as f:
|
|
47
|
+
# _json = f.read()
|
|
48
|
+
# pac_id_json = json.loads(_json)
|
|
49
|
+
|
|
50
|
+
matches = [self._evaluate_against_cit(pac_id_json, cit) for cit in self.cits]
|
|
51
|
+
return matches
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _evaluate_against_cit(self, pac_id_json, cit:CIT):
|
|
55
|
+
cit_evaluated = CITEvaluated(origin=cit.origin)
|
|
56
|
+
for block in cit.cit:
|
|
57
|
+
_, is_applicable = self._evaluate_applicable_if(pac_id_json, block.applicable_if)
|
|
58
|
+
if not is_applicable:
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
for e in block.entries:
|
|
62
|
+
url = self.eval_url_template(pac_id_json, e.template_url)
|
|
63
|
+
cit_evaluated.services.append(Service(
|
|
64
|
+
service_name=e.service_name,
|
|
65
|
+
application_intents=e.application_intents,
|
|
66
|
+
service_type=e.service_type,
|
|
67
|
+
url = url
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
return cit_evaluated
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _evaluate_applicable_if(self, pac_id_json:str, expression) -> tuple[str, bool]:
|
|
74
|
+
expression = self._apply_convenience_substitutions(expression)
|
|
75
|
+
|
|
76
|
+
tokens = self._tokenize_jsonpath_expression(expression)
|
|
77
|
+
expression_for_eval = self._expression_from_tokens(pac_id_json, tokens)
|
|
78
|
+
applicable = eval(expression_for_eval, {}, {})
|
|
79
|
+
|
|
80
|
+
return expression_for_eval, applicable
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _apply_convenience_substitutions(self, query):
|
|
84
|
+
''' applies a few substitutions, which enable abbreviated syntax.'''
|
|
85
|
+
|
|
86
|
+
# allow access to array elements by key
|
|
87
|
+
q_mod = re.sub(r"\[('.+?')\]", r"[?(@.key == \1)]", query )
|
|
88
|
+
|
|
89
|
+
# allow shorter path
|
|
90
|
+
# substitutions = [
|
|
91
|
+
# (r'(?<=^)id', 'pac.id'),
|
|
92
|
+
# (r'(?<=^)cat', 'pac.id.cat'),
|
|
93
|
+
# (r'(?<=\.)id(?=\.)', 'identifier'),
|
|
94
|
+
# (r'(?<=\.)cat$', 'categories'),
|
|
95
|
+
# (r'(?<=\.)cat(?=\[)', 'categories'),
|
|
96
|
+
# (r'(?<=\.)seg$', 'segments'),
|
|
97
|
+
# (r'(?<=\.)seg(?=\[)', 'segments'),
|
|
98
|
+
# (r'(?<=^)isu', 'pac.isu'),
|
|
99
|
+
# (r'(?<=\.)isu', 'issuer'),
|
|
100
|
+
# (r'(?<=^)ext', 'pac.ext'),
|
|
101
|
+
# (r'(?<=\.)ext(?=$)', 'extensions'),
|
|
102
|
+
# (r'(?<=\.)ext(?=\[)', 'extensions'),
|
|
103
|
+
# ]
|
|
104
|
+
# for sub in substitutions:
|
|
105
|
+
# q_mod = re.sub(sub[0], sub[1], q_mod)
|
|
106
|
+
|
|
107
|
+
return q_mod
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _tokenize_jsonpath_expression(self, expr: str):
|
|
111
|
+
token_pattern = re.compile(
|
|
112
|
+
r"""
|
|
113
|
+
(?P<LPAREN>\() |
|
|
114
|
+
(?P<RPAREN>\)) |
|
|
115
|
+
(?P<LOGIC>\bAND\b|\bOR\b|\bNOT\b) |
|
|
116
|
+
(?P<OPERATOR>==|!=|<=|>=|<|>) |
|
|
117
|
+
(?P<JSONPATH>
|
|
118
|
+
\$ # starts with $
|
|
119
|
+
(?:
|
|
120
|
+
[^\s\[\]()]+ # path segments, dots, etc.
|
|
121
|
+
|
|
|
122
|
+
\[ # open bracket
|
|
123
|
+
(?: # non-capturing group
|
|
124
|
+
[^\[\]]+ # anything but brackets
|
|
125
|
+
|
|
|
126
|
+
\[[^\[\]]*\] # nested brackets (1 level)
|
|
127
|
+
)*
|
|
128
|
+
\]
|
|
129
|
+
)+ # one or more bracket/segment blocks
|
|
130
|
+
) |
|
|
131
|
+
(?P<LITERAL>
|
|
132
|
+
[A-Za-z_][\w\.\-]*[A-Za-z0-9] # domain-like literals
|
|
133
|
+
)
|
|
134
|
+
""",
|
|
135
|
+
re.VERBOSE
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
tokens = []
|
|
139
|
+
pos = 0
|
|
140
|
+
while pos < len(expr):
|
|
141
|
+
match = token_pattern.match(expr, pos)
|
|
142
|
+
if match:
|
|
143
|
+
group_type = match.lastgroup
|
|
144
|
+
value = match.group().strip()
|
|
145
|
+
tokens.append((value, group_type))
|
|
146
|
+
pos = match.end()
|
|
147
|
+
elif expr[pos].isspace():
|
|
148
|
+
pos += 1 # skip whitespace
|
|
149
|
+
else:
|
|
150
|
+
raise SyntaxError(f"Unexpected character at position {pos}: {expr[pos]}")
|
|
151
|
+
|
|
152
|
+
return tokens
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _expression_from_tokens(self, pac_id_json:str, tokens: tuple[str, str]):
|
|
156
|
+
out = []
|
|
157
|
+
for i in range(len(tokens)):
|
|
158
|
+
prev_token = tokens[i-1] if i > 0 else (None, None)
|
|
159
|
+
curr_token = tokens[i]
|
|
160
|
+
next_token = tokens[i+1] if i < len(tokens)-1 else (None, None)
|
|
161
|
+
if curr_token[1] == 'JSONPATH':
|
|
162
|
+
res = self._evaluate_jsonpath(pac_id_json, curr_token[0])
|
|
163
|
+
|
|
164
|
+
if prev_token[1] == 'OPERATOR' or next_token[1] == 'OPERATOR':
|
|
165
|
+
# if token is part of comparison return the value of the node
|
|
166
|
+
if len(res) == 0:
|
|
167
|
+
out.append('""')
|
|
168
|
+
else:
|
|
169
|
+
out.append(f'"{res[0].upper()}"')
|
|
170
|
+
else:
|
|
171
|
+
# if token is not part of comparison evaluate to boolean
|
|
172
|
+
if len(res) == 0:
|
|
173
|
+
out.append(False)
|
|
174
|
+
else:
|
|
175
|
+
out.append(True)
|
|
176
|
+
|
|
177
|
+
elif curr_token[1] == 'LOGIC':
|
|
178
|
+
out.append(curr_token[0].lower())
|
|
179
|
+
|
|
180
|
+
elif curr_token[1] == 'LITERAL':
|
|
181
|
+
t = curr_token[0]
|
|
182
|
+
if t[0] != '"':
|
|
183
|
+
t = '"' + t
|
|
184
|
+
if t[-1] != '"':
|
|
185
|
+
t = t + '"'
|
|
186
|
+
out.append(t.upper())
|
|
187
|
+
else:
|
|
188
|
+
out.append(curr_token[0])
|
|
189
|
+
|
|
190
|
+
s = ' '.join([str(e) for e in out])
|
|
191
|
+
return s
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def eval_url_template(self, pac_id_json, url_template):
|
|
197
|
+
url = url_template
|
|
198
|
+
placeholders = re.findall(r'\{(.+?)\}', url_template)
|
|
199
|
+
for placeholder in placeholders:
|
|
200
|
+
expanded_placeholder = self._apply_convenience_substitutions(placeholder)
|
|
201
|
+
res = self._evaluate_jsonpath(pac_id_json, expanded_placeholder) or ['']
|
|
202
|
+
url = url.replace(f'{{{placeholder}}}', str(res[0]))
|
|
203
|
+
# res = self.substitute_jsonpath_expressions(expanded_placeholder, Patterns.jsonpath.value, as_bool=False)
|
|
204
|
+
# url = url.replace(f'{{{placeholder}}}', res)
|
|
205
|
+
return url
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _evaluate_jsonpath(self, pac_id_json, jp_query):
|
|
211
|
+
jsonpath_expr = jsonpath.parse(jp_query)
|
|
212
|
+
matches = [match.value for match in jsonpath_expr.find(pac_id_json)]
|
|
213
|
+
return matches
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
if __name__ == '__main__':
|
|
220
|
+
r = PAC_ID_Resolver()
|
|
221
|
+
r.resolve()
|
|
@@ -436,7 +436,7 @@ class TREX_Table(TREX_Segment):
|
|
|
436
436
|
self.add_validation_message(
|
|
437
437
|
source=f"Table {self.key}",
|
|
438
438
|
type="Error",
|
|
439
|
-
msg=f"Size mismatch: Table header contains {self.
|
|
439
|
+
msg=f"Size mismatch: Table header contains {self.column_names} keys, while most rows have {most_common_len}",
|
|
440
440
|
highlight_pattern = self.key
|
|
441
441
|
)
|
|
442
442
|
expected_row_len = most_common_len
|
|
@@ -527,7 +527,7 @@ class TREX_Table(TREX_Segment):
|
|
|
527
527
|
r.append(Quantity(value=e.value, unit=unit))
|
|
528
528
|
else:
|
|
529
529
|
r.append(e.value_to_python_type())
|
|
530
|
-
|
|
530
|
+
table.append(r)
|
|
531
531
|
return table
|
|
532
532
|
|
|
533
533
|
|
|
@@ -15,7 +15,7 @@ class ValidationMessage(BaseModel):
|
|
|
15
15
|
problem_msg:str
|
|
16
16
|
recommendation_msg: str = ""
|
|
17
17
|
highlight:str = "" #this can be used to highlight problematic parts
|
|
18
|
-
highlight_sub:list[str] = Field(default_factory=list
|
|
18
|
+
highlight_sub:list[str] = Field(default_factory=list)
|
|
19
19
|
|
|
20
20
|
@property
|
|
21
21
|
def emphazised_highlight(self):
|
|
@@ -113,7 +113,7 @@ class BaseModelWithValidationMessages(BaseModel):
|
|
|
113
113
|
return filter_warnings(self.get_nested_validation_messages())
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def print_validation_messages(self, str_to_highlight_in=None):
|
|
116
|
+
def print_validation_messages(self, str_to_highlight_in=None, target='console'):
|
|
117
117
|
if not str_to_highlight_in:
|
|
118
118
|
str_to_highlight_in = str(self)
|
|
119
119
|
msgs = self.get_nested_validation_messages()
|
|
@@ -124,6 +124,10 @@ class BaseModelWithValidationMessages(BaseModel):
|
|
|
124
124
|
]
|
|
125
125
|
)
|
|
126
126
|
)
|
|
127
|
+
|
|
128
|
+
if not msgs:
|
|
129
|
+
print('All clear!')
|
|
130
|
+
return
|
|
127
131
|
|
|
128
132
|
for m in msgs:
|
|
129
133
|
if m.type.casefold() == "error":
|
|
@@ -133,7 +137,13 @@ class BaseModelWithValidationMessages(BaseModel):
|
|
|
133
137
|
|
|
134
138
|
text = Text.from_markup(f'\n [bold {color}]{m.type} [/bold {color}] in \t {m.source}' )
|
|
135
139
|
print(text)
|
|
136
|
-
|
|
140
|
+
match target:
|
|
141
|
+
case 'markdown':
|
|
142
|
+
formatted_highlight = m.emphazised_highlight.replace('emph', f'🔸').replace('[/', '').replace('[', '').replace(']', '')
|
|
143
|
+
case 'console':
|
|
144
|
+
formatted_highlight = m.emphazised_highlight.replace('emph', f'bold {color}')
|
|
145
|
+
case 'html':
|
|
146
|
+
formatted_highlight = m.emphazised_highlight.replace('emph', f'b').replace('[', '<').replace(']', '>')
|
|
137
147
|
fmtd = str_to_highlight_in.replace(m.highlight, formatted_highlight)
|
|
138
148
|
fmtd = Text.from_markup(fmtd)
|
|
139
149
|
print(fmtd)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
from datetime import datetime
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
from labfreed.IO.parse_pac import PAC_Parser
|
|
7
|
+
from labfreed.PAC_ID_Resolver.resolver import PAC_ID_Resolver, load_cit
|
|
3
8
|
from labfreed.TREX.data_model import TREX
|
|
4
9
|
from labfreed.TREX.parse import TREX_Parser
|
|
5
10
|
from labfreed.DisplayNameExtension.DisplayNameExtension import DisplayNames
|
|
@@ -9,10 +14,25 @@ from labfreed.PAC_CAT.data_model import *
|
|
|
9
14
|
from labfreed.utilities.base36 import base36
|
|
10
15
|
from labfreed.utilities.utility_types import DataTable, Quantity, Unit
|
|
11
16
|
|
|
12
|
-
from labfreed.
|
|
17
|
+
from labfreed.IO.generate_qr import save_qr_with_markers
|
|
13
18
|
|
|
14
19
|
if __name__ == "__main__":
|
|
15
20
|
|
|
21
|
+
pac = PAC_Parser().parse('HTTPS://PAC.METTORIUS.COM/-DR/MADFGAI/-MD/ABCG/ACG/PC:1234')
|
|
22
|
+
|
|
23
|
+
dir = os.path.dirname(__file__)
|
|
24
|
+
p = os.path.join(dir, 'cit_internal.yaml')
|
|
25
|
+
cit = load_cit(p)
|
|
26
|
+
|
|
27
|
+
services = PAC_ID_Resolver(cits=[cit]).resolve(pac)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
cat = PAC_CAT.from_pac_id(pac.pac_id)
|
|
31
|
+
cat.identifier
|
|
32
|
+
jsn = cat.model_dump_json(indent=2)
|
|
33
|
+
print(jsn)
|
|
34
|
+
|
|
35
|
+
|
|
16
36
|
|
|
17
37
|
save_qr_with_markers('HTTPS://PAC.METTORIUS.COM/-MD/ABCG/ACG')
|
|
18
38
|
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from .generate_qr import generate_qr_with_markers_svg
|
|
4
|
-
|
|
5
|
-
from labfreed.PAC_ID.data_model import PACID
|
|
6
|
-
from labfreed.DisplayNameExtension.DisplayNameExtension import DisplayNames
|
|
7
|
-
from labfreed.IO.parse_pac import PACID_With_Extensions
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def generate_label_200_100(pac_url, pac:PACID_With_Extensions):
|
|
14
|
-
title, infos = get_label_fields(pac)
|
|
15
|
-
pac_svg = generate_qr_with_markers_svg(pac_url, height=60, width=100, border=0)
|
|
16
|
-
label = _generate_label(pac_svg, 200, 100, title, infos)
|
|
17
|
-
return label
|
|
18
|
-
|
|
19
|
-
def generate_label_credit_card_size(pac_url, pac:PACID_With_Extensions):
|
|
20
|
-
title, infos = get_label_fields(pac)
|
|
21
|
-
pac_svg = generate_qr_with_markers_svg(pac_url, height=100, width=200, border=0)
|
|
22
|
-
label = _generate_label(pac_svg, 240, 150, title, infos)
|
|
23
|
-
return label
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _generate_label(qr_svg, width, height, title=None, infos=[]):
|
|
27
|
-
if not qr_svg:
|
|
28
|
-
raise ValueError("no valid qr given")
|
|
29
|
-
env = Environment(
|
|
30
|
-
loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")),
|
|
31
|
-
autoescape=select_autoescape()
|
|
32
|
-
)
|
|
33
|
-
template = env.get_template("pac_label.jinja.svg")
|
|
34
|
-
svg = template.render(width=width, height= height, pac_qr=qr_svg, title=title, info1=infos[0], info2=infos[1], info3=infos[2])
|
|
35
|
-
|
|
36
|
-
return svg
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def get_label_fields(pac:PACID_With_Extensions) -> tuple[str, list[tuple[str, str]]]:
|
|
41
|
-
'''
|
|
42
|
-
returns a list of exactly length 3. Containing either info tuples (key value) or None
|
|
43
|
-
'''
|
|
44
|
-
if dn_extension := next((e for e in pac.extensions if isinstance(e, DisplayNames)), None): #find extension of type DisplayName
|
|
45
|
-
title = dn_extension.display_names[0]
|
|
46
|
-
else:
|
|
47
|
-
title = ""
|
|
48
|
-
|
|
49
|
-
infos = []
|
|
50
|
-
cat = pac.pac_id.identifier.categories[0]
|
|
51
|
-
for s in cat.segments:
|
|
52
|
-
lbl = pretty_print_segment_label(cat.key, s.key)
|
|
53
|
-
infos.append((lbl, s.value))
|
|
54
|
-
|
|
55
|
-
while len(infos) < 3:
|
|
56
|
-
infos.append(None)
|
|
57
|
-
|
|
58
|
-
if len(infos) > 3:
|
|
59
|
-
infos = infos[0:3]
|
|
60
|
-
|
|
61
|
-
return title,infos
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def pretty_print_segment_label(category:str, segment_key:str):
|
|
65
|
-
if not segment_key:
|
|
66
|
-
return 'no key'
|
|
67
|
-
|
|
68
|
-
cat = CAT_from_category_key(category)
|
|
69
|
-
if cat:
|
|
70
|
-
alias_to_field = {v.alias: k for k, v in cat.model_fields.items() if v.alias}
|
|
71
|
-
segment_label = alias_to_field.get(segment_key, segment_key)
|
|
72
|
-
segment_label = segment_label.replace('_', ' ').title()
|
|
73
|
-
|
|
74
|
-
segment_label = segment_label.replace('Id', 'ID')
|
|
75
|
-
|
|
76
|
-
else:
|
|
77
|
-
segment_label = segment_key
|
|
78
|
-
|
|
79
|
-
return segment_label
|
|
80
|
-
|
|
81
|
-
|
|
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.0.19 → labfreed-0.1.0}/tests/test_(de)_serialization_incl_extension/test__parse.py
RENAMED
|
File without changes
|
{labfreed-0.0.19 → labfreed-0.1.0}/tests/test_(de)_serialization_incl_extension/test__serialize.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
|