labfreed 0.0.9__tar.gz → 0.0.11__tar.gz

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.

Files changed (50) hide show
  1. {labfreed-0.0.9 → labfreed-0.0.11}/.vscode/launch.json +10 -0
  2. labfreed-0.0.11/.vscode/settings.json +11 -0
  3. {labfreed-0.0.9 → labfreed-0.0.11}/PKG-INFO +1 -1
  4. labfreed-0.0.11/README.md +152 -0
  5. labfreed-0.0.11/examples.py +114 -0
  6. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/DisplayNameExtension/DisplayNameExtension.py +5 -2
  7. labfreed-0.0.11/labfreed/PAC_CAT/data_model copy.py +232 -0
  8. labfreed-0.0.11/labfreed/PAC_CAT/data_model.py +369 -0
  9. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/PAC_ID/data_model.py +42 -112
  10. labfreed-0.0.11/labfreed/PAC_ID/extensions.py +55 -0
  11. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/TREX/data_model.py +316 -396
  12. labfreed-0.0.11/labfreed/TREX/parse.py +60 -0
  13. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/TREX/unece_units.py +17 -1
  14. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/__init__.py +1 -1
  15. labfreed-0.0.9/labfreed/PAC_ID/parse.py → labfreed-0.0.11/labfreed/parse_pac.py +104 -59
  16. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/utilities/base36.py +29 -13
  17. labfreed-0.0.11/labfreed/utilities/extension_intertpreters.py +4 -0
  18. labfreed-0.0.11/labfreed/utilities/utility_types.py +103 -0
  19. labfreed-0.0.9/labfreed/PAC_ID/well_known_segment_keys.py → labfreed-0.0.11/labfreed/utilities/well_known_keys.py +1 -1
  20. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/validation.py +3 -1
  21. labfreed-0.0.11/main.py +57 -0
  22. labfreed-0.0.11/publish.ps1 +17 -0
  23. labfreed-0.0.11/publish.sh +18 -0
  24. labfreed-0.0.11/publish_commands +2 -0
  25. {labfreed-0.0.9 → labfreed-0.0.11}/pyproject.toml +1 -1
  26. {labfreed-0.0.9 → labfreed-0.0.11}/pytest.ini +0 -1
  27. labfreed-0.0.11/tests/test_(de)_serialization_incl_extension/test__parse.py +87 -0
  28. labfreed-0.0.11/tests/test_(de)_serialization_incl_extension/test__serialize.py +48 -0
  29. labfreed-0.0.11/tests/test_PAC_CAT/test_PAC_CAT_parse.py +145 -0
  30. labfreed-0.0.9/tests/test_PAC_CAT/test_PAC_CAT.py → labfreed-0.0.11/tests/test_PAC_CAT/test_PAC_CAT_serialize.py +0 -6
  31. labfreed-0.0.11/tests/test_PAC_ID/test_PAC_ID_serialize.py +15 -0
  32. labfreed-0.0.11/tests/test_PAC_ID/test_pac_id_parse.py +78 -0
  33. {labfreed-0.0.9/tests/test_TREXExtension → labfreed-0.0.11/tests/test_TREX}/test_TREX_parse.py +48 -26
  34. labfreed-0.0.11/update_readme.py +27 -0
  35. labfreed-0.0.9/.vscode/settings.json +0 -8
  36. labfreed-0.0.9/README.md +0 -21
  37. labfreed-0.0.9/labfreed/PAC_CAT/data_model.py +0 -109
  38. labfreed-0.0.9/labfreed/PAC_ID/serialize.py +0 -60
  39. labfreed-0.0.9/labfreed/TREX/parse.py +0 -128
  40. labfreed-0.0.9/labfreed/TREX/serialize.py +0 -3
  41. labfreed-0.0.9/labfreed/conversion_tools/uncertainty.py +0 -32
  42. labfreed-0.0.9/labfreed/conversion_tools/unit_utilities.py +0 -109
  43. labfreed-0.0.9/main.py +0 -142
  44. labfreed-0.0.9/tests/test_PAC_ID/test_pac_id_parse.py +0 -271
  45. labfreed-0.0.9/tests/test_PAC_ID/test_pac_id_serialization.py +0 -49
  46. {labfreed-0.0.9 → labfreed-0.0.11}/LICENSE +0 -0
  47. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/PAC_CAT/__init__.py +0 -0
  48. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/PAC_ID/__init__.py +0 -0
  49. {labfreed-0.0.9 → labfreed-0.0.11}/labfreed/TREX/UneceUnits.json +0 -0
  50. {labfreed-0.0.9/tests/test_TREXExtension → labfreed-0.0.11/tests/test_TREX}/test_TREX_serialize.py +0 -0
