labfreed 0.2.1__py3-none-any.whl → 0.2.3__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 CHANGED
@@ -2,7 +2,10 @@
2
2
  Python implementation of LabFREED building blocks
3
3
  '''
4
4
 
5
- __version__ = "0.2.1"
5
+ __version__ = "0.2.3"
6
6
 
7
- from labfreed.pac_id import PAC_ID # noqa: F401
8
- from labfreed.labfreed_infrastructure import LabFREED_ValidationError # noqa: F401
7
+ from labfreed.pac_id import * # noqa: F403
8
+ from labfreed.pac_cat import * # noqa: F403
9
+ from labfreed.pac_id_resolver import * # noqa: F403
10
+ from labfreed.trex import * # noqa: F403
11
+ from labfreed.labfreed_infrastructure import * # noqa: F403
@@ -159,25 +159,9 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
159
159
  return warnings_list
160
160
 
161
161
 
162
-
163
- def _emphasize_in(self, validation_msg, validation_node_str:str, fmt, color='black'):
164
- if validation_msg.highlight_sub_patterns:
165
- replacements = validation_msg.highlight_sub_patterns
166
- else:
167
- replacements = [validation_msg.highlight]
168
- # Sort patterns by length descending to avoid subpattern clobbering
169
- sorted_patterns = sorted(replacements, key=len, reverse=True)
170
- # Escape the patterns for regex safety
171
- escaped_patterns = [re.escape(p) for p in sorted_patterns]
172
- # Create one regex pattern with alternation (longest first)
173
- pattern = re.compile("|".join(escaped_patterns))
174
-
175
- out = pattern.sub(lambda m: fmt(m.group(0)), validation_node_str)
176
- return out
177
-
178
162
 
179
163
  def print_validation_messages(self, target='console'):
180
- msgs = self._get_nested_validation_messages()
164
+ msgs = self.format_validation_messages(target=target)
181
165
 
182
166
  table = Table(title="Validation Results", show_header=False, title_justify='left')
183
167
 
@@ -185,12 +169,29 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
185
169
  return table.add_column(s, vertical='top')
186
170
  col("-")
187
171
 
188
-
189
172
  if not msgs:
190
173
  table.add_row('All clear!', end_section=True)
191
174
  return
192
175
 
193
176
  for m in msgs:
177
+ table.add_row(m)
178
+ table.add_section()
179
+
180
+ logging.info(table)
181
+ print(table)
182
+
183
+
184
+ def format_validation_messages(self, target='console') -> list[str]:
185
+ """Format validation messages
186
+
187
+ Args:
188
+ target (str, optional): Target format: 'markdown', 'console', 'html', 'html_styled'.
189
+
190
+ Returns:
191
+ list[str]: formated messages
192
+ """
193
+ formatted_msg = list()
194
+ for m in self.validation_messages():
194
195
  if m.level == ValidationMsgLevel.ERROR:
195
196
  color = 'red'
196
197
  else:
@@ -199,70 +200,48 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
199
200
  match target:
200
201
  case 'markdown':
201
202
  fmt = lambda s: f'👉{s}👈' # noqa: E731
203
+ fmt_title = lambda s: f'**{s}**' # noqa: E731
204
+ br = '\n'
202
205
  case 'console':
203
206
  fmt = lambda s: f'[{color} bold]{s}[/{color} bold]' # noqa: E731
207
+ fmt_title = fmt
208
+ br = '\n'
204
209
  case 'html':
205
- fmt = lambda s: f'<span class="val_{color}">{s}</span>' # noqa: E731
210
+ fmt = lambda s: f'<span class="validation-{m.level.name.lower()}">{s}</span>' # noqa: E731
211
+ fmt_title = lambda s: f'<span class="validation-title">{s}</span>' # noqa: E731
212
+ br = '<br>'
206
213
  case 'html_styled':
207
- fmt = lambda s: f'<b style="color:{color}>{s}</b>' # noqa: E731
214
+ fmt = lambda s: f'<b style="color:{color}">{s}</b>' # noqa: E731
215
+ fmt_title = fmt
216
+ br = '<br>'
208
217
 
209
218
  serialized = str(self)
210
- emphazised_highlight = self._emphasize_in(m, serialized, fmt=fmt, color=color)
219
+ emphazised_highlight = self._emphasize_in(m, serialized, fmt=fmt)
211
220
  emphazised_highlight = emphazised_highlight.replace('👈👉','') # removes two consecutive markers, to make it cleaner
212
221
 
213
- txt = f'[bold {color}]{m.level.name} [/bold {color}] in {m.source}'
214
- txt += '\n' + f'{m.msg}'
215
- txt += '\n\n' + emphazised_highlight
216
-
217
- table.add_row( txt)
218
- table.add_section()
219
-
220
- logging.info(table)
221
- print(table)
222
+ txt = f'{fmt_title(m.level.name)} in {m.source}'
223
+ txt += br + f'{m.msg}'
224
+ txt += br+br + emphazised_highlight
222
225
 
223
-
224
-
225
-
226
+ formatted_msg.append(txt)
227
+ return formatted_msg
226
228
 
227
- # def print_validation_messages_(self, str_to_highlight_in=None, target='console'):
228
- # if not str_to_highlight_in:
229
- # str_to_highlight_in = str(self)
230
- # msgs = self.get_nested_validation_messages()
231
- # print('\n'.join(['\n',
232
- # '=======================================',
233
- # 'Validation Results',
234
- # '---------------------------------------'
235
- # ]
236
- # )
237
- # )
238
-
239
- # if not msgs:
240
- # print('All clear!')
241
- # return
242
-
243
- # for m in msgs:
244
- # if m.level.casefold() == "error":
245
- # color = 'red'
246
- # else:
247
- # color = 'yellow'
248
-
249
- # text = Text.from_markup(f'\n [bold {color}]{m.level} [/bold {color}] in \t {m.source}' )
250
- # print(text)
251
- # match target:
252
- # case 'markdown':
253
- # formatted_highlight = m.emphazised_highlight.replace('emph', f'🔸').replace('[/', '').replace('[', '').replace(']', '')
254
- # case 'console':
255
- # formatted_highlight = m.emphazised_highlight.replace('emph', f'bold {color}')
256
- # case 'html':
257
- # formatted_highlight = m.emphazised_highlight.replace('emph', f'b').replace('[', '<').replace(']', '>')
258
- # fmtd = str_to_highlight_in.replace(m.highlight, formatted_highlight)
259
- # fmtd = Text.from_markup(fmtd)
260
- # print(fmtd)
261
- # print(Text.from_markup(f'{m.problem_msg}'))
229
+ def _emphasize_in(self, validation_msg, validation_node_str:str, fmt):
230
+ if validation_msg.highlight_sub_patterns:
231
+ replacements = validation_msg.highlight_sub_patterns
232
+ else:
233
+ replacements = [validation_msg.highlight]
234
+ # Sort patterns by length descending to avoid subpattern clobbering
235
+ sorted_patterns = sorted(replacements, key=len, reverse=True)
236
+ # Escape the patterns for regex safety
237
+ escaped_patterns = [re.escape(p) for p in sorted_patterns]
238
+ # Create one regex pattern with alternation (longest first)
239
+ pattern = re.compile("|".join(escaped_patterns))
262
240
 
263
-
264
-
265
-
241
+ out = pattern.sub(lambda m: fmt(m.group(0)), validation_node_str)
242
+ return out
243
+
244
+
266
245
 
267
246
  def _filter_errors(val_msg:list[ValidationMessage]) -> list[ValidationMessage]:
268
247
  return [ m for m in val_msg if m.level == ValidationMsgLevel.ERROR ]
@@ -1,6 +1,9 @@
1
1
  from .pac_cat import PAC_CAT
2
2
  from .category_base import Category
3
- from .predefined_categories import Material_Device, Material_Substance, Material_Consumable, Material_Misc, Data_Method, Data_Result, Data_Progress, Data_Calibration, Data_Abstract
3
+ from .predefined_categories import (
4
+ Material_Device, Material_Substance, Material_Consumable, Material_Misc, Data_Method, Data_Result, Data_Progress,
5
+ Data_Calibration, Data_Abstract, category_key_to_class_map
6
+ )
4
7
 
5
8
  __all__ = [
6
9
  "PAC_CAT",
@@ -10,7 +10,7 @@ from rich.table import Table
10
10
  from labfreed.labfreed_infrastructure import ValidationMsgLevel
11
11
 
12
12
  from labfreed.pac_cat.category_base import Category
13
- from labfreed.pac_cat.predefined_categories import Data_Calibration, Data_Method, Data_Progress, Data_Result, Material_Consumable, Material_Device, Material_Misc, Material_Substance, Data_Static
13
+ from labfreed.pac_cat.predefined_categories import category_key_to_class_map
14
14
  from labfreed.pac_id.id_segment import IDSegment
15
15
  from labfreed.pac_id.pac_id import PAC_ID
16
16
 
@@ -68,18 +68,7 @@ class PAC_CAT(PAC_ID):
68
68
  category_key = segments[0].value
69
69
  segments.pop(0)
70
70
 
71
- mapping = {
72
- '-MD': Material_Device,
73
- '-MS': Material_Substance,
74
- '-MC': Material_Consumable,
75
- '-MM': Material_Misc,
76
- '-DM': Data_Method,
77
- '-DR': Data_Result,
78
- '-DC': Data_Calibration,
79
- '-DP': Data_Progress,
80
- '-DS': Data_Static
81
- }
82
- known_cat = mapping.get(category_key)
71
+ known_cat = category_key_to_class_map.get(category_key)
83
72
 
84
73
  if not known_cat:
85
74
  return Category(key=category_key, segments=segments)
@@ -157,3 +146,5 @@ class PAC_CAT(PAC_ID):
157
146
  table.add_section()
158
147
  print(table)
159
148
 
149
+
150
+
@@ -188,3 +188,14 @@ class Data_Static(Data_Abstract):
188
188
  ''' Category segments, which are not defined in the specification'''
189
189
 
190
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
+ }
@@ -15,6 +15,8 @@ class ExtensionBase(ABC):
15
15
  def data(self) -> str:
16
16
  raise NotImplementedError("Subclasses must implement 'data'")
17
17
 
18
+ def __str__(self):
19
+ return f'{self.name}${self.type}/{self.data}'
18
20
 
19
21
 
20
22
 
@@ -43,6 +45,4 @@ class Extension(LabFREED_BaseModel,ExtensionBase):
43
45
  "extra": "allow", # Allow extra keys during pre-validation
44
46
  }
45
47
 
46
- def __str__(self):
47
- return f'{self.name}${self.type}/{self.data}'
48
-
48
+
@@ -8,7 +8,7 @@ from pydantic import RootModel, model_validator
8
8
  from labfreed.trex.trex_base_models import Value
9
9
  from labfreed.well_known_keys.unece.unece_units import unece_unit_codes
10
10
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
11
- from labfreed.trex.trex_base_models import AlphanumericValue, BinaryValue, BoolValue, DateValue, ErrorValue, NumericValue, TREX_Segment, TextValue, str_to_value_type
11
+ from labfreed.trex.trex_base_models import AlphanumericValue, BinaryValue, BoolValue, DateValue, ErrorValue, NumericValue, TREX_Segment, TextValue
12
12
 
13
13
 
14
14
  class ColumnHeader(LabFREED_BaseModel):
@@ -217,11 +217,28 @@ def _deserialize_table_segment_from_trex_segment_str(trex_segment_str) -> TableS
217
217
  headers.append(ColumnHeader(key=col_key, type=col_type))
218
218
 
219
219
  data = [row.split(':') for row in body.split('::') ]
220
- col_types = [h.type for h in headers]
221
220
  # convert to correct value types
222
- data_with_types = [[str_to_value_type(c,t) for c, t in zip(r, col_types)] for r in data]
221
+ data_with_types = [[_str_to_value_type(h.type, cv) for cv, h in zip(r, headers)] for r in data]
223
222
  data = [ TableRow(r) for r in data_with_types]
224
223
 
225
- out = TableSegment(column_headers=headers, data=data_with_types, key=name)
224
+ out = TableSegment(column_headers=headers, data=data, key=name)
225
+ return out
226
+
227
+ def _str_to_value_type(type_, s):
228
+ match type_:
229
+ case 'T.D':
230
+ out = DateValue(value=s)
231
+ case 'T.B':
232
+ out = BoolValue(value=s)
233
+ case 'T.A':
234
+ out = AlphanumericValue(value=s)
235
+ case 'T.T':
236
+ out = TextValue(value=s)
237
+ case 'T.X':
238
+ out = BinaryValue(value=s)
239
+ case 'E':
240
+ out = ErrorValue(value=s)
241
+ case _:
242
+ out = NumericValue(value=s)
226
243
  return out
227
244
 
@@ -4,11 +4,10 @@ import re
4
4
 
5
5
 
6
6
 
7
- from pydantic import PrivateAttr, field_validator, model_validator
7
+ from pydantic import PrivateAttr, model_validator
8
8
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
9
- from abc import ABC, abstractclassmethod, abstractmethod
9
+ from abc import ABC, abstractmethod
10
10
 
11
- from labfreed.utilities.base36 import base36, to_base36, from_base36
12
11
 
13
12
 
14
13
  ''' Configure pdoc'''
@@ -32,22 +31,9 @@ class Value(LabFREED_BaseModel, ABC):
32
31
  def serialize(self):
33
32
  return self.value
34
33
 
35
- @abstractclassmethod
36
- def _from_python_type(cls, v):
37
- ...
38
-
39
- @abstractmethod
40
- def _value_to_python_type(self):
41
- ...
42
34
 
43
35
 
44
36
  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
37
 
52
38
  @model_validator(mode='after')
53
39
  def _validate(self):
@@ -56,7 +42,7 @@ class NumericValue(Value):
56
42
  self._add_validation_message(
57
43
  source=f"TREX numeric value {value}",
58
44
  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",
45
+ msg=f"Characters {_quote_texts(not_allowed_chars)} are not allowed in quantity segment. Must be a number.",
60
46
  highlight_pattern = f'{value}',
61
47
  highlight_sub=not_allowed_chars
62
48
  )
@@ -68,36 +54,9 @@ class NumericValue(Value):
68
54
  highlight_pattern = f'{value}'
69
55
  )
70
56
  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
57
 
80
58
  class DateValue(Value):
81
59
  _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
60
 
102
61
  @model_validator(mode='after')
103
62
  def _validate(self):
@@ -134,24 +93,8 @@ class DateValue(Value):
134
93
  self._date_time_dict = d
135
94
  return self
136
95
 
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
-
96
+
147
97
  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
98
 
156
99
  @model_validator(mode='after')
157
100
  def _validate(self):
@@ -164,21 +107,9 @@ class BoolValue(Value):
164
107
  highlight_sub=[c for c in self.value]
165
108
  )
166
109
  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
-
110
+
176
111
 
177
112
  class AlphanumericValue(Value):
178
- @field_validator('value', mode='before')
179
- @classmethod
180
- def _from_python_type(cls, v:str):
181
- return v
182
113
 
183
114
  @model_validator(mode='after')
184
115
  def _validate(self):
@@ -199,20 +130,9 @@ class AlphanumericValue(Value):
199
130
  highlight_sub=not_allowed_chars
200
131
  )
201
132
  return self
202
-
203
- def _value_to_python_type(self) -> str:
204
- return self.value
205
-
133
+
206
134
 
207
135
  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
136
 
217
137
  @model_validator(mode='after')
218
138
  def _validate(self):
@@ -225,20 +145,9 @@ class TextValue(Value):
225
145
  highlight_sub=not_allowed_chars
226
146
  )
227
147
  return self
228
-
229
- def _value_to_python_type(self) -> str:
230
- decoded = from_base36(self.value)
231
- return decoded
232
-
148
+
233
149
 
234
150
  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
151
 
243
152
  @model_validator(mode='after')
244
153
  def _validate(self):
@@ -251,17 +160,9 @@ class BinaryValue(Value):
251
160
  highlight_sub=not_allowed_chars
252
161
  )
253
162
  return self
254
-
255
- def _value_to_python_type(self) -> bytes:
256
- decoded = bytes(from_base36(self))
257
- return decoded
258
-
163
+
259
164
 
260
165
  class ErrorValue(Value):
261
- @field_validator('value', mode='before')
262
- @classmethod
263
- def _from_python_type(cls, v:str):
264
- return v
265
166
 
266
167
  @model_validator(mode='after')
267
168
  def _validate(self):
@@ -274,10 +175,6 @@ class ErrorValue(Value):
274
175
  highlight_sub=not_allowed_chars
275
176
  )
276
177
  return self
277
-
278
-
279
- def _value_to_python_type(self) -> str:
280
- return self.value
281
178
 
282
179
 
283
180
  class TREX_Segment(LabFREED_BaseModel, ABC):
@@ -305,31 +202,8 @@ class TREX_Segment(LabFREED_BaseModel, ABC):
305
202
 
306
203
 
307
204
 
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
-
205
+
206
+
333
207
 
334
208
 
335
209
 
@@ -89,19 +89,9 @@ def _deserialize_value_segment_from_trex_segment_str(trex_segment_str) -> ValueS
89
89
  case 'T.A':
90
90
  out = AlphanumericSegment(key=key, value=value, type=type_)
91
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
92
+ out = TextSegment(key=key, value=value, type=type_)
98
93
  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
94
+ out = BinarySegment(key=key, value=value, type=type_)
105
95
  case 'E':
106
96
  out = ErrorSegment(key=key, value=value, type=type_)
107
97
  case _:
@@ -15,7 +15,7 @@ class DisplayNameExtension(ExtensionBase, LabFREED_BaseModel):
15
15
  @property
16
16
  def data(self)->str:
17
17
  # return '/'.join([to_base36(dn) for dn in self.display_name])
18
- return to_base36(self.display_name)
18
+ return to_base36(self.display_name).root
19
19
 
20
20
  @staticmethod
21
21
  def from_extension(ext:ExtensionBase) -> Self:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labfreed
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Python implementation of LabFREED building blocks
5
5
  Author-email: Reto Thürer <thuerer.r@buchi.com>
6
6
  Requires-Python: >=3.11
@@ -98,30 +98,28 @@ There is a nice function to highlight problems
98
98
  pac.print_validation_messages()
99
99
  ```
100
100
  ```text
101
- >> Validation Results
102
- >> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
103
- >> │ RECOMMENDATION in id segment value bal500
104
- >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and │
105
- >> │ '+'
106
- >> │
107
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:👉bal👈500/21:@1234 │
108
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
109
- >> │ RECOMMENDATION in id segment value @1234
110
- >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
111
- >> │
112
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:👉@👈1234 │
113
- >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
114
- >> │ RECOMMENDATION in id segment value bal500
115
- >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and
116
- >> │ '+'
117
- >> │ │
118
- >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:👉bal👈500/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
- >> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
101
+ >> Validation Results
102
+ >> ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
103
+ >> │ **RECOMMENDATION** in id segment value bal500
104
+ >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
105
+ >> │
106
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:👉bal👈500/21:@1234
107
+ >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
108
+ >> │ **RECOMMENDATION** in id segment value @1234 │
109
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
110
+ >> │
111
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:👉@👈1234
112
+ >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
113
+ >> │ **RECOMMENDATION** in id segment value bal500 │
114
+ >> │ Characters 'a','l','b' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+'
115
+ >> │
116
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:👉bal👈500/21:@1234
117
+ >> ├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
118
+ >> │ **RECOMMENDATION** in id segment value @1234
119
+ >> │ Characters '@' should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' │
120
+ >> │
121
+ >> │ HTTPS://PAC.METTORIUS.COM/-MD/240:bal500/21:👉@👈1234
122
+ >> └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
125
123
  ```
126
124
  ### Save as QR Code
127
125
 
@@ -141,7 +139,7 @@ PAC-CAT defines a (optional) way how the identifier is structured.
141
139
  PAC_ID.from_url() automatically converts to PAC-CAT if possible.
142
140
 
143
141
  ```python
144
- from labfreed.pac_cat import PAC_CAT
142
+ from labfreed import PAC_CAT
145
143
  pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
146
144
  pac = PAC_ID.from_url(pac_str)
147
145
  if isinstance(pac, PAC_CAT):
@@ -197,7 +195,7 @@ print(f'WEIGHT = {v.value}')
197
195
  #### Create PAC-ID
198
196
 
199
197
  ```python
200
- from labfreed.pac_id import PAC_ID, IDSegment
198
+ from labfreed import PAC_ID, IDSegment
201
199
  from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys
202
200
 
203
201
  pac = PAC_ID(issuer='METTORIUS.COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
@@ -213,9 +211,9 @@ Note that utility types for Quantity (number with unit) and table are needed
213
211
 
214
212
  ```python
215
213
  from datetime import datetime
216
- from labfreed.trex.python_convenience.pyTREX import pyTREX
217
- from labfreed.trex.python_convenience.data_table import DataTable
218
- from labfreed.trex.python_convenience.quantity import Quantity
214
+ from labfreed.trex.python_convenience import pyTREX
215
+ from labfreed.trex.python_convenience import DataTable
216
+ from labfreed.trex.python_convenience import Quantity
219
217
 
220
218
  # Value segments of different type
221
219
  segments = {
@@ -245,18 +243,18 @@ trex.print_validation_messages()
245
243
  ```text
246
244
  >> Validation Results
247
245
  >> ┌────────────────────────────────────────────────────────────┐
248
- >> │ ERROR in TREX table column Date
249
- >> │ Column header key contains invalid characters: 'a','e','t' │
246
+ >> │ **ERROR** in TREX table column Date
247
+ >> │ Column header key contains invalid characters: 'a','t','e' │
250
248
  >> │ │
251
249
  >> │ STOP$T.D:20240505T1306 │
252
250
  >> │ +TEMP$KEL:10.15 │
253
251
  >> │ +OK$T.B:F │
254
252
  >> │ +COMMENT$T.A:FOO │
255
- >> │ +COMMENT2$T.T:QMDTNXIKU
253
+ >> │ +COMMENT2$T.T:12G3
256
254
  >> │ +TABLE$$DURATION$HUR:D👉ate👈$T.D:OK$T.B:COMMENT$T.A:: │
257
- >> │ 1:20250424T161739.312:T:FOO:: │
258
- >> │ 1.1:20250424T161739.312:T:BAR:: │
259
- >> │ 1.3:20250424T161739.312:F:BLUBB │
255
+ >> │ 1:20250425T081731.192:T:FOO:: │
256
+ >> │ 1.1:20250425T081731.192:T:BAR:: │
257
+ >> │ 1.3:20250425T081731.192:F:BLUBB │
260
258
  >> └────────────────────────────────────────────────────────────┘
261
259
  ```
262
260
  #### Combine PAC-ID and TREX and serialize
@@ -268,12 +266,12 @@ pac_str = pac.to_url()
268
266
  print(pac_str)
269
267
  ```
270
268
  ```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:20250424T161739.312:T:FOO::1.1:20250424T161739.312:T:BAR::1.3:20250424T161739.312:F:BLUBB
269
+ >> 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:12G3+TABLE$$DURATION$HUR:Date$T.D:OK$T.B:COMMENT$T.A::1:20250425T081731.192:T:FOO::1.1:20250425T081731.192:T:BAR::1.3:20250425T081731.192:F:BLUBB
272
270
  ```
273
271
  ## PAC-ID Resolver
274
272
 
275
273
  ```python
276
- from labfreed.pac_id_resolver import PAC_ID_Resolver, load_cit
274
+ from labfreed import PAC_ID_Resolver, load_cit
277
275
  # Get a CIT
278
276
  dir = os.path.join(os.getcwd(), 'examples')
279
277
  p = os.path.join(dir, 'cit_mine.yaml')
@@ -299,7 +297,26 @@ for sg in service_groups:
299
297
 
300
298
  ```
301
299
  ```text
302
- >> [Error during execution: No Internet Connection]
300
+ >> Services from origin 'PERSONAL
301
+ >> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
302
+ >> ┃ Service Name ┃ URL ┃ Reachable ┃
303
+ >> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
304
+ >> │ CAS Search │ https://pubchem.ncbi.nlm.nih.gov/#query=7732-18-5 │ ACTIVE │
305
+ >> └──────────────┴───────────────────────────────────────────────────┴───────────┘
306
+ >> Services from origin 'MY_COMPANY
307
+ >> ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
308
+ >> ┃ Service Name ┃ URL ┃ Reachable ┃
309
+ >> ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
310
+ >> │ Chemical Management │ https://chem-manager.com/METTORIUS.COM/-MS/240:X3511/CAS:7732-18-5 │ INACTIVE │
311
+ >> └─────────────────────┴────────────────────────────────────────────────────────────────────┴───────────┘
312
+ >> Services from origin 'METTORIUS.COM
313
+ >> ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
314
+ >> ┃ Service Name ┃ URL ┃ Reachable ┃
315
+ >> ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
316
+ >> │ CoA │ https://mettorius.com/CoA.pdf │ ACTIVE │
317
+ >> │ MSDS │ https://mettorius.com/MSDS.pdf │ ACTIVE │
318
+ >> │ Shop │ https://mettorius.com/shop.html │ ACTIVE │
319
+ >> └──────────────┴─────────────────────────────────┴───────────┘
303
320
  ```
304
321
  <!-- END EXAMPLES -->
305
322
 
@@ -307,6 +324,13 @@ for sg in service_groups:
307
324
 
308
325
  <!-- BEGIN CHANGELOG -->
309
326
  ## Change Log
327
+ ### v0.2.3
328
+ - improvements in formatting of validation messages
329
+ - bugfix in DisplayNameExtension
330
+
331
+ ### v0.2.2
332
+ - minor changes for better access of subfunctions. No change in existing API
333
+
310
334
  ### v0.2.1
311
335
  - improved docu. no code changes
312
336
 
@@ -329,6 +353,7 @@ for sg in service_groups:
329
353
  - supports PAC-ID, PAC-CAT, TREX and DisplayName
330
354
  - QR generation
331
355
  - ok-ish test coverage
356
+ <!-- END CHANGELOG -->
332
357
 
333
358
  # Attributions
334
359
  The following tools were used:
@@ -336,4 +361,4 @@ The following tools were used:
336
361
  - [Pydantic](https://docs.pydantic.dev/latest/)
337
362
  - json with UNECE units from (https://github.com/quadient/unece-units/blob/main/python/src/unece_excel_parser/parsedUneceUnits.json)
338
363
  - json with GS1 codes from (https://ref.gs1.org/ai/GS1_Application_Identifiers.jsonld)
339
- <!-- END CHANGELOG -->
364
+
@@ -1,11 +1,11 @@
1
- labfreed/__init__.py,sha256=CgFS3IzTYiq7S4f4VZzeneIGDHNBVqFn-9BQ0xCpzbQ,224
2
- labfreed/labfreed_infrastructure.py,sha256=V-5sLhqKkfckKim5VxlB_D1qIzk-Ztxfx4VIlwaR6Jc,10850
3
- labfreed/pac_cat/__init__.py,sha256=rJ2dFTN8aErTvGf4xwcNZ04xrbTieLAE2v5C2bmgPOA,507
1
+ labfreed/__init__.py,sha256=u22Ofcr-NJRBA0uSXtqxzEu8z_hdB3j3ZiY0FdoKIDo,336
2
+ labfreed/labfreed_infrastructure.py,sha256=lkYs8DKLdt30D3X2ITGYuODbNyZaU1Lr7eY0lXQejaU,9945
3
+ labfreed/pac_cat/__init__.py,sha256=S4T-QgzcEDjup44WvHCY3_K30B6_e8ducR--DxBb_DM,554
4
4
  labfreed/pac_cat/category_base.py,sha256=lFQNiTUukyhWdaSCAI7CZxLtj6kNtnBCE4UsePwsGqE,1801
5
- labfreed/pac_cat/pac_cat.py,sha256=UxWyPsuZsekq3ZmHSQLBdB1tocvVlxz_FOQXxHg_dlU,5800
6
- labfreed/pac_cat/predefined_categories.py,sha256=BEf7rxN5IcKVhuxMNhdfQ_1xnkax5l8Z1pJMRIROKpw,8510
5
+ labfreed/pac_cat/pac_cat.py,sha256=AJUYDsyGOHy-sRRpXpY0awtbf3HCvn3RhMax6ofvYRA,5318
6
+ labfreed/pac_cat/predefined_categories.py,sha256=5wnMCj-CrACV2W4lH13w7qynWIwi506G3uLNcxuJQGg,8832
7
7
  labfreed/pac_id/__init__.py,sha256=NGMbzkwQ4txKeT5pxdIZordwHO8J3_q84jzPanjKoHg,675
8
- labfreed/pac_id/extension.py,sha256=uIs_9aasJ_n7ua067wR7XvtL05H-JZP4f_HtW4qnQDw,1114
8
+ labfreed/pac_id/extension.py,sha256=zuHI8e51otPbpO1r1xDFh32uXTT8L5pCJn74B-aOUpI,1112
9
9
  labfreed/pac_id/id_segment.py,sha256=r5JU1SJuRXhZJJxy5T3xjrb598wIDTLpivSJhIUAzjQ,4526
10
10
  labfreed/pac_id/pac_id.py,sha256=IWXYlKFjQKB_9U5bINWC5_Lb5pcVbuleocvGs79A28w,5300
11
11
  labfreed/pac_id/url_parser.py,sha256=016Gd-V2OORDN2toAZEPmnXobrWNVVXM_zICBrQtqGY,5863
@@ -18,10 +18,10 @@ labfreed/pac_id_resolver/services.py,sha256=TPoH6YlSwa0hmawHpOiMwIpBAinhoRhMSoex
18
18
  labfreed/qr/__init__.py,sha256=fdKwP6W2Js--yMbBUdn-g_2uq2VqPpfQJeDLHsMDO-Y,61
19
19
  labfreed/qr/generate_qr.py,sha256=mSt-U872O3ReHB_UdS-MzYu0wRgdlKcAOEfTxg5CLRk,16616
20
20
  labfreed/trex/__init__.py,sha256=r0MYrGk_XxsqSKo9c2i9jRXApTDeTZD8QRXcRpkOVXY,428
21
- labfreed/trex/table_segment.py,sha256=nfUPbzzqFdkkOCZ73Qsb32iE8Ev0eMBBK5FhAxk51D8,8707
21
+ labfreed/trex/table_segment.py,sha256=VZRciE329lmnwfnaykVBaNlrkG80UrDVrjUgQMVREHM,9133
22
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
23
+ labfreed/trex/trex_base_models.py,sha256=3twGEZLYEznUyJ_ctcTcPYsy1qOciMRqLcslE9dxEqw,7567
24
+ labfreed/trex/value_segments.py,sha256=mmjyEKWLlDIoiFlk2wKELOfWn2_0xwreNEBTs8RgjOY,3774
25
25
  labfreed/trex/python_convenience/__init__.py,sha256=dyAQG7t-uYN6VfGXbWIq2bHxGcGI63l7FS2-VPYs2RQ,137
26
26
  labfreed/trex/python_convenience/data_table.py,sha256=aUQWvsWZ_zAYc2s1iU4Bvl9b-SB5MSY42z69twLxWJ0,1730
27
27
  labfreed/trex/python_convenience/pyTREX.py,sha256=qiOJ6PGAfsbnfpQBI_G2_C-3DaKqTfIW5xdZReQVxI8,9824
@@ -29,7 +29,7 @@ labfreed/trex/python_convenience/quantity.py,sha256=d3w-ThY4Cp7n3fde8pct-X5kHc7v
29
29
  labfreed/utilities/base36.py,sha256=_yX8aQ1OwrK5tnJU1NUEzQSFGr9xAVnNvPObpNzCPYs,2895
30
30
  labfreed/well_known_extensions/__init__.py,sha256=CjZTjx8Cn8763Hhnv_--Wj1LcFpFs2cyQwWrrzOS4xM,246
31
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
32
+ labfreed/well_known_extensions/display_name_extension.py,sha256=1BZY3Tffak4bNeirBKAktYCG1u2dhEvx8RqamUT6TxU,1465
33
33
  labfreed/well_known_extensions/trex_extension.py,sha256=tffklaambkFPExcIDRAG9GJ7CHXeuFAagl6FuwS-2kI,929
34
34
  labfreed/well_known_keys/gs1/__init__.py,sha256=LOFycgqS6OuV8t22TmtHy-ZI2iuXc3jJfVFwRFVDM3I,103
35
35
  labfreed/well_known_keys/gs1/gs1.py,sha256=LIyy-W89m9L0gVxOu-lpBotsHN6CHvmdE3Vu2VwxUQA,79
@@ -38,7 +38,7 @@ labfreed/well_known_keys/labfreed/well_known_keys.py,sha256=nqk66kHdSwJTJfMKlP-x
38
38
  labfreed/well_known_keys/unece/UneceUnits.json,sha256=kwfQSp_nTuWbADfBBgqTWrvPl6XtM5SedEVLbMJrM7M,898953
39
39
  labfreed/well_known_keys/unece/__init__.py,sha256=MSP9lmjg9_D9iqG9Yq2_ajYfQSNS9wIT7FXA1c--59M,122
40
40
  labfreed/well_known_keys/unece/unece_units.py,sha256=gNDQk6KGl-nGMf9Ycq_fQ8P2xxKITgLkcQWPd4H49gI,1630
41
- labfreed-0.2.1.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
42
- labfreed-0.2.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
43
- labfreed-0.2.1.dist-info/METADATA,sha256=TZAAEd3mlli0oXeEkQ7x5kbfpwMI59mx8WVIbS5KxQU,14965
44
- labfreed-0.2.1.dist-info/RECORD,,
41
+ labfreed-0.2.3.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
42
+ labfreed-0.2.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
43
+ labfreed-0.2.3.dist-info/METADATA,sha256=YwsvjFnaTIQMJlI5u1x2gIk4ybLTPkTDr2ab_o9chVA,18090
44
+ labfreed-0.2.3.dist-info/RECORD,,