labfreed 0.0.5__py3-none-any.whl → 0.2.0b1__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 (58) 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/{PAC_ID/well_known_segment_keys.py → well_known_keys/labfreed/well_known_keys.py} +1 -1
  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.0b1.dist-info/METADATA +329 -0
  42. labfreed-0.2.0b1.dist-info/RECORD +44 -0
  43. {labfreed-0.0.5.dist-info → labfreed-0.2.0b1.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 -215
  49. labfreed/PAC_ID/parse.py +0 -142
  50. labfreed/PAC_ID/serialize.py +0 -60
  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 -143
  55. labfreed/validation.py +0 -71
  56. labfreed-0.0.5.dist-info/METADATA +0 -34
  57. labfreed-0.0.5.dist-info/RECORD +0 -19
  58. {labfreed-0.0.5.dist-info → labfreed-0.2.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,336 @@
1
+ from datetime import date, datetime, time
2
+ import logging
3
+ import re
4
+
5
+
6
+
7
+ from pydantic import PrivateAttr, field_validator, model_validator
8
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
9
+ from abc import ABC, abstractclassmethod, abstractmethod
10
+
11
+ from labfreed.utilities.base36 import base36, to_base36, from_base36
12
+
13
+
14
+ ''' Configure pdoc'''
15
+ v_segs = ["NumericSegment",
16
+ "DateSegment",
17
+ "BoolSegment",
18
+ "AlphanumericSegment",
19
+ "TextSegment",
20
+ "BinarySegment"]
21
+ __all__ = ["TREX"] + v_segs + ["TableSegment"] # noqa: F822
22
+
23
+
24
+
25
+
26
+ class Value(LabFREED_BaseModel, ABC):
27
+ '''@private
28
+ Helper to add validation for various types to ValueSegments and Tables
29
+ '''
30
+ value:str
31
+
32
+ def serialize(self):
33
+ return self.value
34
+
35
+ @abstractclassmethod
36
+ def _from_python_type(cls, v):
37
+ ...
38
+
39
+ @abstractmethod
40
+ def _value_to_python_type(self):
41
+ ...
42
+
43
+
44
+ class NumericValue(Value):
45
+ @field_validator('value', mode='before')
46
+ @classmethod
47
+ def _from_python_type(cls, v:str| int|float):
48
+ if isinstance(v, str):
49
+ return v
50
+ return str(v)
51
+
52
+ @model_validator(mode='after')
53
+ def _validate(self):
54
+ value = self.value
55
+ if not_allowed_chars := set(re.sub(r'[0-9\.\-E]', '', value)):
56
+ self._add_validation_message(
57
+ source=f"TREX numeric value {value}",
58
+ level=ValidationMsgLevel.ERROR,
59
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in quantity segment. Base36 encoding only allows A-Z0-9",
60
+ highlight_pattern = f'{value}',
61
+ highlight_sub=not_allowed_chars
62
+ )
63
+ if not re.fullmatch(r'-?\d+(\.\d+)?(E-?\d+)?', value):
64
+ self._add_validation_message(
65
+ source=f"TREX numeric value {value}",
66
+ level=ValidationMsgLevel.ERROR,
67
+ msg=f"{value} cannot be converted to number",
68
+ highlight_pattern = f'{value}'
69
+ )
70
+ return self
71
+
72
+ def _value_to_python_type(self) -> str:
73
+ v = float(self.value)
74
+ if '.' not in self.value and 'E' not in self.value:
75
+ return int(v)
76
+ else:
77
+ return v
78
+
79
+
80
+ class DateValue(Value):
81
+ _date_time_dict:dict|None = PrivateAttr(default=None)
82
+ @field_validator('value', mode='before')
83
+ @classmethod
84
+ def _from_python_type(cls, v:str| date|time|datetime):
85
+ if isinstance(v, str):
86
+ return v
87
+
88
+ sd = ""
89
+ st = ""
90
+ if isinstance(v, date) or isinstance(v, datetime):
91
+ sd = v.strftime('%Y%m%d')
92
+ if isinstance(v, time) or isinstance(v, datetime):
93
+ if v.microsecond:
94
+ st = v.strftime("T%H%M%S.") + f"{v.microsecond // 1000:03d}"
95
+ elif v.second:
96
+ st = v.strftime("T%H%M%S")
97
+ else:
98
+ st = v.strftime("T%H%M")
99
+
100
+ return sd + st
101
+
102
+ @model_validator(mode='after')
103
+ def _validate(self):
104
+ pattern:str = r'((?P<year>\d{4})(?P<month>\d{2})(?P<day>\d{2}))?(T(?P<hour>\d{2})(?P<minute>\d{2})(?P<second>\d{2})?(\.(?P<millisecond>\d{3}))?)?'
105
+ value=self.value
106
+ if not re.fullmatch(pattern, value):
107
+ self._add_validation_message(
108
+ source=f"TREX date value {value}",
109
+ level=ValidationMsgLevel.ERROR,
110
+ msg=f'{value} is not in a valid format. Valid format for date: YYYYMMDD; Valid for time: THHMM, THHMMSS, THHMMSS.SSS; Datetime any combination of valid date and time',
111
+ highlight_pattern = f'{value}'
112
+ )
113
+ return self
114
+
115
+ matches = re.match(pattern, value)
116
+ d = matches.groupdict()
117
+ d = {k: int(v) for k,v in d.items() if v }
118
+ if 'millisecond' in d.keys():
119
+ ms = d.pop('millisecond')
120
+ d.update({'microsecond': ms * 1000})
121
+ try:
122
+ if d.get('year'): # input is only a time
123
+ datetime(**d)
124
+ else:
125
+ time(**d)
126
+ except ValueError:
127
+ self._add_validation_message(
128
+ source=f"TREX date value {value}",
129
+ level=ValidationMsgLevel.ERROR,
130
+ msg=f'{value} is no valid date or time.',
131
+ highlight_pattern = f'{value}'
132
+ )
133
+
134
+ self._date_time_dict = d
135
+ return self
136
+
137
+ def _value_to_python_type(self) -> str:
138
+ d = self._date_time_dict
139
+ if d.get('year') and d.get('hour'): # input is only a time
140
+ return datetime(**d)
141
+ elif d.get('year'):
142
+ return date(**d)
143
+ else:
144
+ return time(**d)
145
+
146
+
147
+ class BoolValue(Value):
148
+ @field_validator('value', mode='before')
149
+ @classmethod
150
+ def _from_python_type(cls, v:str| bool):
151
+ if isinstance(v, str):
152
+ return v
153
+
154
+ return 'T' if v else 'F'
155
+
156
+ @model_validator(mode='after')
157
+ def _validate(self):
158
+ if self.value not in ['T', 'F']:
159
+ self._add_validation_message(
160
+ source=f"TREX boolean value {self.value}",
161
+ level= ValidationMsgLevel.ERROR,
162
+ msg=f'{self.value} is no valid boolean. Must be T or F',
163
+ highlight_pattern = f'{self.value}',
164
+ highlight_sub=[c for c in self.value]
165
+ )
166
+ return self
167
+
168
+ def _value_to_python_type(self) -> str:
169
+ if self.value == 'T':
170
+ return True
171
+ elif self.value == 'F':
172
+ return False
173
+ else:
174
+ Exception(f'{self} is not valid boolean. That really should not have been possible -- Contact the maintainers of the library')
175
+
176
+
177
+ class AlphanumericValue(Value):
178
+ @field_validator('value', mode='before')
179
+ @classmethod
180
+ def _from_python_type(cls, v:str):
181
+ return v
182
+
183
+ @model_validator(mode='after')
184
+ def _validate(self):
185
+ if re.match(r'[a-z]', self.value):
186
+ self._add_validation_message(
187
+ source=f"TREX value {self.value}",
188
+ level= ValidationMsgLevel.ERROR,
189
+ msg="Lower case characters are not allowed.",
190
+ highlight_pattern = self.value
191
+ )
192
+
193
+ if not_allowed_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.value)):
194
+ self._add_validation_message(
195
+ source=f"TREX value {self.value}",
196
+ level= ValidationMsgLevel.ERROR,
197
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in alphanumeric segment",
198
+ highlight_pattern = self.value,
199
+ highlight_sub=not_allowed_chars
200
+ )
201
+ return self
202
+
203
+ def _value_to_python_type(self) -> str:
204
+ return self.value
205
+
206
+
207
+ class TextValue(Value):
208
+ @field_validator('value', mode='before')
209
+ @classmethod
210
+ def _from_python_type(cls, v:base36|str):
211
+ if isinstance(v, str):
212
+ logging.info('Got str for text value > converting to base36')
213
+ return to_base36(v).root
214
+ else:
215
+ return v.root
216
+
217
+ @model_validator(mode='after')
218
+ def _validate(self):
219
+ if not_allowed_chars := set(re.sub(r'[A-Z0-9]', '', self.value)):
220
+ self._add_validation_message(
221
+ source=f"TREX value {self.value}",
222
+ level= ValidationMsgLevel.ERROR,
223
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in text segment. Base36 encoding only allows A-Z0-9",
224
+ highlight_pattern = self.value,
225
+ highlight_sub=not_allowed_chars
226
+ )
227
+ return self
228
+
229
+ def _value_to_python_type(self) -> str:
230
+ decoded = from_base36(self.value)
231
+ return decoded
232
+
233
+
234
+ class BinaryValue(Value):
235
+ @field_validator('value', mode='before')
236
+ @classmethod
237
+ def _from_python_type(cls, v:base36|str):
238
+ if isinstance(v, str):
239
+ return v
240
+ else:
241
+ return v.root
242
+
243
+ @model_validator(mode='after')
244
+ def _validate(self):
245
+ if not_allowed_chars := set(re.sub(r'[A-Z0-9]', '', self.value)):
246
+ self._add_validation_message(
247
+ source=f"TREX value {self.value}",
248
+ level= ValidationMsgLevel.ERROR,
249
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in text segment. Base36 encoding only allows A-Z0-9",
250
+ highlight_pattern = self.value,
251
+ highlight_sub=not_allowed_chars
252
+ )
253
+ return self
254
+
255
+ def _value_to_python_type(self) -> bytes:
256
+ decoded = bytes(from_base36(self))
257
+ return decoded
258
+
259
+
260
+ class ErrorValue(Value):
261
+ @field_validator('value', mode='before')
262
+ @classmethod
263
+ def _from_python_type(cls, v:str):
264
+ return v
265
+
266
+ @model_validator(mode='after')
267
+ def _validate(self):
268
+ if not_allowed_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.value)):
269
+ self._add_validation_message(
270
+ source=f"TREX value {self.value}",
271
+ level= ValidationMsgLevel.ERROR,
272
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in error segment",
273
+ highlight_pattern = self.value,
274
+ highlight_sub=not_allowed_chars
275
+ )
276
+ return self
277
+
278
+
279
+ def _value_to_python_type(self) -> str:
280
+ return self.value
281
+
282
+
283
+ class TREX_Segment(LabFREED_BaseModel, ABC):
284
+ '''@private
285
+ Abstract class representing a TREX_Segment
286
+ '''
287
+ key: str
288
+
289
+ @abstractmethod
290
+ def serialize(self):
291
+ raise NotImplementedError("Subclasses must implement 'serialize_as_trex()' method")
292
+
293
+
294
+ @model_validator(mode='after')
295
+ def _validate_key(self):
296
+ if not_allowed_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.key)):
297
+ self._add_validation_message(
298
+ source=f"TREX segment key {self.key}",
299
+ level=ValidationMsgLevel.ERROR,
300
+ msg=f"Segment key contains invalid characters: {_quote_texts(not_allowed_chars)}",
301
+ highlight_pattern = f'{self.key}$',
302
+ highlight_sub=not_allowed_chars
303
+ )
304
+ return self
305
+
306
+
307
+
308
+
309
+
310
+ def str_to_value_type(s:str, t:str):
311
+ match t:
312
+ case 'T.D':
313
+ v = DateValue(value=s)
314
+ case 'T.B':
315
+ v = BoolValue(value=s)
316
+ case 'T.A':
317
+ v = AlphanumericValue(value=s)
318
+ case 'T.T':
319
+ try:
320
+ value = base36(s)
321
+ except ValueError:
322
+ logging.error('String given as T.T contains characters which base36 should not')
323
+ value = s
324
+ v = TextValue(value=value)
325
+ case 'T.X':
326
+ v = BinaryValue(value=s)
327
+ case 'E' :
328
+ v = ErrorValue(value=s)
329
+ case _ :
330
+ v = NumericValue(value=s)
331
+ return v
332
+
333
+
334
+
335
+
336
+
@@ -0,0 +1,111 @@
1
+ from abc import ABC
2
+ import logging
3
+ import re
4
+ from typing import Literal
5
+ from pydantic import Field, model_validator
6
+ from labfreed.utilities.base36 import base36
7
+ from labfreed.well_known_keys.unece.unece_units import unece_unit_codes
8
+ from labfreed.labfreed_infrastructure import ValidationMsgLevel
9
+ from labfreed.trex.trex_base_models import AlphanumericValue, BinaryValue, BoolValue, DateValue, ErrorValue, NumericValue, TREX_Segment, TextValue, Value
10
+
11
+
12
+
13
+ class ValueSegment(TREX_Segment, Value, ABC):
14
+ '''@private: Abstract base class for value segments'''
15
+ type:str
16
+
17
+ @model_validator(mode='after')
18
+ def _validate_type(self):
19
+ valid_types = valid_types = unece_unit_codes() + ['T.D', 'T.B', 'T.A', 'T.T', 'T.X', 'E']
20
+ if self.type not in valid_types:
21
+ self._add_validation_message(
22
+ source=f"TREX value segment {self.key}",
23
+ level= ValidationMsgLevel.ERROR,
24
+ msg=f"Type {self.type} is invalid. Must be 'T.D', 'T.B', 'T.A', 'T.T', 'T.X', 'E' or a UNECE unit",
25
+ highlight_pattern = self.type
26
+ )
27
+ return self
28
+
29
+
30
+ def serialize(self) -> str:
31
+ return f'{self.key}${self.type}:{self.value}'
32
+
33
+
34
+ class NumericSegment(ValueSegment, NumericValue):
35
+ '''Represents a TREX segment holding a numeric value'''
36
+ type: str
37
+ key:str
38
+ value:str
39
+
40
+ class DateSegment(ValueSegment, DateValue):
41
+ '''Represents a TREX segment holding a date'''
42
+ type: Literal['T.D'] = Field('T.D', frozen=True)
43
+ key:str
44
+ value:str
45
+
46
+ class BoolSegment(ValueSegment, BoolValue):
47
+ '''Represents a TREX segment holding a boolean value'''
48
+ type: Literal['T.B'] = Field('T.B', frozen=True)
49
+ key:str
50
+ value:str
51
+ class AlphanumericSegment(ValueSegment, AlphanumericValue):
52
+ '''Represents a TREX segment holding a alphanumeric text'''
53
+ type: Literal['T.A'] = Field('T.A', frozen=True)
54
+ key:str
55
+ value:str
56
+ class TextSegment(ValueSegment, TextValue):
57
+ '''Represents a TREX segment holding a text with arbitrary characters'''
58
+ type: Literal['T.T'] = Field('T.T', frozen=True)
59
+ key:str
60
+ value:str
61
+
62
+ class BinarySegment(ValueSegment, BinaryValue):
63
+ '''Represents a TREX segment holding binary data'''
64
+ type: Literal['T.X'] = Field('T.X', frozen=True)
65
+ key:str
66
+ value:str
67
+
68
+ class ErrorSegment(ValueSegment, ErrorValue):
69
+ '''Represents a TREX segment which has erroneous content'''
70
+ type: Literal['E'] = Field('E', frozen=True)
71
+ key:str
72
+ value:str
73
+
74
+
75
+ def _deserialize_value_segment_from_trex_segment_str(trex_segment_str) -> ValueSegment:
76
+ #re_scalar_pattern = re.compile(f"(?P<name>[\w\.-]*?)\$(?P<unit>[\w\.]*?):(?P<value>.*)")
77
+ re_scalar_pattern = re.compile("(?P<name>.+?)\$(?P<unit>.+?):(?P<value>.+)")
78
+ matches = re_scalar_pattern.match(trex_segment_str)
79
+ if not matches:
80
+ return None
81
+
82
+ key, type_, value = matches.groups()
83
+
84
+ match type_:
85
+ case 'T.D':
86
+ out = DateSegment(key=key, value=value, type=type_)
87
+ case 'T.B':
88
+ out = BoolSegment(key=key, value=value, type=type_)
89
+ case 'T.A':
90
+ out = AlphanumericSegment(key=key, value=value, type=type_)
91
+ case 'T.T':
92
+ try:
93
+ value = base36(value)
94
+ except ValueError:
95
+ logging.error('String given as T.T contains characters which base36 should not')
96
+ value = value
97
+ out = TextSegment(key=key, value=value, type=type_) # prevent repeated conversion from str to base36 and make explict that when parsing we assume the string tpo be base36 already
98
+ case 'T.X':
99
+ try:
100
+ value = base36(value)
101
+ except ValueError:
102
+ logging.error('String given as T.X contains characters which base36 should not')
103
+ value = value
104
+ out = BinarySegment(key=key, value=value, type=type_) # prevent repeated conversion from str to base36 and make explict that when parsing we assume the string tpo be base36 already
105
+ case 'E':
106
+ out = ErrorSegment(key=key, value=value, type=type_)
107
+ case _:
108
+ out = NumericSegment(value=value, key=key, type=type_)
109
+
110
+ return out
111
+
@@ -1,31 +1,37 @@
1
+ import re
1
2
  import string
