labfreed 0.0.8__py2.py3-none-any.whl → 0.0.9__py2.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/DisplayNameExtension/DisplayNameExtension.py +1 -1
- labfreed/PAC_ID/data_model.py +60 -28
- labfreed/PAC_ID/parse.py +6 -4
- labfreed/TREX/UneceUnits.json +33730 -0
- labfreed/TREX/data_model.py +869 -0
- labfreed/TREX/parse.py +128 -0
- labfreed/TREX/serialize.py +3 -0
- labfreed/TREX/unece_units.py +90 -0
- labfreed/__init__.py +1 -1
- labfreed/{TREXExtension → conversion_tools}/unit_utilities.py +8 -42
- labfreed/validation.py +115 -39
- {labfreed-0.0.8.dist-info → labfreed-0.0.9.dist-info}/METADATA +1 -1
- labfreed-0.0.9.dist-info/RECORD +22 -0
- labfreed/TREXExtension/data_model.py +0 -239
- labfreed/TREXExtension/parse.py +0 -53
- labfreed-0.0.8.dist-info/RECORD +0 -19
- /labfreed/{TREXExtension → conversion_tools}/uncertainty.py +0 -0
- /labfreed/{DisplayNameExtension → utilities}/base36.py +0 -0
- {labfreed-0.0.8.dist-info → labfreed-0.0.9.dist-info}/WHEEL +0 -0
- {labfreed-0.0.8.dist-info → labfreed-0.0.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
from enum import Enum
|
|
3
|
-
import logging
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
from pydantic import BaseModel, ValidationError, field_validator
|
|
7
|
-
from abc import ABC
|
|
8
|
-
|
|
9
|
-
from .unit_utilities import *
|
|
10
|
-
from ..PAC_ID.data_model import Extension
|
|
11
|
-
|
|
12
|
-
re_table_pattern = re.compile(f"(?P<tablename>[\w\.-]*?)\$\$(?P<header>[\w\.,\$:]*?)::(?P<body>.*)")
|
|
13
|
-
re_col_head_pattern = re.compile(f"(?P<name>[\w\.-]*?)\$(?P<unit>[\w\.]*)")
|
|
14
|
-
re_scalar_pattern = re.compile(f"(?P<name>[\w\.-]*?)\$(?P<unit>[\w\.]*?):(?P<value>.*)")
|
|
15
|
-
|
|
16
|
-
TREX_DATEFORMAT = '%Y%m%dT%H%M%S'
|
|
17
|
-
TREX_TIMEFORMAT = '%Y%m%d'
|
|
18
|
-
|
|
19
|
-
class TREX_types(Enum):
|
|
20
|
-
BOOL = 'T.B'
|
|
21
|
-
DATE = 'T.D'
|
|
22
|
-
TEXT = 'T.A'
|
|
23
|
-
ERROR = 'E'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class T_REX_Segment_ParseError(BaseException):
|
|
28
|
-
pass
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class TREX_Segment(BaseModel, ABC):
|
|
32
|
-
segment_name: str = None
|
|
33
|
-
|
|
34
|
-
def as_trex_segment_str(self, segment_name):
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class TREX_SimpleSegment(TREX_Segment):
|
|
39
|
-
type: str
|
|
40
|
-
value: str
|
|
41
|
-
|
|
42
|
-
@field_validator('type', mode='before')
|
|
43
|
-
def validate_type(t):
|
|
44
|
-
if isinstance(t, TREX_types):
|
|
45
|
-
t = t.value
|
|
46
|
-
return t
|
|
47
|
-
|
|
48
|
-
@staticmethod
|
|
49
|
-
def from_trex_segmentstring(segment_str):
|
|
50
|
-
|
|
51
|
-
matches = re_scalar_pattern.match(segment_str)
|
|
52
|
-
if not matches:
|
|
53
|
-
raise T_REX_Segment_ParseError("Segment is not a valid TREX Scalar")
|
|
54
|
-
|
|
55
|
-
name, type_, value = matches.groups()
|
|
56
|
-
|
|
57
|
-
out = TREX_SimpleSegment(type=type_, value=value, segment_name=name)
|
|
58
|
-
return out
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def value_as_builtin_or_quantity_type(self) -> datetime|bool|str|PydanticUncertainQuantity:
|
|
62
|
-
return _value_as_builtin_or_quantity(self.value, self.type)
|
|
63
|
-
|
|
64
|
-
def as_trex_segment_str(self, segment_name) -> str:
|
|
65
|
-
return f'{segment_name}${self.type}:{self.value}'
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class TREX_Table(TREX_Segment):
|
|
69
|
-
col_names: list[str]
|
|
70
|
-
col_types: list[str]
|
|
71
|
-
data: list[list[str]]
|
|
72
|
-
|
|
73
|
-
@staticmethod
|
|
74
|
-
def from_trex_segmentstring( segment_str:str):
|
|
75
|
-
matches = re_table_pattern.match(segment_str)
|
|
76
|
-
if not matches:
|
|
77
|
-
raise T_REX_Segment_ParseError(f"Segment is not a valid TREX table: {segment_str}")
|
|
78
|
-
name, header, body = matches.groups()
|
|
79
|
-
|
|
80
|
-
column_heads = [re_col_head_pattern.match(colhead).groups() for colhead in header.split(':')]
|
|
81
|
-
col_names = [ch[0] for ch in column_heads]
|
|
82
|
-
col_types = [ch[1] for ch in column_heads]
|
|
83
|
-
|
|
84
|
-
data = [row.split(':') for row in body.split('::') ]
|
|
85
|
-
|
|
86
|
-
out = TREX_Table(col_names=col_names, col_types=col_types, data=data, segment_name=name)
|
|
87
|
-
return out
|
|
88
|
-
|
|
89
|
-
def n_rows(self) -> int:
|
|
90
|
-
return len(self.data)
|
|
91
|
-
|
|
92
|
-
def n_cols(self) -> int:
|
|
93
|
-
return len(self.col_names)
|
|
94
|
-
|
|
95
|
-
def row_data(self, row:int) -> list:
|
|
96
|
-
out = [_value_as_builtin_or_quantity(element, self.col_types[i]) for i, element in enumerate(self.data)]
|
|
97
|
-
return out
|
|
98
|
-
|
|
99
|
-
def col_data(self, col:str|int) -> list:
|
|
100
|
-
col_index = self._get_col_index(col)
|
|
101
|
-
type = self.col_types[col_index]
|
|
102
|
-
out = [_value_as_builtin_or_quantity(row[col_index],type) for row in self.data]
|
|
103
|
-
return out
|
|
104
|
-
|
|
105
|
-
def cell_data(self, row:int, col:str|int):
|
|
106
|
-
try:
|
|
107
|
-
col_index = self._get_col_index(col)
|
|
108
|
-
value = self.data[row][col_index]
|
|
109
|
-
type = self.col_types[col_index]
|
|
110
|
-
except ValueError:
|
|
111
|
-
logging.warning(f"row {row}, column {col} not found")
|
|
112
|
-
return None
|
|
113
|
-
|
|
114
|
-
return _value_as_builtin_or_quantity(value, type)
|
|
115
|
-
|
|
116
|
-
def _get_col_index(self, col:str|int):
|
|
117
|
-
if isinstance(col, str):
|
|
118
|
-
col_index = self.col_names.index(col)
|
|
119
|
-
elif isinstance(col, int):
|
|
120
|
-
col_index = col
|
|
121
|
-
else:
|
|
122
|
-
raise TypeError(f"Column must be specified as string or int: {col.__name__}")
|
|
123
|
-
return col_index
|
|
124
|
-
|
|
125
|
-
def as_trex_segment_str(self, name):
|
|
126
|
-
header = ':'.join([f'{el[0]}${el[1]}' for el in zip(self.col_names, self.col_types)])
|
|
127
|
-
date_rows = list()
|
|
128
|
-
for r in self.data:
|
|
129
|
-
row = ':'.join([str(cell) for cell in r])
|
|
130
|
-
date_rows.append(row)
|
|
131
|
-
data = '::'.join(date_rows)
|
|
132
|
-
s = f'{name}$${header}::{data}'
|
|
133
|
-
return s
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
class TREX(Extension, BaseModel):
|
|
138
|
-
name_:str
|
|
139
|
-
segments: dict[str,TREX_Segment]
|
|
140
|
-
|
|
141
|
-
@property
|
|
142
|
-
def name(self)->str:
|
|
143
|
-
return self.name_
|
|
144
|
-
|
|
145
|
-
@property
|
|
146
|
-
def type(self)->str:
|
|
147
|
-
return 'TREX'
|
|
148
|
-
|
|
149
|
-
@property
|
|
150
|
-
def data(self)->str:
|
|
151
|
-
seg_strings = list()
|
|
152
|
-
for s_name, s in self.segments.items():
|
|
153
|
-
seg_strings.append(s.as_trex_segment_str(s_name))
|
|
154
|
-
s_out = '+'.join(seg_strings)
|
|
155
|
-
return s_out
|
|
156
|
-
|
|
157
|
-
@staticmethod
|
|
158
|
-
def from_spec_fields(name, type, data):
|
|
159
|
-
if type != 'TREX':
|
|
160
|
-
logging.warning(f'Type {name} was given, but this extension should only be used with type "TREX". Will try to parse data as TREX')
|
|
161
|
-
|
|
162
|
-
if not data:
|
|
163
|
-
raise ValueError(f'T-REX must be a string of non zero length')
|
|
164
|
-
|
|
165
|
-
trex_str = data
|
|
166
|
-
|
|
167
|
-
# remove extension indicator. Precaution in case it is not done yet
|
|
168
|
-
if trex_str[0]=="*":
|
|
169
|
-
trex_str=trex_str[1:-1]
|
|
170
|
-
# remove line breaks. for editing T-REXes it's more convenient to have them in, so one never knows
|
|
171
|
-
trex_str = re.sub(r"\s+", "", trex_str)
|
|
172
|
-
|
|
173
|
-
segment_strings = trex_str.split('+')
|
|
174
|
-
out_segments = dict()
|
|
175
|
-
for s in segment_strings:
|
|
176
|
-
# there are only two valid options. The segment is a scalar or a table.
|
|
177
|
-
# Constructors do the parsing anyways and raise exceptions if invalid data
|
|
178
|
-
# try both options and then let it fail
|
|
179
|
-
try:
|
|
180
|
-
segment = TREX_SimpleSegment.from_trex_segmentstring(s)
|
|
181
|
-
except T_REX_Segment_ParseError:
|
|
182
|
-
segment = TREX_Table.from_trex_segmentstring(s)
|
|
183
|
-
out_segments[segment.segment_name] = segment
|
|
184
|
-
|
|
185
|
-
return TREX(name_=name, segments=out_segments)
|
|
186
|
-
|
|
187
|
-
def get_segment(self, segment_id:str) -> TREX_Segment:
|
|
188
|
-
return self.segments.get(segment_id)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
class TREX_Struct(TREX_Segment):
|
|
194
|
-
"""Struct is a special interpretation of a T-REX Table with one row"""
|
|
195
|
-
wrapped_table:TREX_Table
|
|
196
|
-
|
|
197
|
-
@property
|
|
198
|
-
def segment_name_(self):
|
|
199
|
-
return self.wrapped_table.segment_name
|
|
200
|
-
|
|
201
|
-
@field_validator('wrapped_table')
|
|
202
|
-
def validate_table(table):
|
|
203
|
-
if len(table.data) != 1:
|
|
204
|
-
raise ValidationError("Too many input rows. Struct can only have one row")
|
|
205
|
-
return table
|
|
206
|
-
|
|
207
|
-
def get(self, key):
|
|
208
|
-
return self.wrapped_table.cell_data(0, key)
|
|
209
|
-
|
|
210
|
-
def keys(self):
|
|
211
|
-
return self.wrapped_table.col_names
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def _to_datetime(trex_datetime):
|
|
217
|
-
try:
|
|
218
|
-
# return datetime.fromisoformat(trex_datetime) # should work with python 3.11
|
|
219
|
-
return datetime.strptime(trex_datetime, TREX_DATEFORMAT)
|
|
220
|
-
except (ValueError , TypeError) as e:
|
|
221
|
-
try:
|
|
222
|
-
return datetime.strptime(trex_datetime, TREX_TIMEFORMAT)
|
|
223
|
-
except (ValueError, TypeError):
|
|
224
|
-
return None
|
|
225
|
-
|
|
226
|
-
def _value_as_builtin_or_quantity(v:str|list[str], type:str) -> datetime|bool|str|PydanticUncertainQuantity:
|
|
227
|
-
match type:
|
|
228
|
-
case 'T.D':
|
|
229
|
-
return _to_datetime(v)
|
|
230
|
-
case 'T.B':
|
|
231
|
-
return v == 'T' or bool(v)
|
|
232
|
-
case 'T.A':
|
|
233
|
-
return v
|
|
234
|
-
case 'T.X':
|
|
235
|
-
raise NotImplementedError("Base36 encoded T-REX segment not implemented")
|
|
236
|
-
case 'E':
|
|
237
|
-
return v
|
|
238
|
-
case _:
|
|
239
|
-
return quantity_from_UN_CEFACT(v, type)
|
labfreed/TREXExtension/parse.py
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
from .data_model import TREX, T_REX_Segment_ParseError, TREX_SimpleSegment, TREX_Table
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def from_trex_string(trex_str, name=None, enforce_type=True) -> TREX:
|
|
8
|
-
if not trex_str:
|
|
9
|
-
raise ValueError(f'T-REX must be a string of non zero length')
|
|
10
|
-
|
|
11
|
-
# remove extension indicator. Precaution in case it is not done yet
|
|
12
|
-
if trex_str[0]=="*":
|
|
13
|
-
trex_str=trex_str[1:-1]
|
|
14
|
-
# remove line breaks. for editing T-REXes it's more convenient to have them in, so one never knows
|
|
15
|
-
trex_str = trex_str.replace('\n','')
|
|
16
|
-
|
|
17
|
-
d = re.match('((?P<name>.+)\$(?P<type>.+)/)?(?P<data>.+)', trex_str).groupdict()
|
|
18
|
-
if not d:
|
|
19
|
-
raise ValueError('TREX is invalid.')
|
|
20
|
-
|
|
21
|
-
type = d.get('type')
|
|
22
|
-
if not type:
|
|
23
|
-
logging.warning('No type given. Assume its trex')
|
|
24
|
-
elif type != 'TREX' and enforce_type:
|
|
25
|
-
logging.error(f'Extension type {type} is not TREX. Aborting')
|
|
26
|
-
raise ValueError(f'Extension type {type} is not TREX.')
|
|
27
|
-
else:
|
|
28
|
-
logging.warning('Extension type {type} is not TREX. Try anyways')
|
|
29
|
-
|
|
30
|
-
s_name = d.get('name')
|
|
31
|
-
if name and s_name:
|
|
32
|
-
logging.warning(f'conflicting names given. The string contained {s_name}, method parameter was {name}. Method parameter wins.')
|
|
33
|
-
elif not name and not s_name:
|
|
34
|
-
raise ValueError('No extension name was given')
|
|
35
|
-
elif s_name:
|
|
36
|
-
name = s_name
|
|
37
|
-
|
|
38
|
-
data = d.get('data')
|
|
39
|
-
|
|
40
|
-
segment_strings = data.split('+')
|
|
41
|
-
out_segments = dict()
|
|
42
|
-
for s in segment_strings:
|
|
43
|
-
# there are only two valid options. The segment is a scalar or a table.
|
|
44
|
-
# Constructors do the parsing anyways and raise exceptions if invalid data
|
|
45
|
-
# try both options and then let it fail
|
|
46
|
-
try:
|
|
47
|
-
segment = TREX_SimpleSegment.from_trex_segmentstring(s)
|
|
48
|
-
except T_REX_Segment_ParseError:
|
|
49
|
-
segment = TREX_Table.from_trex_segmentstring(s)
|
|
50
|
-
out_segments[segment.segment_name] = segment
|
|
51
|
-
trex = TREX(name_= name, segments=out_segments)
|
|
52
|
-
trex._trex_str = trex_str
|
|
53
|
-
return trex
|
labfreed-0.0.8.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
labfreed/__init__.py,sha256=tnLtz0mQ8ljO5N2ESqqI0ENkU-_cJi9_XwG0WkGZYKQ,87
|
|
2
|
-
labfreed/validation.py,sha256=dG-SubAguub67RTvw51xlErkqXbzzq_5rwxJwWg6SfY,2869
|
|
3
|
-
labfreed/DisplayNameExtension/DisplayNameExtension.py,sha256=FlT53u1EntpsLmho6GZtgIWBZBNWkl9STxzJBvojR6M,1033
|
|
4
|
-
labfreed/DisplayNameExtension/base36.py,sha256=2lwmEMWm8qrFJkcrP-nMPwS0eCm2THhCJ3Vk-TdGQg0,2455
|
|
5
|
-
labfreed/PAC_CAT/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
6
|
-
labfreed/PAC_CAT/data_model.py,sha256=hob-WNs2-633LmxQ7Ot3RBpcvStYFzdj20QDQZOQyqY,4306
|
|
7
|
-
labfreed/PAC_ID/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
labfreed/PAC_ID/data_model.py,sha256=DKU9Rptl9DcxiUdFf28XMyy4YcGUG7-a1EWuEpIFXVc,7931
|
|
9
|
-
labfreed/PAC_ID/parse.py,sha256=t38ABXZ0siUVDW5oEDmkF7uQ6iSX8Dbkeg-lWNlOgWA,5011
|
|
10
|
-
labfreed/PAC_ID/serialize.py,sha256=0BhF7aXGlLpr312lkBvl1O5fXDFZeLLPgSBddO9Y86Q,1963
|
|
11
|
-
labfreed/PAC_ID/well_known_segment_keys.py,sha256=zrzMvvS42urPpiwinI-IhHPgT3r86zEBl4TlEMOfzbU,338
|
|
12
|
-
labfreed/TREXExtension/data_model.py,sha256=eT4KyQklTO6m-wA28KyJ8wzT8ONhG3fOB3JU6b19ScY,8011
|
|
13
|
-
labfreed/TREXExtension/parse.py,sha256=OpRKu2FICAiOaaur6oxFY828xg1JEFj4fgPnXW79G4o,2118
|
|
14
|
-
labfreed/TREXExtension/uncertainty.py,sha256=l3WxrLnWTQYfX28gFisXwDcVPvT8bCAd4q6Xl02dRdE,1117
|
|
15
|
-
labfreed/TREXExtension/unit_utilities.py,sha256=WzrC1CZMgBccxADXFP6nLkMWVDqCCkNb3trPyt3BvF8,3826
|
|
16
|
-
labfreed-0.0.8.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
|
|
17
|
-
labfreed-0.0.8.dist-info/WHEEL,sha256=BXjIu84EnBiZ4HkNUBN93Hamt5EPQMQ6VkF7-VZ_Pu0,100
|
|
18
|
-
labfreed-0.0.8.dist-info/METADATA,sha256=36ASUvZ6qHjozWU0zI9_jPyPx3YNIqdppd5DGUSHjIs,206
|
|
19
|
-
labfreed-0.0.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|