labfreed 0.0.5__py3-none-any.whl → 0.2.0b0__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/PAC_CAT/__init__.py +16 -0
- labfreed/PAC_CAT/category_base.py +51 -0
- labfreed/PAC_CAT/pac_cat.py +159 -0
- labfreed/PAC_CAT/predefined_categories.py +190 -0
- labfreed/PAC_ID/__init__.py +19 -0
- labfreed/PAC_ID/extension.py +48 -0
- labfreed/PAC_ID/id_segment.py +90 -0
- labfreed/PAC_ID/pac_id.py +140 -0
- labfreed/PAC_ID/url_parser.py +154 -0
- labfreed/PAC_ID/url_serializer.py +80 -0
- labfreed/PAC_ID_Resolver/__init__.py +2 -0
- labfreed/PAC_ID_Resolver/cit_v1.py +149 -0
- labfreed/PAC_ID_Resolver/cit_v2.py +303 -0
- labfreed/PAC_ID_Resolver/resolver.py +81 -0
- labfreed/PAC_ID_Resolver/services.py +80 -0
- labfreed/__init__.py +4 -1
- labfreed/labfreed_infrastructure.py +276 -0
- labfreed/qr/__init__.py +1 -0
- labfreed/qr/generate_qr.py +422 -0
- labfreed/trex/__init__.py +16 -0
- labfreed/trex/python_convenience/__init__.py +3 -0
- labfreed/trex/python_convenience/data_table.py +45 -0
- labfreed/trex/python_convenience/pyTREX.py +242 -0
- labfreed/trex/python_convenience/quantity.py +46 -0
- labfreed/trex/table_segment.py +227 -0
- labfreed/trex/trex.py +69 -0
- labfreed/trex/trex_base_models.py +336 -0
- labfreed/trex/value_segments.py +111 -0
- labfreed/{DisplayNameExtension → utilities}/base36.py +29 -13
- labfreed/well_known_extensions/__init__.py +5 -0
- labfreed/well_known_extensions/default_extension_interpreters.py +7 -0
- labfreed/well_known_extensions/display_name_extension.py +40 -0
- labfreed/well_known_extensions/trex_extension.py +31 -0
- labfreed/well_known_keys/gs1/__init__.py +6 -0
- labfreed/well_known_keys/gs1/gs1.py +4 -0
- labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py +57 -0
- labfreed/{PAC_ID/well_known_segment_keys.py → well_known_keys/labfreed/well_known_keys.py} +1 -1
- labfreed/well_known_keys/unece/UneceUnits.json +33730 -0
- labfreed/well_known_keys/unece/__init__.py +4 -0
- labfreed/well_known_keys/unece/unece_units.py +68 -0
- labfreed-0.2.0b0.dist-info/METADATA +329 -0
- labfreed-0.2.0b0.dist-info/RECORD +44 -0
- {labfreed-0.0.5.dist-info → labfreed-0.2.0b0.dist-info}/WHEEL +1 -1
- labfreed/DisplayNameExtension/DisplayNameExtension.py +0 -34
- labfreed/PAC_CAT/data_model.py +0 -109
- labfreed/PAC_ID/data_model.py +0 -215
- labfreed/PAC_ID/parse.py +0 -142
- labfreed/PAC_ID/serialize.py +0 -60
- labfreed/TREXExtension/data_model.py +0 -239
- labfreed/TREXExtension/parse.py +0 -46
- labfreed/TREXExtension/uncertainty.py +0 -32
- labfreed/TREXExtension/unit_utilities.py +0 -143
- labfreed/validation.py +0 -71
- labfreed-0.0.5.dist-info/METADATA +0 -34
- labfreed-0.0.5.dist-info/RECORD +0 -19
- {labfreed-0.0.5.dist-info → labfreed-0.2.0b0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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))
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
from typing_extensions import Annotated
|
|
3
|
-
from pydantic import BaseModel, AfterValidator
|
|
4
|
-
import quantities as pq
|
|
5
|
-
from quantities import Quantity, UnitQuantity, units, dimensionless
|
|
6
|
-
from .uncertainty import to_significant_digits_str
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def validate_unit(unit_name:str) -> str :
|
|
11
|
-
"""
|
|
12
|
-
Pydantic validator function for the unit.
|
|
13
|
-
Checks if the unit is a valid unit.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
unit (str): unit symbol, e.g. 'kg'
|
|
18
|
-
|
|
19
|
-
Returns:
|
|
20
|
-
str: the input unit.
|
|
21
|
-
|
|
22
|
-
Errors:
|
|
23
|
-
raises an AssertionError if validation fails
|
|
24
|
-
"""
|
|
25
|
-
if hasattr(pq, unit_name):
|
|
26
|
-
return unit_name
|
|
27
|
-
else:
|
|
28
|
-
assert False
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class PydanticUncertainQuantity(BaseModel):
|
|
32
|
-
data:int|float
|
|
33
|
-
unit_name: Annotated[str, AfterValidator(validate_unit)]
|
|
34
|
-
unit_symbol: str
|
|
35
|
-
uncertainty:float|None=None
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def for_display(self):
|
|
39
|
-
return self.__str__()
|
|
40
|
-
|
|
41
|
-
def as_strings(self):
|
|
42
|
-
unit_symbol = self.unit_symbol
|
|
43
|
-
if unit_symbol == "dimensionless":
|
|
44
|
-
unit_symbol = ""
|
|
45
|
-
s = ''
|
|
46
|
-
|
|
47
|
-
val_str = to_significant_digits_str(self.data, self.uncertainty)
|
|
48
|
-
return f"{val_str}", f"{unit_symbol}", f"{val_str} {unit_symbol}"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def __str__(self):
|
|
53
|
-
unit_symbol = self.unit_symbol
|
|
54
|
-
if unit_symbol == "dimensionless":
|
|
55
|
-
unit_symbol = ""
|
|
56
|
-
|
|
57
|
-
s = f"{to_significant_digits_str(self.data, self.uncertainty)} {unit_symbol}"
|
|
58
|
-
return s
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
unit_map = [
|
|
62
|
-
('MGM', units.milligram),
|
|
63
|
-
('GRM', units.gram),
|
|
64
|
-
('KGM', units.kilogram),
|
|
65
|
-
|
|
66
|
-
('CEL', units.celsius),
|
|
67
|
-
|
|
68
|
-
('LTR', units.liter),
|
|
69
|
-
('MLT', units.milliliter),
|
|
70
|
-
|
|
71
|
-
('C34', units.mole),
|
|
72
|
-
('D43',units.atomic_mass_unit),
|
|
73
|
-
|
|
74
|
-
('1', units.dimensionless),
|
|
75
|
-
('C62', units.dimensionless),
|
|
76
|
-
|
|
77
|
-
('BAR',units.bar),
|
|
78
|
-
('MBR',units.millibar),
|
|
79
|
-
('KBA',units.kilobar),
|
|
80
|
-
|
|
81
|
-
('RPM', units.rpm),
|
|
82
|
-
|
|
83
|
-
('HTZ', units.hertz),
|
|
84
|
-
('KHZ', units.kilohertz),
|
|
85
|
-
('MHZ',units.megahertz),
|
|
86
|
-
|
|
87
|
-
('SEC', units.second),
|
|
88
|
-
('MIN', units.minute),
|
|
89
|
-
('HUR', units.hour),
|
|
90
|
-
|
|
91
|
-
('MTR', units.meter)
|
|
92
|
-
|
|
93
|
-
]
|
|
94
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if __name__ == "__main__":
|
|
138
|
-
pass
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
labfreed/validation.py
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field, PrivateAttr
|
|
2
|
-
from typing import List, Set, Tuple
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
domain_name_pattern = r"(?!-)([A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,63}"
|
|
6
|
-
hsegment_pattern = r"[A-Za-z0-9_\-\.~!$&'()+,:;=@]|%[0-9A-Fa-f]{2}"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class ValidationWarning(BaseModel):
|
|
10
|
-
source:str
|
|
11
|
-
type: str
|
|
12
|
-
problem_msg:str
|
|
13
|
-
recommendation_msg: str = ""
|
|
14
|
-
highlight:str = "" #this can be used to highlight problematic parts
|
|
15
|
-
highlight_sub:list[str] = Field(default_factory=list())
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class BaseModelWithWarnings(BaseModel):
|
|
21
|
-
""" Extension of Pydantic BaseModel, so that validator can issue warnings.
|
|
22
|
-
The purpose of that is to allow only minimal validation but on top check for stricter recommendations"""
|
|
23
|
-
_warnings: list[ValidationWarning] = PrivateAttr(default_factory=list)
|
|
24
|
-
|
|
25
|
-
def add_warning(self, *, msg: str, type:str, recommendation:str="", source:str="", highlight_pattern="", highlight_sub=None):
|
|
26
|
-
if not highlight_sub:
|
|
27
|
-
highlight_sub = []
|
|
28
|
-
w = ValidationWarning(problem_msg=msg, recommendation_msg=recommendation, source=source, type=type, highlight=highlight_pattern, highlight_sub=highlight_sub)
|
|
29
|
-
if not w in self._warnings:
|
|
30
|
-
self._warnings.append(w)
|
|
31
|
-
|
|
32
|
-
def get_warnings(self) -> list[ValidationWarning]:
|
|
33
|
-
return self._warnings
|
|
34
|
-
|
|
35
|
-
def clear_warnings(self):
|
|
36
|
-
self._warnings.clear()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Function to extract warnings from a model and its nested models
|
|
40
|
-
def extract_warnings(model: BaseModelWithWarnings, parent_name: str = "", visited: Set[int] = None) -> List[ValidationWarning]:
|
|
41
|
-
"""
|
|
42
|
-
Recursively extract warnings from a Pydantic model and its nested fields.
|
|
43
|
-
|
|
44
|
-
:param model: The Pydantic model instance to inspect.
|
|
45
|
-
:param parent_name: The name of the parent model to track the path.
|
|
46
|
-
:return: List of tuples containing (model name, warning message).
|
|
47
|
-
"""
|
|
48
|
-
if visited is None:
|
|
49
|
-
visited = set()
|
|
50
|
-
|
|
51
|
-
model_id = id(model)
|
|
52
|
-
if model_id in visited:
|
|
53
|
-
return []
|
|
54
|
-
visited.add(model_id)
|
|
55
|
-
|
|
56
|
-
warnings_list = [(parent_name or model.__class__.__name__, model_id, warning) for warning in model.get_warnings()]
|
|
57
|
-
|
|
58
|
-
for field_name, field in model.__fields__.items():
|
|
59
|
-
full_path = f"{parent_name}.{field_name}" if parent_name else field_name
|
|
60
|
-
value = getattr(model, field_name)
|
|
61
|
-
|
|
62
|
-
if isinstance(value, BaseModelWithWarnings):
|
|
63
|
-
warnings_list.extend(extract_warnings(value, full_path, visited))
|
|
64
|
-
elif isinstance(value, list):
|
|
65
|
-
for index, item in enumerate(value):
|
|
66
|
-
if isinstance(item, BaseModelWithWarnings):
|
|
67
|
-
list_path = f"{full_path}[{index}]"
|
|
68
|
-
warnings_list.extend(extract_warnings(item, list_path, visited))
|
|
69
|
-
|
|
70
|
-
return warnings_list
|
|
71
|
-
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: labfreed
|
|
3
|
-
Version: 0.0.5
|
|
4
|
-
Summary: Python implementation of LabFREED building blocks
|
|
5
|
-
Author-email: Reto Thürer <thuerer.r@buchi.com>
|
|
6
|
-
Requires-Python: >=3.10
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
|
|
13
|
-
# LabFREED for Python
|
|
14
|
-
|
|
15
|
-
A python implementation of [LabFREED](www.labfreed.com).
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# Examples:
|
|
20
|
-
|
|
21
|
-
## Parse a PAC ID
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
## Parse a PAC-ID with extensions
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Create a PAC-ID
|
|
29
|
-
|
|
30
|
-
## Create a PAC-ID and T-REX for a titration curve
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
labfreed-0.0.5.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
labfreed/__init__.py,sha256=z3yAWKlMQqYGwnI20F528XNm0hlpE6qhvvfIkhRuSoI,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=Y04UK1KlMLG9tE_d7cQOiAJpm8Zh49UoJYjY7Lfqa4Y,1812
|
|
14
|
-
labfreed/TREXExtension/uncertainty.py,sha256=l3WxrLnWTQYfX28gFisXwDcVPvT8bCAd4q6Xl02dRdE,1117
|
|
15
|
-
labfreed/TREXExtension/unit_utilities.py,sha256=WzrC1CZMgBccxADXFP6nLkMWVDqCCkNb3trPyt3BvF8,3826
|
|
16
|
-
labfreed-0.0.5.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
|
|
17
|
-
labfreed-0.0.5.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
|
|
18
|
-
labfreed-0.0.5.dist-info/METADATA,sha256=T_mGIHKlIMASuQnbuW_L4XXqUuxUc0DB6yGOcI-N-wE,594
|
|
19
|
-
labfreed-0.0.5.dist-info/RECORD,,
|
|
File without changes
|