labfreed 0.0.15__tar.gz → 0.0.16__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 (40) hide show
  1. {labfreed-0.0.15 → labfreed-0.0.16}/PKG-INFO +60 -37
  2. {labfreed-0.0.15 → labfreed-0.0.16}/README.md +59 -36
  3. {labfreed-0.0.15 → labfreed-0.0.16}/examples.py +13 -21
  4. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/PAC_CAT/data_model.py +26 -8
  5. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/TREX/data_model.py +1 -0
  6. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/__init__.py +1 -1
  7. labfreed-0.0.16/update_readme.py +93 -0
  8. labfreed-0.0.15/labfreed/PAC_CAT/data_model copy.py +0 -232
  9. labfreed-0.0.15/update_readme.py +0 -63
  10. {labfreed-0.0.15 → labfreed-0.0.16}/.vscode/launch.json +0 -0
  11. {labfreed-0.0.15 → labfreed-0.0.16}/.vscode/settings.json +0 -0
  12. {labfreed-0.0.15 → labfreed-0.0.16}/LICENSE +0 -0
  13. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/DisplayNameExtension/DisplayNameExtension.py +0 -0
  14. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/PAC_CAT/__init__.py +0 -0
  15. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/PAC_ID/__init__.py +0 -0
  16. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/PAC_ID/data_model.py +0 -0
  17. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/PAC_ID/extensions.py +0 -0
  18. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/TREX/UneceUnits.json +0 -0
  19. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/TREX/parse.py +0 -0
  20. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/TREX/unece_units.py +0 -0
  21. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/parse_pac.py +0 -0
  22. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/utilities/base36.py +0 -0
  23. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/utilities/extension_intertpreters.py +0 -0
  24. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/utilities/utility_types.py +0 -0
  25. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/utilities/well_known_keys.py +0 -0
  26. {labfreed-0.0.15 → labfreed-0.0.16}/labfreed/validation.py +0 -0
  27. {labfreed-0.0.15 → labfreed-0.0.16}/main.py +0 -0
  28. {labfreed-0.0.15 → labfreed-0.0.16}/publish.ps1 +0 -0
  29. {labfreed-0.0.15 → labfreed-0.0.16}/publish.sh +0 -0
  30. {labfreed-0.0.15 → labfreed-0.0.16}/publish_commands +0 -0
  31. {labfreed-0.0.15 → labfreed-0.0.16}/pyproject.toml +0 -0
  32. {labfreed-0.0.15 → labfreed-0.0.16}/pytest.ini +0 -0
  33. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_(de)_serialization_incl_extension/test__parse.py +0 -0
  34. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_(de)_serialization_incl_extension/test__serialize.py +0 -0
  35. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_PAC_CAT/test_PAC_CAT_parse.py +0 -0
  36. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_PAC_CAT/test_PAC_CAT_serialize.py +0 -0
  37. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_PAC_ID/test_PAC_ID_serialize.py +0 -0
  38. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_PAC_ID/test_pac_id_parse.py +0 -0
  39. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_TREX/test_TREX_parse.py +0 -0
  40. {labfreed-0.0.15 → labfreed-0.0.16}/tests/test_TREX/test_TREX_serialize.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labfreed
3
- Version: 0.0.15
3
+ Version: 0.0.16
4
4
  Summary: Python implementation of LabFREED building blocks
5
5
  Author-email: Reto Thürer <thuerer.r@buchi.com>
6
6
  Description-Content-Type: text/markdown
@@ -32,13 +32,9 @@ pip install labfreed
32
32
 
33
33
 
34
34
  ## Usage Examples
35
+ > ⚠️ **Note:** These examples are building on each other. Imports and parsing are not repeated in each example.
35
36
  <!-- BEGIN EXAMPLES -->
36
- ```python
37
-
38
- ```
39
-
40
- ### Parse a simple PAC-ID
41
- The
37
+ ### Parse a simple PAC-ID
42
38
 
43
39
  ```python
44
40
  from labfreed.parse_pac import PAC_Parser
@@ -51,33 +47,54 @@ pac_id = PAC_Parser().parse(pac_str).pac_id
51
47
  pac_id = PAC_Parser().parse(pac_str).pac_id
52
48
  is_valid = pac_id.is_valid()
53
49
  print('PAC-ID is valid: {is_valid}')
54
- # >> PAC-ID is valid: {is_valid}
55
50
  ```