2
3
 
3
- def alphabet(base):
4
- """ returns an alphabet, which corresponds to what pythons int(s:str, base:int=10) function used.
5
- """
6
- if base < 2 or base > 36:
7
- ValueError('base can only be between 2 and 36')
8
- alphabet = (string.digits + string.ascii_uppercase)[0:base]
9
- return alphabet
4
+ from pydantic import field_validator, RootModel
5
+
6
+ class base36(RootModel[str]):
7
+ @field_validator('root')
8
+ @classmethod
9
+ def validate_format(cls, v: str) -> str:
10
+ if not re.fullmatch(r'[A-Z0-9]*', v):
11
+ raise ValueError("Value must only contain uppercase letters and digits (A-Z, 0-9)")
12
+ return v
13
+
10
14
 
11
- def to_base36(s:str):
15
+ def to_base36(s:str) -> base36:
12
16
  """Takes a string, encodes it in UTF-8 and then as base36 string."""
13
17
  utf8_encoded = s.encode('utf-8')
14
18
  num = int.from_bytes(utf8_encoded, byteorder='big', signed=False)
15
19
 
16
20
  # note: this cannot be arbitrarily chosen. The choice here corresponds to what pythons int(s:str, base:int=10) function used.
17
- base36_chars = alphabet(base=36)
21
+ base36_chars = _alphabet(base=36)
18
22
  if num == 0:
