labfreed 0.0.9__py2.py3-none-any.whl → 0.0.11__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 +5 -2
- labfreed/PAC_CAT/data_model copy.py +232 -0
- labfreed/PAC_CAT/data_model.py +319 -59
- labfreed/PAC_ID/data_model.py +42 -112
- labfreed/PAC_ID/extensions.py +55 -0
- labfreed/TREX/data_model.py +316 -396
- labfreed/TREX/parse.py +1 -69
- labfreed/TREX/unece_units.py +17 -1
- labfreed/__init__.py +1 -1
- labfreed/{PAC_ID/parse.py → parse_pac.py} +104 -59
- labfreed/utilities/base36.py +29 -13
- labfreed/utilities/extension_intertpreters.py +4 -0
- labfreed/utilities/utility_types.py +103 -0
- labfreed/{PAC_ID/well_known_segment_keys.py → utilities/well_known_keys.py} +1 -1
- labfreed/validation.py +3 -1
- {labfreed-0.0.9.dist-info → labfreed-0.0.11.dist-info}/METADATA +1 -1
- labfreed-0.0.11.dist-info/RECORD +22 -0
- labfreed/PAC_ID/serialize.py +0 -60
- labfreed/TREX/serialize.py +0 -3
- labfreed/conversion_tools/uncertainty.py +0 -32
- labfreed/conversion_tools/unit_utilities.py +0 -109
- labfreed-0.0.9.dist-info/RECORD +0 -22
- {labfreed-0.0.9.dist-info → labfreed-0.0.11.dist-info}/WHEEL +0 -0
- {labfreed-0.0.9.dist-info → labfreed-0.0.11.dist-info}/licenses/LICENSE +0 -0
labfreed/TREX/parse.py
CHANGED
|
@@ -51,78 +51,10 @@ def _from_trex_string(trex_str, name=None, enforce_type=True) -> TREX:
|
|
|
51
51
|
|
|
52
52
|
data = d.get('data')
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
out_segments = list()
|
|
56
|
-
for s in segment_strings:
|
|
57
|
-
# there are only two valid options. The segment is a scalar or a table.
|
|
58
|
-
# Constructors do the parsing anyways and raise exceptions if invalid data
|
|
59
|
-
# try both options and then let it fail
|
|
60
|
-
segment = _deserialize_table_segment_from_trex_segment_str(s)
|
|
61
|
-
if not segment:
|
|
62
|
-
segment = _deserialize_value_segment_from_trex_segment_str(s)
|
|
63
|
-
if not segment:
|
|
64
|
-
raise ValueError('TREX contains neither valid value segment nor table')
|
|
65
|
-
|
|
66
|
-
out_segments.append(segment)
|
|
67
|
-
trex = TREX(name_= name, segments=out_segments)
|
|
68
|
-
trex._trex_str = trex_str
|
|
54
|
+
trex = TREX.from_spec_fields(name=name, data=data)
|
|
69
55
|
|
|
70
56
|
return trex
|
|
71
57
|
|
|
72
58
|
|
|
73
59
|
|
|
74
|
-
def _deserialize_value_segment_from_trex_segment_str(trex_segment_str) -> ValueSegment:
|
|
75
|
-
#re_scalar_pattern = re.compile(f"(?P<name>[\w\.-]*?)\$(?P<unit>[\w\.]*?):(?P<value>.*)")
|
|
76
|
-
re_scalar_pattern = re.compile(f"(?P<name>.+?)\$(?P<unit>.+?):(?P<value>.+)")
|
|
77
|
-
matches = re_scalar_pattern.match(trex_segment_str)
|
|
78
|
-
if not matches:
|
|
79
|
-
return None
|
|
80
|
-
|
|
81
|
-
name, type_, value = matches.groups()
|
|
82
|
-
out = ValueSegment.get_subclass(type=type_, value=value, key=name)
|
|
83
|
-
return out
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def _deserialize_table_segment_from_trex_segment_str(trex_segment_str) -> TREX_Table:
|
|
87
|
-
# re_table_pattern = re.compile(f"(?P<tablename>[\w\.-]*?)\$\$(?P<header>[\w\.,\$:]*?)::(?P<body>.*)")
|
|
88
|
-
# re_col_head_pattern = re.compile(f"(?P<name>[\w\.-]*?)\$(?P<unit>[\w\.]*)")
|
|
89
|
-
re_table_pattern = re.compile(r"(?P<tablename>.+?)\$\$(?P<header>.+?)::(?P<body>.+)")
|
|
90
|
-
|
|
91
|
-
matches = re_table_pattern.match(trex_segment_str)
|
|
92
|
-
if not matches:
|
|
93
|
-
return None
|
|
94
|
-
name, header, body = matches.groups()
|
|
95
|
-
|
|
96
|
-
column_headers_str = header.split(':')
|
|
97
|
-
|
|
98
|
-
headers = []
|
|
99
|
-
for colum_header in column_headers_str:
|
|
100
|
-
ch = colum_header.split('$')
|
|
101
|
-
col_key = ch[0]
|
|
102
|
-
col_type = ch[1] if len(ch) > 1 else ''
|
|
103
|
-
headers.append(ColumnHeader(key=col_key, type=col_type))
|
|
104
|
-
|
|
105
|
-
data = [row.split(':') for row in body.split('::') ]
|
|
106
|
-
col_types = [h.type for h in headers]
|
|
107
|
-
# convert to correct value types
|
|
108
|
-
data_with_types = [[str_to_value_type(c,t) for c, t in zip(r, col_types)] for r in data]
|
|
109
|
-
data = [ TableRow(r) for r in data_with_types]
|
|
110
|
-
|
|
111
|
-
out = TREX_Table(column_headers=headers, data=data_with_types, key=name)
|
|
112
|
-
return out
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def str_to_value_type(s:str, t:str):
|
|
116
|
-
match t:
|
|
117
|
-
case 'T.D': v = DateValue(value=s)
|
|
118
|
-
case 'T.B': v = BoolValue(value=s)
|
|
119
|
-
case 'T.A': v = AlphanumericValue(value=s)
|
|
120
|
-
case 'T.T': v = TextValue(value=s)
|
|
121
|
-
case 'T.X': v = BinaryValue(value=s)
|
|
122
|
-
case 'E' : v = ErrorValue(value=s)
|
|
123
|
-
case _ : v = NumericValue(value=s)
|
|
124
|
-
return v
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
60
|
|
labfreed/TREX/unece_units.py
CHANGED
|
@@ -3,7 +3,6 @@ import json
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
@cache
|
|
8
7
|
def unece_units() -> list[dict]:
|
|
9
8
|
p = Path(__file__).parent / 'UneceUnits.json'
|
|
@@ -17,6 +16,23 @@ def unece_unit_codes():
|
|
|
17
16
|
return codes
|
|
18
17
|
|
|
19
18
|
|
|
19
|
+
def unece_unit(unit_code):
|
|
20
|
+
unit = [u for u in unece_units() if u['commonCode'] == unit_code]
|
|
21
|
+
if len(unit) == 0:
|
|
22
|
+
return None
|
|
23
|
+
else:
|
|
24
|
+
return unit[0]
|
|
25
|
+
|
|
26
|
+
def unit_symbol(unit:dict) ->str:
|
|
27
|
+
return unit.get('symbol')
|
|
28
|
+
|
|
29
|
+
def unit_name(unit:dict) ->str:
|
|
30
|
+
return unit.get('name')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
20
36
|
# def quantity_from_UN_CEFACT(value:str, unit_UN_CEFACT) -> UnitQuantity:
|
|
21
37
|
# """
|
|
22
38
|
# Maps units from https://unece.org/trade/documents/revision-17-annexes-i-iii
|
labfreed/__init__.py
CHANGED
|
@@ -2,36 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
from types import MappingProxyType
|
|
5
|
-
from .data_model import *
|
|
6
5
|
|
|
7
|
-
from
|
|
6
|
+
from labfreed.DisplayNameExtension.DisplayNameExtension import DisplayNames
|
|
7
|
+
from labfreed.PAC_CAT.data_model import PAC_CAT
|
|
8
|
+
from labfreed.PAC_ID.extensions import Extension, UnknownExtension
|
|
9
|
+
from labfreed.TREX.data_model import TREX
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
from .PAC_ID.data_model import *
|
|
13
|
+
|
|
14
|
+
from .validation import ValidationMessage, LabFREEDValidationError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PACID_With_Extensions(BaseModelWithValidationMessages):
|
|
22
|
+
pac_id: PACID
|
|
23
|
+
extensions: list[Extension] = Field(default_factory=list)
|
|
24
|
+
|
|
25
|
+
def __str__(self):
|
|
26
|
+
out = str(self.pac_id)
|
|
27
|
+
out += '*'.join(str(e) for e in self.extensions)
|
|
28
|
+
|
|
29
|
+
def get_extension_of_type(self, type:str) -> list[Extension]:
|
|
30
|
+
return [e for e in self.extensions if e.type == type]
|
|
31
|
+
|
|
32
|
+
def get_extension(self, name:str) -> Extension|None:
|
|
33
|
+
out = [e for e in self.extensions if e.name == name]
|
|
34
|
+
if not out:
|
|
35
|
+
return None
|
|
36
|
+
return out[0]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def serialize(self, use_short_notation_for_extensions=False, uppercase_only=False):
|
|
40
|
+
extensions_str = self._serialize_extensions(self.extensions, use_short_notation_for_extensions)
|
|
41
|
+
out = self.pac_id.serialize() + extensions_str
|
|
42
|
+
if uppercase_only:
|
|
43
|
+
out = out.upper()
|
|
44
|
+
return out
|
|
45
|
+
|
|
46
|
+
def to_url(self, use_short_notation_for_extensions=False, uppercase_only=False) -> str:
|
|
47
|
+
return self.serialize(use_short_notation_for_extensions, uppercase_only)
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def deserialize(cls, url, extension_interpreters ):
|
|
51
|
+
parser = PAC_Parser(extension_interpreters)
|
|
52
|
+
return parser.parse_pac_with_extensions(url)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _serialize_extensions(self, extensions:list[Extension], use_short_notation_for_extensions):
|
|
58
|
+
out = ''
|
|
59
|
+
short_notation = use_short_notation_for_extensions
|
|
60
|
+
for i, e in enumerate(extensions):
|
|
61
|
+
|
|
62
|
+
if short_notation and i==0:
|
|
63
|
+
if e.name=='N':
|
|
64
|
+
out += f'*{e.data}'
|
|
65
|
+
continue
|
|
66
|
+
else:
|
|
67
|
+
short_notation = False
|
|
68
|
+
if short_notation and i==1:
|
|
69
|
+
if e.name=='SUM':
|
|
70
|
+
out += f'*{e.data}'
|
|
71
|
+
continue
|
|
72
|
+
else:
|
|
73
|
+
short_notation = False
|
|
74
|
+
|
|
75
|
+
out += f'*{e.name}${e.type}/{e.data}'
|
|
76
|
+
return out
|
|
77
|
+
|
|
8
78
|
|
|
9
79
|
|
|
10
|
-
category_conventions = MappingProxyType(
|
|
11
|
-
{
|
|
12
|
-
'-MD': ['240', '21'],
|
|
13
|
-
'-MS': ['240', '10', '20', '21', '250'],
|
|
14
|
-
'-MC': ['240', '10', '20', '21', '250'],
|
|
15
|
-
'-MM': ['240', '10', '20', '21', '250']
|
|
16
|
-
}
|
|
17
|
-
)
|
|
18
80
|
|
|
19
81
|
|
|
20
|
-
extension_convention = MappingProxyType(
|
|
21
|
-
{
|
|
22
|
-
0: { 'name': 'N', 'type': 'N'},
|
|
23
|
-
1: { 'name': 'SUM', 'type': 'TREX'}
|
|
24
|
-
}
|
|
25
|
-
)
|
|
26
82
|
|
|
27
83
|
|
|
28
84
|
|
|
29
85
|
class PAC_Parser():
|
|
30
86
|
|
|
31
87
|
def __init__(self, extension_interpreters:dict[str, Extension]=None):
|
|
32
|
-
self.extension_interpreters = extension_interpreters or {}
|
|
88
|
+
self.extension_interpreters = extension_interpreters or {'TREX': TREX, 'N': DisplayNames}
|
|
33
89
|
|
|
34
|
-
def
|
|
90
|
+
def parse_pac_with_extensions(self, pac_url:str) -> PACID_With_Extensions:
|
|
35
91
|
if '*' in pac_url:
|
|
36
92
|
id_str, ext_str = pac_url.split('*', 1)
|
|
37
93
|
else:
|
|
@@ -42,14 +98,34 @@ class PAC_Parser():
|
|
|
42
98
|
extensions = self.parse_extensions(ext_str)
|
|
43
99
|
|
|
44
100
|
pac_with_extension = PACID_With_Extensions(pac_id=pac_id, extensions=extensions)
|
|
45
|
-
pac_with_extension.print_validation_messages(pac_url)
|
|
46
101
|
if not pac_with_extension.is_valid():
|
|
47
102
|
raise LabFREEDValidationError(validation_msgs = pac_with_extension.get_nested_validation_messages())
|
|
48
103
|
|
|
49
104
|
return pac_with_extension
|
|
50
105
|
|
|
51
106
|
|
|
52
|
-
def
|
|
107
|
+
def parse_pac_id(self,id_str:str) -> PACID:
|
|
108
|
+
m = re.match(f'(HTTPS://)?(PAC.)?(?P<issuer>.+?\..+?)/(?P<identifier>.*)', id_str)
|
|
109
|
+
d = m.groupdict()
|
|
110
|
+
|
|
111
|
+
id_segments = list()
|
|
112
|
+
default_keys = None
|
|
113
|
+
id_segments = self._parse_id_segments(d.get('identifier'))
|
|
114
|
+
|
|
115
|
+
pac = PACID(issuer= d.get('issuer'),
|
|
116
|
+
identifier=id_segments
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# if a segment starts with '-' the pac is interpreted as category
|
|
120
|
+
if any([s for s in pac.identifier if '-' in s.value]):
|
|
121
|
+
pac = PAC_CAT.from_pac_id(pac)
|
|
122
|
+
|
|
123
|
+
return pac
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _parse_id_segments(self, identifier:str):
|
|
53
129
|
if not identifier:
|
|
54
130
|
return []
|
|
55
131
|
|
|
@@ -70,45 +146,21 @@ class PAC_Parser():
|
|
|
70
146
|
return id_segments
|
|
71
147
|
|
|
72
148
|
|
|
73
|
-
def _apply_category_defaults(self, segments_in: list[IDSegment]):
|
|
74
|
-
|
|
75
|
-
segments = segments_in.copy()
|
|
76
|
-
default_keys = None
|
|
77
|
-
for s in segments:
|
|
78
|
-
if not s.key and default_keys:
|
|
79
|
-
s.key = default_keys.pop(0)
|
|
80
|
-
else:
|
|
81
|
-
default_keys = None
|
|
82
|
-
|
|
83
|
-
# category starts: start with new defaults.
|
|
84
|
-
if s.value in category_conventions.keys():
|
|
85
|
-
default_keys = category_conventions.get(s.value).copy() #copy, so the entries can be popped when used
|
|
86
|
-
return segments
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def parse_pac_id(self,id_str:str) -> PACID:
|
|
91
|
-
m = re.match(f'(HTTPS://)?(PAC.)?(?P<issuer>.+?\..+?)/(?P<identifier>.*)', id_str)
|
|
92
|
-
d = m.groupdict()
|
|
93
|
-
|
|
94
|
-
id_segments = list()
|
|
95
|
-
default_keys = None
|
|
96
|
-
id_segments = self.parse_id_segments(d.get('identifier'))
|
|
97
|
-
id_segments = self._apply_category_defaults(id_segments)
|
|
98
|
-
|
|
99
|
-
pac = PACID(issuer= d.get('issuer'),
|
|
100
|
-
identifier=Identifier(segments=id_segments)
|
|
101
|
-
)
|
|
102
|
-
return pac
|
|
103
149
|
|
|
104
150
|
|
|
105
151
|
def parse_extensions(self, extensions_str:str|None) -> list[Extension]:
|
|
152
|
+
|
|
106
153
|
extensions = list()
|
|
107
154
|
|
|
108
155
|
if not extensions_str:
|
|
109
156
|
return extensions
|
|
110
157
|
|
|
111
|
-
defaults =
|
|
158
|
+
defaults = MappingProxyType(
|
|
159
|
+
{
|
|
160
|
+
0: { 'name': 'N', 'type': 'N'},
|
|
161
|
+
1: { 'name': 'SUM', 'type': 'TREX'}
|
|
162
|
+
}
|
|
163
|
+
)
|
|
112
164
|
for i, e in enumerate(extensions_str.split('*')):
|
|
113
165
|
if e == '': #this will happen if first extension starts with *
|
|
114
166
|
continue
|
|
@@ -134,11 +186,4 @@ class PAC_Parser():
|
|
|
134
186
|
|
|
135
187
|
return extensions
|
|
136
188
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if __name__ == "__main__":
|
|
142
|
-
pacid_str = 'HTTPS://PAC.METTORIUS.COM/-DR/AB378/-MD/B-500/1235/-MS/AB/X:88/WWW/-MS/240:11/BB*ABCFD*A$HUR:25+B$CEL:99*BLUBB$TREX/A$HUR:25+B$CEL:99'
|
|
143
|
-
|
|
144
|
-
pac = PAC_Parser().parse_pac(pacid_str)
|
|
189
|
+
|
labfreed/utilities/base36.py
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import string
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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 =
|
|
21
|
+
base36_chars = _alphabet(base=36)
|
|
18
22
|
if num == 0:
|
|
19
23
|
return base36_chars[0]
|
|
20
|
-
|
|
24
|
+
base_36 = []
|
|
21
25
|
_num = num
|
|
22
26
|
while _num:
|
|
23
27
|
_num, i = divmod(_num, 36)
|
|
24
|
-
|
|
25
|
-
|
|
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,103 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from rich import print
|
|
6
|
+
|
|
7
|
+
from typing import Any, Tuple
|
|
8
|
+
from typing_extensions import Annotated
|
|
9
|
+
from pydantic import BaseModel, AfterValidator
|
|
10
|
+
import quantities as pq
|
|
11
|
+
from quantities import units
|
|
12
|
+
|
|
13
|
+
from labfreed.TREX.unece_units import unece_units
|
|
14
|
+
|
|
15
|
+
def validate_unit(unit_name:str) -> str :
|
|
16
|
+
"""
|
|
17
|
+
Pydantic validator function for the unit.
|
|
18
|
+
Checks if the unit is a valid unit.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
unit (str): unit symbol, e.g. 'kg'
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
str: the input unit.
|
|
26
|
+
|
|
27
|
+
Errors:
|
|
28
|
+
raises an AssertionError if validation fails
|
|
29
|
+
"""
|
|
30
|
+
if hasattr(pq, unit_name):
|
|
31
|
+
return unit_name
|
|
32
|
+
else:
|
|
33
|
+
assert False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Unit(BaseModel):
|
|
37
|
+
name: str
|
|
38
|
+
symbol: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Quantity(BaseModel):
|
|
42
|
+
value:int|float
|
|
43
|
+
unit: Unit
|
|
44
|
+
|
|
45
|
+
def __str__(self):
|
|
46
|
+
unit_symbol = self.unit.symbol
|
|
47
|
+
if unit_symbol == "dimensionless":
|
|
48
|
+
unit_symbol = ""
|
|
49
|
+
|
|
50
|
+
s = f"{str(self.value)} {unit_symbol}"
|
|
51
|
+
return s
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def unece_unit_code_from_quantity(q:Quantity):
|
|
55
|
+
by_name = [ u['commonCode'] for u in unece_units() if u.get('name','') == q.unit.name]
|
|
56
|
+
by_symbol = [ u['commonCode'] for u in unece_units() if u.get('symbol','') == q.unit.symbol]
|
|
57
|
+
code = list(set(by_name) | set(by_symbol))
|
|
58
|
+
if len(code) != 1:
|
|
59
|
+
raise ValueError(f'No UNECE unit code found for Quantity {str(q)}' )
|
|
60
|
+
return code[0]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# class DataTable(list):
|
|
64
|
+
# def __init__(self, headers:tuple[str, Any]):
|
|
65
|
+
# for h in headers:
|
|
66
|
+
# if len(h) != 2:
|
|
67
|
+
# raise ValueError(f'Headers must be tuples of length two. With a column name and type.')
|
|
68
|
+
# if not isinstance(h[0], str):
|
|
69
|
+
# raise ValueError(f'Invalid type of header name {h[0]}. Must be str')
|
|
70
|
+
# if not (h[1]):
|
|
71
|
+
# raise ValueError(f'Header type cannot be None')
|
|
72
|
+
# self.headers = headers
|
|
73
|
+
# super().__init__()
|
|
74
|
+
|
|
75
|
+
# def append(self, row:list):
|
|
76
|
+
# if len(row) != len(self.headers):
|
|
77
|
+
# raise ValueError(f'Row has different length than headers')
|
|
78
|
+
# super().append(row)
|
|
79
|
+
|
|
80
|
+
class DataTable(list):
|
|
81
|
+
def __init__(self, col_names:list[str]=None):
|
|
82
|
+
self.col_names = col_names
|
|
83
|
+
self.row_template = None
|
|
84
|
+
super().__init__()
|
|
85
|
+
|
|
86
|
+
def append(self, row:list):
|
|
87
|
+
if not self.row_template:
|
|
88
|
+
self.row_template = row.copy()
|
|
89
|
+
super().append(row)
|
|
90
|
+
|
|
91
|
+
def extend(self, iterable):
|
|
92
|
+
for item in iterable:
|
|
93
|
+
self.append(item)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
labfreed/validation.py
CHANGED
|
@@ -113,7 +113,9 @@ class BaseModelWithValidationMessages(BaseModel):
|
|
|
113
113
|
return filter_warnings(self.get_nested_validation_messages())
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def print_validation_messages(self, str_to_highlight_in):
|
|
116
|
+
def print_validation_messages(self, str_to_highlight_in=None):
|
|
117
|
+
if not str_to_highlight_in:
|
|
118
|
+
str_to_highlight_in = str(self)
|
|
117
119
|
msgs = self.get_nested_validation_messages()
|
|
118
120
|
print('\n'.join(['\n',
|
|
119
121
|
'=======================================',
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
labfreed/__init__.py,sha256=tRirlmqlRWmM41CGkaBq1os_KepqXPPCnouvqpWHAvg,88
|
|
2
|
+
labfreed/parse_pac.py,sha256=HA3-jAnw2crsXMW_D7Tw-z99qnUWL5MBQVXEzdYP2m4,6287
|
|
3
|
+
labfreed/validation.py,sha256=QwkZWJhAjWbPUZtJJwjVYsw9TxeFhdbZaKjrPPIpuAA,5937
|
|
4
|
+
labfreed/DisplayNameExtension/DisplayNameExtension.py,sha256=l9JZY2eRS0V-H5h3-WXIHiiBJuljns-_e_t9Bp84_CU,1155
|
|
5
|
+
labfreed/PAC_CAT/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
6
|
+
labfreed/PAC_CAT/data_model copy.py,sha256=JWMVkwkX9vWZayOLOzdTHk3VZVYBuyupumNqL-cWCxU,9611
|
|
7
|
+
labfreed/PAC_CAT/data_model.py,sha256=pcib1lEQuqejWP7dfmPUtLakz-y-zeDb9CIe94Jmz0A,13677
|
|
8
|
+
labfreed/PAC_ID/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
labfreed/PAC_ID/data_model.py,sha256=g09qgC-TV6fjJw9VyDF6mTJ6co2i2RKZc0Z-BmiiUIQ,7483
|
|
10
|
+
labfreed/PAC_ID/extensions.py,sha256=bvuZnlNKUdwsDLrPm8fyifqPn_PR4wCVkkScFnvRiuM,1158
|
|
11
|
+
labfreed/TREX/UneceUnits.json,sha256=kwfQSp_nTuWbADfBBgqTWrvPl6XtM5SedEVLbMJrM7M,898953
|
|
12
|
+
labfreed/TREX/data_model.py,sha256=_xhnYGMcMPa0uf_020epq88zqHT1wdsUPC2ELJcSRWE,29684
|
|
13
|
+
labfreed/TREX/parse.py,sha256=86962VEJpkrTcT436iFIB5dNed5WHABzpjxRjkA3PXo,2043
|
|
14
|
+
labfreed/TREX/unece_units.py,sha256=scPKdsPzY1neAdFOhA08_tRZaR-yplM8mBhIzzDqZBk,3006
|
|
15
|
+
labfreed/utilities/base36.py,sha256=_yX8aQ1OwrK5tnJU1NUEzQSFGr9xAVnNvPObpNzCPYs,2895
|
|
16
|
+
labfreed/utilities/extension_intertpreters.py,sha256=B3IFJLfVMJQuPfBBtX6ywlDUZEi7_x6tY4g8V7SpWSs,124
|
|
17
|
+
labfreed/utilities/utility_types.py,sha256=Zhk8Mu4hHjkn1gs8oh7vOxxaT7L7wLMVG40ZOWCKGK4,2865
|
|
18
|
+
labfreed/utilities/well_known_keys.py,sha256=nqk66kHdSwJTJfMKlP-xQbBglS8F_NoWsGkfOVITFN0,331
|
|
19
|
+
labfreed-0.0.11.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
|
|
20
|
+
labfreed-0.0.11.dist-info/WHEEL,sha256=BXjIu84EnBiZ4HkNUBN93Hamt5EPQMQ6VkF7-VZ_Pu0,100
|
|
21
|
+
labfreed-0.0.11.dist-info/METADATA,sha256=G-lDt39hiUI8Sup2HPxXxuyPmrLZCvDXxQCXrFNpU_Y,207
|
|
22
|
+
labfreed-0.0.11.dist-info/RECORD,,
|
labfreed/PAC_ID/serialize.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
from .data_model import *
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class PAC_Serializer():
|
|
7
|
-
def to_url(self, pac:PACID|PACID_With_Extensions, extensions:list[Extension]=None, use_short_notation_for_extensions=False, uppercase_only=False) -> str:
|
|
8
|
-
if isinstance(pac, PACID_With_Extensions):
|
|
9
|
-
if extensions:
|
|
10
|
-
raise ValueError('Extensions were given twice, as part of PACID_With_Extension and as method parameter.')
|
|
11
|
-
extensions = pac.extensions
|
|
12
|
-
pac = pac.pac_id
|
|
13
|
-
issuer = pac.issuer
|
|
14
|
-
extensions_str = self._serialize_extensions(extensions, use_short_notation_for_extensions)
|
|
15
|
-
id_segments = self._serialize_id_segments(pac.identifier.segments)
|
|
16
|
-
out = f"HTTPS://PAC.{issuer}{id_segments}{extensions_str}"
|
|
17
|
-
if uppercase_only:
|
|
18
|
-
out = out.upper()
|
|
19
|
-
return out
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _serialize_id_segments(self, segments):
|
|
23
|
-
out = ''
|
|
24
|
-
for s in segments:
|
|
25
|
-
if s.key:
|
|
26
|
-
out += f'/{s.key}:{s.value}'
|
|
27
|
-
else:
|
|
28
|
-
out += f'/{s.value}'
|
|
29
|
-
return out
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _serialize_extensions(self, extensions:list[Extension], use_short_notation_for_extensions):
|
|
33
|
-
out = ''
|
|
34
|
-
short_notation = use_short_notation_for_extensions
|
|
35
|
-
for i, e in enumerate(extensions):
|
|
36
|
-
|
|
37
|
-
if short_notation and i==0:
|
|
38
|
-
if e.name=='N':
|
|
39
|
-
out += f'*{e.data}'
|
|
40
|
-
continue
|
|
41
|
-
else:
|
|
42
|
-
short_notation = False
|
|
43
|
-
if short_notation and i==1:
|
|
44
|
-
if e.name=='SUM':
|
|
45
|
-
out += f'*{e.data}'
|
|
46
|
-
continue
|
|
47
|
-
else:
|
|
48
|
-
short_notation = False
|
|
49
|
-
|
|
50
|
-
out += f'*{e.name}${e.type}/{e.data}'
|
|
51
|
-
return out
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def main():
|
|
56
|
-
pass
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if __name__ == "__main__":
|
|
60
|
-
main()
|
labfreed/TREX/serialize.py
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
from math import floor, log10, pow
|
|
2
|
-
|
|
3
|
-
def to_significant_digits_str(x:int|float, uncertainty:float|int) -> str:
|
|
4
|
-
if uncertainty == None:
|
|
5
|
-
if isinstance(x, float):
|
|
6
|
-
Warning(f'Uncertainty was given as none. Returning unrounded number')
|
|
7
|
-
return str(x)
|
|
8
|
-
else:
|
|
9
|
-
uncertainty = 1
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
log_least_significant_digit = floor(log10(uncertainty))
|
|
13
|
-
digits = -log_least_significant_digit
|
|
14
|
-
|
|
15
|
-
x_significant = round(x, digits)
|
|
16
|
-
|
|
17
|
-
if digits <= 0:
|
|
18
|
-
return str(int(x_significant))
|
|
19
|
-
else:
|
|
20
|
-
return str(x_significant)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if __name__ == "__main__":
|
|
25
|
-
print(to_significant_digits_str(111111.1111111, 1000))
|
|
26
|
-
print(to_significant_digits_str(111111.1111111, 100))
|
|
27
|
-
print(to_significant_digits_str(111111.1111111, 10))
|
|
28
|
-
print(to_significant_digits_str(111111.1111111, 1))
|
|
29
|
-
print(to_significant_digits_str(111111.1111111, 0.1))
|
|
30
|
-
print(to_significant_digits_str(111111.1111111, 0.01))
|
|
31
|
-
print(to_significant_digits_str(111111.1111111, 0.001))
|
|
32
|
-
print(to_significant_digits_str(111111.1111111, 0.0001))
|