56
-
57
- Show recommendations:
58
- Note that the PAC-ID, while valid uses characters, which are not recommended (results in larger QR code).
51
+ ```text
52
+ >> PAC-ID is valid: {is_valid}
53
+ ```
54
+ ### Show recommendations:
55
+ Note that the PAC-ID -- while valid -- uses characters which are not recommended (results in larger QR code).
59
56
  There is a nice function to highlight problems
60
57
 
61
58
  ```python
62
59
  pac_id.print_validation_messages()
63
- # >>
64
- # =======================================
65
- # Validation Results
66
- # ---------------------------------------
67
-
68
- # Recommendation in id segment value bal500
69
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
70
- # Characters a b l should not be used.
71
-
72
- # Recommendation in id segment value @1234
73
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
74
- # Characters @ should not be used.
75
-
76
- # Warning in Category -MD
77
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
78
- # Category key -MD is not a well known key. It is recommended to use well known keys only
79
60
  ```
61
+ ```text
62
+ >> =======================================
63
+ >> Validation Results
64
+ >> ---------------------------------------
65
+ >>
66
+ >> Recommendation in id segment value bal500
67
+ >> HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
68
+ >> Characters a b l should not be used.
69
+ >>
70
+ >> Recommendation in id segment value @1234
71
+ >> HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
72
+ >> Characters @ should not be used.
73
+ >>
74
+ >> Warning in Category -MD
75
+ >> HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
76
+ >> Category key -MD is not a well known key. It is recommended to use well known keys only
77
+ ```
78
+ ### PAC-CAT
80
79
 
80
+ ```python
81
+ from labfreed.PAC_CAT.data_model import PAC_CAT
82
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
83
+ pac_id = PAC_Parser().parse(pac_str).pac_id
84
+ if isinstance(pac_id, PAC_CAT):
85
+ pac_id.print_categories()
86
+ ```
87
+ ```text
88
+ >> Main Category
89
+ >> ----------
90
+ >> key (): -DR
91
+ >> id (21): XQ908756
92
+ >> Category
93
+ >> ------
94
+ >> key (): -MD
95
+ >> model_number (240): bal500
96
+ >> serial_number (21): @1234
97
+ ```
81
98
  ### Parse a PAC-ID with extensions
82
99
  PAC-ID can have extensions. Here we parse a PAC-ID with attached display names and summary.
83
100
 
@@ -87,17 +104,21 @@ pac_id = PAC_Parser().parse(pac_str)
87
104
 
88
105
  # Display Name
89
106
  display_names = pac_id.get_extension('N') # display name has name 'N'
90
- print(f'\n {display_names}')
91
- # >> Display names: My Balance ❤️
92
-
107
+ print(display_names)
108
+ ```
109
+ ```text
110
+ >> Display names: My Balance ❤️
111
+ ```
112
+ ```python
93
113
  # TREX
94
114
  trexes = pac_id.get_extension_of_type('TREX')
95
115
  trex = trexes[0] # there could be multiple trexes. In this example there is only one, though
96
116
  v = trex.get_segment('WEIGHT').to_python_type()
97
117
  print(f'WEIGHT = {v}')
98
- # >> WEIGHT = 67.89 g
99
118
  ```
100
-
119
+ ```text
120
+ >> WEIGHT = 67.89 g
121
+ ```
101
122
  ### Create a PAC-ID with Extensions
102
123
 
103
124
  #### Create PAC-ID
@@ -108,10 +129,11 @@ from labfreed.utilities.well_known_keys import WellKnownKeys
108
129
 
109
130
  pac_id = PACID(issuer='METTORIUS:COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
110
131
  pac_str = pac_id.serialize()
111
- pac_str = pac_id.serialize()
112
132
  print(pac_str)
113
133
  ```
114
-
134
+ ```text
135
+ >> HTTPS://PAC.METTORIUS:COM/21:1234
136
+ ```
115
137
  #### Create a TREX
116
138
  TREX can conveniently be created from a python dictionary.