19
23
  return base36_chars[0]
20
- base36 = []
24
+ base_36 = []
21
25
  _num = num
22
26
  while _num:
23
27
  _num, i = divmod(_num, 36)
24
- base36.append(base36_chars[i])
25
- return ''.join(reversed(base36))
28
+ base_36.append(base36_chars[i])
29
+ b36_str = ''.join(reversed(base_36))
30
+ b36_str = base36(b36_str)
31
+ return b36_str
26
32
 
27
33
 
28
- def from_base36(s36:str):
34
+ def from_base36(s36:base36) -> str:
29
35
  """inverse of to_base36"""
30
36
  # this built in function interprets each character as number in a base represented by the standartd alphabet [0-9 (A-Z|a-z)][0:base] it is case INsensitive.
31
37
  num = int(s36, 36)
@@ -34,6 +40,16 @@ def from_base36(s36:str):
34
40
  s = _bytes.decode('utf-8')
35
41
  return s
36
42
 
43
+
44
+ def _alphabet(base):
45
+ """ returns an alphabet, which corresponds to what pythons int(s:str, base:int=10) function used.
46
+ """
47
+ if base < 2 or base > 36:
48
+ ValueError('base can only be between 2 and 36')
49
+ alphabet = (string.digits + string.ascii_uppercase)[0:base]
50
+ return alphabet
51
+
52
+
37
53
  if __name__ == "__main__":
