labfreed 0.0.4__py3-none-any.whl → 0.2.0b0__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.
- labfreed/PAC_CAT/__init__.py +16 -0
- labfreed/PAC_CAT/category_base.py +51 -0
- labfreed/PAC_CAT/pac_cat.py +159 -0
- labfreed/PAC_CAT/predefined_categories.py +190 -0
- labfreed/PAC_ID/__init__.py +19 -0
- labfreed/PAC_ID/extension.py +48 -0
- labfreed/PAC_ID/id_segment.py +90 -0
- labfreed/PAC_ID/pac_id.py +140 -0
- labfreed/PAC_ID/url_parser.py +154 -0
- labfreed/PAC_ID/url_serializer.py +80 -0
- labfreed/PAC_ID_Resolver/__init__.py +2 -0
- labfreed/PAC_ID_Resolver/cit_v1.py +149 -0
- labfreed/PAC_ID_Resolver/cit_v2.py +303 -0
- labfreed/PAC_ID_Resolver/resolver.py +81 -0
- labfreed/PAC_ID_Resolver/services.py +80 -0
- labfreed/__init__.py +4 -1
- labfreed/labfreed_infrastructure.py +276 -0
- labfreed/qr/__init__.py +1 -0
- labfreed/qr/generate_qr.py +422 -0
- labfreed/trex/__init__.py +16 -0
- labfreed/trex/python_convenience/__init__.py +3 -0
- labfreed/trex/python_convenience/data_table.py +45 -0
- labfreed/trex/python_convenience/pyTREX.py +242 -0
- labfreed/trex/python_convenience/quantity.py +46 -0
- labfreed/trex/table_segment.py +227 -0
- labfreed/trex/trex.py +69 -0
- labfreed/trex/trex_base_models.py +336 -0
- labfreed/trex/value_segments.py +111 -0
- labfreed/{DisplayNameExtension → utilities}/base36.py +29 -13
- labfreed/well_known_extensions/__init__.py +5 -0
- labfreed/well_known_extensions/default_extension_interpreters.py +7 -0
- labfreed/well_known_extensions/display_name_extension.py +40 -0
- labfreed/well_known_extensions/trex_extension.py +31 -0
- labfreed/well_known_keys/gs1/__init__.py +6 -0
- labfreed/well_known_keys/gs1/gs1.py +4 -0
- labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py +57 -0
- labfreed/well_known_keys/labfreed/well_known_keys.py +16 -0
- labfreed/well_known_keys/unece/UneceUnits.json +33730 -0
- labfreed/well_known_keys/unece/__init__.py +4 -0
- labfreed/well_known_keys/unece/unece_units.py +68 -0
- labfreed-0.2.0b0.dist-info/METADATA +329 -0
- labfreed-0.2.0b0.dist-info/RECORD +44 -0
- {labfreed-0.0.4.dist-info → labfreed-0.2.0b0.dist-info}/WHEEL +1 -1
- labfreed/DisplayNameExtension/DisplayNameExtension.py +0 -34
- labfreed/PAC_CAT/data_model.py +0 -109
- labfreed/PAC_ID/data_model.py +0 -114
- labfreed/PAC_ID/parse.py +0 -133
- labfreed/PAC_ID/serialize.py +0 -57
- labfreed/TREXExtension/data_model.py +0 -239
- labfreed/TREXExtension/parse.py +0 -46
- labfreed/TREXExtension/uncertainty.py +0 -32
- labfreed/TREXExtension/unit_utilities.py +0 -134
- labfreed-0.0.4.dist-info/METADATA +0 -15
- labfreed-0.0.4.dist-info/RECORD +0 -17
- {labfreed-0.0.4.dist-info → labfreed-0.2.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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,329 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: labfreed
|
|
3
|
+
Version: 0.2.0b0
|
|
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: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=2.2.4
|
|
19
|
+
Requires-Dist: pydantic>=2.11.3
|
|
20
|
+
Requires-Dist: segno>=1.6.6
|
|
21
|
+
Requires-Dist: typer>=0.15.2
|
|
22
|
+
Requires-Dist: PyYAML>=6.0.2
|
|
23
|
+
Requires-Dist: jsonpath-ng>=1.7.0
|
|
24
|
+
Requires-Dist: pytest>=8.3.5 ; extra == "dev"
|
|
25
|
+
Requires-Dist: pdoc>=15.0.1 ; extra == "dev"
|
|
26
|
+
Requires-Dist: flit>=3.12.0 ; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.11.5 ; extra == "dev"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
|
|
30
|
+
# LabFREED for Python
|
|
31
|
+
|
|
32
|
+
[](LICENSE) [](https://pypi.org/project/labfreed/) 
|
|
33
|
+
|
|
34
|
+
<!--
|
|
35
|
+
[](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml)
|
|
36
|
+
-->
|
|
37
|
+
|
|
38
|
+
This is a Python implementation of [LabFREED](https://labfreed.wega-it.com) building blocks.
|
|
39
|
+
|
|
40
|
+
## Supported Building Blocks
|
|
41
|
+
- PAC-ID
|
|
42
|
+
- Parsing
|
|
43
|
+
- Serialization
|
|
44
|
+
- PAC-CAT
|
|
45
|
+
- Interpretation of PAC-ID as categories
|
|
46
|
+
- T-REX
|
|
47
|
+
- Parsing
|
|
48
|
+
- Serialization
|
|
49
|
+
- Display Extension
|
|
50
|
+
- base36 <> str conversions
|
|
51
|
+
- PAC-ID Resolver
|
|
52
|
+
- support for CIT v1
|
|
53
|
+
- draft support for CIT v1 (improved version)
|
|
54
|
+
- combined use of multiple cit in any combination of version
|
|
55
|
+
- Generation of QR codes (PAC-ID with extensions)
|
|
56
|
+
|
|
57
|
+
- Validation (with Errors Recommendations)
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
You can install LabFREED from [PyPI](https://pypi.org/project/labfreed/) using pip:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install labfreed
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Usage Examples
|
|
68
|
+
> ⚠️ **Note:** These examples are building on each other. Imports and parsing are not repeated in each example.
|
|
69
|
+
<!-- BEGIN EXAMPLES -->
|
|
70
|
+
```python
|
|
71
|
+
# import built ins
|
|
72
|
+
import os
|
|
73
|
+
|
|
74
|
+
target = 'console'
|
|
75
|
+
```
|
|
76
|
+
### Parse a simple PAC-ID
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Parse the PAC-ID
|
|
80
|
+
from labfreed.labfreed_infrastructure import LabFREED_ValidationError # noqa: E402
|
|
81
|
+
from labfreed import PAC_ID, LabFREED_ValidationError # noqa: E402, F811
|
|
82
|
+
|
|
83
|
+
pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
|
|
84
|
+
try:
|
|
85
|
+
pac = PAC_ID.from_url(pac_str)
|
|
86
|
+
except LabFREED_ValidationError:
|
|
87
|
+
pass
|
|
88
|
+
# Check validity of this PAC-ID
|
|
89
|
+
is_valid = pac.is_valid
|
|
90
|
+
print(f'PAC-ID is valid: {is_valid}')
|
|
91
|
+
```
|
|
92
|
+
```text
|
|
93
|
+
>> PAC-ID is valid: True
|
|
94
|
+
```
|
|
95
|
+
### Show recommendations:
|
|
96
|
+
Note that the PAC-ID -- while valid -- uses characters which are not recommended (results in larger QR code).
|
|
97
|
+
There is a nice function to highlight problems
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
pac.print_validation_messages(target=target)
|
|
101
|
+
```
|
|
102
|
+
```text
|
|
103
|
+
>> Validation Results
|
|
104
|
+
>> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
105
|
+
>> │ RECOMMENDATION in id segment value bal500 │
|
|
106
|
+
>> │ Characters 'b','a','l' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
|
|
107
|
+
>> │ │
|
|
108
|
+
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
|
|
109
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
110
|
+
>> │ RECOMMENDATION in id segment value @1234 │
|
|
111
|
+
>> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
|
|
112
|
+
>> │ │
|
|
113
|
+
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
|
|
114
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
115
|
+
>> │ RECOMMENDATION in id segment value bal500 │
|
|
116
|
+
>> │ Characters 'b','a','l' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
|
|
117
|
+
>> │ │
|
|
118
|
+
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
|
|
119
|
+
>> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
120
|
+
>> │ RECOMMENDATION in id segment value @1234 │
|
|
121
|
+
>> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
|
|
122
|
+
>> │ │
|
|
123
|
+
>> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:@1234 │
|
|
124
|
+
>> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
125
|
+
```
|
|
126
|
+
### Save as QR Code
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from labfreed.qr import save_qr_with_markers # noqa: E402
|
|
130
|
+
|
|
131
|
+
save_qr_with_markers(pac_str, fmt='png')
|
|
132
|
+
```
|
|
133
|
+
```text
|
|
134
|
+
>> Large QR: Provided URL is not alphanumeric!
|
|
135
|
+
>> Size: 29
|
|
136
|
+
>> Version: 3
|
|
137
|
+
>> Error Level: M
|
|
138
|
+
```
|
|
139
|
+
### PAC-CAT
|
|
140
|
+
PAC-CAT defines a (optional) way how the identifier is structured.
|
|
141
|
+
PAC_ID.from_url() automatically converts to PAC-CAT if possible.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from labfreed.pac_cat import PAC_CAT # noqa: E402
|
|
145
|
+
pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
|
|
146
|
+
pac = PAC_ID.from_url(pac_str)
|
|
147
|
+
if isinstance(pac, PAC_CAT):
|
|
148
|
+
categories = pac.categories
|
|
149
|
+
pac.print_categories()
|
|
150
|
+
```
|
|
151
|
+
```text
|
|
152
|
+
>> Categories in
|
|
153
|
+
>> HTTPS://PAC.METTORIUS.COM/-MD/240:
|
|
154
|
+
>> bal500/21:@1234
|
|
155
|
+
>> ┌────────────────────┬───────────┐
|
|
156
|
+
>> │ Main Category │ │
|
|
157
|
+
>> │ key () │ -DR │
|
|
158
|
+
>> │ id (21) │ XQ908756 │
|
|
159
|
+
>> ├────────────────────┼───────────┤
|
|
160
|
+
>> │ Category │ │
|
|
161
|
+
>> │ key () │ -MD │
|
|
162
|
+
>> │ model_number (240) │ bal500 │
|
|
163
|
+
>> │ serial_number (21) │ @1234 │
|
|
164
|
+
>> └────────────────────┴───────────┘
|
|
165
|
+
```
|
|
166
|
+
### Parse a PAC-ID with extensions
|
|
167
|
+
PAC-ID can have extensions. Here we parse a PAC-ID with attached display names and summary.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/BAL500/1234*N$N/WM633OV3E5DGJW2BEG0PDM1EA7*SUM$TREX/WEIGHT$GRM:67.89'
|
|
171
|
+
pac = PAC_ID.from_url(pac_str)
|
|
172
|
+
```
|
|
173
|
+
#### Display Name
|
|
174
|
+
Note that the Extension is automatically converted to a DisplayNameExtension
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
display_name = pac.get_extension('N') # display name has name 'N'
|
|
178
|
+
print(display_name)
|
|
179
|
+
```
|
|
180
|
+
```text
|
|
181
|
+
>> Display name: My Balance ❤️
|
|
182
|
+
```
|
|
183
|
+
#### TREX
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
trexes = pac.get_extension_of_type('TREX')
|
|
187
|
+
trex_extension = trexes[0] # there could be multiple trexes. In this example there is only one, though
|
|
188
|
+
trex = trex_extension.trex
|
|
189
|
+
v = trex.get_segment('WEIGHT')
|
|
190
|
+
print(f'WEIGHT = {v.value}')
|
|
191
|
+
```
|
|
192
|
+
```text
|
|
193
|
+
>> WEIGHT = 67.89
|
|
194
|
+
```
|
|
195
|
+
### Create a PAC-ID with Extensions
|
|
196
|
+
|
|
197
|
+
#### Create PAC-ID
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from labfreed.pac_id import PAC_ID, IDSegment # noqa: E402
|
|
201
|
+
from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys # noqa: E402
|
|
202
|
+
|
|
203
|
+
pac = PAC_ID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
|
|
204
|
+
pac_str = pac.to_url()
|
|
205
|
+
print(pac_str)
|
|
206
|
+
```
|
|
207
|
+
```text
|
|
208
|
+
>> HTTPS://PAC.METTORIUS.COM/21:1234
|
|
209
|
+
```
|
|
210
|
+
#### Create a TREX
|
|
211
|
+
TREX can conveniently be created from a python dictionary.
|
|
212
|
+
Note that utility types for Quantity (number with unit) and table are needed
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from datetime import datetime # noqa: E402
|
|
216
|
+
from labfreed.trex.python_convenience.pyTREX import pyTREX # noqa: E402
|
|
217
|
+
from labfreed.trex.python_convenience.data_table import DataTable # noqa: E402
|
|
218
|
+
from labfreed.trex.python_convenience.quantity import Quantity # noqa: E402
|
|
219
|
+
|
|
220
|
+
# Value segments of different type
|
|
221
|
+
segments = {
|
|
222
|
+
'STOP': datetime(year=2024,month=5,day=5,hour=13,minute=6),
|
|
223
|
+
'TEMP': Quantity(value=10.15, unit= 'K'),
|
|
224
|
+
'OK':False,
|
|
225
|
+
'COMMENT': 'FOO',
|
|
226
|
+
'COMMENT2':'£'
|
|
227
|
+
}
|
|
228
|
+
mydata = pyTREX(segments)
|
|
229
|
+
|
|
230
|
+
# Create a table
|
|
231
|
+
table = DataTable(col_names=['DURATION', 'Date', 'OK', 'COMMENT'])
|
|
232
|
+
table.append([Quantity(value=1, unit= 'hour'), datetime.now(), True, 'FOO'])
|
|
233
|
+
table.append([ 1.1, datetime.now(), True, 'BAR'])
|
|
234
|
+
table.append([ 1.3, datetime.now(), False, 'BLUBB'])
|
|
235
|
+
#add the table to the pytrex
|
|
236
|
+
mydata.update({'TABLE': table})
|
|
237
|
+
|
|
238
|
+
# Create TREX
|
|
239
|
+
trex = mydata.to_trex()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# Validation also works the same way for TREX
|
|
243
|
+
trex.print_validation_messages(target=target)
|
|
244
|
+
```
|
|
245
|
+
```text
|
|
246
|
+
>> Validation Results
|
|
247
|
+
>> ┌────────────────────────────────────────────────────────────┐
|
|
248
|
+
>> │ ERROR in TREX table column Date │
|
|
249
|
+
>> │ Column header key contains invalid characters: 'a','t','e' │
|
|
250
|
+
>> │ │
|
|
251
|
+
>> │ STOP$T.D:20240505T1306 │
|
|
252
|
+
>> │ +TEMP$KEL:10.15 │
|
|
253
|
+
>> │ +OK$T.B:F │
|
|
254
|
+
>> │ +COMMENT$T.A:FOO │
|
|
255
|
+
>> │ +COMMENT2$T.T:QMDTNXIKU │
|
|
256
|
+
>> │ +TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A:: │
|
|
257
|
+
>> │ 1:20250423T142912.783:T:FOO:: │
|
|
258
|
+
>> │ 1.1:20250423T142912.783:T:BAR:: │
|
|
259
|
+
>> │ 1.3:20250423T142912.783:F:BLUBB │
|
|
260
|
+
>> └────────────────────────────────────────────────────────────┘
|
|
261
|
+
```
|
|
262
|
+
#### Combine PAC-ID and TREX and serialize
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from labfreed.well_known_extensions import TREX_Extension # noqa: E402
|
|
266
|
+
pac.extensions = [TREX_Extension(name='MYTREX', trex=trex)]
|
|
267
|
+
pac_str = pac.to_url()
|
|
268
|
+
print(pac_str)
|
|
269
|
+
```
|
|
270
|
+
```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:QMDTNXIKU+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250423T142912.783:T:FOO::1.1:20250423T142912.783:T:BAR::1.3:20250423T142912.783:F:BLUBB
|
|
272
|
+
```
|
|
273
|
+
## PAC-ID Resolver
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from labfreed.pac_id_resolver import PAC_ID_Resolver, load_cit # noqa: E402
|
|
277
|
+
# Get a CIT
|
|
278
|
+
dir = os.path.join(os.getcwd(), 'examples')
|
|
279
|
+
p = os.path.join(dir, 'cit_mine.yaml')
|
|
280
|
+
cit = load_cit(p)
|
|
281
|
+
|
|
282
|
+
# validate the CIT
|
|
283
|
+
cit.is_valid
|
|
284
|
+
cit.print_validation_messages(target=target)
|
|
285
|
+
```
|
|
286
|
+
```python
|
|
287
|
+
# get a second cit
|
|
288
|
+
p = os.path.join(dir, 'coupling-information-table')
|
|
289
|
+
cit2 = load_cit(p)
|
|
290
|
+
cit2.origin = 'MY_COMPANY'
|
|
291
|
+
```
|
|
292
|
+
```python
|
|
293
|
+
# resolve a pac id
|
|
294
|
+
pac_str = 'HTTPS://PAC.METTORIUS.COM/-MS/X3511/CAS:7732-18-5'
|
|
295
|
+
service_groups = PAC_ID_Resolver(cits=[cit, cit2]).resolve(pac_str)
|
|
296
|
+
for sg in service_groups:
|
|
297
|
+
sg.update_states()
|
|
298
|
+
sg.print()
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
```text
|
|
302
|
+
>> Services from origin 'PERSONAL
|
|
303
|
+
>> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
304
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
305
|
+
>> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
306
|
+
>> │ CAS Search │ https://pubchem.ncbi.nlm.nih.gov/#query=7732-18-5 │ ACTIVE │
|
|
307
|
+
>> └──────────────┴───────────────────────────────────────────────────┴───────────┘
|
|
308
|
+
>> Services from origin 'MY_COMPANY
|
|
309
|
+
>> ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
310
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
311
|
+
>> ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
312
|
+
>> │ Chemical Management │ https://chem-manager.com/METTORIUS.COM/-MS/240:X3511/CAS:7732-18-5 │ INACTIVE │
|
|
313
|
+
>> └─────────────────────┴────────────────────────────────────────────────────────────────────┴───────────┘
|
|
314
|
+
>> Services from origin 'METTORIUS.COM
|
|
315
|
+
>> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
316
|
+
>> ┃ Service Name ┃ URL ┃ Reachable ┃
|
|
317
|
+
>> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
318
|
+
>> │ CoA │ https://mettorius.com/CoA.pdf │ ACTIVE │
|
|
319
|
+
>> │ MSDS │ https://mettorius.com/MSDS.pdf │ ACTIVE │
|
|
320
|
+
>> │ Shop │ https://mettorius.com/shop.html │ ACTIVE │
|
|
321
|
+
>> └──────────────┴─────────────────────────────────┴───────────┘
|
|
322
|
+
```
|
|
323
|
+
<!-- END EXAMPLES -->
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
## Change Log
|
|
328
|
+
[> Change Log](/CHANGELOG.md)
|
|
329
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
labfreed/__init__.py,sha256=2TONirDl7uJbvNSmgaVqTB7M8HFqtagQT4dV9cc1iWg,226
|
|
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.0b0.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
|
|
42
|
+
labfreed-0.2.0b0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
43
|
+
labfreed-0.2.0b0.dist-info/METADATA,sha256=yvB9hDNcYwlADZAAFLLj69mpSnpt5zH78rxxEybZYP4,17208
|
|
44
|
+
labfreed-0.2.0b0.dist-info/RECORD,,
|
|
@@ -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
|
-
|
labfreed/PAC_CAT/data_model.py
DELETED
|
@@ -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
|