117
139
  Note that utility types for Quantity (number with unit) and table are needed
@@ -149,7 +171,6 @@ if trex.get_nested_validation_messages():
149
171
  # Side Note: The TREX can be turned back into a dict
150
172
  d = trex.dict()
151
173
  ```
152
-
153
174
  #### Combine PAC-ID and TREX and serialize
154
175
 
155
176
  ```python
@@ -159,13 +180,15 @@ pac_with_trex = PACID_With_Extensions(pac_id=pac_id, extensions=[trex])
159
180
  pac_str = pac_with_trex.serialize()
160
181
  print(pac_str)
161
182
  ```
162
-
183
+ ```text
184
+ >> HTTPS://PAC.METTORIUS:COM/21:1234*DEMO$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:DATE$T.D:OK$T.B:COMMENT$T.A::1:20250408T204437.738:T:FOO::1.1:20250408T204437.739:T:BAR::1.3:20250408T204437.739:F:BLUBB
185
+ ```
163
186
  <!-- END EXAMPLES -->
164
187
 
165
188
 
166
189
 
167
190
  ## Change Log
168
191
 
169
- ### v0.0.9
192
+ ### v0.0.16
170
193
  - supports PAC-ID, PAC-CAT, TREX and DisplayName
171
194
  - ok-ish test coverage
@@ -23,13 +23,9 @@ pip install labfreed
23
23
 
24
24
 
25
25
  ## Usage Examples
26
+ > ⚠️ **Note:** These examples are building on each other. Imports and parsing are not repeated in each example.
26
27
  <!-- BEGIN EXAMPLES -->
27
- ```python
28
-
29
- ```
30
-
31
- ### Parse a simple PAC-ID
32
- The
28
+ ### Parse a simple PAC-ID
33
29
 
34
30
  ```python
35
31
  from labfreed.parse_pac import PAC_Parser
@@ -42,33 +38,54 @@ pac_id = PAC_Parser().parse(pac_str).pac_id
42
38
  pac_id = PAC_Parser().parse(pac_str).pac_id
43
39
  is_valid = pac_id.is_valid()
44
40
  print('PAC-ID is valid: {is_valid}')
45
- # >> PAC-ID is valid: {is_valid}
46
41
  ```
47
-
48
- Show recommendations:
49
- Note that the PAC-ID, while valid uses characters, which are not recommended (results in larger QR code).
42
+ ```text
43
+ >> PAC-ID is valid: {is_valid}
44
+ ```
45
+ ### Show recommendations:
46
+ Note that the PAC-ID -- while valid -- uses characters which are not recommended (results in larger QR code).
50
47
  There is a nice function to highlight problems
51
48
 
52
49
  ```python
53
50
  pac_id.print_validation_messages()
54
- # >>
55
- # =======================================
56
- # Validation Results
57
- # ---------------------------------------
58
-
59
- # Recommendation in id segment value bal500
60
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
61
- # Characters a b l should not be used.
62
-
63
- # Recommendation in id segment value @1234
64
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
65
- # Characters @ should not be used.
66
-
67
- # Warning in Category -MD
68
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
69
- # Category key -MD is not a well known key. It is recommended to use well known keys only
70
51
  ```
52
+ ```text
53
+ >> =======================================
54
+ >> Validation Results
55
+ >> ---------------------------------------
56
+ >>
57
+ >> Recommendation in id segment value bal500
58
+ >> HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
59
+ >> Characters a b l should not be used.
60
+ >>
61
+ >> Recommendation in id segment value @1234
62
+ >> HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
63
+ >> Characters @ should not be used.
64
+ >>
65
+ >> Warning in Category -MD
66
+ >> HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
67
+ >> Category key -MD is not a well known key. It is recommended to use well known keys only
68
+ ```
69
+ ### PAC-CAT
71
70
 
71
+ ```python
72
+ from labfreed.PAC_CAT.data_model import PAC_CAT
73
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
74
+ pac_id = PAC_Parser().parse(pac_str).pac_id
75
+ if isinstance(pac_id, PAC_CAT):
76
+ pac_id.print_categories()
77
+ ```
78
+ ```text
79
+ >> Main Category
80
+ >> ----------
81
+ >> key (): -DR
82
+ >> id (21): XQ908756
83
+ >> Category
84
+ >> ------
85
+ >> key (): -MD
86
+ >> model_number (240): bal500
87
+ >> serial_number (21): @1234
88
+ ```
72
89
  ### Parse a PAC-ID with extensions
73
90
  PAC-ID can have extensions. Here we parse a PAC-ID with attached display names and summary.
74
91
 
@@ -78,17 +95,21 @@ pac_id = PAC_Parser().parse(pac_str)
78
95
 
79
96
  # Display Name
80
97
  display_names = pac_id.get_extension('N') # display name has name 'N'
81
- print(f'\n {display_names}')
82
- # >> Display names: My Balance ❤️
83
-
98
+ print(display_names)
99
+ ```
100
+ ```text
101
+ >> Display names: My Balance ❤️
102
+ ```
103
+ ```python
84
104
  # TREX
85
105
  trexes = pac_id.get_extension_of_type('TREX')
86
106
  trex = trexes[0] # there could be multiple trexes. In this example there is only one, though
87
107
  v = trex.get_segment('WEIGHT').to_python_type()
88
108
  print(f'WEIGHT = {v}')
89
- # >> WEIGHT = 67.89 g
90
109
  ```
