labfreed 0.2.6a6__py3-none-any.whl → 0.2.8__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.
Potentially problematic release.
This version of labfreed might be problematic. Click here for more details.
- labfreed/__init__.py +11 -11
- labfreed/labfreed_infrastructure.py +258 -258
- labfreed/pac_cat/__init__.py +19 -19
- labfreed/pac_cat/category_base.py +51 -51
- labfreed/pac_cat/pac_cat.py +150 -150
- labfreed/pac_cat/predefined_categories.py +200 -200
- labfreed/pac_id/__init__.py +19 -19
- labfreed/pac_id/extension.py +48 -48
- labfreed/pac_id/id_segment.py +89 -89
- labfreed/pac_id/pac_id.py +140 -140
- labfreed/pac_id/url_parser.py +155 -155
- labfreed/pac_id/url_serializer.py +84 -80
- labfreed/pac_id_resolver/__init__.py +2 -2
- labfreed/pac_id_resolver/cit_common.py +81 -81
- labfreed/pac_id_resolver/cit_v1.py +244 -247
- labfreed/pac_id_resolver/cit_v2.py +313 -313
- labfreed/pac_id_resolver/resolver.py +97 -98
- labfreed/pac_id_resolver/services.py +82 -79
- labfreed/qr/__init__.py +1 -1
- labfreed/qr/generate_qr.py +422 -422
- labfreed/trex/__init__.py +16 -16
- labfreed/trex/python_convenience/__init__.py +3 -3
- labfreed/trex/python_convenience/data_table.py +87 -87
- labfreed/trex/python_convenience/pyTREX.py +248 -248
- labfreed/trex/python_convenience/quantity.py +66 -66
- labfreed/trex/table_segment.py +245 -245
- labfreed/trex/trex.py +69 -69
- labfreed/trex/trex_base_models.py +209 -209
- labfreed/trex/value_segments.py +99 -99
- labfreed/utilities/base36.py +82 -82
- labfreed/well_known_extensions/__init__.py +4 -4
- labfreed/well_known_extensions/default_extension_interpreters.py +6 -6
- labfreed/well_known_extensions/display_name_extension.py +40 -40
- labfreed/well_known_extensions/trex_extension.py +30 -30
- labfreed/well_known_keys/gs1/__init__.py +5 -5
- labfreed/well_known_keys/gs1/gs1.py +3 -3
- labfreed/well_known_keys/labfreed/well_known_keys.py +15 -15
- labfreed/well_known_keys/unece/__init__.py +3 -3
- labfreed/well_known_keys/unece/unece_units.py +67 -67
- {labfreed-0.2.6a6.dist-info → labfreed-0.2.8.dist-info}/METADATA +36 -21
- labfreed-0.2.8.dist-info/RECORD +45 -0
- {labfreed-0.2.6a6.dist-info → labfreed-0.2.8.dist-info}/licenses/LICENSE +21 -21
- labfreed-0.2.6a6.dist-info/RECORD +0 -45
- {labfreed-0.2.6a6.dist-info → labfreed-0.2.8.dist-info}/WHEEL +0 -0
labfreed/pac_id/id_segment.py
CHANGED
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from pydantic import model_validator
|
|
3
|
-
from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
|
|
4
|
-
from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
_hsegment_pattern = r"[A-Za-z0-9_\-\.~!$&'()+,:;=@]|%[0-9A-Fa-f]{2}"
|
|
8
|
-
|
|
9
|
-
class IDSegment(LabFREED_BaseModel):
|
|
10
|
-
""" Represents an id segment of a PAC-ID. It can be a value or a key value pair.
|
|
11
|
-
"""
|
|
12
|
-
key:str|None = None
|
|
13
|
-
''' The key of the segment. This is optional.'''
|
|
14
|
-
value:str
|
|
15
|
-
''' The value of the segment. (mandatory)'''
|
|
16
|
-
|
|
17
|
-
@model_validator(mode="after")
|
|
18
|
-
def _validate_segment(self):
|
|
19
|
-
key = self.key or ""
|
|
20
|
-
value = self.value
|
|
21
|
-
|
|
22
|
-
# MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
|
|
23
|
-
# This means it must be true for both, key and value
|
|
24
|
-
if not_allowed_chars := set(re.sub(_hsegment_pattern, '', key)):
|
|
25
|
-
self._add_validation_message(
|
|
26
|
-
source=f"id segment key {key}",
|
|
27
|
-
level = ValidationMsgLevel.ERROR,
|
|
28
|
-
msg=f"{_quote_texts(not_allowed_chars)} must not be used. The segment key must be a valid hsegment",
|
|
29
|
-
highlight_pattern = key,
|
|
30
|
-
highlight_sub = not_allowed_chars
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
if not_allowed_chars := set(re.sub(_hsegment_pattern, '', value)):
|
|
34
|
-
self._add_validation_message(
|
|
35
|
-
source=f"id segment key {value}",
|
|
36
|
-
level = ValidationMsgLevel.ERROR,
|
|
37
|
-
msg=f"{_quote_texts(not_allowed_chars)} must not be used. The segment key must be a valid hsegment",
|
|
38
|
-
highlight_pattern = value,
|
|
39
|
-
highlight_sub = not_allowed_chars
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
# Segment key SHOULD be limited to A-Z, 0-9, and -+..
|
|
43
|
-
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
|
|
44
|
-
self._add_validation_message(
|
|
45
|
-
source=f"id segment key {key}",
|
|
46
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
47
|
-
msg=f"{_quote_texts(not_recommended_chars)} should not be used. Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' ",
|
|
48
|
-
highlight_pattern = key,
|
|
49
|
-
highlight_sub = not_recommended_chars
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Segment key should be in Well know keys
|
|
53
|
-
if key and key not in [k.value for k in WellKnownKeys]:
|
|
54
|
-
self._add_validation_message(
|
|
55
|
-
source=f"id segment key {key}",
|
|
56
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
57
|
-
msg=f"{key} is not a well known segment key. It is RECOMMENDED to use well-known keys.",
|
|
58
|
-
highlight_pattern = key,
|
|
59
|
-
highlight_sub=[key]
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# Segment value SHOULD be limited to A-Z, 0-9, and -+..
|
|
64
|
-
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
|
|
65
|
-
self._add_validation_message(
|
|
66
|
-
source=f"id segment value {value}",
|
|
67
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
68
|
-
msg=f"Characters {_quote_texts(not_recommended_chars)} should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' ",
|
|
69
|
-
highlight_pattern = value,
|
|
70
|
-
highlight_sub = not_recommended_chars
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
# Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
|
|
74
|
-
# this means that ":" in key or value is problematic
|
|
75
|
-
if ':' in key:
|
|
76
|
-
self._add_validation_message(
|
|
77
|
-
source=f"id segment key {key}",
|
|
78
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
79
|
-
msg="Character ':' should not be used in segment key, since this character is used to separate key and value this can lead to undefined behaviour.",
|
|
80
|
-
highlight_pattern = key
|
|
81
|
-
)
|
|
82
|
-
if ':' in value:
|
|
83
|
-
self._add_validation_message(
|
|
84
|
-
source=f"id segment value {value}",
|
|
85
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
86
|
-
msg="Character ':' should not be used in segment value, since this character is used to separate key and value this can lead to undefined behaviour.",
|
|
87
|
-
highlight_pattern = value
|
|
88
|
-
)
|
|
89
|
-
|
|
1
|
+
import re
|
|
2
|
+
from pydantic import model_validator
|
|
3
|
+
from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
|
|
4
|
+
from labfreed.well_known_keys.labfreed.well_known_keys import WellKnownKeys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_hsegment_pattern = r"[A-Za-z0-9_\-\.~!$&'()+,:;=@]|%[0-9A-Fa-f]{2}"
|
|
8
|
+
|
|
9
|
+
class IDSegment(LabFREED_BaseModel):
|
|
10
|
+
""" Represents an id segment of a PAC-ID. It can be a value or a key value pair.
|
|
11
|
+
"""
|
|
12
|
+
key:str|None = None
|
|
13
|
+
''' The key of the segment. This is optional.'''
|
|
14
|
+
value:str
|
|
15
|
+
''' The value of the segment. (mandatory)'''
|
|
16
|
+
|
|
17
|
+
@model_validator(mode="after")
|
|
18
|
+
def _validate_segment(self):
|
|
19
|
+
key = self.key or ""
|
|
20
|
+
value = self.value
|
|
21
|
+
|
|
22
|
+
# MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
|
|
23
|
+
# This means it must be true for both, key and value
|
|
24
|
+
if not_allowed_chars := set(re.sub(_hsegment_pattern, '', key)):
|
|
25
|
+
self._add_validation_message(
|
|
26
|
+
source=f"id segment key {key}",
|
|
27
|
+
level = ValidationMsgLevel.ERROR,
|
|
28
|
+
msg=f"{_quote_texts(not_allowed_chars)} must not be used. The segment key must be a valid hsegment",
|
|
29
|
+
highlight_pattern = key,
|
|
30
|
+
highlight_sub = not_allowed_chars
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if not_allowed_chars := set(re.sub(_hsegment_pattern, '', value)):
|
|
34
|
+
self._add_validation_message(
|
|
35
|
+
source=f"id segment key {value}",
|
|
36
|
+
level = ValidationMsgLevel.ERROR,
|
|
37
|
+
msg=f"{_quote_texts(not_allowed_chars)} must not be used. The segment key must be a valid hsegment",
|
|
38
|
+
highlight_pattern = value,
|
|
39
|
+
highlight_sub = not_allowed_chars
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Segment key SHOULD be limited to A-Z, 0-9, and -+..
|
|
43
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
|
|
44
|
+
self._add_validation_message(
|
|
45
|
+
source=f"id segment key {key}",
|
|
46
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
47
|
+
msg=f"{_quote_texts(not_recommended_chars)} should not be used. Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' ",
|
|
48
|
+
highlight_pattern = key,
|
|
49
|
+
highlight_sub = not_recommended_chars
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Segment key should be in Well know keys
|
|
53
|
+
if key and key not in [k.value for k in WellKnownKeys]:
|
|
54
|
+
self._add_validation_message(
|
|
55
|
+
source=f"id segment key {key}",
|
|
56
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
57
|
+
msg=f"{key} is not a well known segment key. It is RECOMMENDED to use well-known keys.",
|
|
58
|
+
highlight_pattern = key,
|
|
59
|
+
highlight_sub=[key]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Segment value SHOULD be limited to A-Z, 0-9, and -+..
|
|
64
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
|
|
65
|
+
self._add_validation_message(
|
|
66
|
+
source=f"id segment value {value}",
|
|
67
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
68
|
+
msg=f"Characters {_quote_texts(not_recommended_chars)} should not be used., Characters SHOULD be limited to upper case letters (A-Z), numbers (0-9), '-' and '+' ",
|
|
69
|
+
highlight_pattern = value,
|
|
70
|
+
highlight_sub = not_recommended_chars
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
|
|
74
|
+
# this means that ":" in key or value is problematic
|
|
75
|
+
if ':' in key:
|
|
76
|
+
self._add_validation_message(
|
|
77
|
+
source=f"id segment key {key}",
|
|
78
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
79
|
+
msg="Character ':' should not be used in segment key, since this character is used to separate key and value this can lead to undefined behaviour.",
|
|
80
|
+
highlight_pattern = key
|
|
81
|
+
)
|
|
82
|
+
if ':' in value:
|
|
83
|
+
self._add_validation_message(
|
|
84
|
+
source=f"id segment value {value}",
|
|
85
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
86
|
+
msg="Character ':' should not be used in segment value, since this character is used to separate key and value this can lead to undefined behaviour.",
|
|
87
|
+
highlight_pattern = value
|
|
88
|
+
)
|
|
89
|
+
|
|
90
90
|
return self
|
labfreed/pac_id/pac_id.py
CHANGED
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from typing_extensions import Self
|
|
3
|
-
from pydantic import Field, conlist, model_validator
|
|
4
|
-
|
|
5
|
-
from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel
|
|
6
|
-
from labfreed.pac_id.id_segment import IDSegment
|
|
7
|
-
from labfreed.pac_id.extension import Extension
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
from typing import TYPE_CHECKING
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
pass
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
_domain_name_pattern = r"(?!-)([A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,63}"
|
|
16
|
-
|
|
17
|
-
class PAC_ID(LabFREED_BaseModel):
|
|
18
|
-
'''Represents a PAC-ID.
|
|
19
|
-
Refer to the [specification](https://github.com/ApiniLabs/PAC-ID?tab=readme-ov-file#specification) for details.
|
|
20
|
-
'''
|
|
21
|
-
issuer:str
|
|
22
|
-
'''The issuer of the PAC-ID.'''
|
|
23
|
-
identifier: conlist(IDSegment) = Field(..., default_factory=list) # type: ignore # exclude=True prevents this from being serialized by Pydantic
|
|
24
|
-
'''The identifier of the PAC-ID is a series of IDSegments'''
|
|
25
|
-
|
|
26
|
-
extensions: list[Extension] = Field(default_factory=list)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def get_extension_of_type(self, type:str) -> list[Extension]:
|
|
30
|
-
'''Get all extensions of a certain type.'''
|
|
31
|
-
return [e for e in self.extensions if e.type == type]
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def get_extension(self, name:str) -> Extension|None:
|
|
35
|
-
'''Get extension of certain name'''
|
|
36
|
-
out = [e for e in self.extensions if e.name == name]
|
|
37
|
-
if not out:
|
|
38
|
-
return None
|
|
39
|
-
return out[0]
|
|
40
|
-
|
|
41
|
-
@classmethod
|
|
42
|
-
def from_url(cls, url, *, extension_interpreters='default',
|
|
43
|
-
try_pac_cat=True,
|
|
44
|
-
suppress_validation_errors=False) -> Self:
|
|
45
|
-
from labfreed.pac_id.url_parser import PAC_Parser
|
|
46
|
-
return PAC_Parser.from_url(url, try_pac_cat=try_pac_cat, suppress_validation_errors=suppress_validation_errors, extension_interpreters=extension_interpreters)
|
|
47
|
-
|
|
48
|
-
def to_url(self, use_short_notation=
|
|
49
|
-
from labfreed.pac_id.url_serializer import PACID_Serializer
|
|
50
|
-
return PACID_Serializer.to_url(self, use_short_notation=use_short_notation, uppercase_only=uppercase_only)
|
|
51
|
-
|
|
52
|
-
def to_json(self, indent=None) -> str:
|
|
53
|
-
if not indent:
|
|
54
|
-
return self.model_dump_json()
|
|
55
|
-
else:
|
|
56
|
-
return self.model_dump_json(indent=indent)
|
|
57
|
-
|
|
58
|
-
def to_dict(self) -> dict:
|
|
59
|
-
return self.model_dump()
|
|
60
|
-
|
|
61
|
-
def __str__(self):
|
|
62
|
-
return self.to_url()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@model_validator(mode='after')
|
|
66
|
-
def _check_at_least_one_segment(self) -> Self:
|
|
67
|
-
if not len(self.identifier) >= 1:
|
|
68
|
-
self._add_validation_message(
|
|
69
|
-
source="identifier",
|
|
70
|
-
level = ValidationMsgLevel.ERROR,
|
|
71
|
-
msg='Identifier must contain et least one segment.'
|
|
72
|
-
)
|
|
73
|
-
return self
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@model_validator(mode='after')
|
|
77
|
-
def _check_length(self) -> Self:
|
|
78
|
-
length = 0
|
|
79
|
-
for s in self.identifier:
|
|
80
|
-
s:IDSegment = s
|
|
81
|
-
if s.key:
|
|
82
|
-
length += len(s.key)
|
|
83
|
-
length += 1 # for ":"
|
|
84
|
-
length += len(s.value)
|
|
85
|
-
length += len(self.identifier) - 1 # account for "/" separating the segments
|
|
86
|
-
|
|
87
|
-
if length > 256:
|
|
88
|
-
self._add_validation_message(
|
|
89
|
-
source="identifier",
|
|
90
|
-
level = ValidationMsgLevel.ERROR,
|
|
91
|
-
msg=f'Identifier is {length} characters long, Identifier must not exceed 256 characters.'
|
|
92
|
-
)
|
|
93
|
-
return self
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@model_validator(mode="after")
|
|
97
|
-
def _validate_issuer(self):
|
|
98
|
-
if not re.fullmatch(_domain_name_pattern, self.issuer):
|
|
99
|
-
self._add_validation_message(
|
|
100
|
-
source="PAC-ID",
|
|
101
|
-
level = ValidationMsgLevel.ERROR,
|
|
102
|
-
highlight_pattern=self.issuer,
|
|
103
|
-
msg="Issuer must be a valid domain name. "
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# recommendation that A-Z, 0-9, -, and . should be used
|
|
107
|
-
if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.issuer)):
|
|
108
|
-
self._add_validation_message(
|
|
109
|
-
source="PAC-ID",
|
|
110
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
111
|
-
highlight_pattern=self.issuer,
|
|
112
|
-
highlight_sub=not_recommended_chars,
|
|
113
|
-
msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
|
|
114
|
-
)
|
|
115
|
-
return self
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
@model_validator(mode='after')
|
|
119
|
-
def _check_identifier_segment_keys_are_unique(self) -> Self:
|
|
120
|
-
keys = [s.key for s in self.identifier if s.key]
|
|
121
|
-
duplicate_keys = [k for k in set(keys) if keys.count(k) > 1]
|
|
122
|
-
if duplicate_keys:
|
|
123
|
-
for k in duplicate_keys:
|
|
124
|
-
self._add_validation_message(
|
|
125
|
-
source=f"identifier {k}",
|
|
126
|
-
level = ValidationMsgLevel.RECOMMENDATION,
|
|
127
|
-
msg=f"Duplicate segment key {k}. This will probably lead to undefined behaviour",
|
|
128
|
-
highlight_pattern = k
|
|
129
|
-
)
|
|
130
|
-
return self
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
1
|
+
import re
|
|
2
|
+
from typing_extensions import Self
|
|
3
|
+
from pydantic import Field, conlist, model_validator
|
|
4
|
+
|
|
5
|
+
from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel
|
|
6
|
+
from labfreed.pac_id.id_segment import IDSegment
|
|
7
|
+
from labfreed.pac_id.extension import Extension
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_domain_name_pattern = r"(?!-)([A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,63}"
|
|
16
|
+
|
|
17
|
+
class PAC_ID(LabFREED_BaseModel):
|
|
18
|
+
'''Represents a PAC-ID.
|
|
19
|
+
Refer to the [specification](https://github.com/ApiniLabs/PAC-ID?tab=readme-ov-file#specification) for details.
|
|
20
|
+
'''
|
|
21
|
+
issuer:str
|
|
22
|
+
'''The issuer of the PAC-ID.'''
|
|
23
|
+
identifier: conlist(IDSegment) = Field(..., default_factory=list) # type: ignore # exclude=True prevents this from being serialized by Pydantic
|
|
24
|
+
'''The identifier of the PAC-ID is a series of IDSegments'''
|
|
25
|
+
|
|
26
|
+
extensions: list[Extension] = Field(default_factory=list)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_extension_of_type(self, type:str) -> list[Extension]:
|
|
30
|
+
'''Get all extensions of a certain type.'''
|
|
31
|
+
return [e for e in self.extensions if e.type == type]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_extension(self, name:str) -> Extension|None:
|
|
35
|
+
'''Get extension of certain name'''
|
|
36
|
+
out = [e for e in self.extensions if e.name == name]
|
|
37
|
+
if not out:
|
|
38
|
+
return None
|
|
39
|
+
return out[0]
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_url(cls, url, *, extension_interpreters='default',
|
|
43
|
+
try_pac_cat=True,
|
|
44
|
+
suppress_validation_errors=False) -> Self:
|
|
45
|
+
from labfreed.pac_id.url_parser import PAC_Parser
|
|
46
|
+
return PAC_Parser.from_url(url, try_pac_cat=try_pac_cat, suppress_validation_errors=suppress_validation_errors, extension_interpreters=extension_interpreters)
|
|
47
|
+
|
|
48
|
+
def to_url(self, use_short_notation:None|bool=None, uppercase_only=False) -> str:
|
|
49
|
+
from labfreed.pac_id.url_serializer import PACID_Serializer
|
|
50
|
+
return PACID_Serializer.to_url(self, use_short_notation=use_short_notation, uppercase_only=uppercase_only)
|
|
51
|
+
|
|
52
|
+
def to_json(self, indent=None) -> str:
|
|
53
|
+
if not indent:
|
|
54
|
+
return self.model_dump_json()
|
|
55
|
+
else:
|
|
56
|
+
return self.model_dump_json(indent=indent)
|
|
57
|
+
|
|
58
|
+
def to_dict(self) -> dict:
|
|
59
|
+
return self.model_dump()
|
|
60
|
+
|
|
61
|
+
def __str__(self):
|
|
62
|
+
return self.to_url()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@model_validator(mode='after')
|
|
66
|
+
def _check_at_least_one_segment(self) -> Self:
|
|
67
|
+
if not len(self.identifier) >= 1:
|
|
68
|
+
self._add_validation_message(
|
|
69
|
+
source="identifier",
|
|
70
|
+
level = ValidationMsgLevel.ERROR,
|
|
71
|
+
msg='Identifier must contain et least one segment.'
|
|
72
|
+
)
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@model_validator(mode='after')
|
|
77
|
+
def _check_length(self) -> Self:
|
|
78
|
+
length = 0
|
|
79
|
+
for s in self.identifier:
|
|
80
|
+
s:IDSegment = s
|
|
81
|
+
if s.key:
|
|
82
|
+
length += len(s.key)
|
|
83
|
+
length += 1 # for ":"
|
|
84
|
+
length += len(s.value)
|
|
85
|
+
length += len(self.identifier) - 1 # account for "/" separating the segments
|
|
86
|
+
|
|
87
|
+
if length > 256:
|
|
88
|
+
self._add_validation_message(
|
|
89
|
+
source="identifier",
|
|
90
|
+
level = ValidationMsgLevel.ERROR,
|
|
91
|
+
msg=f'Identifier is {length} characters long, Identifier must not exceed 256 characters.'
|
|
92
|
+
)
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@model_validator(mode="after")
|
|
97
|
+
def _validate_issuer(self):
|
|
98
|
+
if not re.fullmatch(_domain_name_pattern, self.issuer):
|
|
99
|
+
self._add_validation_message(
|
|
100
|
+
source="PAC-ID",
|
|
101
|
+
level = ValidationMsgLevel.ERROR,
|
|
102
|
+
highlight_pattern=self.issuer,
|
|
103
|
+
msg="Issuer must be a valid domain name. "
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# recommendation that A-Z, 0-9, -, and . should be used
|
|
107
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.issuer)):
|
|
108
|
+
self._add_validation_message(
|
|
109
|
+
source="PAC-ID",
|
|
110
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
111
|
+
highlight_pattern=self.issuer,
|
|
112
|
+
highlight_sub=not_recommended_chars,
|
|
113
|
+
msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
|
|
114
|
+
)
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@model_validator(mode='after')
|
|
119
|
+
def _check_identifier_segment_keys_are_unique(self) -> Self:
|
|
120
|
+
keys = [s.key for s in self.identifier if s.key]
|
|
121
|
+
duplicate_keys = [k for k in set(keys) if keys.count(k) > 1]
|
|
122
|
+
if duplicate_keys:
|
|
123
|
+
for k in duplicate_keys:
|
|
124
|
+
self._add_validation_message(
|
|
125
|
+
source=f"identifier {k}",
|
|
126
|
+
level = ValidationMsgLevel.RECOMMENDATION,
|
|
127
|
+
msg=f"Duplicate segment key {k}. This will probably lead to undefined behaviour",
|
|
128
|
+
highlight_pattern = k
|
|
129
|
+
)
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|