labfreed 0.0.7__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.7.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.7.dist-info/RECORD +0 -19
- /labfreed/{TREXExtension → conversion_tools}/uncertainty.py +0 -0
- /labfreed/{DisplayNameExtension → utilities}/base36.py +0 -0
- {labfreed-0.0.7.dist-info → labfreed-0.0.9.dist-info}/WHEEL +0 -0
- {labfreed-0.0.7.dist-info → labfreed-0.0.9.dist-info}/licenses/LICENSE +0 -0
labfreed/TREX/parse.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from .data_model import *
|
|
5
|
+
from labfreed.validation import LabFREEDValidationError
|
|
6
|
+
|
|
7
|
+
class TREX_Parser():
|
|
8
|
+
def __init__(self, suppress_errors=False):
|
|
9
|
+
self._suppress_errors = suppress_errors
|
|
10
|
+
|
|
11
|
+
def parse_trex_str(self, trex_str, name=None) -> TREX:
|
|
12
|
+
trex = _from_trex_string(trex_str, name=name)
|
|
13
|
+
|
|
14
|
+
trex.print_validation_messages(trex_str)
|
|
15
|
+
if not trex.is_valid() and not self._suppress_errors:
|
|
16
|
+
raise LabFREEDValidationError(validation_msgs = trex.get_nested_validation_messages())
|
|
17
|
+
|
|
18
|
+
return trex
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _from_trex_string(trex_str, name=None, enforce_type=True) -> TREX:
|
|
22
|
+
if not trex_str:
|
|
23
|
+
raise ValueError(f'T-REX must be a string of non zero length')
|
|
24
|
+
|
|
25
|
+
# remove extension indicator. Precaution in case it is not done yet
|
|
26
|
+
if trex_str[0]=="*":
|
|
27
|
+
trex_str=trex_str[1:-1]
|
|
28
|
+
# remove line breaks. for editing T-REXes it's more convenient to have them in, so one never knows
|
|
29
|
+
trex_str = trex_str.replace('\n','')
|
|
30
|
+
|
|
31
|
+
d = re.match('((?P<name>.+)\$(?P<type>.+)/)?(?P<data>.+)', trex_str).groupdict()
|
|
32
|
+
if not d:
|
|
33
|
+
raise ValueError('TREX is invalid.')
|
|
34
|
+
|
|
35
|
+
type = d.get('type')
|
|
36
|
+
if not type:
|
|
37
|
+
logging.warning('No type given. Assume its trex')
|
|
38
|
+
elif type != 'TREX' and enforce_type:
|
|
39
|
+
logging.error(f'Extension type {type} is not TREX. Aborting')
|
|
40
|
+
raise ValueError(f'Extension type {type} is not TREX.')
|
|
41
|
+
else:
|
|
42
|
+
logging.warning('Extension type {type} is not TREX. Try anyways')
|
|
43
|
+
|
|
44
|
+
s_name = d.get('name')
|
|
45
|
+
if name and s_name:
|
|
46
|
+
logging.warning(f'conflicting names given. The string contained {s_name}, method parameter was {name}. Method parameter wins.')
|
|
47
|
+
elif not name and not s_name:
|
|
48
|
+
raise ValueError('No extension name was given')
|
|
49
|
+
elif s_name:
|
|
50
|
+
name = s_name
|
|
51
|
+
|
|
52
|
+
data = d.get('data')
|
|
53
|
+
|
|
54
|
+
segment_strings = data.split('+')
|
|
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
|
|
69
|
+
|
|
70
|
+
return trex
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
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
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@cache
|
|
8
|
+
def unece_units() -> list[dict]:
|
|
9
|
+
p = Path(__file__).parent / 'UneceUnits.json'
|
|
10
|
+
with open(p) as f:
|
|
11
|
+
l = json.load(f)
|
|
12
|
+
return l
|
|
13
|
+
|
|
14
|
+
@cache
|
|
15
|
+
def unece_unit_codes():
|
|
16
|
+
codes= [u.get('commonCode') for u in unece_units() if u.get('state') == 'ACTIVE']
|
|
17
|
+
return codes
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# def quantity_from_UN_CEFACT(value:str, unit_UN_CEFACT) -> UnitQuantity:
|
|
21
|
+
# """
|
|
22
|
+
# Maps units from https://unece.org/trade/documents/revision-17-annexes-i-iii
|
|
23
|
+
# to an object of the quantities library https://python-quantities.readthedocs.io/en/latest/index.html
|
|
24
|
+
# """
|
|
25
|
+
# # cast to numeric type. try int first, which will fail if string has no decimals.
|
|
26
|
+
# # nothing to worry yet: try floast next. if that fails the input was not a str representation of a number
|
|
27
|
+
# try:
|
|
28
|
+
# value_out = int(value)
|
|
29
|
+
# except ValueError:
|
|
30
|
+
# try:
|
|
31
|
+
# value_out = float(value)
|
|
32
|
+
# except ValueError as e:
|
|
33
|
+
# raise Exception(f'Input {value} is not a str representation of a number') from e
|
|
34
|
+
|
|
35
|
+
# d = {um[0]: um[1] for um in unit_map}
|
|
36
|
+
|
|
37
|
+
# unit = d.get(unit_UN_CEFACT)
|
|
38
|
+
# if not unit:
|
|
39
|
+
# raise NotImplementedError(f"lookup for unit {unit} not implemented")
|
|
40
|
+
# out = UnitQuantity(data=value_out, unit_name=unit.name, unit_symbol=unit.symbol)
|
|
41
|
+
|
|
42
|
+
# return out
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# def quantity_to_UN_CEFACT(value:UnitQuantity ) -> Tuple[int|float, str]:
|
|
47
|
+
# d = {um[1].symbol: um[0] for um in unit_map}
|
|
48
|
+
|
|
49
|
+
# unit_un_cefact = d.get(value.unit_symbol)
|
|
50
|
+
# if not unit_un_cefact:
|
|
51
|
+
# raise NotImplementedError(f"lookup for unit {value.unit_symbol} not implemented")
|
|
52
|
+
# return value.data, unit_un_cefact
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def check_compatibility_unece_quantities():
|
|
59
|
+
unece = get_unece_units()
|
|
60
|
+
print(f'Number of units in file: {len(unece)}')
|
|
61
|
+
|
|
62
|
+
failed = list()
|
|
63
|
+
sucess = list()
|
|
64
|
+
for u in unece:
|
|
65
|
+
if u.get('state') == 'ACTIVE':
|
|
66
|
+
try:
|
|
67
|
+
if not u.get('symbol'):
|
|
68
|
+
assert False
|
|
69
|
+
u.get('name')
|
|
70
|
+
validate_unit(u.get('symbol'))
|
|
71
|
+
sucess.append(u)
|
|
72
|
+
except AssertionError as e:
|
|
73
|
+
failed.append(u)
|
|
74
|
+
else:
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
print('[blue] FAILED [/blue]')
|
|
80
|
+
for u in failed:
|
|
81
|
+
print(f'{u.get('commonCode')}: {u.get('name')}')
|
|
82
|
+
|
|
83
|
+
print('[yellow] SUCCESSFUL [/yellow]')
|
|
84
|
+
for u in sucess:
|
|
85
|
+
print(u)
|
|
86
|
+
|
|
87
|
+
print(f'{len(failed)} / {len(unece)} failed to convert')
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
labfreed/__init__.py
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from rich import print
|
|
6
|
+
|
|
1
7
|
from typing import Tuple
|
|
2
8
|
from typing_extensions import Annotated
|
|
3
9
|
from pydantic import BaseModel, AfterValidator
|
|
4
10
|
import quantities as pq
|
|
5
|
-
from quantities import
|
|
11
|
+
from quantities import units
|
|
6
12
|
from .uncertainty import to_significant_digits_str
|
|
7
13
|
|
|
8
14
|
|
|
9
|
-
|
|
10
15
|
def validate_unit(unit_name:str) -> str :
|
|
11
16
|
"""
|
|
12
17
|
Pydantic validator function for the unit.
|
|
@@ -92,51 +97,12 @@ unit_map = [
|
|
|
92
97
|
|
|
93
98
|
]
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
def quantity_from_UN_CEFACT(value:str, unit_UN_CEFACT) -> PydanticUncertainQuantity:
|
|
97
|
-
"""
|
|
98
|
-
Maps units from https://unece.org/trade/documents/revision-17-annexes-i-iii
|
|
99
|
-
to an object of the quantities library https://python-quantities.readthedocs.io/en/latest/index.html
|
|
100
|
-
"""
|
|
101
|
-
# cast to numeric type. try int first, which will fail if string has no decimals.
|
|
102
|
-
# nothing to worry yet: try floast next. if that fails the input was not a str representation of a number
|
|
103
|
-
try:
|
|
104
|
-
value_out = int(value)
|
|
105
|
-
except ValueError:
|
|
106
|
-
try:
|
|
107
|
-
value_out = float(value)
|
|
108
|
-
except ValueError as e:
|
|
109
|
-
raise Exception(f'Input {value} is not a str representation of a number') from e
|
|
110
|
-
|
|
111
|
-
d = {um[0]: um[1] for um in unit_map}
|
|
112
|
-
|
|
113
|
-
unit = d.get(unit_UN_CEFACT)
|
|
114
|
-
if not unit:
|
|
115
|
-
raise NotImplementedError(f"lookup for unit {unit} not implemented")
|
|
116
|
-
out = PydanticUncertainQuantity(data=value_out, unit_name=unit.name, unit_symbol=unit.symbol)
|
|
117
|
-
|
|
118
|
-
return out
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def quantity_to_UN_CEFACT(value:PydanticUncertainQuantity ) -> Tuple[int|float, str]:
|
|
122
|
-
d = {um[1].symbol: um[0] for um in unit_map}
|
|
123
|
-
|
|
124
|
-
unit_un_cefact = d.get(value.unit_symbol)
|
|
125
|
-
if not unit_un_cefact:
|
|
126
|
-
raise NotImplementedError(f"lookup for unit {value.unit_symbol} not implemented")
|
|
127
|
-
return value.data, unit_un_cefact
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
100
|
|
|
133
101
|
|
|
134
102
|
|
|
135
103
|
|
|
136
|
-
|
|
137
104
|
if __name__ == "__main__":
|
|
138
|
-
pass
|
|
139
|
-
|
|
105
|
+
pass
|
|
140
106
|
|
|
141
107
|
|
|
142
108
|
|
labfreed/validation.py
CHANGED
|
@@ -1,71 +1,147 @@
|
|
|
1
1
|
from pydantic import BaseModel, Field, PrivateAttr
|
|
2
2
|
from typing import List, Set, Tuple
|
|
3
3
|
|
|
4
|
+
from rich import print
|
|
5
|
+
from rich.text import Text
|
|
6
|
+
|
|
4
7
|
|
|
5
8
|
domain_name_pattern = r"(?!-)([A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,63}"
|
|
6
9
|
hsegment_pattern = r"[A-Za-z0-9_\-\.~!$&'()+,:;=@]|%[0-9A-Fa-f]{2}"
|
|
7
10
|
|
|
8
11
|
|
|
9
|
-
class
|
|
12
|
+
class ValidationMessage(BaseModel):
|
|
10
13
|
source:str
|
|
11
14
|
type: str
|
|
12
15
|
problem_msg:str
|
|
13
16
|
recommendation_msg: str = ""
|
|
14
17
|
highlight:str = "" #this can be used to highlight problematic parts
|
|
15
18
|
highlight_sub:list[str] = Field(default_factory=list())
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def emphazised_highlight(self):
|
|
22
|
+
fmt = lambda s: f'[emph]{s}[/emph]'
|
|
23
|
+
|
|
24
|
+
if not self.highlight_sub:
|
|
25
|
+
return fmt(self.highlight)
|
|
26
|
+
|
|
27
|
+
result = []
|
|
28
|
+
for c in self.highlight:
|
|
29
|
+
if c in self.highlight_sub:
|
|
30
|
+
result.append(fmt(c))
|
|
31
|
+
else:
|
|
32
|
+
result.append(c)
|
|
33
|
+
|
|
34
|
+
return ''.join(result)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LabFREEDValidationError(ValueError):
|
|
38
|
+
def __init__(self, message=None, validation_msgs=None):
|
|
39
|
+
super().__init__(message)
|
|
40
|
+
self._validation_msgs = validation_msgs
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def validation_msgs(self):
|
|
44
|
+
return self._validation_msgs
|
|
16
45
|
|
|
17
46
|
|
|
18
47
|
|
|
19
48
|
|
|
20
|
-
class
|
|
49
|
+
class BaseModelWithValidationMessages(BaseModel):
|
|
21
50
|
""" Extension of Pydantic BaseModel, so that validator can issue warnings.
|
|
22
51
|
The purpose of that is to allow only minimal validation but on top check for stricter recommendations"""
|
|
23
|
-
|
|
52
|
+
_validation_messages: list[ValidationMessage] = PrivateAttr(default_factory=list)
|
|
24
53
|
|
|
25
|
-
def
|
|
54
|
+
def add_validation_message(self, *, msg: str, type:str, recommendation:str="", source:str="", highlight_pattern="", highlight_sub=None):
|
|
26
55
|
if not highlight_sub:
|
|
27
56
|
highlight_sub = []
|
|
28
|
-
w =
|
|
29
|
-
if not w in self._warnings:
|
|
30
|
-
self._warnings.append(w)
|
|
57
|
+
w = ValidationMessage(problem_msg=msg, recommendation_msg=recommendation, source=source, type=type, highlight=highlight_pattern, highlight_sub=highlight_sub)
|
|
31
58
|
|
|
32
|
-
|
|
33
|
-
|
|
59
|
+
if not w in self._validation_messages:
|
|
60
|
+
self._validation_messages.append(w)
|
|
34
61
|
|
|
35
|
-
def
|
|
36
|
-
self.
|
|
62
|
+
def get_validation_messages(self) -> list[ValidationMessage]:
|
|
63
|
+
return self._validation_messages
|
|
37
64
|
|
|
65
|
+
def get_errors(self) -> list[ValidationMessage]:
|
|
66
|
+
return filter_errors(self._validation_messages)
|
|
38
67
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"""
|
|
42
|
-
Recursively extract warnings from a Pydantic model and its nested fields.
|
|
68
|
+
def get_warnings(self) -> list[ValidationMessage]:
|
|
69
|
+
return filter_warnings(self._validation_messages)
|
|
43
70
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
def is_valid(self) -> bool:
|
|
72
|
+
return len(filter_errors(self.get_nested_validation_messages())) == 0
|
|
73
|
+
|
|
74
|
+
# Function to extract warnings from a model and its nested models
|
|
75
|
+
def get_nested_validation_messages(self, parent_name: str = "", visited: Set[int] = None) -> List[ValidationMessage]:
|
|
76
|
+
"""
|
|
77
|
+
Recursively extract warnings from a Pydantic model and its nested fields.
|
|
78
|
+
|
|
79
|
+
:param model: The Pydantic model instance to inspect.
|
|
80
|
+
:param parent_name: The name of the parent model to track the path.
|
|
81
|
+
:return: List of tuples containing (model name, warning message).
|
|
82
|
+
"""
|
|
83
|
+
if visited is None:
|
|
84
|
+
visited = set()
|
|
85
|
+
|
|
86
|
+
model_id = id(self)
|
|
87
|
+
if model_id in visited:
|
|
88
|
+
return []
|
|
89
|
+
visited.add(model_id)
|
|
90
|
+
|
|
91
|
+
warnings_list = [warning for warning in self.get_validation_messages()]
|
|
92
|
+
# warnings_list = [(parent_name or self.__class__.__name__, model_id, warning) for warning in self.get_validation_messages()]
|
|
93
|
+
|
|
57
94
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
95
|
+
for field_name, field in self.__fields__.items():
|
|
96
|
+
full_path = f"{parent_name}.{field_name}" if parent_name else field_name
|
|
97
|
+
value = getattr(self, field_name)
|
|
61
98
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
if isinstance(value, BaseModelWithValidationMessages):
|
|
100
|
+
warnings_list.extend(value.get_nested_validation_messages(full_path, visited))
|
|
101
|
+
elif isinstance(value, list):
|
|
102
|
+
for index, item in enumerate(value):
|
|
103
|
+
if isinstance(item, BaseModelWithValidationMessages):
|
|
104
|
+
list_path = f"{full_path}[{index}]"
|
|
105
|
+
warnings_list.extend(item.get_nested_validation_messages(list_path, visited))
|
|
106
|
+
return warnings_list
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_nested_errors(self) -> list[ValidationMessage]:
|
|
110
|
+
return filter_errors(self.get_nested_validation_messages())
|
|
111
|
+
|
|
112
|
+
def get_nested_warnings(self) -> list[ValidationMessage]:
|
|
113
|
+
return filter_warnings(self.get_nested_validation_messages())
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def print_validation_messages(self, str_to_highlight_in):
|
|
117
|
+
msgs = self.get_nested_validation_messages()
|
|
118
|
+
print('\n'.join(['\n',
|
|
119
|
+
'=======================================',
|
|
120
|
+
'Validation Results',
|
|
121
|
+
'---------------------------------------'
|
|
122
|
+
]
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
for m in msgs:
|
|
127
|
+
if m.type.casefold() == "error":
|
|
128
|
+
color = 'red'
|
|
129
|
+
else:
|
|
130
|
+
color = 'yellow'
|
|
131
|
+
|
|
132
|
+
text = Text.from_markup(f'\n [bold {color}]{m.type} [/bold {color}] in \t {m.source}' )
|
|
133
|
+
print(text)
|
|
134
|
+
formatted_highlight = m.emphazised_highlight.replace('emph', f'bold {color}')
|
|
135
|
+
fmtd = str_to_highlight_in.replace(m.highlight, formatted_highlight)
|
|
136
|
+
fmtd = Text.from_markup(fmtd)
|
|
137
|
+
print(fmtd)
|
|
138
|
+
print(Text.from_markup(f'{m.problem_msg}'))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def filter_errors(val_msg:list[ValidationMessage]) -> list[ValidationMessage]:
|
|
143
|
+
return [ m for m in val_msg if m.type.casefold() == "error" ]
|
|
69
144
|
|
|
70
|
-
|
|
145
|
+
def filter_warnings(val_msg:list[ValidationMessage]) -> list[ValidationMessage]:
|
|
146
|
+
return [ m for m in val_msg if m.type.casefold() != "error" ]
|
|
71
147
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
labfreed/__init__.py,sha256=NqKE8cu-i6wAIxbyZat0L_dbFSjpJgHYzJYqGEGXBBk,87
|
|
2
|
+
labfreed/validation.py,sha256=3w69iAX_fH6QdceMj_sgR9pQhJsSy5mXhxLxz_YNHfU,5850
|
|
3
|
+
labfreed/DisplayNameExtension/DisplayNameExtension.py,sha256=MKc9YzI5KKEfnH8glXEteB29ZMfZxbmvFzJTLKbOX_g,1051
|
|
4
|
+
labfreed/PAC_CAT/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
5
|
+
labfreed/PAC_CAT/data_model.py,sha256=hob-WNs2-633LmxQ7Ot3RBpcvStYFzdj20QDQZOQyqY,4306
|
|
6
|
+
labfreed/PAC_ID/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
labfreed/PAC_ID/data_model.py,sha256=c2exBF2AXwXxudPS9Cd74xex0VB5Q-EeJHQi43LRmwQ,9300
|
|
8
|
+
labfreed/PAC_ID/parse.py,sha256=g0AXzJ9El8TZ2c05GOMN6yfa3FUAFDFVndx5mefe_ZM,5175
|
|
9
|
+
labfreed/PAC_ID/serialize.py,sha256=0BhF7aXGlLpr312lkBvl1O5fXDFZeLLPgSBddO9Y86Q,1963
|
|
10
|
+
labfreed/PAC_ID/well_known_segment_keys.py,sha256=zrzMvvS42urPpiwinI-IhHPgT3r86zEBl4TlEMOfzbU,338
|
|
11
|
+
labfreed/TREX/UneceUnits.json,sha256=kwfQSp_nTuWbADfBBgqTWrvPl6XtM5SedEVLbMJrM7M,898953
|
|
12
|
+
labfreed/TREX/data_model.py,sha256=727da6PPvl-5gwPbljTDNprU38de80Cs1Q1bxKJ6DWI,29804
|
|
13
|
+
labfreed/TREX/parse.py,sha256=rV7EDCaY9cmBJNqsrSZQLNzcivyOCzLsKXW2sAOitaA,4867
|
|
14
|
+
labfreed/TREX/serialize.py,sha256=5M0c8l4xTtiX4PIKVRI3Gt-jNFYNcKOeuO3C-m1HE5g,89
|
|
15
|
+
labfreed/TREX/unece_units.py,sha256=7PL4eR8SGklnuR5gC4ooAvgFFYg9dCF9HmwIU25OZYw,2682
|
|
16
|
+
labfreed/conversion_tools/uncertainty.py,sha256=l3WxrLnWTQYfX28gFisXwDcVPvT8bCAd4q6Xl02dRdE,1117
|
|
17
|
+
labfreed/conversion_tools/unit_utilities.py,sha256=5NXDt-XRkajcg2lLdg0vDBWbmfhUCqeY4hu_k6PkbCY,2445
|
|
18
|
+
labfreed/utilities/base36.py,sha256=2lwmEMWm8qrFJkcrP-nMPwS0eCm2THhCJ3Vk-TdGQg0,2455
|
|
19
|
+
labfreed-0.0.9.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
|
|
20
|
+
labfreed-0.0.9.dist-info/WHEEL,sha256=BXjIu84EnBiZ4HkNUBN93Hamt5EPQMQ6VkF7-VZ_Pu0,100
|
|
21
|
+
labfreed-0.0.9.dist-info/METADATA,sha256=whgM7VD1R3S7JlP96c9mVBYWoDGwJzjdxdmE2nZRMFU,206
|
|
22
|
+
labfreed-0.0.9.dist-info/RECORD,,
|