91
-
110
+ ```text
111
+ >> WEIGHT = 67.89 g
112
+ ```
92
113
  ### Create a PAC-ID with Extensions
93
114
 
94
115
  #### Create PAC-ID
@@ -99,10 +120,11 @@ from labfreed.utilities.well_known_keys import WellKnownKeys
99
120
 
100
121
  pac_id = PACID(issuer='METTORIUS:COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
101
122
  pac_str = pac_id.serialize()
102
- pac_str = pac_id.serialize()
103
123
  print(pac_str)
104
124
  ```
105
-
125
+ ```text
126
+ >> HTTPS://PAC.METTORIUS:COM/21:1234
127
+ ```
106
128
  #### Create a TREX
107
129
  TREX can conveniently be created from a python dictionary.
108
130
  Note that utility types for Quantity (number with unit) and table are needed
@@ -140,7 +162,6 @@ if trex.get_nested_validation_messages():
140
162
  # Side Note: The TREX can be turned back into a dict
141
163
  d = trex.dict()
142
164
  ```
143
-
144
165
  #### Combine PAC-ID and TREX and serialize
145
166
 
146
167
  ```python
@@ -150,13 +171,15 @@ pac_with_trex = PACID_With_Extensions(pac_id=pac_id, extensions=[trex])
150
171
  pac_str = pac_with_trex.serialize()
151
172
  print(pac_str)
152
173
  ```
153
-
174
+ ```text
175
+ >> HTTPS://PAC.METTORIUS:COM/21:1234*DEMO$TREX/STOP$T.D:20240505T1306+TEMP$KEL:10.15+OK$T.B:F+COMMENT$T.A:FOO+COMMENT2$T.T:12G3+TABLE$$DURATION$HUR:DATE$T.D:OK$T.B:COMMENT$T.A::1:20250408T204437.738:T:FOO::1.1:20250408T204437.739:T:BAR::1.3:20250408T204437.739:F:BLUBB
176
+ ```
154
177
  <!-- END EXAMPLES -->
155
178
 
156
179
 
157
180
 
158
181
  ## Change Log
159
182
 
160
- ### v0.0.9
183
+ ### v0.0.16
161
184
  - supports PAC-ID, PAC-CAT, TREX and DisplayName
162
185
  - ok-ish test coverage
@@ -1,6 +1,5 @@
1
1
  '''
2
2
  ### Parse a simple PAC-ID
3
- The
4
3
  '''
5
4
  from labfreed.parse_pac import PAC_Parser
6
5
 
@@ -12,30 +11,25 @@ pac_id = PAC_Parser().parse(pac_str).pac_id
12
11
  pac_id = PAC_Parser().parse(pac_str).pac_id
13
12
  is_valid = pac_id.is_valid()
14
13
  print('PAC-ID is valid: {is_valid}')
15
- # >> PAC-ID is valid: {is_valid}
16
14
 
17
15
  '''
18
- Show recommendations:
19
- Note that the PAC-ID, while valid uses characters, which are not recommended (results in larger QR code).
16
+ ### Show recommendations:
17
+ Note that the PAC-ID -- while valid -- uses characters which are not recommended (results in larger QR code).
20
18
  There is a nice function to highlight problems
21
19
  '''
22
20
  pac_id.print_validation_messages()
23
- # >>
24
- # =======================================
25
- # Validation Results
26
- # ---------------------------------------
27
21
 
28
- # Recommendation in id segment value bal500
29
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
30
- # Characters a b l should not be used.
31
22
 
32
- # Recommendation in id segment value @1234
33
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
34
- # Characters @ should not be used.
23
+ '''
24
+ ### PAC-CAT
25
+ '''
26
+ from labfreed.PAC_CAT.data_model import PAC_CAT
27
+ pac_str = 'HTTPS://PAC.METTORIUS.COM/-DR/XQ908756/-MD/bal500/@1234'
28
+ pac_id = PAC_Parser().parse(pac_str).pac_id
29
+ if isinstance(pac_id, PAC_CAT):
30
+ pac_id.print_categories()
31
+
35
32
 
36
- # Warning in Category -MD
37
- # HTTPS://PAC.METTORIUS.COM/-MD/bal500/@1234
38
- # Category key -MD is not a well known key. It is recommended to use well known keys only
39
33
 
40
34
 
41
35
 
@@ -48,15 +42,14 @@ pac_id = PAC_Parser().parse(pac_str)
48
42
 
49
43
  # Display Name
50
44
  display_names = pac_id.get_extension('N') # display name has name 'N'
51
- print(f'\n {display_names}')
52
- # >> Display names: My Balance ❤️
45
+ print(display_names)
53
46
 
47
+ ''''''
54
48
  # TREX
55
49
  trexes = pac_id.get_extension_of_type('TREX')
56
50
  trex = trexes[0] # there could be multiple trexes. In this example there is only one, though
57
51
  v = trex.get_segment('WEIGHT').to_python_type()
58
52
  print(f'WEIGHT = {v}')
59
- # >> WEIGHT = 67.89 g
60
53
 
61
54
 
62
55
 
@@ -70,7 +63,6 @@ from labfreed.utilities.well_known_keys import WellKnownKeys
70
63
 
71
64
  pac_id = PACID(issuer='METTORIUS:COM', identifier=[IDSegment(key=WellKnownKeys.SERIAL, value='1234')])
72
65
  pac_str = pac_id.serialize()
73
- pac_str = pac_id.serialize()
74
66
  print(pac_str)
75
67
 
76
68
 
@@ -138,6 +138,19 @@ class PAC_CAT(PACID):
138
138
  )