@@ -23,6 +23,16 @@
23
23
  "env": {
24
24
  "PYTHONPATH": "$(pwd)"
25
25
  }
26
+ },
27
+ {
28
+ "name": "Python Debugger: Examples",
29
+ "type": "debugpy",
30
+ "request": "launch",
31
+ "program": "examples.py",
32
+ "console": "integratedTerminal",
33
+ "env": {
34
+ "PYTHONPATH": "$(pwd)"
35
+ }
26
36
  }
27
37
  ]
28
38
  }
@@ -0,0 +1,11 @@
1
+ {
2
+ "python.testing.pytestArgs": [
3
+ ".",
4
+ "-s"
5
+ ],
6
+ "python.testing.unittestEnabled": false,
7
+ "python.testing.pytestEnabled": true,
8
+ "python.analysis.extraPaths": [
9
+ "./labfreed/utilities"
10
+ ]
11
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labfreed
3
- Version: 0.0.9
3
+ Version: 0.0.11
4
4
  Summary: Python implementation of LabFREED building blocks
5
5
  Author-email: Reto Thürer <thuerer.r@buchi.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,152 @@
1
+ # LabFREED for Python
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![PyPI](https://img.shields.io/pypi/v/labfreed.svg)](https://pypi.org/project/labfreed/) ![Python Version](https://img.shields.io/pypi/pyversions/labfreed)
4
+
5
+ <!--
6
+ [![Tests](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml/badge.svg)](https://github.com/retothuerer/LabFREED/actions/workflows/ci.yml)
7
+ -->
8
+
9
+ This is a Python implementation of [LabFREED](www.labfreed.wega-it.com) building blocks.
10
+
11
+ ## Supported Building Blocks
12
+ - PAC-ID
13
+ - PAC-CAT
14
+ - TREX
15
+ - Display Extension
16
+
17
+ ## Installation
18
+ You can install LabFREED from [PyPI](https://pypi.org/project/labfreed/) using pip:
19
+
20
+ ```bash
21
+ pip install labfreed
22
+ ```
23
+
24
+
25
+ ## Usage Examples
26
+ <!-- BEGIN EXAMPLES -->
27
+ ```python
28
+ from datetime import datetime
29
+ from typing import Annotated
30
+
31
+ # Parse a PAC ID
32
+ from labfreed.PAC_ID.data_model import PACID, IDSegment
33
+ from labfreed.parse_pac import PAC_Parser, PACID_With_Extensions
34
+ from utility_types import Quantity
35
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/BAL500/1234'
36
+ pac = PAC_Parser().parse_pac_id(pac_str)
37
+
38
+ ## Check validity
39
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
40
+ pac = PAC_Parser().parse_pac_id(pac_str)
41
+ is_valid = pac.is_valid()
42
+ print('PAC-ID is valid: {is_valid}')
43
+
44
+ # Show recommendations
45
+ pac.print_validation_messages()
46
+
47
+
48
+
49
+
50
+ ## Parse a PAC-ID with extensions
51
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/BAL500/1234*N$N/WM633OV3E5DGJW2BEG0PDM1EA7*SUM$TREX/WEIGHT$GRM:67.89'
52
+ pac = PAC_Parser().parse_pac_with_extensions(pac_str)
53
+
54
+ # Display Name
55
+ display_names = pac.get_extension('N')
56
+ print(f'\nDisplay names: {display_names}')
57
+
58
+ # TREX
59
+ from labfreed.TREX.data_model import TREX, ColumnHeader, TREX_Table, TableRow
60
+ trexes = pac.get_extension_of_type('TREX')
61
+ trex:TREX = trexes[0]
62
+ v = trex.get_segment('WEIGHT').to_python_type()
63
+
64
+
65
+
66
+
67
+ ## Create a PAC-ID
68
+ from labfreed.utilities.well_known_keys import WellKnownKeys
69
+ pac = PACID(issuer='METTORIUS:COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
70
+ pac_str = pac.serialize()
71
+ pac_str = pac.serialize()
72
+ print(pac_str)
73
+
74
+
75
+
76
+
77
+ # create a TREX
78
+ from labfreed.TREX.data_model import TREX, DateSegment, NumericSegment, BoolSegment, AlphanumericSegment, TextSegment, BinarySegment
79
+
80
+ trex = TREX(name_='DEMO-TREX')
81
+
82
+ trex.segments.append(DateSegment(key='START',value='20240505T1204'))
83
+ trex.segments.append(DateSegment(key='STOP', value=datetime(year=2024,month=5,day=5,hour=13,minute=6)) )
84
+
85
+ trex.segments.append(NumericSegment(key='TEMP',type='KEL', value=10.15) )
86
+ trex.segments.append(BoolSegment(key='OK', value=False) )
87
+ trex.segments.append(AlphanumericSegment(key='COMMENT', value='FOO') )
88
+ trex.segments.append(TextSegment(key='COMMENT2', value='🐯') )
89
+ trex.segments.append(TextSegment(key='COMMENT2', value='BAR') )
90
+
91
+ # if the sting is already in base36 you have to be explicit
92
+ from labfreed.utilities.base36 import base36
93
+ trex.segments.append(TextSegment(key='COMMENT3', value=base36('1URIOQ7')) )
94
+
95
+ # table
96
+ table = TREX_Table(key='TABLE',
97
+ column_headers=[
98
+ ColumnHeader(key='COL1', type='HUR'),
99
+ ColumnHeader(key='COL2', type='T.A'),
100
+ ColumnHeader(key='COL3', type='T.D'),
101
+ ColumnHeader(key='COL4', type='T.B')
102
+ ],
103
+ data=[
104
+ TableRow()
105
+ ])
106
+ trex.segments.append(table)
107
+
108
+ if trex.is_valid():
109
+ print(trex.data)
110
+ else:
111
+ trex.print_validation_messages()
112
+
113
+
114
+ p = PACID_With_Extensions(pac_id=pac, extensions=[trex])
115
+ print(p.serialize())
116
+
117
+
118
+
119
+
120
+
121
+ # for convenience a TREX can be created from a dict
122
+ trex2 = TREX(name_='DEMO-TREX2')
123
+ trex2.update(
124
+ {
125
+ 'STOP': datetime(year=2024,month=5,day=5,hour=13,minute=6),
126
+ 'TEMP': Quantity(value=10.15, unit_name='meter', unit_symbol='m'),
127
+ 'OK':False,
128
+ 'COMMENT': 'FOO',
129
+ 'COMMENT2':'🐯'
130
+ }
131
+ )
132
+
133
+ d = trex2.dict()
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+ ```
144
+ <!-- END EXAMPLES -->
145
+
146
+
147
+
148
+ ## Change Log
149
+
150
+ ### v0.0.9
151
+ - supports PAC-ID, PAC-CAT, TREX and DisplayName
152
+ - ok-ish test coverage
@@ -0,0 +1,114 @@
1
+ from datetime import datetime
2
+ from typing import Annotated
3
+
4
+ # Parse a PAC ID
5
+ from labfreed.PAC_ID.data_model import PACID, IDSegment
6
+ from labfreed.parse_pac import PAC_Parser, PACID_With_Extensions
7
+ from utility_types import Quantity
8
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/BAL500/1234'
9
+ pac = PAC_Parser().parse_pac_id(pac_str)
10
+
11
+ ## Check validity
12
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234'
13
+ pac = PAC_Parser().parse_pac_id(pac_str)
14
+ is_valid = pac.is_valid()
15
+ print('PAC-ID is valid: {is_valid}')
16
+
17
+ # Show recommendations
18
+ pac.print_validation_messages()
19
+
20
+
21
+
22
+
23
+ ## Parse a PAC-ID with extensions
24
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-MD/BAL500/1234*N$N/WM633OV3E5DGJW2BEG0PDM1EA7*SUM$TREX/WEIGHT$GRM:67.89'
25
+ pac = PAC_Parser().parse_pac_with_extensions(pac_str)
26
+
27
+ # Display Name
28
+ display_names = pac.get_extension('N')
29
+ print(f'\nDisplay names: {display_names}')
30
+
31
+ # TREX
32
+ from labfreed.TREX.data_model import TREX, ColumnHeader, TREX_Table, TableRow
33
+ trexes = pac.get_extension_of_type('TREX')
34
+ trex:TREX = trexes[0]
35
+ v = trex.get_segment('WEIGHT').to_python_type()
36
+
37
+
38
+
39
+
40
+ ## Create a PAC-ID
41
+ from labfreed.utilities.well_known_keys import WellKnownKeys
42
+ pac = PACID(issuer='METTORIUS:COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
43
+ pac_str = pac.serialize()
44
+ pac_str = pac.serialize()
45
+ print(pac_str)
46
+
47
+
48
+
49
+
50
+ # create a TREX
51
+ from labfreed.TREX.data_model import TREX, DateSegment, NumericSegment, BoolSegment, AlphanumericSegment, TextSegment, BinarySegment
52
+
53
+ trex = TREX(name_='DEMO-TREX')
54
+
55
+ trex.segments.append(DateSegment(key='START',value='20240505T1204'))
56
+ trex.segments.append(DateSegment(key='STOP', value=datetime(year=2024,month=5,day=5,hour=13,minute=6)) )
57
+
58
+ trex.segments.append(NumericSegment(key='TEMP',type='KEL', value=10.15) )
59
+ trex.segments.append(BoolSegment(key='OK', value=False) )
60
+ trex.segments.append(AlphanumericSegment(key='COMMENT', value='FOO') )
61
+ trex.segments.append(TextSegment(key='COMMENT2', value='🐯') )
62
+ trex.segments.append(TextSegment(key='COMMENT2', value='BAR') )
63
+
64
+ # if the sting is already in base36 you have to be explicit
65
+ from labfreed.utilities.base36 import base36
66
+ trex.segments.append(TextSegment(key='COMMENT3', value=base36('1URIOQ7')) )
67
+
68
+ # table
69
+ table = TREX_Table(key='TABLE',
70
+ column_headers=[
71
+ ColumnHeader(key='COL1', type='HUR'),
72
+ ColumnHeader(key='COL2', type='T.A'),
73
+ ColumnHeader(key='COL3', type='T.D'),
74
+ ColumnHeader(key='COL4', type='T.B')
75
+ ],
76
+ data=[
77
+ TableRow()
78
+ ])
79
+ trex.segments.append(table)
80
+
81
+ if trex.is_valid():
82
+ print(trex.data)
83
+ else:
84
+ trex.print_validation_messages()
85
+
86
+
87
+ p = PACID_With_Extensions(pac_id=pac, extensions=[trex])
88
+ print(p.serialize())
89
+
90
+
91
+
92
+
93
+
94
+ # for convenience a TREX can be created from a dict
95
+ trex2 = TREX(name_='DEMO-TREX2')
96
+ trex2.update(
97
+ {
98
+ 'STOP': datetime(year=2024,month=5,day=5,hour=13,minute=6),
99
+ 'TEMP': Quantity(value=10.15, unit_name='meter', unit_symbol='m'),
100
+ 'OK':False,
101
+ 'COMMENT': 'FOO',
102
+ 'COMMENT2':'🐯'
103
+ }
104
+ )
105
+
106
+ d = trex2.dict()
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from pydantic import BaseModel
3
- from ..PAC_ID.data_model import Extension
3
+ from labfreed.PAC_ID.extensions import Extension
4
4
  from labfreed.utilities.base36 import from_base36, to_base36
5
5
 
6
6
 
@@ -20,7 +20,7 @@ class DisplayNames(Extension, BaseModel):
20
20
  return '/'.join([to_base36(dn) for dn in self.display_names])
21
21
 
22
22
  @staticmethod
23
- def from_spec_fields(name, type, data):
23
+ def from_spec_fields(*, name, type, data):
24
24
  if name != 'N':
25
25
  logging.warning(f'Name {name} was given, but this extension should only be used with name "N". Will ignore input')
26
26
 
@@ -30,5 +30,8 @@ class DisplayNames(Extension, BaseModel):
30
30
  display_names = [from_base36(b36) for b36 in data.split('/')]
31
31
 
32
32
  return DisplayNames(display_names=display_names)
33
+
34
+ def __str__(self):
35
+ return 'Display names: '+ ';'.join(self.display_names)
33
36
 
34
37
 
@@ -0,0 +1,232 @@
1
+ # import re
2
+ # from typing import Optional
3
+ # from typing_extensions import Self
4
+ # from pydantic import Field, ValidationInfo, computed_field, conlist, model_validator, field_validator
5
+
6
+ # from abc import ABC, abstractproperty, abstractstaticmethod
7
+
8
+ # from labfreed.PAC_ID.data_model import PACID
9
+
10
+ # from ..utilities.well_known_keys import WellKnownKeys
11
+ # from labfreed.validation import BaseModelWithValidationMessages, ValidationMessage, hsegment_pattern, domain_name_pattern
12
+
13
+
14
+ # # class IDSegment(BaseModelWithValidationMessages):
15
+ # # key:str|None = None
16
+ # # value:str
17
+
18
+ # # @model_validator(mode="after")
19
+ # # def validate_segment(self):
20
+ # # key = self.key or ""
21
+ # # value = self.value
22
+
23
+ # # # MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
24
+ # # # This means it must be true for both, key and value
25
+ # # if not_allowed_chars := set(re.sub(hsegment_pattern, '', key)):
26
+ # # self.add_validation_message(
27
+ # # source=f"id segment key {key}",
28
+ # # type="Error",
29
+ # # msg=f"{' '.join(not_allowed_chars)} must not be used.",
30
+ # # recommendation = "The segment key must be a valid hsegment",
31
+ # # highlight_pattern = key,
32
+ # # highlight_sub = not_allowed_chars
33
+ # # )
34
+
35
+ # # if not_allowed_chars := set(re.sub(hsegment_pattern, '', value)):
36
+ # # self.add_validation_message(
37
+ # # source=f"id segment key {value}",
38
+ # # type="Error",
39
+ # # msg=f"{' '.join(not_allowed_chars)} must not be used.",
40
+ # # recommendation = "The segment key must be a valid hsegment",
41
+ # # highlight_pattern = value,
42
+ # # highlight_sub = not_allowed_chars
43
+ # # )
44
+
45
+ # # # Segment key SHOULD be limited to A-Z, 0-9, and -+..
46
+ # # if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
47
+ # # self.add_validation_message(
48
+ # # source=f"id segment key {key}",
49
+ # # type="Recommendation",
50
+ # # msg=f"{' '.join(not_recommended_chars)} should not be used.",
51
+ # # recommendation = "SHOULD be limited to A-Z, 0-9, and -+",
52
+ # # highlight_pattern = key,
53
+ # # highlight_sub = not_recommended_chars
54
+ # # )
55
+
56
+ # # # Segment key should be in Well know keys
57
+ # # if key and key not in [k.value for k in WellKnownKeys]:
58
+ # # self.add_validation_message(
59
+ # # source=f"id segment key {key}",
60
+ # # type="Recommendation",
61
+ # # msg=f"{key} is not a well known segment key.",
62
+ # # recommendation = "RECOMMENDED to be a well-known id segment key.",
63
+ # # highlight_pattern = key
64
+ # # )
65
+
66
+
67
+ # # # Segment value SHOULD be limited to A-Z, 0-9, and -+..
68
+ # # if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
69
+ # # self.add_validation_message(
70
+ # # source=f"id segment value {value}",
71
+ # # type="Recommendation",
72
+ # # msg=f"Characters {' '.join(not_recommended_chars)} should not be used.",
73
+ # # recommendation = "SHOULD be limited to A-Z, 0-9, and -+",
74
+ # # highlight_pattern = value,
75
+ # # highlight_sub = not_recommended_chars
76
+ # # )
77
+
78
+ # # # Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
79
+ # # # this means that ":" in key or value is problematic
80
+ # # if ':' in key:
81
+ # # self.add_validation_message(
82
+ # # source=f"id segment key {key}",
83
+ # # type="Recommendation",
84
+ # # 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.",
85
+ # # highlight_pattern = key
86
+ # # )
87
+ # # if ':' in value:
88
+ # # self.add_validation_message(
89
+ # # source=f"id segment value {value}",
90
+ # # type="Recommendation",
91
+ # # 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.",
92
+ # # highlight_pattern = value
93
+ # # )
94
+
95
+ # # return self
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ # class PAC_CAT(PACID):
104
+
105
+ # @computed_field
106
+ # @property
107
+ # def categories(self) -> list[Category]:
108
+ # categories = list()
109
+ # c = Category(segments=[])
110
+ # categories.append(c)
111
+ # for s in self.segments:
112
+ # # new category starts with "-"
113
+ # if s.value[0] == '-':
114
+ # cat_key = s.value
115
+ # c = Category(key=cat_key, segments=[])
116
+ # categories.append(c)
117
+ # else:
118
+ # c.segments.append(s)
119
+
120
+ # # the first category might have no segments. remove categories without segments
121
+ # if not categories[0].segments:
122
+ # categories = categories[1:]
123
+
124
+ # return categories
125
+
126
+ # @model_validator(mode='after')
127
+ # def check_keys_are_unique_in_each_category(self) -> Self:
128
+ # for c in self.categories:
129
+ # keys = [s.key for s in c.segments if s.key]
130
+ # duplicate_keys = [k for k in set(keys) if keys.count(k) > 1]
131
+ # if duplicate_keys:
132
+ # for k in duplicate_keys:
133
+ # self.add_validation_message(
134
+ # source=f"identifier {k}",
135
+ # type="Error",
136
+ # msg=f"Duplicate key {k} in category {c.key}",
137
+ # highlight_pattern = k
138
+ # )
139
+ # return self
140
+
141
+ # # @model_validator(mode='after')
142
+ # # def check_length(self) -> Self:
143
+ # # l = 0
144
+ # # for s in self.segments:
145
+ # # if s.key:
146
+ # # l += len(s.key)
147
+ # # l += 1 # for ":"
148
+ # # l += len(s.value)
149
+ # # l += len(self.segments) - 1 # account for "/" separating the segments
150
+
151
+ # # if l > 256:
152
+ # # self.add_validation_message(
153
+ # # source=f"identifier",
154
+ # # type="Error",
155
+ # # msg=f'Identifier is {l} characters long, Identifier must not exceed 256 characters.',
156
+ # # highlight_pattern = ""
157
+ # # )
158
+ # # return self
159
+
160
+ # @staticmethod
161
+ # def from_categories(categories:list[Category]) :
162
+ # segments = list()
163
+ # for c in categories:
164
+ # if c.key:
165
+ # segments.append(IDSegment(value=c.key))
166
+ # segments.extend(c.segments)
167
+ # return Identifier(segments=segments)
168
+
169
+
170
+
171
+
172
+
173
+ # # class PACID(BaseModelWithValidationMessages):
174
+ # # issuer:str
175
+ # # identifier: Identifier
176
+
177
+ # # @model_validator(mode="after")
178
+ # # def validate_issuer(self):
179
+ # # if not re.fullmatch(domain_name_pattern, self.issuer):
180
+ # # self.add_validation_message(
181
+ # # source="PAC-ID",
182
+ # # type="Error",
183
+ # # highlight_pattern=self.issuer,
184
+ # # msg=f"Issuer must be a valid domain name. "
185
+ # # )
186
+
187
+ # # # recommendation that A-Z, 0-9, -, and . should be used
188
+ # # if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.issuer)):
189
+ # # self.add_validation_message(
190
+ # # source="PAC-ID",
191
+ # # type="Recommendation",
192
+ # # highlight_pattern=self.issuer,
193
+ # # highlight_sub=not_recommended_chars,
194
+ # # msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
195
+ # # )
196
+ # # return self
197
+
198
+ # # def __str__(self):
199
+ # # id_segments = ''
200
+ # # for s in self.identifier.segments:
201
+ # # if s.key:
202
+ # # id_segments += f'/{s.key}:{s.value}'
203
+ # # else:
204
+ # # id_segments += f'/{s.value}'
205
+
206
+ # # out = f"HTTPS://PAC.{self.issuer}{id_segments}"
207
+ # # return out
208
+
209
+
210
+
211
+
212
+ # # class PACID_With_Extensions(BaseModelWithValidationMessages):
213
+ # # pac_id: PACID
214
+ # # extensions: list[Extension] = Field(default_factory=list)
215
+
216
+ # # def __str__(self):
217
+ # # out = str(self.pac_id)
218
+ # # out += '*'.join(str(e) for e in self.extensions)
219
+
220
+ # # def get_extension_of_type(self, type:str) -> list[Extension]:
221
+ # # return [e for e in self.extensions if e.type == type]
222
+
223
+ # # def get_extension(self, name:str) -> Extension|None:
224
+ # # out = [e for e in self.extensions if e.name == name]
225
+ # # if not out:
226
+ # # return None
227
+ # # return out[0]
228
+
229
+
230
+
231
+
232
+