38
54
  ss = ["A",
39
55
  "B-500 B",
@@ -0,0 +1,5 @@
1
+ from .display_name_extension import DisplayNameExtension
2
+ from .trex_extension import TREX_Extension
3
+ from .default_extension_interpreters import default_extension_interpreters # noqa: F401
4
+
5
+ __all__ = ["DisplayNameExtension", "TREX_Extension"]
@@ -0,0 +1,7 @@
1
+ from .display_name_extension import DisplayNameExtension
2
+ from .trex_extension import TREX_Extension
3
+
4
+ default_extension_interpreters = {
5
+ 'TREX': TREX_Extension,
6
+ 'N': DisplayNameExtension
7
+ }
@@ -0,0 +1,40 @@
1
+ import logging
2
+ from typing import Literal, Self
3
+ from pydantic import computed_field
4
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel
5
+ from labfreed.pac_id.extension import ExtensionBase
6
+ from labfreed.utilities.base36 import from_base36, to_base36
7
+
8
+
9
+ class DisplayNameExtension(ExtensionBase, LabFREED_BaseModel):
10
+ name:Literal['N'] = 'N'
11
+ type:Literal['N'] = 'N'
12
+ display_name: str
13
+
14
+ @computed_field
15
+ @property
16
+ def data(self)->str:
17
+ # return '/'.join([to_base36(dn) for dn in self.display_name])
18
+ return to_base36(self.display_name)
19
+
20
+ @staticmethod
21
+ def from_extension(ext:ExtensionBase) -> Self:
22
+ return DisplayNameExtension.create(name=ext.name,
23
+ type=ext.type,
24
+ data=ext.data)
25
+
26
+ @staticmethod
27
+ def create(*, name, type, data):
28
+ if name != 'N':
29
+ logging.warning(f'Name {name} was given, but this extension should only be used with name "N". Will ignore input')
30
+
31
+ if type != 'N':
32
+ 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')
33
+
34
+ display_name = from_base36(data)
35
+
36
+ return DisplayNameExtension(display_name=display_name)
37
+
38
+ def __str__(self):
39
+ return 'Display name: '+ self.display_name
40
+
@@ -0,0 +1,31 @@
1
+ from typing import Literal, Self
2
+
3
+ from pydantic import computed_field
4
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel
5
+ from labfreed.pac_id.extension import ExtensionBase
6
+ from labfreed.trex.trex import TREX
7
+
8
+
9
+ class TREX_Extension(ExtensionBase, LabFREED_BaseModel):
10
+ name:str
11
+ type:Literal['TREX'] = 'TREX'
12
+ trex:TREX
13
+
14
+ @computed_field
15
+ @property
16
+ def data(self)->str:
17
+ trex_str = self.trex.serialize()
18
+ return trex_str
19
+
20
+ @staticmethod
21
+ def from_extension(ext:ExtensionBase) -> Self:
22
+ return TREX_Extension.create(name=ext.name,
23
+ type=ext.type,
24
+ data=ext.data)
25
+
26
+ @staticmethod
27
+ def create(*, name, data, type='TREX'):
28
+ trex_extension = TREX_Extension(name= name, trex = TREX.deserialize(data))
29
+ return trex_extension
30
+
31
+
@@ -0,0 +1,6 @@
1
+ '''
2
+ Data from
3
+ https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld
4
+
5
+ processes by ChatGPT
6
+ '''
@@ -0,0 +1,4 @@
1
+ '''
2
+ Data from
3
+ https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld
4
+ '''
@@ -0,0 +1,57 @@
1
+ from enum import Enum
2
+
3
+ class GS1ApplicationIdentifier(Enum):
4
+ SSCC = "00"
5
+ GTIN = "01"
6
+ CONTENT = "02"
7
+ MTO_GTIN = "03"
8
+ BATCH_LOT = "10"
9
+ PROD_DATE = "11"
10
+ DUE_DATE = "12"
11
+ PACK_DATE = "13"
12
+ BEST_BEFORE_OR_BEST_BY = "15"
13
+ SELL_BY = "16"
14
+ USE_BY_OR_EXPIRY = "17"
15
+ VARIANT = "20"
16
+ SERIAL = "21"
17
+ CPV = "22"
18
+ VAR_COUNT = "30"
19
+ COUNT = "37"
20
+ INTERNAL = "90"
21
+ TPX = "235"
22
+ ADDITIONAL_ID = "240"
23
+ CUST_PART_NO = "241"
24
+ MTO_VARIANT = "242"
25
+ PCN = "243"
26
+ SECONDARY_SERIAL = "250"
27
+ REF_TO_SOURCE = "251"
28
+ GDTI = "253"
29
+ GLN_EXTENSION_COMPONENT = "254"
30
+ GCN = "255"
31
+ ORDER_NUMBER = "400"
32
+ GINC = "401"
33
+ GSIN = "402"
34
+ ROUTE = "403"
35
+ SHIP_TO_LOC = "410"
36
+ BILL_TO = "411"
37
+ PURCHASE_FROM = "412"
38
+ SHIP_FOR_LOC = "413"
39
+ LOC_NO = "414"
40
+ PAY_TO = "415"
41
+ PROD_SERV_LOC = "416"
42
+ PARTY = "417"
43
+ SHIP_TO_POST = "420"
44
+ ORIGIN = "422"
45
+ COUNTRY___INITIAL_PROCESS = "423"
46
+ COUNTRY___PROCESS = "424"
47
+ COUNTRY___DISASSEMBLY = "425"
48
+ COUNTRY___FULL_PROCESS = "426"
49
+ ORIGIN_SUBDIVISION = "427"
50
+ NHRN_PZN = "710"
51
+ NHRN_CIP = "711"
52
+ NHRN_CN = "712"
53
+ NHRN_DRN = "713"
54
+ NHRN_AIM = "714"
55
+ NHRN_NDC = "715"
56
+ NHRN_AIC = "716"
57
+
@@ -1,7 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
3
 
4
- class WellKnownSegmentKeys(Enum):
4
+ class WellKnownKeys(Enum):
5
5
  GTIN = '01'
6
6
  BATCH = '10'
7
7
  SERIAL = '21'