139
139
  return self
140
140
 
141
+ def print_categories(self):
142
+ s = ''
143
+ for i, c in enumerate(self.categories):
144
+ if i == 0:
145
+ title = 'Main Category\n----------'
146
+ else:
147
+ title = 'Category\n------ '
148
+
149
+ s += f'{title}\n'
150
+ s += str(c)
151
+ s += '\n'
152
+ print(s)
153
+
141
154
 
142
155
  # @classmethod
143
156
  # def from_categories(cls, issuer:str, categories:list[Category]):
@@ -198,6 +211,11 @@ class Category(BaseModelWithValidationMessages):
198
211
  return self
199
212
 
200
213
 
214
+ def __str__(self):
215
+ s = '\n'.join( [f'{field_name} \t ({field_info.alias or ''}): \t {getattr(self, field_name)}' for field_name, field_info in self.model_fields.items() if getattr(self, field_name)])
216
+ return s
217
+
218
+
201
219
  # def to_identifier_category(self, use_short_notation=False):
202
220
  # '''Creates a Category with the correct segments.
203
221
  # Segments are in order of the Pydantic model fields.
@@ -261,14 +279,14 @@ class Material_Device(Category):
261
279
  self.add_validation_message(
262
280
  source=f"Category {self.key}",
263
281
  type="Error",
264
- msg=f'Category key {self.key} is missing mandatory field Model NUmber',
282
+ msg=f'Category key {self.key} is missing mandatory field Model Number',
265
283
  highlight_pattern = f"{self.key}"
266
284
  )
