labfreed 0.2.6a6__py3-none-any.whl → 0.2.8__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.
- labfreed/__init__.py +11 -11
- labfreed/labfreed_infrastructure.py +258 -258
- labfreed/pac_cat/__init__.py +19 -19
- labfreed/pac_cat/category_base.py +51 -51
- labfreed/pac_cat/pac_cat.py +150 -150
- labfreed/pac_cat/predefined_categories.py +200 -200
- labfreed/pac_id/__init__.py +19 -19
- labfreed/pac_id/extension.py +48 -48
- labfreed/pac_id/id_segment.py +89 -89
- labfreed/pac_id/pac_id.py +140 -140
- labfreed/pac_id/url_parser.py +155 -155
- labfreed/pac_id/url_serializer.py +84 -80
- labfreed/pac_id_resolver/__init__.py +2 -2
- labfreed/pac_id_resolver/cit_common.py +81 -81
- labfreed/pac_id_resolver/cit_v1.py +244 -247
- labfreed/pac_id_resolver/cit_v2.py +313 -313
- labfreed/pac_id_resolver/resolver.py +97 -98
- labfreed/pac_id_resolver/services.py +82 -79
- labfreed/qr/__init__.py +1 -1
- labfreed/qr/generate_qr.py +422 -422
- labfreed/trex/__init__.py +16 -16
- labfreed/trex/python_convenience/__init__.py +3 -3
- labfreed/trex/python_convenience/data_table.py +87 -87
- labfreed/trex/python_convenience/pyTREX.py +248 -248
- labfreed/trex/python_convenience/quantity.py +66 -66
- labfreed/trex/table_segment.py +245 -245
- labfreed/trex/trex.py +69 -69
- labfreed/trex/trex_base_models.py +209 -209
- labfreed/trex/value_segments.py +99 -99
- labfreed/utilities/base36.py +82 -82
- labfreed/well_known_extensions/__init__.py +4 -4
- labfreed/well_known_extensions/default_extension_interpreters.py +6 -6
- labfreed/well_known_extensions/display_name_extension.py +40 -40
- labfreed/well_known_extensions/trex_extension.py +30 -30
- labfreed/well_known_keys/gs1/__init__.py +5 -5
- labfreed/well_known_keys/gs1/gs1.py +3 -3
- labfreed/well_known_keys/labfreed/well_known_keys.py +15 -15
- labfreed/well_known_keys/unece/__init__.py +3 -3
- labfreed/well_known_keys/unece/unece_units.py +67 -67
- {labfreed-0.2.6a6.dist-info → labfreed-0.2.8.dist-info}/METADATA +36 -21
- labfreed-0.2.8.dist-info/RECORD +45 -0
- {labfreed-0.2.6a6.dist-info → labfreed-0.2.8.dist-info}/licenses/LICENSE +21 -21
- labfreed-0.2.6a6.dist-info/RECORD +0 -45
- {labfreed-0.2.6a6.dist-info → labfreed-0.2.8.dist-info}/WHEEL +0 -0
|
@@ -1,201 +1,201 @@
|
|
|
1
|
-
## Materials
|
|
2
|
-
from abc import ABC
|
|
3
|
-
from pydantic import Field, computed_field, model_validator
|
|
4
|
-
|
|
5
|
-
from labfreed.labfreed_infrastructure import ValidationMsgLevel
|
|
6
|
-
from labfreed.pac_cat.category_base import Category
|
|
7
|
-
from labfreed.pac_id.id_segment import IDSegment
|
|
8
|
-
|
|
9
|
-
class PredefinedCategory(Category, ABC):
|
|
10
|
-
'''@private
|
|
11
|
-
Base for Predefined catergories
|
|
12
|
-
'''
|
|
13
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
14
|
-
''' Category segments, which are not defined in the specification'''
|
|
15
|
-
|
|
16
|
-
@computed_field
|
|
17
|
-
@property
|
|
18
|
-
def segments(self) -> list[IDSegment]:
|
|
19
|
-
return self._get_segments(use_short_notation=False)
|
|
20
|
-
|
|
21
|
-
def _get_segments(self, use_short_notation=False) -> list[IDSegment]:
|
|
22
|
-
segments = []
|
|
23
|
-
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
|
|
24
|
-
for field_name, field_info in self.model_fields.items():
|
|
25
|
-
if field_name in ['key', 'additional_segments']:
|
|
26
|
-
continue
|
|
27
|
-
if value := getattr(self, field_name):
|
|
28
|
-
if can_omit_keys:
|
|
29
|
-
key = None
|
|
30
|
-
else:
|
|
31
|
-
key = field_info.alias
|
|
32
|
-
segments.append(IDSegment(key= key, value= value) )
|
|
33
|
-
else:
|
|
34
|
-
can_omit_keys = False
|
|
35
|
-
if self.additional_segments:
|
|
36
|
-
segments.extend(self.additional_segments)
|
|
37
|
-
return segments
|
|
38
|
-
|
|
39
|
-
model_config = {
|
|
40
|
-
"populate_by_name": True
|
|
41
|
-
}
|
|
42
|
-
''' @private Pydantic tweak to allows model fields to be populated using their Python name, even if they have an alias defined.
|
|
43
|
-
The alias we need to use the GS1 code in serialization
|
|
44
|
-
'''
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class Material_Device(PredefinedCategory):
|
|
49
|
-
'''Represents the -MD category'''
|
|
50
|
-
key: str = Field(default='-MD', frozen=True)
|
|
51
|
-
model_number: str|None = Field( alias='240')
|
|
52
|
-
serial_number: str|None = Field( alias='21')
|
|
53
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
54
|
-
''' Category segments, which are not defined in the specification'''
|
|
55
|
-
|
|
56
|
-
@model_validator(mode='after')
|
|
57
|
-
def _validate_mandatory_fields(self):
|
|
58
|
-
if not self.model_number:
|
|
59
|
-
self._add_validation_message(
|
|
60
|
-
source=f"Category {self.key}",
|
|
61
|
-
level = ValidationMsgLevel.ERROR,
|
|
62
|
-
msg=f'Category key {self.key} is missing mandatory field Model Number',
|
|
63
|
-
highlight_pattern = f"{self.key}"
|
|
64
|
-
)
|
|
65
|
-
if not self.serial_number:
|
|
66
|
-
self._add_validation_message(
|
|
67
|
-
source=f"Category {self.key}",
|
|
68
|
-
level = ValidationMsgLevel.ERROR,
|
|
69
|
-
msg=f'Category key {self.key} is missing mandatory field Serial Number',
|
|
70
|
-
highlight_pattern = f"{self.key}"
|
|
71
|
-
)
|
|
72
|
-
return self
|
|
73
|
-
|
|
74
|
-
class Material_Substance(PredefinedCategory):
|
|
75
|
-
'''Represents the -MS category'''
|
|
76
|
-
key: str = Field(default='-MS', frozen=True)
|
|
77
|
-
product_number:str|None = Field( alias='240')
|
|
78
|
-
batch_number:str|None = Field(default=None, alias='10')
|
|
79
|
-
container_size:str|None = Field(default=None, alias='20')
|
|
80
|
-
container_number:str|None = Field(default=None, alias='21')
|
|
81
|
-
aliquot:str|None = Field(default=None, alias='250')
|
|
82
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
83
|
-
''' Category segments, which are not defined in the specification'''
|
|
84
|
-
|
|
85
|
-
@model_validator(mode='after')
|
|
86
|
-
def _validate_mandatory_fields(self):
|
|
87
|
-
if not self.product_number:
|
|
88
|
-
self._add_validation_message(
|
|
89
|
-
source=f"Category {self.key}",
|
|
90
|
-
level = ValidationMsgLevel.ERROR,
|
|
91
|
-
msg=f'Category key {self.key} is missing mandatory field Product Number',
|
|
92
|
-
highlight_pattern = f"{self.key}"
|
|
93
|
-
)
|
|
94
|
-
return self
|
|
95
|
-
|
|
96
|
-
class Material_Consumable(PredefinedCategory):
|
|
97
|
-
'''Represents the -MC category'''
|
|
98
|
-
key: str = Field(default='-MC', frozen=True)
|
|
99
|
-
product_number:str|None = Field( alias='240')
|
|
100
|
-
batch_number:str|None = Field(default=None, alias='10')
|
|
101
|
-
packaging_size:str|None = Field(default=None, alias='20')
|
|
102
|
-
serial_number:str|None = Field(default=None, alias='21')
|
|
103
|
-
aliquot:str|None = Field(default=None, alias='250')
|
|
104
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
105
|
-
''' Category segments, which are not defined in the specification'''
|
|
106
|
-
|
|
107
|
-
@model_validator(mode='after')
|
|
108
|
-
def _validate_mandatory_fields(self):
|
|
109
|
-
if not self.product_number:
|
|
110
|
-
self._add_validation_message(
|
|
111
|
-
source=f"Category {self.key}",
|
|
112
|
-
level = ValidationMsgLevel.ERROR,
|
|
113
|
-
msg=f"Category key {self.key} is missing mandatory field 'Product Number'",
|
|
114
|
-
highlight_pattern = f"{self.key}"
|
|
115
|
-
)
|
|
116
|
-
return self
|
|
117
|
-
|
|
118
|
-
class Material_Misc(Material_Consumable):
|
|
119
|
-
'''Represents the -MC category'''
|
|
120
|
-
# same fields as Consumable
|
|
121
|
-
key: str = Field(default='-MM', frozen=True)
|
|
122
|
-
product_number:str|None = Field( alias='240')
|
|
123
|
-
batch_number:str|None = Field(default=None, alias='10')
|
|
124
|
-
packaging_size:str|None = Field(default=None, alias='20')
|
|
125
|
-
serial_number:str|None = Field(default=None, alias='21')
|
|
126
|
-
aliquot:str|None = Field(default=None, alias='250')
|
|
127
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
128
|
-
''' Category segments, which are not defined in the specification'''
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
## Data
|
|
133
|
-
class Data_Abstract(PredefinedCategory, ABC):
|
|
134
|
-
'''@private'''
|
|
135
|
-
key: str
|
|
136
|
-
id:str|None = Field( alias='21')
|
|
137
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
138
|
-
''' Category segments, which are not defined in the specification'''
|
|
139
|
-
|
|
140
|
-
@model_validator(mode='after')
|
|
141
|
-
def _validate_mandatory_fields(self):
|
|
142
|
-
if not self.id:
|
|
143
|
-
self._add_validation_message(
|
|
144
|
-
source=f"Category {self.key}",
|
|
145
|
-
level = ValidationMsgLevel.ERROR,
|
|
146
|
-
msg=f"Category key {self.key} is missing mandatory field 'ID'",
|
|
147
|
-
highlight_pattern = f"{self.key}"
|
|
148
|
-
)
|
|
149
|
-
return self
|
|
150
|
-
|
|
151
|
-
class Data_Result(Data_Abstract):
|
|
152
|
-
'''Represents the -DR category'''
|
|
153
|
-
key: str = Field(default='-DR', frozen=True)
|
|
154
|
-
id:str|None = Field( alias='21')
|
|
155
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
156
|
-
''' Category segments, which are not defined in the specification'''
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class Data_Method(Data_Abstract):
|
|
160
|
-
'''Represents the -DM category'''
|
|
161
|
-
key: str = Field(default='-DM', frozen=True)
|
|
162
|
-
id:str|None = Field( alias='21')
|
|
163
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
164
|
-
''' Category segments, which are not defined in the specification'''
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
class Data_Calibration(Data_Abstract):
|
|
168
|
-
'''Represents the -DC category'''
|
|
169
|
-
key: str = Field(default='-DC', frozen=True)
|
|
170
|
-
id:str|None = Field( alias='21')
|
|
171
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
172
|
-
''' Category segments, which are not defined in the specification'''
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
class Data_Progress(Data_Abstract):
|
|
176
|
-
'''Represents the -DP category'''
|
|
177
|
-
key: str = Field(default='-DP', frozen=True)
|
|
178
|
-
id:str|None = Field( alias='21')
|
|
179
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
180
|
-
''' Category segments, which are not defined in the specification'''
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
class Data_Static(Data_Abstract):
|
|
184
|
-
'''Represents the -DS category'''
|
|
185
|
-
key: str = Field(default='-DS', frozen=True)
|
|
186
|
-
id:str|None = Field( alias='21')
|
|
187
|
-
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
188
|
-
''' Category segments, which are not defined in the specification'''
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
category_key_to_class_map = {
|
|
192
|
-
'-MD': Material_Device,
|
|
193
|
-
'-MS': Material_Substance,
|
|
194
|
-
'-MC': Material_Consumable,
|
|
195
|
-
'-MM': Material_Misc,
|
|
196
|
-
'-DM': Data_Method,
|
|
197
|
-
'-DR': Data_Result,
|
|
198
|
-
'-DC': Data_Calibration,
|
|
199
|
-
'-DP': Data_Progress,
|
|
200
|
-
'-DS': Data_Static
|
|
1
|
+
## Materials
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from pydantic import Field, computed_field, model_validator
|
|
4
|
+
|
|
5
|
+
from labfreed.labfreed_infrastructure import ValidationMsgLevel
|
|
6
|
+
from labfreed.pac_cat.category_base import Category
|
|
7
|
+
from labfreed.pac_id.id_segment import IDSegment
|
|
8
|
+
|
|
9
|
+
class PredefinedCategory(Category, ABC):
|
|
10
|
+
'''@private
|
|
11
|
+
Base for Predefined catergories
|
|
12
|
+
'''
|
|
13
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
14
|
+
''' Category segments, which are not defined in the specification'''
|
|
15
|
+
|
|
16
|
+
@computed_field
|
|
17
|
+
@property
|
|
18
|
+
def segments(self) -> list[IDSegment]:
|
|
19
|
+
return self._get_segments(use_short_notation=False)
|
|
20
|
+
|
|
21
|
+
def _get_segments(self, use_short_notation=False) -> list[IDSegment]:
|
|
22
|
+
segments = []
|
|
23
|
+
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
|
|
24
|
+
for field_name, field_info in self.model_fields.items():
|
|
25
|
+
if field_name in ['key', 'additional_segments']:
|
|
26
|
+
continue
|
|
27
|
+
if value := getattr(self, field_name):
|
|
28
|
+
if can_omit_keys:
|
|
29
|
+
key = None
|
|
30
|
+
else:
|
|
31
|
+
key = field_info.alias
|
|
32
|
+
segments.append(IDSegment(key= key, value= value) )
|
|
33
|
+
else:
|
|
34
|
+
can_omit_keys = False
|
|
35
|
+
if self.additional_segments:
|
|
36
|
+
segments.extend(self.additional_segments)
|
|
37
|
+
return segments
|
|
38
|
+
|
|
39
|
+
model_config = {
|
|
40
|
+
"populate_by_name": True
|
|
41
|
+
}
|
|
42
|
+
''' @private Pydantic tweak to allows model fields to be populated using their Python name, even if they have an alias defined.
|
|
43
|
+
The alias we need to use the GS1 code in serialization
|
|
44
|
+
'''
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Material_Device(PredefinedCategory):
|
|
49
|
+
'''Represents the -MD category'''
|
|
50
|
+
key: str = Field(default='-MD', frozen=True)
|
|
51
|
+
model_number: str|None = Field( alias='240')
|
|
52
|
+
serial_number: str|None = Field( alias='21')
|
|
53
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
54
|
+
''' Category segments, which are not defined in the specification'''
|
|
55
|
+
|
|
56
|
+
@model_validator(mode='after')
|
|
57
|
+
def _validate_mandatory_fields(self):
|
|
58
|
+
if not self.model_number:
|
|
59
|
+
self._add_validation_message(
|
|
60
|
+
source=f"Category {self.key}",
|
|
61
|
+
level = ValidationMsgLevel.ERROR,
|
|
62
|
+
msg=f'Category key {self.key} is missing mandatory field Model Number',
|
|
63
|
+
highlight_pattern = f"{self.key}"
|
|
64
|
+
)
|
|
65
|
+
if not self.serial_number:
|
|
66
|
+
self._add_validation_message(
|
|
67
|
+
source=f"Category {self.key}",
|
|
68
|
+
level = ValidationMsgLevel.ERROR,
|
|
69
|
+
msg=f'Category key {self.key} is missing mandatory field Serial Number',
|
|
70
|
+
highlight_pattern = f"{self.key}"
|
|
71
|
+
)
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
class Material_Substance(PredefinedCategory):
|
|
75
|
+
'''Represents the -MS category'''
|
|
76
|
+
key: str = Field(default='-MS', frozen=True)
|
|
77
|
+
product_number:str|None = Field( alias='240')
|
|
78
|
+
batch_number:str|None = Field(default=None, alias='10')
|
|
79
|
+
container_size:str|None = Field(default=None, alias='20')
|
|
80
|
+
container_number:str|None = Field(default=None, alias='21')
|
|
81
|
+
aliquot:str|None = Field(default=None, alias='250')
|
|
82
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
83
|
+
''' Category segments, which are not defined in the specification'''
|
|
84
|
+
|
|
85
|
+
@model_validator(mode='after')
|
|
86
|
+
def _validate_mandatory_fields(self):
|
|
87
|
+
if not self.product_number:
|
|
88
|
+
self._add_validation_message(
|
|
89
|
+
source=f"Category {self.key}",
|
|
90
|
+
level = ValidationMsgLevel.ERROR,
|
|
91
|
+
msg=f'Category key {self.key} is missing mandatory field Product Number',
|
|
92
|
+
highlight_pattern = f"{self.key}"
|
|
93
|
+
)
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
class Material_Consumable(PredefinedCategory):
|
|
97
|
+
'''Represents the -MC category'''
|
|
98
|
+
key: str = Field(default='-MC', frozen=True)
|
|
99
|
+
product_number:str|None = Field( alias='240')
|
|
100
|
+
batch_number:str|None = Field(default=None, alias='10')
|
|
101
|
+
packaging_size:str|None = Field(default=None, alias='20')
|
|
102
|
+
serial_number:str|None = Field(default=None, alias='21')
|
|
103
|
+
aliquot:str|None = Field(default=None, alias='250')
|
|
104
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
105
|
+
''' Category segments, which are not defined in the specification'''
|
|
106
|
+
|
|
107
|
+
@model_validator(mode='after')
|
|
108
|
+
def _validate_mandatory_fields(self):
|
|
109
|
+
if not self.product_number:
|
|
110
|
+
self._add_validation_message(
|
|
111
|
+
source=f"Category {self.key}",
|
|
112
|
+
level = ValidationMsgLevel.ERROR,
|
|
113
|
+
msg=f"Category key {self.key} is missing mandatory field 'Product Number'",
|
|
114
|
+
highlight_pattern = f"{self.key}"
|
|
115
|
+
)
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
class Material_Misc(Material_Consumable):
|
|
119
|
+
'''Represents the -MC category'''
|
|
120
|
+
# same fields as Consumable
|
|
121
|
+
key: str = Field(default='-MM', frozen=True)
|
|
122
|
+
product_number:str|None = Field( alias='240')
|
|
123
|
+
batch_number:str|None = Field(default=None, alias='10')
|
|
124
|
+
packaging_size:str|None = Field(default=None, alias='20')
|
|
125
|
+
serial_number:str|None = Field(default=None, alias='21')
|
|
126
|
+
aliquot:str|None = Field(default=None, alias='250')
|
|
127
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
128
|
+
''' Category segments, which are not defined in the specification'''
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
## Data
|
|
133
|
+
class Data_Abstract(PredefinedCategory, ABC):
|
|
134
|
+
'''@private'''
|
|
135
|
+
key: str
|
|
136
|
+
id:str|None = Field( alias='21')
|
|
137
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
138
|
+
''' Category segments, which are not defined in the specification'''
|
|
139
|
+
|
|
140
|
+
@model_validator(mode='after')
|
|
141
|
+
def _validate_mandatory_fields(self):
|
|
142
|
+
if not self.id:
|
|
143
|
+
self._add_validation_message(
|
|
144
|
+
source=f"Category {self.key}",
|
|
145
|
+
level = ValidationMsgLevel.ERROR,
|
|
146
|
+
msg=f"Category key {self.key} is missing mandatory field 'ID'",
|
|
147
|
+
highlight_pattern = f"{self.key}"
|
|
148
|
+
)
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
class Data_Result(Data_Abstract):
|
|
152
|
+
'''Represents the -DR category'''
|
|
153
|
+
key: str = Field(default='-DR', frozen=True)
|
|
154
|
+
id:str|None = Field( alias='21')
|
|
155
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
156
|
+
''' Category segments, which are not defined in the specification'''
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class Data_Method(Data_Abstract):
|
|
160
|
+
'''Represents the -DM category'''
|
|
161
|
+
key: str = Field(default='-DM', frozen=True)
|
|
162
|
+
id:str|None = Field( alias='21')
|
|
163
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
164
|
+
''' Category segments, which are not defined in the specification'''
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Data_Calibration(Data_Abstract):
|
|
168
|
+
'''Represents the -DC category'''
|
|
169
|
+
key: str = Field(default='-DC', frozen=True)
|
|
170
|
+
id:str|None = Field( alias='21')
|
|
171
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
172
|
+
''' Category segments, which are not defined in the specification'''
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class Data_Progress(Data_Abstract):
|
|
176
|
+
'''Represents the -DP category'''
|
|
177
|
+
key: str = Field(default='-DP', frozen=True)
|
|
178
|
+
id:str|None = Field( alias='21')
|
|
179
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
180
|
+
''' Category segments, which are not defined in the specification'''
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class Data_Static(Data_Abstract):
|
|
184
|
+
'''Represents the -DS category'''
|
|
185
|
+
key: str = Field(default='-DS', frozen=True)
|
|
186
|
+
id:str|None = Field( alias='21')
|
|
187
|
+
additional_segments: list[IDSegment] = Field(default_factory=list, exclude=True)
|
|
188
|
+
''' Category segments, which are not defined in the specification'''
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
category_key_to_class_map = {
|
|
192
|
+
'-MD': Material_Device,
|
|
193
|
+
'-MS': Material_Substance,
|
|
194
|
+
'-MC': Material_Consumable,
|
|
195
|
+
'-MM': Material_Misc,
|
|
196
|
+
'-DM': Data_Method,
|
|
197
|
+
'-DR': Data_Result,
|
|
198
|
+
'-DC': Data_Calibration,
|
|
199
|
+
'-DP': Data_Progress,
|
|
200
|
+
'-DS': Data_Static
|
|
201
201
|
}
|
labfreed/pac_id/__init__.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
from .pac_id import PAC_ID
|
|
2
|
-
from .id_segment import IDSegment
|
|
3
|
-
from .extension import Extension
|
|
4
|
-
|
|
5
|
-
'''@private
|
|
6
|
-
From a SW engineering perspective it would be best to have no dependencies from other modules to pac_id.
|
|
7
|
-
However from a Python users convenience perspective it is better to have one place where a pac url can be parsed and magically the extensions are in a meaningful type (e.g. TREX in TREX aware format) and categories are known of possible.
|
|
8
|
-
|
|
9
|
-
>> We have given priority to convenient usage and therefore chose to have dependencies from pac_id to pac_cat and well_known_extensions
|
|
10
|
-
'''
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
__all__ = [
|
|
14
|
-
"PAC_ID",
|
|
15
|
-
"IDSegment",
|
|
16
|
-
"Extension"
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
from .pac_id import PAC_ID
|
|
2
|
+
from .id_segment import IDSegment
|
|
3
|
+
from .extension import Extension
|
|
4
|
+
|
|
5
|
+
'''@private
|
|
6
|
+
From a SW engineering perspective it would be best to have no dependencies from other modules to pac_id.
|
|
7
|
+
However from a Python users convenience perspective it is better to have one place where a pac url can be parsed and magically the extensions are in a meaningful type (e.g. TREX in TREX aware format) and categories are known of possible.
|
|
8
|
+
|
|
9
|
+
>> We have given priority to convenient usage and therefore chose to have dependencies from pac_id to pac_cat and well_known_extensions
|
|
10
|
+
'''
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"PAC_ID",
|
|
15
|
+
"IDSegment",
|
|
16
|
+
"Extension"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
labfreed/pac_id/extension.py
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
from abc import ABC, abstractproperty
|
|
3
|
-
|
|
4
|
-
from pydantic import model_validator
|
|
5
|
-
|
|
6
|
-
from labfreed.labfreed_infrastructure import LabFREED_BaseModel
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class ExtensionBase(ABC):
|
|
10
|
-
name: str
|
|
11
|
-
type: str
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@abstractproperty
|
|
15
|
-
def data(self) -> str:
|
|
16
|
-
raise NotImplementedError("Subclasses must implement 'data'")
|
|
17
|
-
|
|
18
|
-
def __str__(self):
|
|
19
|
-
return f'{self.name}${self.type}/{self.data}'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class Extension(LabFREED_BaseModel,ExtensionBase):
|
|
24
|
-
'''Implementation of Extension for unknown extension types'''
|
|
25
|
-
name:str
|
|
26
|
-
type:str
|
|
27
|
-
data_:str
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def data(self) -> str:
|
|
31
|
-
return self.data_
|
|
32
|
-
|
|
33
|
-
@staticmethod
|
|
34
|
-
def create(*, name, type, data):
|
|
35
|
-
return Extension(name=name, type=type, data=data)
|
|
36
|
-
|
|
37
|
-
@model_validator(mode='before')
|
|
38
|
-
@classmethod
|
|
39
|
-
def move_data_field(cls, values):
|
|
40
|
-
if "data" in values:
|
|
41
|
-
values["data_"] = values.pop("data")
|
|
42
|
-
return values
|
|
43
|
-
|
|
44
|
-
model_config = {
|
|
45
|
-
"extra": "allow", # Allow extra keys during pre-validation
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
1
|
+
|
|
2
|
+
from abc import ABC, abstractproperty
|
|
3
|
+
|
|
4
|
+
from pydantic import model_validator
|
|
5
|
+
|
|
6
|
+
from labfreed.labfreed_infrastructure import LabFREED_BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ExtensionBase(ABC):
|
|
10
|
+
name: str
|
|
11
|
+
type: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@abstractproperty
|
|
15
|
+
def data(self) -> str:
|
|
16
|
+
raise NotImplementedError("Subclasses must implement 'data'")
|
|
17
|
+
|
|
18
|
+
def __str__(self):
|
|
19
|
+
return f'{self.name}${self.type}/{self.data}'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Extension(LabFREED_BaseModel,ExtensionBase):
|
|
24
|
+
'''Implementation of Extension for unknown extension types'''
|
|
25
|
+
name:str
|
|
26
|
+
type:str
|
|
27
|
+
data_:str
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def data(self) -> str:
|
|
31
|
+
return self.data_
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def create(*, name, type, data):
|
|
35
|
+
return Extension(name=name, type=type, data=data)
|
|
36
|
+
|
|
37
|
+
@model_validator(mode='before')
|
|
38
|
+
@classmethod
|
|
39
|
+
def move_data_field(cls, values):
|
|
40
|
+
if "data" in values:
|
|
41
|
+
values["data_"] = values.pop("data")
|
|
42
|
+
return values
|
|
43
|
+
|
|
44
|
+
model_config = {
|
|
45
|
+
"extra": "allow", # Allow extra keys during pre-validation
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|