labfreed 0.2.6a2__py3-none-any.whl → 0.2.6a4__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 CHANGED
@@ -2,7 +2,7 @@
2
2
  Python implementation of LabFREED building blocks
3
3
  '''
4
4
 
5
- __version__ = "0.2.6a2"
5
+ __version__ = "0.2.6a4"
6
6
 
7
7
  from labfreed.pac_id import * # noqa: F403
8
8
  from labfreed.pac_cat import * # noqa: F403
@@ -194,9 +194,9 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
194
194
  formatted_msg = list()
195
195
  for m in self.validation_messages():
196
196
  if m.level == ValidationMsgLevel.ERROR:
197
- color = 'red'
197
+ color = '#d70000'
198
198
  else:
199
- color = 'orange'
199
+ color = '#d78700'
200
200
 
201
201
  match target:
202
202
  case 'markdown':
@@ -218,7 +218,7 @@ class LabFREED_BaseModel(PDOC_Workaround_Base):
218
218
  br = '<br>'
219
219
 
220
220
  serialized = str(self)
221
- serialized.replace('\n', br)
221
+ serialized = serialized.replace('\n', br)
222
222
  emphazised_highlight = self._emphasize_in(m, serialized, fmt=fmt)
223
223
  emphazised_highlight = emphazised_highlight.replace('👈👉','') # removes two consecutive markers, to make it cleaner
224
224
 
@@ -0,0 +1,82 @@
1
+ from enum import Enum
2
+ import re
3
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
4
+
5
+
6
+
7
+ class ServiceType(Enum):
8
+ USER_HANDOVER_GENERIC = 'userhandover-generic'
9
+ ATTRIBUTE_SERVICE_GENERIC = 'attributes-generic'
10
+
11
+
12
+ def _validate_service_name(service_name):
13
+ msg_dict = []
14
+ if not_allowed_chars := set(re.sub(r'[A-Za-z0-9\-\x20]', '', service_name)):
15
+ msg_dict.append( {
16
+ "level": ValidationMsgLevel.ERROR,
17
+ "msg": f'Service name ontains invalid characters {_quote_texts(not_allowed_chars)}',
18
+ "highlight_sub": not_allowed_chars
19
+ }
20
+ )
21
+
22
+ if len(service_name) == 0 or len(service_name) > 255:
23
+ msg_dict.append( {
24
+ "level": ValidationMsgLevel.ERROR,
25
+ "msg": 'Service name must be at least one and maximum 255 characters long'
26
+ }
27
+ )
28
+ return msg_dict
29
+
30
+
31
+ def _validate_application_intent(intent):
32
+ msg_dict = []
33
+ if re.fullmatch('.*-generic$', intent):
34
+ msg_dict.append( {
35
+ "level": ValidationMsgLevel.ERROR,
36
+ "msg": "Application intent ends with '-generic'. This is not permitted, since it is reserved for future uses'",
37
+ "highlight_sub": [intent]
38
+ }
39
+ )
40
+
41
+ if not_allowed_chars := set(re.sub(r'[A-Za-z0-9\-]', '', intent)):
42
+ msg_dict.append( {
43
+ "level": ValidationMsgLevel.ERROR,
44
+ "msg": f'Application intent contains invalid characters {_quote_texts(not_allowed_chars)}',
45
+ "highlight_sub": not_allowed_chars
46
+ }
47
+ )
48
+
49
+ if len(intent) == 0 or len(intent) > 255:
50
+ msg_dict.append( {
51
+ "level": ValidationMsgLevel.ERROR,
52
+ "source": f'Application intent {intent}',
53
+ "msg": 'Must be at least one and maximum 255 characters long'
54
+ }
55
+ )
56
+ return msg_dict
57
+
58
+
59
+ def _validate_service_type(service_type):
60
+ msg_dict = []
61
+ if isinstance(service_type, ServiceType):
62
+ service_type= service_type.value
63
+ else:
64
+ service_type= service_type
65
+ allowed_types = [ServiceType.ATTRIBUTE_SERVICE_GENERIC.value, ServiceType.USER_HANDOVER_GENERIC.value]
66
+ if service_type not in allowed_types:
67
+ msg_dict.append( {
68
+ "level": ValidationMsgLevel.ERROR,
69
+ "msg": f'Invalid service type. Must be {_quote_texts(allowed_types)} must be at least one and maximum 255 characters long',
70
+ "highlight_sub": service_type
71
+ }
72
+ )
73
+ return msg_dict
74
+
75
+
76
+ def _add_msg_to_cit_entry_model(msg_dict, model):
77
+ for m in msg_dict:
78
+ m.update({"source": model.service_name})
79
+ model._add_validation_message(**m)
80
+ return model
81
+
82
+
@@ -2,23 +2,73 @@
2
2
  from enum import Enum
3
3
  import logging
4
4
  import re
5
+ import traceback
5
6
 
6
- from pydantic import Field
7
+ from pydantic import Field, model_validator
7
8
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMessage, ValidationMsgLevel
8
9
  from labfreed.pac_id.pac_id import PAC_ID
9
10
  from labfreed.pac_id_resolver.services import Service, ServiceGroup
11
+ from labfreed.pac_id_resolver.cit_common import ( _add_msg_to_cit_entry_model,
12
+ _validate_service_name,
13
+ _validate_application_intent,
14
+ _validate_service_type,
15
+ ServiceType)
10
16
 
11
- class ServiceType(Enum):
12
- USER_HANDOVER_GENERIC = 'userhandover-generic'
13
- ATTRIBUTE_SERVICE_GENERIC = 'attributes-generic'
14
17
 
15
18
 
16
19
  class CITEntry_v1(LabFREED_BaseModel):
17
20
  applicable_if: str = Field(..., min_length=1)
18
21
  service_name: str = Field(..., min_length=1)
19
22
  application_intent:str = Field(..., min_length=1)
20
- service_type:ServiceType
23
+ service_type:ServiceType|str
21
24
  template_url:str = Field(..., min_length=1)
25
+
26
+
27
+ @model_validator(mode='after')
28
+ def _validate_model(self):
29
+ if self.applicable_if:
30
+ conditions = self.applicable_if.split(';')
31
+ for c in conditions:
32
+ if '=' in c:
33
+ query, expected = c.split('=')
34
+ query = query.strip()
35
+ else:
36
+ query = c.strip()
37
+
38
+ try:
39
+ # use this function to check if the pattern is valid. it returns a PatternError if not
40
+ _find_pattern_in_pac(query, '')
41
+ except PatternError:
42
+ self._add_validation_message(
43
+ level=ValidationMsgLevel.ERROR,
44
+ source=f'Service {self.service_name}',
45
+ msg=f'Applicable if contains invalid pattern {query}',
46
+ highlight_sub=query
47
+ )
48
+ except Exception:
49
+ pass # if no PatternError everything is fine
50
+ return self
51
+
52
+ @model_validator(mode='after')
53
+ def _validate_service_name(self):
54
+ msg_dict= _validate_service_name(self.service_name)
55
+ return _add_msg_to_cit_entry_model(msg_dict, self)
56
+
57
+
58
+ @model_validator(mode='after')
59
+ def _validate_application_intent(self):
60
+ msg_dict= _validate_application_intent(self.application_intent)
61
+ return _add_msg_to_cit_entry_model(msg_dict, self)
62
+
63
+
64
+ @model_validator(mode='after')
65
+ def _validate_service_type(self):
66
+ msg_dict= _validate_service_type(self.service_type)
67
+ return _add_msg_to_cit_entry_model(msg_dict, self)
68
+
69
+
70
+
71
+
22
72
 
23
73
 
24
74
  class CIT_v1(LabFREED_BaseModel):
@@ -40,11 +90,34 @@ class CIT_v1(LabFREED_BaseModel):
40
90
  continue
41
91
 
42
92
  cols = [c.strip() for c in line.split('\t')]
93
+ if len(cols) < 5:
94
+ logging.error(f'invalid line {line}')
95
+ msg = ValidationMessage(
96
+ level=ValidationMsgLevel.ERROR,
97
+ source='CIT line',
98
+ source_id=0,
99
+ msg=f'Invalid line in CIT. There are {5 - len(cols)} columns missing.',
100
+ highlight_sub_patterns=line
101
+ )
102
+ errors.append(msg)
103
+ continue
104
+ if len(cols) > 5:
105
+ logging.error(f'invalid line {line}')
106
+ msg = ValidationMessage(
107
+ level=ValidationMsgLevel.ERROR,
108
+ source='CIT line',
109
+ source_id=0,
110
+ msg=f'Invalid line in CIT. There are {len(cols) -5} too many columns',
111
+ highlight_sub_patterns=line
112
+ )
113
+ errors.append(msg)
114
+ continue
43
115
  try:
116
+
44
117
  entry = CITEntry_v1(
45
118
  service_name = cols[0],
46
119
  application_intent = cols[1],
47
- service_type = ServiceType(cols[2]),
120
+ service_type = cols[2],
48
121
  applicable_if = cols[3],
49
122
  template_url = cols[4]
50
123
  )
@@ -52,23 +125,32 @@ class CIT_v1(LabFREED_BaseModel):
52
125
  except ValueError:
53
126
  logging.error(f'invalid line {line}')
54
127
  msg = ValidationMessage(
55
- level=ValidationMsgLevel.WARNING,
128
+ level=ValidationMsgLevel.ERROR,
56
129
  source='CIT line',
57
130
  source_id=0,
58
- msg='Invalid line in CIT. Line was ignored. Remaining CIT is functional.',
131
+ msg='Invalid line in CIT.',
59
132
  highlight_sub_patterns=line
60
133
  )
61
134
  errors.append(msg)
62
135
 
63
-
64
136
  cit = CIT_v1(origin=origin, entries=entries)
137
+ if not cit.is_valid:
138
+ errors.insert(0,
139
+ ValidationMessage(
140
+ level=ValidationMsgLevel.WARNING,
141
+ source='CIT ',
142
+ source_id=0,
143
+ msg='Invalid lines in CIT. The lines were ignored. The rest of the CIT is still functional',
144
+ highlight_sub_patterns=''
145
+ )
146
+ )
65
147
  cit._validation_messages.extend(errors)
66
148
  cit._csv_original = csv
67
149
  return cit
68
150
 
69
151
  def evaluate_pac_id(self, pac:PAC_ID):
70
- if not type(pac) is PAC_ID:
71
- raise ValueError(f'CIT v1 does only handle PAC-IDs. PAC-CAT it does not know what to do')
152
+ if type(pac) is not PAC_ID:
153
+ raise ValueError('CIT v1 does only handle PAC-IDs. PAC-CAT it does not know what to do')
72
154
  cit_evaluated = ServiceGroup(origin=self.origin)
73
155
  for e in self.entries:
74
156
  conditions = e.applicable_if.split(';')
@@ -76,18 +158,18 @@ class CIT_v1(LabFREED_BaseModel):
76
158
  for c in conditions:
77
159
  if '=' in c:
78
160
  query, expected = c.split('=')
79
- value = self._find_in_pac(query.strip(), pac)
161
+ value = _find_pattern_in_pac(query.strip(), pac)
80
162
  conditions_evaluated.append(value == expected.strip())
81
163
  else:
82
164
  query = c.strip()
83
- found = self._find_in_pac(query, pac)
165
+ found = _find_pattern_in_pac(query, pac)
84
166
  conditions_evaluated.append(found)
85
167
  is_applicable = all(conditions_evaluated)
86
168
 
87
169
  if not is_applicable:
88
170
  continue
89
171
 
90
- url = re.sub(r"\{([^}]+)\}", lambda v: self._find_in_pac(v.group(0), pac), e.template_url)
172
+ url = re.sub(r"\{([^}]+)\}", lambda v: _find_pattern_in_pac(v.group(0), pac), e.template_url)
91
173
  cit_evaluated.services.append(Service(
92
174
  service_name=e.service_name,
93
175
  application_intents= [ e.application_intent ],
@@ -97,49 +179,7 @@ class CIT_v1(LabFREED_BaseModel):
97
179
  )
98
180
  return cit_evaluated
99
181
 
100
- def _find_in_pac(self, value, pac:PAC_ID):
101
- pac_url =pac.to_url()
102
- if value == '{isu}':
103
- return pac.issuer
104
-
105
- elif value == '{pac}':
106
- return pac_url.split('*')[0]
107
-
108
- elif value == '{id}':
109
- m = re.match(r'^HTTPS://.+?/(.+?)(\*.*)*$', pac_url)
110
- return m.group(1) if m else None
111
-
112
- elif m := re.match(r'\{idSeg(\d+)\}', value):
113
- i = int(m.group(1)) - 1 # CIT is 1 based
114
- seg = pac.identifier[i] if i < len(pac.identifier) else None
115
- if seg:
116
- return f"{(seg.key + ':') if seg.key else ''}{seg.value}"
117
-
118
- elif m := re.match(r'\{idVal(\w+)\}', value):
119
- k = m.group(1)
120
- seg = [s for s in pac.identifier if s.key and s.key == k]
121
- if seg:
122
- seg = seg[0]
123
- return seg.value
124
- else:
125
- return None
126
-
127
- elif value == '{ext}':
128
- m = re.match(r'^.*?(\*.*)*$', pac_url)
129
- ext_str = m.group(1) if m else None
130
- return m.group(1)[1:] if ext_str else None
131
-
132
- elif m := re.match(r'\{ext(\d+)\}', value):
133
- i = int(m.group(1)) - 1 # CIT is 1 based
134
- extensions = pac_url.split('*')
135
- extensions.pop(0)# first element is not extension
136
- return extensions[i] if i < len(extensions) else None
137
- else:
138
- return None
139
-
140
-
141
-
142
-
182
+
143
183
 
144
184
  def __str__(self):
145
185
  if csv:=self._csv_original:
@@ -150,3 +190,56 @@ class CIT_v1(LabFREED_BaseModel):
150
190
  for e in self.entries:
151
191
  s += '\t'.join([e.service_name, e.application_intent, e.service_type.value, e.applicable_if, e.template_url]) + '\n'
152
192
  return s
193
+
194
+
195
+
196
+ def _find_pattern_in_pac(value, pac:PAC_ID|str):
197
+ if not isinstance(pac, str):
198
+ pac_url =pac.to_url()
199
+ else:
200
+ pac_url = pac
201
+
202
+ if value == '{isu}':
203
+ return pac.issuer
204
+
205
+ elif value == '{pac}':
206
+ return pac_url.split('*')[0]
207
+
208
+ elif value == '{id}':
209
+ m = re.match(r'^HTTPS://.+?/(.+?)(\*.*)*$', pac_url)
210
+ return m.group(1) if m else None
211
+
212
+ elif m := re.match(r'\{idSeg(\d+)\}', value):
213
+ i = int(m.group(1)) - 1 # CIT is 1 based
214
+ seg = pac.identifier[i] if i < len(pac.identifier) else None
215
+ if seg:
216
+ return f"{(seg.key + ':') if seg.key else ''}{seg.value}"
217
+
218
+ elif m := re.match(r'\{idVal(\w+)\}', value):
219
+ k = m.group(1)
220
+ seg = [s for s in pac.identifier if s.key and s.key == k]
221
+ if seg:
222
+ seg = seg[0]
223
+ return seg.value
224
+ else:
225
+ return None
226
+
227
+ elif value == '{ext}':
228
+ m = re.match(r'^.*?(\*.*)*$', pac_url)
229
+ ext_str = m.group(1) if m else None
230
+ return m.group(1)[1:] if ext_str else None
231
+
232
+ elif m := re.match(r'\{ext(\d+)\}', value):
233
+ i = int(m.group(1)) - 1 # CIT is 1 based
234
+ extensions = pac_url.split('*')
235
+ extensions.pop(0)# first element is not extension
236
+ return extensions[i] if i < len(extensions) else None
237
+ else:
238
+ raise PatternError(f'{value} is not a recognized pattern for applicable if')
239
+
240
+ class PatternError(ValueError):
241
+ pass
242
+
243
+
244
+
245
+
@@ -9,6 +9,12 @@ import jsonpath_ng.ext as jsonpath
9
9
 
10
10
  from labfreed.pac_id_resolver.services import Service, ServiceGroup
11
11
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
12
+ from labfreed.pac_id_resolver.cit_common import ( _add_msg_to_cit_entry_model,
13
+ _validate_service_name,
14
+ _validate_application_intent,
15
+ _validate_service_type,
16
+ ServiceType)
17
+
12
18
 
13
19
  __all__ = [
14
20
  "CIT_v2",
@@ -16,9 +22,6 @@ __all__ = [
16
22
  "CITEntry_v2"
17
23
  ]
18
24
 
19
- class ServiceType(Enum):
20
- USER_HANDOVER_GENERIC = 'userhandover-generic'
21
- ATTRIBUTE_SERVICE_GENERIC = 'attributes-generic'
22
25
 
23
26
 
24
27
  class CITEntry_v2(LabFREED_BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labfreed
3
- Version: 0.2.6a2
3
+ Version: 0.2.6a4
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.11
@@ -1,5 +1,5 @@
1
- labfreed/__init__.py,sha256=5JS9oL9Mhmmw9OKT1GKsdngscLXm_eZ5TdM3fkc08wM,338
2
- labfreed/labfreed_infrastructure.py,sha256=2XxSe3Tmc9aiRjlJL7VjXi6gM3IfXOJfmWroc-JmV8M,10054
1
+ labfreed/__init__.py,sha256=wKahqMhTOvdvcU9M6ARgaUBWlM0qEwE7q5qixgJXVsY,338
2
+ labfreed/labfreed_infrastructure.py,sha256=EPDSCaGxWakAoPpHyc6ltf-pOuKyS5829lj_EG6wa74,10072
3
3
  labfreed/pac_cat/__init__.py,sha256=KNPtQzBD1XVohvG_ucOs7RJj-oi6biUTGB1k-T2o6pk,568
4
4
  labfreed/pac_cat/category_base.py,sha256=lFQNiTUukyhWdaSCAI7CZxLtj6kNtnBCE4UsePwsGqE,1801
5
5
  labfreed/pac_cat/pac_cat.py,sha256=AJUYDsyGOHy-sRRpXpY0awtbf3HCvn3RhMax6ofvYRA,5318
@@ -11,8 +11,9 @@ labfreed/pac_id/pac_id.py,sha256=IWXYlKFjQKB_9U5bINWC5_Lb5pcVbuleocvGs79A28w,530
11
11
  labfreed/pac_id/url_parser.py,sha256=TAQHxFf7Li8GA517mfOivmnJAJgh782oaSDzmOOkyTE,5942
12
12
  labfreed/pac_id/url_serializer.py,sha256=3D5pwcAP4ZrCQ22BRtxIwqWrFtZuY9913hCLPJNeyPI,2845
13
13
  labfreed/pac_id_resolver/__init__.py,sha256=RNBlrDOSR42gmSNH9wJVhK_xwEX45cvTKVgWW2bjh7Q,113
14
- labfreed/pac_id_resolver/cit_v1.py,sha256=SAI6W9FgJV4wirDN1CZqYKy6wCI8bkHgHmhgnp9vMU8,5965
15
- labfreed/pac_id_resolver/cit_v2.py,sha256=J7KsYUKSz4k7lqSHmLnouRY-etEbwmcj35O5SqVRm-o,11594
14
+ labfreed/pac_id_resolver/cit_common.py,sha256=xEkSSumj_IYgnXn3yKvoTBbgExnIfPY7E-RHU-7pcX8,2905
15
+ labfreed/pac_id_resolver/cit_v1.py,sha256=cC-fGWmPxhUMylZYn3MBDrr9Ds1oL6-j88Pw03csB9k,9436
16
+ labfreed/pac_id_resolver/cit_v2.py,sha256=NCJqhYH5viF6iFCmryHhoQXmuKAyFMxgHvvzo4eBAgI,11838
16
17
  labfreed/pac_id_resolver/resolver.py,sha256=SQlATlaUsmS6EZlEMyBQsp00ixpN3r-3V5pMXcvHYCw,2739
17
18
  labfreed/pac_id_resolver/services.py,sha256=TPoH6YlSwa0hmawHpOiMwIpBAinhoRhMSoexop0YscI,2462
18
19
  labfreed/qr/__init__.py,sha256=fdKwP6W2Js--yMbBUdn-g_2uq2VqPpfQJeDLHsMDO-Y,61
@@ -38,7 +39,7 @@ labfreed/well_known_keys/labfreed/well_known_keys.py,sha256=nqk66kHdSwJTJfMKlP-x
38
39
  labfreed/well_known_keys/unece/UneceUnits.json,sha256=kwfQSp_nTuWbADfBBgqTWrvPl6XtM5SedEVLbMJrM7M,898953
39
40
  labfreed/well_known_keys/unece/__init__.py,sha256=MSP9lmjg9_D9iqG9Yq2_ajYfQSNS9wIT7FXA1c--59M,122
40
41
  labfreed/well_known_keys/unece/unece_units.py,sha256=gNDQk6KGl-nGMf9Ycq_fQ8P2xxKITgLkcQWPd4H49gI,1630
41
- labfreed-0.2.6a2.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
42
- labfreed-0.2.6a2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
43
- labfreed-0.2.6a2.dist-info/METADATA,sha256=yWuY2nBPwWR4OQYG01HmNw30yOLNKZuYaXwN45x5ia8,18616
44
- labfreed-0.2.6a2.dist-info/RECORD,,
42
+ labfreed-0.2.6a4.dist-info/licenses/LICENSE,sha256=gHFOv9FRKHxO8cInP3YXyPoJnuNeqrvcHjaE_wPSsQ8,1100
43
+ labfreed-0.2.6a4.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
44
+ labfreed-0.2.6a4.dist-info/METADATA,sha256=WvHpUfPKFYz537qACQir3e4tmkngdiHU6E_7APE-jg8,18616
45
+ labfreed-0.2.6a4.dist-info/RECORD,,