labfreed 0.0.4__py3-none-any.whl → 0.0.5__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/data_model.py +5 -5
- labfreed/PAC_ID/data_model.py +111 -10
- labfreed/PAC_ID/parse.py +14 -5
- labfreed/PAC_ID/serialize.py +5 -2
- labfreed/PAC_ID/well_known_segment_keys.py +16 -0
- labfreed/TREXExtension/unit_utilities.py +13 -4
- labfreed/__init__.py +1 -1
- labfreed/validation.py +71 -0
- {labfreed-0.0.4.dist-info → labfreed-0.0.5.dist-info}/METADATA +20 -1
- labfreed-0.0.5.dist-info/RECORD +19 -0
- labfreed-0.0.4.dist-info/RECORD +0 -17
- {labfreed-0.0.4.dist-info → labfreed-0.0.5.dist-info}/WHEEL +0 -0
- {labfreed-0.0.4.dist-info → labfreed-0.0.5.dist-info}/licenses/LICENSE +0 -0
labfreed/PAC_CAT/data_model.py
CHANGED
|
@@ -65,23 +65,23 @@ class Material_Misc(Material_Consumable):
|
|
|
65
65
|
|
|
66
66
|
class Data_Result(CATBase):
|
|
67
67
|
category_key: str = Field(default='-DR', frozen=True)
|
|
68
|
-
id:str = Field( alias='
|
|
68
|
+
id:str = Field( alias='21', min_length=1)
|
|
69
69
|
|
|
70
70
|
class Data_Method(CATBase):
|
|
71
71
|
category_key: str = Field(default='-DM', frozen=True)
|
|
72
|
-
id:str = Field( alias='
|
|
72
|
+
id:str = Field( alias='21', min_length=1)
|
|
73
73
|
|
|
74
74
|
class Data_Calibration(CATBase):
|
|
75
75
|
category_key: str = Field(default='-DC', frozen=True)
|
|
76
|
-
id:str = Field( alias='
|
|
76
|
+
id:str = Field( alias='21', min_length=1)
|
|
77
77
|
|
|
78
78
|
class Data_Progress(CATBase):
|
|
79
79
|
category_key: str = Field(default='-DP', frozen=True)
|
|
80
|
-
id:str = Field( alias='
|
|
80
|
+
id:str = Field( alias='21', min_length=1)
|
|
81
81
|
|
|
82
82
|
class Data_Static(CATBase):
|
|
83
83
|
category_key: str = Field(default='-DS', frozen=True)
|
|
84
|
-
id:str = Field( alias='
|
|
84
|
+
id:str = Field( alias='21', min_length=1)
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
|
labfreed/PAC_ID/data_model.py
CHANGED
|
@@ -1,20 +1,90 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from typing import Optional
|
|
2
3
|
from typing_extensions import Self
|
|
3
|
-
from pydantic import
|
|
4
|
+
from pydantic import Field, ValidationInfo, computed_field, conlist, model_validator, field_validator
|
|
5
|
+
from ..validation import BaseModelWithWarnings, ValidationWarning, hsegment_pattern, domain_name_pattern
|
|
4
6
|
from abc import ABC, abstractproperty, abstractstaticmethod
|
|
7
|
+
from .well_known_segment_keys import WellKnownSegmentKeys
|
|
5
8
|
|
|
6
|
-
class IDSegment(BaseModel):
|
|
7
|
-
key:Optional[str] = Field(None, pattern=r'^[A-Z0-9-+]+$', min_length=1)
|
|
8
|
-
value:str = Field(..., pattern=r'^[A-Z0-9-+]+$', min_length=1)
|
|
9
9
|
|
|
10
|
+
class IDSegment(BaseModelWithWarnings):
|
|
11
|
+
key:str|None = None
|
|
12
|
+
value:str
|
|
13
|
+
@model_validator(mode="after")
|
|
14
|
+
def validate_segment(cls, model):
|
|
15
|
+
key = model.key or ""
|
|
16
|
+
value = model.value
|
|
17
|
+
|
|
18
|
+
# MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
|
|
19
|
+
# This means it must be true for both, key and value
|
|
20
|
+
if not_allowed_chars := set(re.sub(hsegment_pattern, '', key)):
|
|
21
|
+
raise ValueError(f"id segment key {key} contains invalid characters {' '.join(not_allowed_chars)}.")
|
|
22
|
+
|
|
23
|
+
if not_allowed_chars := set(re.sub(hsegment_pattern, '', value)):
|
|
24
|
+
raise ValueError(f"id segment key {value} contains invalid characters {' '.join(not_allowed_chars)}.")
|
|
10
25
|
|
|
11
|
-
|
|
26
|
+
# Segment key SHOULD be limited to A-Z, 0-9, and -+..
|
|
27
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
|
|
28
|
+
model.add_warning(
|
|
29
|
+
source=f"id segment key {key}",
|
|
30
|
+
type="Recommendation",
|
|
31
|
+
msg=f"{' '.join(not_recommended_chars)} should not be used.",
|
|
32
|
+
recommendation = "SHOULD be limited to A-Z, 0-9, and -+",
|
|
33
|
+
highlight_pattern = key,
|
|
34
|
+
highlight_sub = not_recommended_chars
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Segment key should be in Well know keys
|
|
38
|
+
if key and key not in [k.value for k in WellKnownSegmentKeys]:
|
|
39
|
+
model.add_warning(
|
|
40
|
+
source=f"id segment key {key}",
|
|
41
|
+
type="Recommendation",
|
|
42
|
+
msg=f"{key} is not a well known segment key.",
|
|
43
|
+
recommendation = "RECOMMENDED to be a well-known id segment key.",
|
|
44
|
+
highlight_pattern = key
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Segment value SHOULD be limited to A-Z, 0-9, and -+..
|
|
49
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
|
|
50
|
+
model.add_warning(
|
|
51
|
+
source=f"id segment value {value}",
|
|
52
|
+
type="Recommendation",
|
|
53
|
+
msg=f"Characters {' '.join(not_recommended_chars)} should not be used.",
|
|
54
|
+
recommendation = "SHOULD be limited to A-Z, 0-9, and -+",
|
|
55
|
+
highlight_pattern = value,
|
|
56
|
+
highlight_sub = not_recommended_chars
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
|
|
60
|
+
# this means that ":" in key or value is problematic
|
|
61
|
+
if ':' in key:
|
|
62
|
+
model.add_warning(
|
|
63
|
+
source=f"id segment key {key}",
|
|
64
|
+
type="Recommendation",
|
|
65
|
+
msg=f"Character ':' should not be used in segment key, since this character is used to separate key and value this can lead to undefined behaviour.",
|
|
66
|
+
highlight_pattern = key
|
|
67
|
+
)
|
|
68
|
+
if ':' in value:
|
|
69
|
+
model.add_warning(
|
|
70
|
+
source=f"id segment value {value}",
|
|
71
|
+
type="Recommendation",
|
|
72
|
+
msg=f"Character ':' should not be used in segment value, since this character is used to separate key and value this can lead to undefined behaviour.",
|
|
73
|
+
highlight_pattern = value
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return model
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Category(BaseModelWithWarnings):
|
|
12
82
|
key:str|None = None
|
|
13
83
|
segments: list[IDSegment]
|
|
14
84
|
|
|
15
85
|
|
|
16
|
-
class Identifier(
|
|
17
|
-
segments: conlist(IDSegment, min_length=1) = Field(..., exclude=True) # exclude=True prevents this from being serialized by Pydantic
|
|
86
|
+
class Identifier(BaseModelWithWarnings):
|
|
87
|
+
segments: conlist(IDSegment, min_length=1) = Field(..., exclude=True) # type: ignore # exclude=True prevents this from being serialized by Pydantic
|
|
18
88
|
|
|
19
89
|
@computed_field
|
|
20
90
|
@property
|
|
@@ -45,6 +115,20 @@ class Identifier(BaseModel):
|
|
|
45
115
|
if duplicate_keys:
|
|
46
116
|
raise ValueError(f'Duplicate keys {",".join(duplicate_keys)} in category {c.key}')
|
|
47
117
|
return self
|
|
118
|
+
|
|
119
|
+
@model_validator(mode='after')
|
|
120
|
+
def check_length(self) -> Self:
|
|
121
|
+
l = 0
|
|
122
|
+
for s in self.segments:
|
|
123
|
+
if s.key:
|
|
124
|
+
l += len(s.key)
|
|
125
|
+
l += 1 # for ":"
|
|
126
|
+
l += len(s.value)
|
|
127
|
+
l += len(self.segments) - 1 # account for "/" separating the segments
|
|
128
|
+
|
|
129
|
+
if l > 256:
|
|
130
|
+
raise ValueError(f'Identifier is {l} characters long, Identifier must not exceed 256 characters.')
|
|
131
|
+
return self
|
|
48
132
|
|
|
49
133
|
@staticmethod
|
|
50
134
|
def from_categories(categories:list[Category]) :
|
|
@@ -57,7 +141,7 @@ class Identifier(BaseModel):
|
|
|
57
141
|
|
|
58
142
|
|
|
59
143
|
|
|
60
|
-
class Extension(ABC,
|
|
144
|
+
class Extension(ABC, BaseModelWithWarnings):
|
|
61
145
|
|
|
62
146
|
@abstractproperty
|
|
63
147
|
def name(self)->str:
|
|
@@ -99,12 +183,29 @@ class UnknownExtension(Extension):
|
|
|
99
183
|
|
|
100
184
|
|
|
101
185
|
|
|
102
|
-
class PACID(
|
|
186
|
+
class PACID(BaseModelWithWarnings):
|
|
103
187
|
issuer:str
|
|
104
188
|
identifier: Identifier
|
|
105
189
|
|
|
190
|
+
@model_validator(mode="after")
|
|
191
|
+
def validate_issuer(cls, model):
|
|
192
|
+
if not re.fullmatch(domain_name_pattern, model.issuer):
|
|
193
|
+
raise ValueError("Issuer must be a valid domain name.")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# recommendation that A-Z, 0-9, -, and . should be used
|
|
197
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', model.issuer)):
|
|
198
|
+
model.add_warning(
|
|
199
|
+
source="PAC-ID",
|
|
200
|
+
type="Recommendation",
|
|
201
|
+
highlight_pattern=model.issuer,
|
|
202
|
+
highlight_sub=not_recommended_chars,
|
|
203
|
+
msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
|
|
204
|
+
)
|
|
205
|
+
return model
|
|
206
|
+
|
|
106
207
|
|
|
107
|
-
class PACID_With_Extensions(
|
|
208
|
+
class PACID_With_Extensions(BaseModelWithWarnings):
|
|
108
209
|
pac_id: PACID
|
|
109
210
|
extensions: list[Extension] = Field(default_factory=list)
|
|
110
211
|
|
labfreed/PAC_ID/parse.py
CHANGED
|
@@ -4,6 +4,8 @@ import re
|
|
|
4
4
|
from types import MappingProxyType
|
|
5
5
|
from .data_model import *
|
|
6
6
|
|
|
7
|
+
from ..validation import extract_warnings, ValidationWarning
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
category_conventions = MappingProxyType(
|
|
9
11
|
{
|
|
@@ -29,7 +31,7 @@ class PAC_Parser():
|
|
|
29
31
|
def __init__(self, extension_interpreters:dict[str, Extension]=None):
|
|
30
32
|
self.extension_interpreters = extension_interpreters or {}
|
|
31
33
|
|
|
32
|
-
def parse_pac_url(self, pac_url:str) -> PACID_With_Extensions:
|
|
34
|
+
def parse_pac_url(self, pac_url:str) -> tuple[PACID_With_Extensions, list[ValidationWarning] ]:
|
|
33
35
|
if '*' in pac_url:
|
|
34
36
|
id_str, ext_str = pac_url.split('*', 1)
|
|
35
37
|
else:
|
|
@@ -37,8 +39,12 @@ class PAC_Parser():
|
|
|
37
39
|
ext_str = ""
|
|
38
40
|
|
|
39
41
|
pac_id = self.parse_pac_id(id_str)
|
|
40
|
-
extensions = self.parse_extensions(ext_str)
|
|
41
|
-
|
|
42
|
+
extensions = self.parse_extensions(ext_str)
|
|
43
|
+
|
|
44
|
+
pac_with_extension = PACID_With_Extensions(pac_id=pac_id, extensions=extensions)
|
|
45
|
+
warnings = extract_warnings(pac_with_extension)
|
|
46
|
+
|
|
47
|
+
return pac_with_extension, warnings
|
|
42
48
|
|
|
43
49
|
|
|
44
50
|
def parse_id_segments(self, identifier:str):
|
|
@@ -46,6 +52,8 @@ class PAC_Parser():
|
|
|
46
52
|
return []
|
|
47
53
|
|
|
48
54
|
id_segments = list()
|
|
55
|
+
if len(identifier) > 0 and identifier[0] == '/':
|
|
56
|
+
identifier = identifier[1:]
|
|
49
57
|
for s in identifier.split('/'):
|
|
50
58
|
tmp = s.split(':')
|
|
51
59
|
|
|
@@ -85,10 +93,11 @@ class PAC_Parser():
|
|
|
85
93
|
default_keys = None
|
|
86
94
|
id_segments = self.parse_id_segments(d.get('identifier'))
|
|
87
95
|
id_segments = self._apply_category_defaults(id_segments)
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
|
|
97
|
+
pac = PACID(issuer= d.get('issuer'),
|
|
90
98
|
identifier=Identifier(segments=id_segments)
|
|
91
99
|
)
|
|
100
|
+
return pac
|
|
92
101
|
|
|
93
102
|
|
|
94
103
|
def parse_extensions(self, extensions_str:str|None) -> list[Extension]:
|
labfreed/PAC_ID/serialize.py
CHANGED
|
@@ -4,7 +4,7 @@ from .data_model import *
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class PAC_Serializer():
|
|
7
|
-
def to_url(self, pac:PACID|PACID_With_Extensions, extensions:list[Extension]=None, use_short_notation_for_extensions=False) -> str:
|
|
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
8
|
if isinstance(pac, PACID_With_Extensions):
|
|
9
9
|
if extensions:
|
|
10
10
|
raise ValueError('Extensions were given twice, as part of PACID_With_Extension and as method parameter.')
|
|
@@ -13,7 +13,10 @@ class PAC_Serializer():
|
|
|
13
13
|
issuer = pac.issuer
|
|
14
14
|
extensions_str = self._serialize_extensions(extensions, use_short_notation_for_extensions)
|
|
15
15
|
id_segments = self._serialize_id_segments(pac.identifier.segments)
|
|
16
|
-
|
|
16
|
+
out = f"HTTPS://PAC.{issuer}{id_segments}{extensions_str}"
|
|
17
|
+
if uppercase_only:
|
|
18
|
+
out = out.upper()
|
|
19
|
+
return out
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
def _serialize_id_segments(self, segments):
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WellKnownSegmentKeys(Enum):
|
|
5
|
+
GTIN = '01'
|
|
6
|
+
BATCH = '10'
|
|
7
|
+
SERIAL = '21'
|
|
8
|
+
ADDITIONAL_IDINTIFIER = '240'
|
|
9
|
+
RUN_ID_ABSOLUTE = 'RNR'
|
|
10
|
+
SAMPLE_ID = 'SMP'
|
|
11
|
+
EXPERIMENT_ID = 'EXP'
|
|
12
|
+
RESULT_ID = 'RST'
|
|
13
|
+
METHOD_ID = 'MTD'
|
|
14
|
+
REPORT_ID = 'RPT'
|
|
15
|
+
TIMESTAMP = 'TS'
|
|
16
|
+
VERSION = 'V'
|
|
@@ -60,26 +60,35 @@ class PydanticUncertainQuantity(BaseModel):
|
|
|
60
60
|
|
|
61
61
|
unit_map = [
|
|
62
62
|
('MGM', units.milligram),
|
|
63
|
+
('GRM', units.gram),
|
|
64
|
+
('KGM', units.kilogram),
|
|
65
|
+
|
|
63
66
|
('CEL', units.celsius),
|
|
67
|
+
|
|
64
68
|
('LTR', units.liter),
|
|
65
69
|
('MLT', units.milliliter),
|
|
66
|
-
|
|
67
|
-
('KGM', units.kilogram),
|
|
70
|
+
|
|
68
71
|
('C34', units.mole),
|
|
69
72
|
('D43',units.atomic_mass_unit),
|
|
73
|
+
|
|
70
74
|
('1', units.dimensionless),
|
|
71
75
|
('C62', units.dimensionless),
|
|
76
|
+
|
|
72
77
|
('BAR',units.bar),
|
|
73
78
|
('MBR',units.millibar),
|
|
74
79
|
('KBA',units.kilobar),
|
|
80
|
+
|
|
75
81
|
('RPM', units.rpm),
|
|
76
|
-
|
|
82
|
+
|
|
77
83
|
('HTZ', units.hertz),
|
|
78
84
|
('KHZ', units.kilohertz),
|
|
79
85
|
('MHZ',units.megahertz),
|
|
86
|
+
|
|
80
87
|
('SEC', units.second),
|
|
81
88
|
('MIN', units.minute),
|
|
82
|
-
('
|
|
89
|
+
('HUR', units.hour),
|
|
90
|
+
|
|
91
|
+
('MTR', units.meter)
|
|
83
92
|
|
|
84
93
|
]
|
|
85
94
|
|
labfreed/__init__.py
CHANGED
labfreed/validation.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labfreed
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
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.10
|
|
@@ -13,3 +13,22 @@ License-File: LICENSE
|
|
|
13
13
|
# LabFREED for Python
|
|
14
14
|
|
|
15
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
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
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,,
|
labfreed-0.0.4.dist-info/RECORD
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
labfreed/__init__.py,sha256=PyF18K_9O9PbwpUrHsMwMVLHyepmCT4UIeVP5iCns90,87
|
|
2
|
-
labfreed/DisplayNameExtension/DisplayNameExtension.py,sha256=FlT53u1EntpsLmho6GZtgIWBZBNWkl9STxzJBvojR6M,1033
|
|
3
|
-
labfreed/DisplayNameExtension/base36.py,sha256=2lwmEMWm8qrFJkcrP-nMPwS0eCm2THhCJ3Vk-TdGQg0,2455
|
|
4
|
-
labfreed/PAC_CAT/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
5
|
-
labfreed/PAC_CAT/data_model.py,sha256=y1cTFZknObi5XOk4aqfgeJv6_G8Rjd9mxTRwNCQR0M4,4311
|
|
6
|
-
labfreed/PAC_ID/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
labfreed/PAC_ID/data_model.py,sha256=oVVqspdZ4B_C4V1rOmcde2_EqWUpDJc6vDUnX4Z1ZPw,3123
|
|
8
|
-
labfreed/PAC_ID/parse.py,sha256=ZmCpuht7o6RgDZCweF7Y0k7QFTd0gLtUpmjFsIlz_wA,4668
|
|
9
|
-
labfreed/PAC_ID/serialize.py,sha256=ujI4SFs-TsFb4FVU2hzwardEWu8-2MYjewE0IyC1yok,1871
|
|
10
|
-
labfreed/TREXExtension/data_model.py,sha256=eT4KyQklTO6m-wA28KyJ8wzT8ONhG3fOB3JU6b19ScY,8011
|
|
11
|
-
labfreed/TREXExtension/parse.py,sha256=Y04UK1KlMLG9tE_d7cQOiAJpm8Zh49UoJYjY7Lfqa4Y,1812
|
|
12
|
-
labfreed/TREXExtension/uncertainty.py,sha256=l3WxrLnWTQYfX28gFisXwDcVPvT8bCAd4q6Xl02dRdE,1117
|
|
13
|
-
labfreed/TREXExtension/unit_utilities.py,sha256=ZhivJnEXMvz7HxLJjI1oJ8y_pcPxW2abKkouiqTo4I4,3775
|
|
14
|
-
labfreed-0.0.4.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
|
|
15
|
-
labfreed-0.0.4.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
|
|
16
|
-
labfreed-0.0.4.dist-info/METADATA,sha256=KKfAscBUcBVVNUSHT3KjlqXjA3_fFd5APom23P_Boh0,446
|
|
17
|
-
labfreed-0.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|