267
285
  if not self.serial_number:
268
286
  self.add_validation_message(
269
287
  source=f"Category {self.key}",
270
288
  type="Error",
271
- msg=f'Category key {self.key} is missing mandatory field Serial NUmber',
289
+ msg=f'Category key {self.key} is missing mandatory field Serial Number',
272
290
  highlight_pattern = f"{self.key}"
273
291
  )
274
292
 
@@ -286,7 +304,7 @@ class Material_Substance(Category):
286
304
  self.add_validation_message(
287
305
  source=f"Category {self.key}",
288
306
  type="Error",
289
- msg=f'Category key {self.key} is missing mandatory field Product NUmber',
307
+ msg=f'Category key {self.key} is missing mandatory field Product Number',
290
308
  highlight_pattern = f"{self.key}"
291
309
  )
292
310
 
@@ -328,19 +346,19 @@ class Data_Abstract(Category, ABC):
328
346
  highlight_pattern = f"{self.key}"
329
347
  )
330
348
 
331
- class Data_Result(Category):
349
+ class Data_Result(Data_Abstract):
332
350
  key: str = Field(default='-DR', frozen=True)
333
351
 
334
- class Data_Method(Category):
352
+ class Data_Method(Data_Abstract):
335
353
  key: str = Field(default='-DM', frozen=True)
336
354
 
337
- class Data_Calibration(Category):
355
+ class Data_Calibration(Data_Abstract):
338
356
  key: str = Field(default='-DC', frozen=True)
339
357
 
340
- class Data_Progress(Category):
358
+ class Data_Progress(Data_Abstract):
341
359
  key: str = Field(default='-DP', frozen=True)
342
360
 
343
- class Data_Static(Category):
361
+ class Data_Static(Data_Abstract):
344
362
  key: str = Field(default='-DS', frozen=True)
345
363
 
346
364
 
@@ -654,6 +654,7 @@ class TREX(Extension, BaseModelWithValidationMessages):
654
654
  data.append(r)
655
655
 
656
656
  self.segments.append(TREX_Table(key=k, column_headers=headers, data=data))
657
+ return self
657
658
 
658
659
 
659
660
  def dict(self):
@@ -2,4 +2,4 @@
2
2
  Python implementation of LabFREED building blocks
