labfreed 0.0.4__py3-none-any.whl → 0.2.0__py3-none-any.whl

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 (57) hide show
  1. labfreed/__init__.py +4 -1
  2. labfreed/labfreed_infrastructure.py +276 -0
  3. labfreed/pac_cat/__init__.py +17 -0
  4. labfreed/pac_cat/category_base.py +51 -0
  5. labfreed/pac_cat/pac_cat.py +159 -0
  6. labfreed/pac_cat/predefined_categories.py +190 -0
  7. labfreed/pac_id/__init__.py +19 -0
  8. labfreed/pac_id/extension.py +48 -0
  9. labfreed/pac_id/id_segment.py +90 -0
  10. labfreed/pac_id/pac_id.py +140 -0
  11. labfreed/pac_id/url_parser.py +154 -0
  12. labfreed/pac_id/url_serializer.py +80 -0
  13. labfreed/pac_id_resolver/__init__.py +2 -0
  14. labfreed/pac_id_resolver/cit_v1.py +149 -0
  15. labfreed/pac_id_resolver/cit_v2.py +303 -0
  16. labfreed/pac_id_resolver/resolver.py +81 -0
  17. labfreed/pac_id_resolver/services.py +80 -0
  18. labfreed/qr/__init__.py +1 -0
  19. labfreed/qr/generate_qr.py +422 -0
  20. labfreed/trex/__init__.py +16 -0
  21. labfreed/trex/python_convenience/__init__.py +3 -0
  22. labfreed/trex/python_convenience/data_table.py +45 -0
  23. labfreed/trex/python_convenience/pyTREX.py +242 -0
  24. labfreed/trex/python_convenience/quantity.py +46 -0
  25. labfreed/trex/table_segment.py +227 -0
  26. labfreed/trex/trex.py +69 -0
  27. labfreed/trex/trex_base_models.py +336 -0
  28. labfreed/trex/value_segments.py +111 -0
  29. labfreed/{DisplayNameExtension → utilities}/base36.py +29 -13
  30. labfreed/well_known_extensions/__init__.py +5 -0
  31. labfreed/well_known_extensions/default_extension_interpreters.py +7 -0
  32. labfreed/well_known_extensions/display_name_extension.py +40 -0
  33. labfreed/well_known_extensions/trex_extension.py +31 -0
  34. labfreed/well_known_keys/gs1/__init__.py +6 -0
  35. labfreed/well_known_keys/gs1/gs1.py +4 -0
  36. labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py +57 -0
  37. labfreed/well_known_keys/labfreed/well_known_keys.py +16 -0
  38. labfreed/well_known_keys/unece/UneceUnits.json +33730 -0
  39. labfreed/well_known_keys/unece/__init__.py +4 -0
  40. labfreed/well_known_keys/unece/unece_units.py +68 -0
  41. labfreed-0.2.0.dist-info/METADATA +357 -0
  42. labfreed-0.2.0.dist-info/RECORD +44 -0
  43. {labfreed-0.0.4.dist-info → labfreed-0.2.0.dist-info}/WHEEL +1 -1
  44. labfreed/DisplayNameExtension/DisplayNameExtension.py +0 -34
  45. labfreed/PAC_CAT/__init__.py +0 -1
  46. labfreed/PAC_CAT/data_model.py +0 -109
  47. labfreed/PAC_ID/__init__.py +0 -0
  48. labfreed/PAC_ID/data_model.py +0 -114
  49. labfreed/PAC_ID/parse.py +0 -133
  50. labfreed/PAC_ID/serialize.py +0 -57
  51. labfreed/TREXExtension/data_model.py +0 -239
  52. labfreed/TREXExtension/parse.py +0 -46
  53. labfreed/TREXExtension/uncertainty.py +0 -32
  54. labfreed/TREXExtension/unit_utilities.py +0 -134
  55. labfreed-0.0.4.dist-info/METADATA +0 -15
  56. labfreed-0.0.4.dist-info/RECORD +0 -17
  57. {labfreed-0.0.4.dist-info → labfreed-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,4 @@
1
+ '''
2
+ Data from
3
+ https://github.com/quadient/unece-units/blob/main/python/src/unece_excel_parser/parsedUneceUnits.json
4
+ '''
@@ -0,0 +1,68 @@
1
+ from functools import cache
2
+ import json
3
+ from pathlib import Path
4
+
5
+
6
+
7
+
8
+ @cache
9
+ def unece_units() -> list[dict]:
10
+ p = Path(__file__).parent / 'UneceUnits.json'
11
+ with open(p) as f:
12
+ l = json.load(f) # noqa: E741
13
+ return l
14
+
15
+ @cache
16
+ def unece_unit_codes():
17
+ codes= [u.get('commonCode') for u in unece_units() if u.get('state') == 'ACTIVE']
18
+ return codes
19
+
20
+
21
+ def unece_unit(unit_code):
22
+ unit = [u for u in unece_units() if u['commonCode'] == unit_code]
23
+ if len(unit) == 0:
24
+ return None
25
+ else:
26
+ return unit[0]
27
+
28
+ def unit_symbol(unit:dict) ->str:
29
+ return unit.get('symbol')
30
+
31
+ def unit_name(unit:dict) ->str:
32
+ return unit.get('name')
33
+
34
+
35
+
36
+ # def check_compatibility_unece_quantities():
37
+ # unece = unece_units()
38
+ # print(f'Number of units in file: {len(unece)}')
39
+
40
+ # failed = list()
41
+ # success = list()
42
+ # for u in unece:
43
+ # if u.get('state') == 'ACTIVE':
44
+ # try:
45
+ # if not u.get('symbol'):
46
+ # assert False
47
+ # u.get('name')
48
+ # validate_unit(u.get('symbol'))
49
+ # success.append(u)
50
+ # except AssertionError:
51
+ # failed.append(u)
52
+ # else:
53
+ # pass
54
+
55
+
56
+
57
+ # print('[blue] FAILED [/blue]')
58
+ # for u in failed:
59
+ # print(f'{u.get('commonCode')}: {u.get('name')}')
60
+
61
+ # print('[yellow] SUCCESSFUL [/yellow]')
62
+ # for u in success:
63
+ # print(u)
64
+
65
+ # print(f'{len(failed)} / {len(unece)} failed to convert')
66
+
67
+
68
+
@@ -0,0 +1,357 @@
1
+ Metadata-Version: 2.4
2
+ Name: labfreed
3
+ Version: 0.2.0
4
+ Summary: Python implementation of LabFREED building blocks
5
+ Author-email: Reto Thürer <thuerer.r@buchi.com>
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Classifier: Topic :: Utilities
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Science/Research
18
+ License-File: LICENSE
19
+ Requires-Dist: numpy>=2.2.4
20
+ Requires-Dist: pydantic>=2.11.3
21
+ Requires-Dist: segno>=1.6.6
22
+ Requires-Dist: typer>=0.15.2
23
+ Requires-Dist: PyYAML>=6.0.2
24
+ Requires-Dist: jsonpath-ng>=1.7.0
25
+ Requires-Dist: requests>=2.32.3
26
+ Requires-Dist: pytest>=8.3.5 ; extra == "dev"
27
+ Requires-Dist: pdoc>=15.0.1 ; extra == "dev"
28
+ Requires-Dist: flit>=3.12.0 ; extra == "dev"
29
+ Requires-Dist: ruff>=0.11.5 ; extra == "dev"
30
+ Provides-Extra: dev
31
+
32
+ # LabFREED for Python
33
+
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![PyPI](https://img.shields.io/pypi/v/labfreed.svg)](https://pypi.org/project/labfreed/) ![Python Version](https://img.shields.io/pypi/pyversions/labfreed)
35
+
36
+ <!--
37
+ [![Tests](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml/badge.svg)](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml)
38
+ -->
39
+
40
+ This is a Python implementation of [LabFREED](https://labfreed.wega-it.com) building blocks.
41
+
42
+ ## Supported Building Blocks
43
+ - PAC-ID
44
+ - Parsing
45
+ - Serialization
46
+ - PAC-CAT
47
+ - Interpretation of PAC-ID as categories
48
+ - T-REX
49
+ - Parsing
50
+ - Serialization
51
+ - Display Extension
52
+ - base36 <> str conversions
53
+ - PAC-ID Resolver
54
+ - support for CIT v1
55
+ - draft support for CIT v1 (improved version)
56
+ - combined use of multiple cit in any combination of version
57
+ - Generation of QR codes (PAC-ID with extensions)
58
+
59
+ - Validation (with Errors Recommendations)
60
+
61
+ ## Installation
62
+ You can install LabFREED from [PyPI](https://pypi.org/project/labfreed/) using pip:
63
+
64
+ ```bash
65
+ pip install labfreed
66
+ ```
67
+
68
+
69
+ ## Usage Examples
70
+ > ⚠️ **Note:** These examples are building on each other. Imports and parsing are not repeated in each example.
71
+ <!-- BEGIN EXAMPLES -->
72
+ ```python
73
+ # import built ins
74
+ import os
75
+
76
+ target = 'console'
77
+ ```
78
+ ### Parse a simple PAC-ID
79
+
80
+ ```python
81
+ # Parse the PAC-ID
82
+ from labfreed.labfreed_infrastructure import LabFREED_ValidationError # noqa: E402
83
+ from labfreed import PAC_ID, LabFREED_ValidationError # noqa: E402, F811
84
+
85
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
86
+ try:
87
+ pac = PAC_ID.from_url(pac_str)
88
+ except LabFREED_ValidationError:
89
+ pass
90
+ # Check validity of this PAC-ID
91
+ is_valid = pac.is_valid
92
+ print(f'PAC-ID is valid: {is_valid}')
93
+ ```
94
+ ```text
95
+ >> PAC-ID is valid: True
96
+ ```
97
+ ### Show recommendations:
98
+ Note that the PAC-ID -- while valid -- uses characters which are not recommended (results in larger QR code).
99
+ There is a nice function to highlight problems
100
+
101
+ ```python
102
+ pac.print_validation_messages(target=target)
103
+ ```
104
+ ```text
105
+ >> Validation Results
106
+ >> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
107
+ >> │ RECOMMENDATION in id segment value bal500 │
108
+ >> │ Characters 'l','a','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
109
+ >> │ │
110
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
111
+ >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
112
+ >> │ RECOMMENDATION in id segment value @1234 │
113
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
114
+ >> │ │
115
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
116
+ >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
117
+ >> │ RECOMMENDATION in id segment value bal500 │
118
+ >> │ Characters 'l','a','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
119
+ >> │ │
120
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
121
+ >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
122
+ >> │ RECOMMENDATION in id segment value @1234 │
123
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
124
+ >> │ │
125
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
126
+ >> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
127
+ ```
128
+ ### Save as QR Code
129
+
130
+ ```python
131
+ from labfreed.qr import save_qr_with_markers # noqa: E402
132
+
133
+ save_qr_with_markers(pac_str, fmt='png')
134
+ ```
135
+ ```text
136
+ >> Large QR: Provided URL is not alphanumeric!
137
+ >> Size: 29
138
+ >> Version: 3
139
+ >> Error Level: M
140
+ ```
141
+ ### PAC-CAT
142
+ PAC-CAT defines a (optional) way how the identifier is structured.
143
+ PAC_ID.from_url() automatically converts to PAC-CAT if possible.
144
+
145
+ ```python
146
+ from labfreed.pac_cat import PAC_CAT # noqa: E402
147
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
148
+ pac = PAC_ID.from_url(pac_str)
149
+ if isinstance(pac, PAC_CAT):
150
+ categories = pac.categories
151
+ pac.print_categories()
152
+ ```
153
+ ```text
154
+ >> Categories in
155
+ >> HTTPS://PAC.METTORIUS.COM/-MD/240:
156
+ >> bal500/21:@1234
157
+ >> ┌────────────────────┬───────────┐
158
+ >> │ Main Category │ │
159
+ >> │ key () │ -DR │
160
+ >> │ id (21) │ XQ908756 │
161
+ >> ├────────────────────┼───────────┤
162
+ >> │ Category │ │
163
+ >> │ key () │ -MD │
164
+ >> │ model_number (240) │ bal500 │
165
+ >> │ serial_number (21) │ @1234 │
166
+ >> └────────────────────┴───────────┘
167
+ ```
168
+ ### Parse a PAC-ID with extensions
169
+ PAC-ID can have extensions. Here we parse a PAC-ID with attached display names and summary.
170
+
171
+ ```python
172
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/BAL500/1234*N$N/WM633OV3E5DGJW2BEG0PDM1EA7*SUM$TREX/WEIGHT$GRM:67.89'
173
+ pac = PAC_ID.from_url(pac_str)
174
+ ```
175
+ #### Display Name
176
+ Note that the Extension is automatically converted to a DisplayNameExtension
177
+
178
+ ```python
179
+ display_name = pac.get_extension('N') # display name has name 'N'
180
+ print(display_name)
181
+ ```
182
+ ```text
183
+ >> Display name: My Balance ❤️
184
+ ```
185
+ #### TREX
186
+
187
+ ```python
188
+ trexes = pac.get_extension_of_type('TREX')
189
+ trex_extension = trexes[0] # there could be multiple trexes. In this example there is only one, though
190
+ trex = trex_extension.trex
191
+ v = trex.get_segment('WEIGHT')
192
+ print(f'WEIGHT = {v.value}')
193
+ ```
194
+ ```text
195
+ >> WEIGHT = 67.89
196
+ ```
197
+ ### Create a PAC-ID with Extensions
198
+
199
+ #### Create PAC-ID
200
+
201
+ ```python
202
+ from labfreed.pac_id import PAC_ID, IDSegment # noqa: E402
203
+ from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys # noqa: E402
204
+
205
+ pac = PAC_ID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
206
+ pac_str = pac.to_url()
207
+ print(pac_str)
208
+ ```
209
+ ```text
210
+ >> HTTPS://PAC.METTORIUS.COM/21:1234
211
+ ```
212
+ #### Create a TREX
213
+ TREX can conveniently be created from a python dictionary.
214
+ Note that utility types for Quantity (number with unit) and table are needed
215
+
216
+ ```python
217
+ from datetime import datetime # noqa: E402
218
+ from labfreed.trex.python_convenience.pyTREX import pyTREX # noqa: E402
219
+ from labfreed.trex.python_convenience.data_table import DataTable # noqa: E402
220
+ from labfreed.trex.python_convenience.quantity import Quantity # noqa: E402
221
+
222
+ # Value segments of different type
223
+ segments = {
224
+ 'STOP': datetime(year=2024,month=5,day=5,hour=13,minute=6),
225
+ 'TEMP': Quantity(value=10.15, unit= 'K'),
226
+ 'OK':False,
227
+ 'COMMENT': 'FOO',
228
+ 'COMMENT2':'£'
229
+ }
230
+ mydata = pyTREX(segments)
231
+
232
+ # Create a table
233
+ table = DataTable(col_names=['DURATION', 'Date', 'OK', 'COMMENT'])
234
+ table.append([Quantity(value=1, unit= 'hour'), datetime.now(), True, 'FOO'])
235
+ table.append([ 1.1, datetime.now(), True, 'BAR'])
236
+ table.append([ 1.3, datetime.now(), False, 'BLUBB'])
237
+ #add the table to the pytrex
238
+ mydata.update({'TABLE': table})
239
+
240
+ # Create TREX
241
+ trex = mydata.to_trex()
242
+
243
+
244
+ # Validation also works the same way for TREX
245
+ trex.print_validation_messages(target=target)
246
+ ```
247
+ ```text
248
+ >> Validation Results
249
+ >> ┌────────────────────────────────────────────────────────────┐
250
+ >> │ ERROR in TREX table column Date │
251
+ >> │ Column header key contains invalid characters: 'e','t','a' │
252
+ >> │ │
253
+ >> │ STOP$T.D:20240505T1306 │
254
+ >> │ +TEMP$KEL:10.15 │
255
+ >> │ +OK$T.B:F │
256
+ >> │ +COMMENT$T.A:FOO │
257
+ >> │ +COMMENT2$T.T:QMDTNXIKU │
258
+ >> │ +TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A:: │
259
+ >> │ 1:20250423T170126.335:T:FOO:: │
260
+ >> │ 1.1:20250423T170126.335:T:BAR:: │
261
+ >> │ 1.3:20250423T170126.335:F:BLUBB │
262
+ >> └────────────────────────────────────────────────────────────┘
263
+ ```
264
+ #### Combine PAC-ID and TREX and serialize
265
+
266
+ ```python
267
+ from labfreed.well_known_extensions import TREX_Extension # noqa: E402
268
+ pac.extensions = [TREX_Extension(name='MYTREX', trex=trex)]
269
+ pac_str = pac.to_url()
270
+ print(pac_str)
271
+ ```
272
+ ```text
273
+ >> 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:QMDTNXIKU+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250423T170126.335:T:FOO::1.1:20250423T170126.335:T:BAR::1.3:20250423T170126.335:F:BLUBB
274
+ ```
275
+ ## PAC-ID Resolver
276
+
277
+ ```python
278
+ from labfreed.pac_id_resolver import PAC_ID_Resolver, load_cit # noqa: E402
279
+ # Get a CIT
280
+ dir = os.path.join(os.getcwd(), 'examples')
281
+ p = os.path.join(dir, 'cit_mine.yaml')
282
+ cit = load_cit(p)
283
+
284
+ # validate the CIT
285
+ cit.is_valid
286
+ cit.print_validation_messages(target=target)
287
+ ```
288
+ ```python
289
+ # get a second cit
290
+ p = os.path.join(dir, 'coupling-information-table')
291
+ cit2 = load_cit(p)
292
+ cit2.origin = 'MY_COMPANY'
293
+ ```
294
+ ```python
295
+ # resolve a pac id
296
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MS/X3511/CAS:7732-18-5'
297
+ service_groups = PAC_ID_Resolver(cits=[cit, cit2]).resolve(pac_str)
298
+ for sg in service_groups:
299
+ sg.update_states()
300
+ sg.print()
301
+
302
+ ```
303
+ ```text
304
+ >> Services from origin 'PERSONAL
305
+ >> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
306
+ >> ┃ Service Name ┃ URL ┃ Reachable ┃
307
+ >> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
308
+ >> │ CAS Search │ https://pubchem.ncbi.nlm.nih.gov/#query=7732-18-5 │ ACTIVE │
309
+ >> └──────────────┴───────────────────────────────────────────────────┴───────────┘
310
+ >> Services from origin 'MY_COMPANY
311
+ >> ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
312
+ >> ┃ Service Name ┃ URL ┃ Reachable ┃
313
+ >> ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
314
+ >> │ Chemical Management │ https://chem-manager.com/METTORIUS.COM/-MS/240:X3511/CAS:7732-18-5 │ INACTIVE │
315
+ >> └─────────────────────┴────────────────────────────────────────────────────────────────────┴───────────┘
316
+ >> Services from origin 'METTORIUS.COM
317
+ >> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
318
+ >> ┃ Service Name ┃ URL ┃ Reachable ┃
319
+ >> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
320
+ >> │ CoA │ https://mettorius.com/CoA.pdf │ ACTIVE │
321
+ >> │ MSDS │ https://mettorius.com/MSDS.pdf │ ACTIVE │
322
+ >> │ Shop │ https://mettorius.com/shop.html │ ACTIVE │
323
+ >> └──────────────┴─────────────────────────────────┴───────────┘
324
+ ```
325
+ <!-- END EXAMPLES -->
326
+
327
+
328
+
329
+ <!-- BEGIN CHANGELOG -->
330
+ ## Change Log
331
+ ### v0.2.0b2
332
+ - improvements in api consistency and ease of use
333
+ - restructured code for better separation of concerns
334
+ - support for coupling information table v1
335
+
336
+ ### v0.1.1
337
+ - minor internal improvements and bugfixes
338
+
339
+ ### v0.1.0
340
+ - DRAFT Support for PAC-ID Resolver
341
+
342
+ ### v0.0.20
343
+ - bugfix in TREX table to dict conversion
344
+ - markdown compatible validation printing
345
+
346
+ ### v0.0.19
347
+ - supports PAC-ID, PAC-CAT, TREX and DisplayName
348
+ - QR generation
349
+ - ok-ish test coverage
350
+
351
+ # Attributions
352
+ The following tools were used:
353
+ - [pdoc](https://pdoc.dev/) was a great help with generating documentation
354
+ - [Pydantic](https://docs.pydantic.dev/latest/)
355
+ - json with UNECE units from (https://github.com/quadient/unece-units/blob/main/python/src/unece_excel_parser/parsedUneceUnits.json)
356
+ - json with GS1 codes from (https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld)
357
+ <!-- END CHANGELOG -->
@@ -0,0 +1,44 @@
1
+ labfreed/__init__.py,sha256=V-o3C9f89puFrWadrkK_h4XuoAyQqDIzJzt1lRHFEZM,224
2
+ labfreed/labfreed_infrastructure.py,sha256=V-5sLhqKkfckKim5VxlB_D1qIzk-Ztxfx4VIlwaR6Jc,10850
3
+ labfreed/pac_cat/__init__.py,sha256=rJ2dFTN8aErTvGf4xwcNZ04xrbTieLAE2v5C2bmgPOA,507
4
+ labfreed/pac_cat/category_base.py,sha256=gx3c5xLrwJw5EXQtV70m8cV0E1scO4Kan02f8p7xLDo,1801
5
+ labfreed/pac_cat/pac_cat.py,sha256=UxWyPsuZsekq3ZmHSQLBdB1tocvVlxz_FOQXxHg_dlU,5800
6
+ labfreed/pac_cat/predefined_categories.py,sha256=BEf7rxN5IcKVhuxMNhdfQ_1xnkax5l8Z1pJMRIROKpw,8510
7
+ labfreed/pac_id/__init__.py,sha256=NGMbzkwQ4txKeT5pxdIZordwHO8J3_q84jzPanjKoHg,675
8
+ labfreed/pac_id/extension.py,sha256=uIs_9aasJ_n7ua067wR7XvtL05H-JZP4f_HtW4qnQDw,1114
9
+ labfreed/pac_id/id_segment.py,sha256=r5JU1SJuRXhZJJxy5T3xjrb598wIDTLpivSJhIUAzjQ,4526
10
+ labfreed/pac_id/pac_id.py,sha256=IWXYlKFjQKB_9U5bINWC5_Lb5pcVbuleocvGs79A28w,5300
11
+ labfreed/pac_id/url_parser.py,sha256=016Gd-V2OORDN2toAZEPmnXobrWNVVXM_zICBrQtqGY,5863
12
+ labfreed/pac_id/url_serializer.py,sha256=3D5pwcAP4ZrCQ22BRtxIwqWrFtZuY9913hCLPJNeyPI,2845
13
+ labfreed/pac_id_resolver/__init__.py,sha256=RNBlrDOSR42gmSNH9wJVhK_xwEX45cvTKVgWW2bjh7Q,113
14
+ labfreed/pac_id_resolver/cit_v1.py,sha256=9dwbQLaRwGq_SDxuuuyDFtpUfx9mZrgGSM0Ed9kFbHE,5800
15
+ labfreed/pac_id_resolver/cit_v2.py,sha256=J7KsYUKSz4k7lqSHmLnouRY-etEbwmcj35O5SqVRm-o,11594
16
+ labfreed/pac_id_resolver/resolver.py,sha256=x0XR0aWQVLptjtEwft8VPjggJEd8leTypQPUdzSyBaI,2160
17
+ labfreed/pac_id_resolver/services.py,sha256=TPoH6YlSwa0hmawHpOiMwIpBAinhoRhMSoexop0YscI,2462
18
+ labfreed/qr/__init__.py,sha256=fdKwP6W2Js--yMbBUdn-g_2uq2VqPpfQJeDLHsMDO-Y,61
19
+ labfreed/qr/generate_qr.py,sha256=mSt-U872O3ReHB_UdS-MzYu0wRgdlKcAOEfTxg5CLRk,16616
20
+ labfreed/trex/__init__.py,sha256=r0MYrGk_XxsqSKo9c2i9jRXApTDeTZD8QRXcRpkOVXY,428
21
+ labfreed/trex/table_segment.py,sha256=nfUPbzzqFdkkOCZ73Qsb32iE8Ev0eMBBK5FhAxk51D8,8707
22
+ labfreed/trex/trex.py,sha256=WDoPvuhiilLtRSIkntCmDGkFBnD6oRZg0E6hhoV-I2g,2400
23
+ labfreed/trex/trex_base_models.py,sha256=OLMRyCUNLIwFVpUataGC4PbS3pW6LM2dApjBYVYeHFQ,11625
24
+ labfreed/trex/value_segments.py,sha256=X-IGUj4oyLAPbmbFfSdm8-RI1G2JiMEUN3lUT4bc4pU,4462
25
+ labfreed/trex/python_convenience/__init__.py,sha256=dyAQG7t-uYN6VfGXbWIq2bHxGcGI63l7FS2-VPYs2RQ,137
26
+ labfreed/trex/python_convenience/data_table.py,sha256=aUQWvsWZ_zAYc2s1iU4Bvl9b-SB5MSY42z69twLxWJ0,1730
27
+ labfreed/trex/python_convenience/pyTREX.py,sha256=qiOJ6PGAfsbnfpQBI_G2_C-3DaKqTfIW5xdZReQVxI8,9824
28
+ labfreed/trex/python_convenience/quantity.py,sha256=d3w-ThY4Cp7n3fde8pct-X5kHc7vbVe1xsJQKROnLks,1470
29
+ labfreed/utilities/base36.py,sha256=_yX8aQ1OwrK5tnJU1NUEzQSFGr9xAVnNvPObpNzCPYs,2895
30
+ labfreed/well_known_extensions/__init__.py,sha256=CjZTjx8Cn8763Hhnv_--Wj1LcFpFs2cyQwWrrzOS4xM,246
31
+ labfreed/well_known_extensions/default_extension_interpreters.py,sha256=3-BkJrAyBa99NN5Q2QPAm59CcWmPket-rvLzgltp8KY,201
32
+ labfreed/well_known_extensions/display_name_extension.py,sha256=xDw6ue54b6BPy1PA-ccCvrbmVLTW4I9Za7tlEWEeLlo,1461
33
+ labfreed/well_known_extensions/trex_extension.py,sha256=tffklaambkFPExcIDRAG9GJ7CHXeuFAagl6FuwS-2kI,929
34
+ labfreed/well_known_keys/gs1/__init__.py,sha256=LOFycgqS6OuV8t22TmtHy-ZI2iuXc3jJfVFwRFVDM3I,103
35
+ labfreed/well_known_keys/gs1/gs1.py,sha256=LIyy-W89m9L0gVxOu-lpBotsHN6CHvmdE3Vu2VwxUQA,79
36
+ labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py,sha256=D8hE6vm-GedS7W2uC52oYrvAKa4zM-7X11JeT_k23oM,1252
37
+ labfreed/well_known_keys/labfreed/well_known_keys.py,sha256=nqk66kHdSwJTJfMKlP-xQbBglS8F_NoWsGkfOVITFN0,331
38
+ labfreed/well_known_keys/unece/UneceUnits.json,sha256=kwfQSp_nTuWbADfBBgqTWrvPl6XtM5SedEVLbMJrM7M,898953
39
+ labfreed/well_known_keys/unece/__init__.py,sha256=MSP9lmjg9_D9iqG9Yq2_ajYfQSNS9wIT7FXA1c--59M,122
40
+ labfreed/well_known_keys/unece/unece_units.py,sha256=gNDQk6KGl-nGMf9Ycq_fQ8P2xxKITgLkcQWPd4H49gI,1630
41
+ labfreed-0.2.0.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
42
+ labfreed-0.2.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
43
+ labfreed-0.2.0.dist-info/METADATA,sha256=zNlqDvB-zMBuHUFHyK7F-_mIFqCPnDBHA1GCmmb2qVs,18162
44
+ labfreed-0.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.11.0
2
+ Generator: flit 3.12.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,34 +0,0 @@
1
- import logging
2
- from pydantic import BaseModel
3
- from ..PAC_ID.data_model import Extension
4
- from .base36 import from_base36, to_base36
5
-
6
-
7
- class DisplayNames(Extension, BaseModel):
8
- display_names: list[str]
9
-
10
- @property
11
- def name(self)->str:
12
- return 'N'
13
-
14
- @property
15
- def type(self)->str:
16
- return 'N'
17
-
18
- @property
19
- def data(self)->str:
20
- return '/'.join([to_base36(dn) for dn in self.display_names])
21
-
22
- @staticmethod
23
- def from_spec_fields(name, type, data):
24
- if name != 'N':
25
- logging.warning(f'Name {name} was given, but this extension should only be used with name "N". Will ignore input')
26
-
27
- if type != 'N':
28
- logging.warning(f'Type {name} was given, but this extension should only be used with type "N". Will try to parse data as display names')
29
-
30
- display_names = [from_base36(b36) for b36 in data.split('/')]
31
-
32
- return DisplayNames(display_names=display_names)
33
-
34
-
@@ -1 +0,0 @@
1
-
@@ -1,109 +0,0 @@
1
- from abc import ABC
2
- from pydantic import Field
3
- from pydantic import BaseModel
4
-
5
- from ..PAC_ID.data_model import IDSegment, Category
6
-
7
- class CATBase(BaseModel, ABC):
8
- category_key:str
9
- additional_segments: list[IDSegment] = Field(default_factory=list)
10
-
11
- class Config:
12
- populate_by_name = True # this will allow field names, as well as aliases in validation
13
-
14
- def to_identifier_category(self, use_short_notation=False):
15
- '''Creates a Category with the correct segments.
16
- Segments are in order of the Pydantic model fields.
17
- Segment keys are omitted as long as the recommendation is followed.
18
- Additional segments are added at the end'''
19
- segments = []
20
- can_omit_keys = use_short_notation # keeps track of whether keys can still be omitted. That is the case when the segment recommendation is followed
21
- for field_name, field_info in self.model_fields.items():
22
- if field_name in ['category_key', 'additional_segments']:
23
- continue
24
- if value := getattr(self, field_name):
25
- if can_omit_keys:
26
- key = None
27
- else:
28
- key = field_info.alias
29
- segments.append(IDSegment(key= key, value= value) )
30
- else:
31
- can_omit_keys = False
32
- if self.additional_segments:
33
- segments.extend(self.additional_segments)
34
- return Category(key=self.category_key,
35
- segments=segments)
36
-
37
-
38
-
39
-
40
- class Material_Device(CATBase):
41
- category_key: str = Field(default='-MD', frozen=True)
42
- model_number: str = Field( alias='240', min_length=1)
43
- serial_number: str = Field( alias='21', min_length=1)
44
-
45
- class Material_Substance(CATBase):
46
- category_key: str = Field(default='-MS', frozen=True)
47
- product_number:str = Field( alias='240', min_length=1)
48
- batch_number:str|None = Field(default=None, alias='10')
49
- container_size:str|None = Field(default=None, alias='20')
50
- container_number:str|None = Field(default=None, alias='21')
51
- aliquot:str|None = Field(default=None, alias='250')
52
-
53
- class Material_Consumable(CATBase):
54
- category_key: str = Field(default='-MC', frozen=True)
55
- product_number:str = Field( alias='240', min_length=1)
56
- batch_number:str|None = Field(default=None, alias='10')
57
- packing_size:str|None = Field(default=None, alias='20')
58
- serial_number:str|None = Field(default=None, alias='21')
59
- aliquot:str|None = Field(default=None, alias='250')
60
-
61
- class Material_Misc(Material_Consumable):
62
- category_key: str = Field(default='-MM', frozen=True)
63
-
64
-
65
-
66
- class Data_Result(CATBase):
67
- category_key: str = Field(default='-DR', frozen=True)
68
- id:str = Field( alias='240', min_length=1)
69
-
70
- class Data_Method(CATBase):
71
- category_key: str = Field(default='-DM', frozen=True)
72
- id:str = Field( alias='240', min_length=1)
73
-
74
- class Data_Calibration(CATBase):
75
- category_key: str = Field(default='-DC', frozen=True)
76
- id:str = Field( alias='240', min_length=1)
77
-
78
- class Data_Progress(CATBase):
79
- category_key: str = Field(default='-DP', frozen=True)
80
- id:str = Field( alias='240', min_length=1)
81
-
82
- class Data_Static(CATBase):
83
- category_key: str = Field(default='-DS', frozen=True)
84
- id:str = Field( alias='240', min_length=1)
85
-
86
-
87
-
88
-
89
- mapping = {
90
- '-MD': Material_Device,
91
- '-MS': Material_Substance,
92
- '-MC': Material_Consumable,
93
- '-MM': Material_Misc,
94
- '-DM': Data_Method,
95
- '-DR': Data_Result,
96
- '-DC': Data_Calibration,
97
- '-DP': Data_Progress,
98
- '-DS': Data_Static
99
- }
100
-
101
- def CAT_from_category(category:Category) -> CATBase|None:
102
- raise NotImplementedError()
103
-
104
- def CAT_from_category_key(category_key) -> CATBase|None:
105
- return mapping.get(category_key)
106
-
107
-
108
- if __name__ == "__main__":
109
- pass
File without changes