3
3
  '''
4
4
 
5
- __version__ = "0.0.15"
5
+ __version__ = "0.0.16"
@@ -0,0 +1,93 @@
1
+ import re
2
+ import io
3
+ import contextlib
4
+
5
+ start_marker = "<!-- BEGIN EXAMPLES -->"
6
+ end_marker = "<!-- END EXAMPLES -->"
7
+
8
+ with open("examples.py", encoding="utf-8") as ex_file:
9
+ example_content = ex_file.read()
10
+
11
+ # Match blocks: either triple-quoted docstrings or plain code chunks
12
+ pattern = r"(?P<docstring>'''(.*?)'''|\"\"\"(.*?)\"\"\")"
13
+ matches = list(re.finditer(pattern, example_content, flags=re.DOTALL))
14
+
15
+ output_parts = []
16
+ last_index = 0
17
+
18
+ shared_globals = {}
19
+
20
+ def run_code_block(code_str):
21
+ stdout = io.StringIO()
22
+ try:
23
+ with contextlib.redirect_stdout(stdout):
24
+ exec(code_str, shared_globals)
25
+ except Exception as e:
26
+ return f"[Error during execution: {e}]"
27
+ return stdout.getvalue().strip()
28
+
29
+
30
+ def format_output_as_html(output: str) -> str:
31
+ lines = output.splitlines()
32
+ formatted = "\n".join(f">> {line}" for line in lines)
33
+ html_output = f"<pre><code style=\"color: green\">{formatted}</code></pre>"
34
+ return html_output
35
+
36
+
37
+ def format_output(output: str) -> str:
38
+ lines = output.splitlines()
39
+ formatted = "\n".join(f">> {line}" for line in lines)
40
+ return f"```text\n{formatted}\n```"
41
+
42
+
43
+ for match in matches:
44
+ # Capture code before this docstring
45
+ code_chunk = example_content[last_index:match.start()]
46
+ if code_chunk.strip():
47
+ code_block = code_chunk.strip("\n")
48
+ output_parts.append(f"```python\n{code_block}\n```")
49
+
50
+ result = run_code_block(code_block)
51
+ if result:
52
+ output_parts.append(format_output(result))
53
+
54
+ # Capture the docstring itself
55
+ doc = match.group(0).strip('\'" \n')
56
+ if doc:
57
+ output_parts.append(doc.strip() + "\n")
58
+
59
+ last_index = match.end()
60
+
61
+ # Add any remaining code after the last docstring
62
+ if last_index < len(example_content):
63
+ remaining_code = example_content[last_index:].strip("\n")
64
+ if remaining_code.strip():
65
+ output_parts.append(f"```python\n{remaining_code}\n```")
66
+
67
+ result = run_code_block(remaining_code)
68
+ if result:
69
+ output_parts.append(format_output(result))
70
+
71
+ # Combine the parts
72
+ example_content_md = "\n".join(part for part in output_parts if part.strip())
73
+
74
+ # Inject into README.md
75
+ with open("README.md", encoding="utf-8") as readme_file:
76
+ readme = readme_file.read()
77
+
78
+ if not readme:
79
+ raise ValueError("README.md is empty")
80
+
81
+ if start_marker not in readme or end_marker not in readme:
82
+ raise ValueError("Markers not found in README.md")
83
+
84
+ new_readme = (
85
+ readme.split(start_marker)[0]
86
+ + start_marker + "\n"
87
+ + example_content_md + "\n"
88
+ + end_marker
89
+ + readme.split(end_marker)[1]
90
+ )
91
+
92
+ with open("README.md", "w", encoding="utf-8") as readme_file:
93
+ readme_file.write(new_readme)
@@ -1,232 +0,0 @@
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
-
@@ -1,63 +0,0 @@
1
- import re
2
-
3
- start_marker = "<!-- BEGIN EXAMPLES -->"
4
- end_marker = "<!-- END EXAMPLES -->"
5
-
6
- with open("examples.py", encoding="utf-8") as ex_file:
7
- example_content = ex_file.read()
8
-
9
- # Split on docstring blocks, keeping the docstring delimiters
10
- pattern = r"(?:'''(.*?)'''|\"\"\"(.*?)\"\"\")"
11
- splits = re.split(pattern, example_content, flags=re.DOTALL)
12
-
13
- # Rebuild output
14
- output_parts = []
15
- in_code_block = False
16
-
17
- def flush_code_block(buffer):
18
- if buffer:
19
- return "```python\n" + "".join(buffer).strip() + "\n```\n"
20
- return ""
21
-
22
- code_buffer = []
23
-
24
- for i in range(len(splits)):
25
- chunk = splits[i]
26
- if chunk is None:
27
- continue
28
-
29
- # Every odd-numbered chunk is a docstring match (because of how re.split works with groups)
30
- if i % 3 == 1 or i % 3 == 2:
31
- # Flush any previous code block
32
- output_parts.append(flush_code_block(code_buffer))
33
- code_buffer = []
34
- docstring = chunk.strip()
35
- output_parts.append(docstring + "\n")
36
- else:
37
- code_buffer.append(chunk)
38
-
39
- # Final flush
40
- output_parts.append(flush_code_block(code_buffer))
41
-
42
- example_content_md = "\n".join(part for part in output_parts if part.strip())
43
-
44
- # Inject into README.md
45
- with open("README.md", encoding="utf-8") as readme_file:
46
- readme = readme_file.read()
47
-
48
- if not readme:
49
- raise ValueError("README.md is empty")
50
-
51
- if start_marker not in readme or end_marker not in readme:
52
- raise ValueError("Markers not found in README.md")
53
-
54
- new_readme = (
55
- readme.split(start_marker)[0]
56
- + start_marker + "\n"
57
- + example_content_md + "\n"
58
- + end_marker
59
- + readme.split(end_marker)[1]
60
- )
61
-
62
- with open("README.md", "w", encoding="utf-8") as readme_file:
63
- readme_file.write(new_readme)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes