labfreed 0.2.11__py3-none-any.whl → 0.3.0a0__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.

Files changed (37) hide show
  1. labfreed/__init__.py +1 -1
  2. labfreed/labfreed_infrastructure.py +3 -3
  3. labfreed/pac_attributes/api_data_models/request.py +56 -0
  4. labfreed/pac_attributes/api_data_models/response.py +184 -0
  5. labfreed/pac_attributes/api_data_models/server_capabilities_response.py +7 -0
  6. labfreed/pac_attributes/client/__init__.py +1 -0
  7. labfreed/pac_attributes/client/attribute_cache.py +78 -0
  8. labfreed/pac_attributes/client/client.py +148 -0
  9. labfreed/pac_attributes/server/attribute_data_sources.py +69 -0
  10. labfreed/pac_attributes/server/server.py +237 -0
  11. labfreed/pac_attributes/server/translation_data_sources.py +60 -0
  12. labfreed/pac_attributes/well_knonw_attribute_keys.py +11 -0
  13. labfreed/pac_cat/category_base.py +19 -0
  14. labfreed/pac_cat/pac_cat.py +8 -1
  15. labfreed/pac_id/extension.py +32 -7
  16. labfreed/pac_id/pac_id.py +2 -2
  17. labfreed/pac_id/url_parser.py +4 -4
  18. labfreed/pac_id/url_serializer.py +11 -5
  19. labfreed/pac_id_resolver/cit_common.py +1 -1
  20. labfreed/pac_id_resolver/cit_v1.py +0 -3
  21. labfreed/pac_id_resolver/cit_v2.py +1 -6
  22. labfreed/pac_id_resolver/resolver.py +12 -7
  23. labfreed/pac_id_resolver/services.py +0 -1
  24. labfreed/trex/python_convenience/quantity.py +86 -5
  25. labfreed/trex/value_segments.py +1 -1
  26. labfreed/utilities/ensure_utc_time.py +6 -0
  27. labfreed/utilities/translations.py +60 -0
  28. labfreed/well_known_extensions/display_name_extension.py +3 -3
  29. labfreed/well_known_keys/gs1/gs1_ai_enum_sorted.py +4 -0
  30. labfreed/well_known_keys/labfreed/well_known_keys.py +12 -2
  31. labfreed/well_known_keys/unece/unece_units.py +2 -1
  32. {labfreed-0.2.11.dist-info → labfreed-0.3.0a0.dist-info}/METADATA +43 -29
  33. labfreed-0.3.0a0.dist-info/RECORD +56 -0
  34. labfreed/well_known_keys/gs1/gs1.py +0 -4
  35. labfreed-0.2.11.dist-info/RECORD +0 -45
  36. {labfreed-0.2.11.dist-info → labfreed-0.3.0a0.dist-info}/WHEEL +0 -0
  37. {labfreed-0.2.11.dist-info → labfreed-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,237 @@
1
+ import traceback
2
+ import warnings
3
+
4
+ import rich
5
+
6
+ from labfreed.pac_attributes.api_data_models.request import AttributeRequestPayload
7
+ from labfreed.pac_attributes.api_data_models.response import AttributeResponsePayload, AttributesOfPACID, ReferenceAttribute
8
+ from labfreed.pac_attributes.api_data_models.server_capabilities_response import ServerCapabilities
9
+ from labfreed.pac_attributes.server.attribute_data_sources import AttributeGroupDataSource
10
+ from labfreed.pac_attributes.server.translation_data_sources import TranslationDataSource
11
+ from labfreed.pac_id.pac_id import PAC_ID
12
+
13
+
14
+ class InvalidRequestError(ValueError):
15
+ pass
16
+
17
+
18
+
19
+ class AttributeServerRequestHandler():
20
+ def __init__(self, data_sources:list[AttributeGroupDataSource], translation_data_sources:list[TranslationDataSource], default_language:str):
21
+ '''Initializes the AttributeServerRequestHandler.
22
+ - does some validation on availability of translations. NOTE: if data_sources or translation_data_sources change, this validation might get outdated'''
23
+ if isinstance(data_sources, AttributeGroupDataSource):
24
+ data_sources = [data_sources]
25
+ self._attribute_group_data_sources: list[AttributeGroupDataSource] = data_sources
26
+
27
+
28
+
29
+ # find which languages the sources consistently know
30
+ self._translation_data_sources: list[TranslationDataSource] = translation_data_sources
31
+ supported_languages = set()
32
+ for tds in self._translation_data_sources:
33
+ supported_languages.update(tds.supported_languages)
34
+ if not supported_languages:
35
+ raise ValueError("translation_data_sources contain no common fully supported language")
36
+
37
+ if default_language not in supported_languages:
38
+ raise ValueError(f"fallback language {default_language} is not supported by all translation data sources.")
39
+ self._supported_languages = supported_languages
40
+ self._default_language = default_language
41
+
42
+ # check there are translations for all provided attributes
43
+ missing_translations = []
44
+ provided_attributes = [attr for ds in self._attribute_group_data_sources for attr in ds.provides_attributes]
45
+ for language in self._supported_languages:
46
+ for attribute_key in provided_attributes:
47
+ if not self._get_display_name_for_key(attribute_key, language):
48
+ missing_translations.append((attribute_key, language))
49
+ if missing_translations:
50
+ rich.print('[yellow bold]WARNING: Missing translations[/yellow bold]')
51
+ for mt in missing_translations:
52
+ rich.print(f"[yellow]WARNING:[/yellow] '{mt[1]}' translation missing for '{mt[0]}'' ")
53
+
54
+
55
+
56
+ def handle_attribute_request(self, json_request_body:str) -> str:
57
+ try:
58
+ r = AttributeRequestPayload.model_validate_json(json_request_body)
59
+ except Exception:
60
+ raise InvalidRequestError
61
+ attributes_for_pac_id = []
62
+ referenced_pac_ids = set()
63
+ for pac_url in r.pac_urls:
64
+ attributes_for_pac = self._get_attributes_for_pac_id(pac_url=pac_url,
65
+ restrict_to_attribute_groups = r.restrict_to_attribute_groups)
66
+ attributes_for_pac_id.append(attributes_for_pac)
67
+ ref = self._get_referenced_pac_ids(attributes_for_pac)
68
+ if ref:
69
+ referenced_pac_ids.update(ref)
70
+
71
+ # also find attributes of referenced pac-ids
72
+ if not r.suppress_forward_lookup:
73
+ for pac_url in referenced_pac_ids:
74
+ attributes_for_pac = self._get_attributes_for_pac_id(pac_url=pac_url,
75
+ restrict_to_attribute_groups = r.restrict_to_attribute_groups)
76
+ attributes_for_pac_id.append(attributes_for_pac)
77
+
78
+ # add translations
79
+ response_language = self._find_response_language(r.language_preferences)
80
+ for e in attributes_for_pac_id:
81
+ self._add_display_names(e, response_language)
82
+
83
+ response = AttributeResponsePayload(pac_attributes=attributes_for_pac_id, language=response_language
84
+ ).to_json()
85
+ return response
86
+
87
+
88
+
89
+
90
+ def _get_attributes_for_pac_id(self, pac_url:str, restrict_to_attribute_groups:list[str]|None=None ) -> AttributesOfPACID:
91
+ attribute_groups = []
92
+ if restrict_to_attribute_groups:
93
+ relevant_data_sources = [ds for ds in self._attribute_group_data_sources if ds.attribute_group_key in restrict_to_attribute_groups]
94
+ else:
95
+ relevant_data_sources = self._attribute_group_data_sources
96
+ for ds in relevant_data_sources:
97
+ try:
98
+ ag = ds.attributes(pac_url)
99
+ if ag:
100
+ attribute_groups.append(ag)
101
+ except Exception as e:
102
+ e.add_note(f'Attribute Source {ds.attribute_group_key} encountered an error')
103
+ traceback.print_exc()
104
+ raise e
105
+
106
+ return AttributesOfPACID(pac_url=pac_url, # return the pac_url as given, i.e. with the extension if there was one
107
+ attribute_groups=attribute_groups)
108
+
109
+
110
+
111
+ def _get_referenced_pac_ids(self, attributes_for_pac:AttributesOfPACID):
112
+ referenced_pacs = []
113
+ for ag in attributes_for_pac.attribute_groups:
114
+ for a in ag.attributes :
115
+ if isinstance(a, ReferenceAttribute):
116
+ try:
117
+ PAC_ID.from_url(a.value)
118
+ referenced_pacs.append(a.value)
119
+ except Exception:
120
+ pass
121
+ return referenced_pacs
122
+
123
+ # def get_translations(self, attributes_for_pac_id):
124
+ # ontology_map = {} # ontology name → list of TermTranslations
125
+
126
+ # for ag_for_pac in attributes_for_pac_id:
127
+ # for ag in ag_for_pac.attribute_groups:
128
+ # ag: AttributeGroup
129
+ # ontology_name = ag.ontology
130
+ # translation_data_source: TranslationDataSource = self._translation_data_sources.get(ontology_name, {})
131
+
132
+ # attribute_keys = [a.key for a in ag.attributes]
133
+ # all_keys = set([ag.key] + attribute_keys)
134
+
135
+ # for k in all_keys:
136
+ # t = translation_data_source.get_translations_for(k)
137
+ # if t:
138
+ # ontology_map.setdefault(ontology_name, []).append(t)
139
+
140
+ # translations_by_ontology = [ TranslationsForOntology(ontology=name, terms=terms) for name, terms in ontology_map.items()
141
+ # ]
142
+ # return translations_by_ontology
143
+
144
+
145
+ # def _get_display_name_for_key(self, key, requested_languages:str):
146
+ # for tds in self._translation_data_sources:
147
+ # if term := tds.get_translations_for(key):
148
+ # # try the languages requested by the user
149
+ # for l in requested_languages:
150
+ # if dn := term.in_language(l):
151
+ # return dn
152
+ # # remove the country codes and try the again
153
+ # for l_fallback in [l.split('-')[0] for l in requested_languages]:
154
+ # if dn := term.in_language(l_fallback):
155
+ # return dn
156
+ # # use the server fallback language
157
+ # if dn := term.in_language(self._default_language):
158
+ # return
159
+ # warnings.warn(f'No translation for {key}')
160
+ # return None
161
+
162
+ def _add_display_names(self, attributes_of_pac:AttributesOfPACID, language:str) -> str:
163
+ '''
164
+ adds the display names in the requested language to attribute group and attributes.
165
+ if no translation can be found in this language it IMMEDIATELY falls back to some - probably inappropriate- magic.
166
+ Note: The server checks for completeness of translations at initialization. Make sure to resolve warnings there and
167
+ this function should never get into the situation not to find translations.
168
+ '''
169
+ for ag in attributes_of_pac.attribute_groups:
170
+ if dn := self._get_display_name_for_key(ag.key, language):
171
+ ag.label = dn
172
+ else:
173
+ ag.label = ag.key.split('/')[-1]
174
+ rich.print(f"[yellow]WARNING:[/yellow] No translation for '{ag.key}' in '{language}'. Falling back to '{ag.label}'")
175
+ for a in ag.attributes:
176
+ if dn := self._get_display_name_for_key(a.key, language):
177
+ a.label = dn
178
+ else:
179
+ a.label = a.key.split('/')[-1]
180
+ rich.print(f"[yellow]WARNING:[/yellow] No translation for '{a.key}' in '{language}'. Falling back to '{a.label}' ")
181
+
182
+
183
+
184
+
185
+
186
+
187
+ def _get_display_name_for_key(self, key, language:str):
188
+ '''call this only with a language you know there is a translation for'''
189
+ for tds in self._translation_data_sources:
190
+ if term := tds.get_translations_for(key):
191
+ return term.in_language(language)
192
+ warnings.warn(f'No translation for {key}.')
193
+ return None
194
+
195
+
196
+ def _find_response_language(self, requested_languages):
197
+ '''finds the language the server will respond in'''
198
+ if not requested_languages:
199
+ return self._default_language
200
+
201
+ for language in requested_languages:
202
+ if language in self._supported_languages:
203
+ return language
204
+
205
+ # remove the country codes and try the again
206
+ for l_fallback in [lang.split('-')[0] for lang in requested_languages]:
207
+ if language in self._supported_languages:
208
+ return l_fallback
209
+
210
+ return self._default_language
211
+
212
+
213
+
214
+ def capabilities(self):
215
+ return ServerCapabilities(supported_languages=self._supported_languages,
216
+ default_language=self._default_language,
217
+ available_attribute_groups= [ds.attribute_group_key for ds in self._attribute_group_data_sources]).model_dump_json()
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
@@ -0,0 +1,60 @@
1
+ from abc import ABC, abstractmethod, abstractproperty
2
+ import json
3
+
4
+ from pydantic import ValidationError
5
+ import rich
6
+ from labfreed.utilities.translations import Terms, Term
7
+
8
+ class TranslationDataSource(ABC):
9
+
10
+ @abstractmethod
11
+ def get_translations_for(self, key:str) -> Term:
12
+ pass
13
+
14
+ @abstractproperty
15
+ def supported_languages(self) -> set[str]:
16
+ pass
17
+
18
+
19
+ class DictTranslationDataSource(TranslationDataSource):
20
+ def __init__(self, data:Terms, supported_languages:set[str]) -> None:
21
+
22
+ self._data = data
23
+
24
+ #check that there are translations for the supported languages. Adjust supported_languages if needed
25
+ supported_languages = set(supported_languages) # to ensure uniqueness
26
+ for language in supported_languages.copy():
27
+ if translation_missing := self._list_missing_translations_to(language):
28
+ rich.print(f"[bold yellow]WARNING:[/bold yellow]: Translation to '{language}' missing for {translation_missing}")
29
+ supported_languages.remove(language)
30
+
31
+ self._supported_languages = supported_languages
32
+
33
+ def _list_missing_translations_to(self, language:str):
34
+ translation_missing_for_keys = []
35
+ for term in self._data.terms:
36
+ if language not in [t.language_code for t in term.translations]:
37
+ translation_missing_for_keys.append(term.key)
38
+ return translation_missing_for_keys
39
+
40
+ def get_translations_for(self, key:str) -> Term:
41
+ t = self._data.translations_for_term(key)
42
+ return t
43
+
44
+ @property
45
+ def supported_languages(self) -> set[str]:
46
+ return self._supported_languages
47
+
48
+
49
+
50
+
51
+
52
+ class JsonFileTranslationDataSource(DictTranslationDataSource):
53
+ def __init__(self, path:str) -> None:
54
+ with open(path) as f:
55
+ data = json.load(f)
56
+ try:
57
+ super().__init__(data=data)
58
+ except ValidationError as e:
59
+ e.add_note('Json must be convertible to OnthologyTranslationDataSource')
60
+ raise e
@@ -0,0 +1,11 @@
1
+ from enum import Enum
2
+
3
+
4
+ class MetaAttributeKeys(Enum):
5
+ DISPLAYNAME = "https://schema.org/name"
6
+ IMAGE = "https://schema.org/image"
7
+ ALIAS = "https://schema.org/alternateName"
8
+ DESCRIPTION = "https://schema.org/description"
9
+ GROUPKEY = "https://labfreed.org/attribute_metadata_group"
10
+
11
+
@@ -43,6 +43,25 @@ class Category(LabFREED_BaseModel):
43
43
  def __str__(self):
44
44
  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)])
45
45
  return s
46
+
47
+ def segments_as_dict(self):
48
+ ''' returns the segments in a dict, with nice keys and values'''
49
+ out = dict()
50
+ for field_name, field_info in self.model_fields.items():
51
+ if field_name =='additional_segments':
52
+ continue
53
+ if v := getattr(self, field_name):
54
+ if field_info.alias:
55
+ k = f"{field_name} ({ field_info.alias})"
56
+ else:
57
+ k = f"{field_name}"
58
+ out.update({k : v } )
59
+
60
+ for s in getattr(self, 'additional_segments'):
61
+ out.update( {s.key or '' : s.value })
62
+ return out
63
+
64
+
46
65
 
47
66
 
48
67
 
@@ -52,7 +52,7 @@ class PAC_CAT(PAC_ID):
52
52
 
53
53
 
54
54
  @classmethod
55
- def from_pac_id(cls, pac_id:PAC_ID) -> Self:
55
+ def from_pac_id(cls, pac_id:PAC_ID) -> PAC_CAT:
56
56
  '''Constructs a PAC-CAT from a PAC-ID'''
57
57
  return PAC_CAT(issuer=pac_id.issuer, identifier=pac_id.identifier)
58
58
 
@@ -125,6 +125,13 @@ class PAC_CAT(PAC_ID):
125
125
  )
126
126
  return self
127
127
 
128
+
129
+ @model_validator(mode='after')
130
+ def _check_identifier_segment_keys_are_unique(self) -> Self:
131
+ ''' override the validator of PAC-ID: in PAC-CAT segments can replicate in different categories'''
132
+ return self
133
+
134
+
128
135
  def print_categories(self):
129
136
  table = Table(title=f'Categories in {str(self)}', show_header=False)
130
137
  table.add_column('0')
@@ -3,12 +3,12 @@ from abc import ABC, abstractproperty
3
3
 
4
4
  from pydantic import model_validator
5
5
 
6
- from labfreed.labfreed_infrastructure import LabFREED_BaseModel
6
+ from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel
7
7
 
8
8
 
9
9
  class ExtensionBase(ABC):
10
- name: str
11
- type: str
10
+ name: str|None
11
+ type: str|None
12
12
 
13
13
 
14
14
  @abstractproperty
@@ -16,14 +16,17 @@ class ExtensionBase(ABC):
16
16
  raise NotImplementedError("Subclasses must implement 'data'")
17
17
 
18
18
  def __str__(self):
19
- return f'{self.name}${self.type}/{self.data}'
19
+ if self.name and self.type:
20
+ return f'{self.name}${self.type}/{self.data}'
21
+ else:
22
+ return self.data
20
23
 
21
24
 
22
25
 
23
26
  class Extension(LabFREED_BaseModel,ExtensionBase):
24
27
  '''Implementation of Extension for unknown extension types'''
25
- name:str
26
- type:str
28
+ name:str|None
29
+ type:str|None
27
30
  data_:str
28
31
 
29
32
  @property
@@ -31,7 +34,7 @@ class Extension(LabFREED_BaseModel,ExtensionBase):
31
34
  return self.data_
32
35
 
33
36
  @staticmethod
34
- def create(*, name, type, data):
37
+ def create(*, name:str|None, type:str|None, data:str):
35
38
  return Extension(name=name, type=type, data=data)
36
39
 
37
40
  @model_validator(mode='before')
@@ -44,5 +47,27 @@ class Extension(LabFREED_BaseModel,ExtensionBase):
44
47
  model_config = {
45
48
  "extra": "allow", # Allow extra keys during pre-validation
46
49
  }
50
+
51
+ @model_validator(mode='after')
52
+ def validate_model(self):
53
+ if self.name and not self.type:
54
+ raise ValueError('Extension has a name, but no type. Either set both or none')
55
+
56
+ if self.type and not self.name:
57
+ raise ValueError('Extension has a type, but no name. Either set both or none')
58
+
59
+ if not self.type and not self.name:
60
+ self._add_validation_message(msg="Extensions has no name and type. It is RECOMMENDED to specify name and type.",
61
+ level=ValidationMsgLevel.RECOMMENDATION,
62
+ source=f"Extension '{self.data[0:10] if len(self.data)>10 else self.data}'",
63
+ highlight_pattern=self.data)
64
+
65
+ return self
66
+
67
+
68
+
69
+ model_config = {
70
+ "extra": "allow", # Allow extra keys during pre-validation
71
+ }
47
72
 
48
73
 
labfreed/pac_id/pac_id.py CHANGED
@@ -45,9 +45,9 @@ class PAC_ID(LabFREED_BaseModel):
45
45
  from labfreed.pac_id.url_parser import PAC_Parser
46
46
  return PAC_Parser.from_url(url, try_pac_cat=try_pac_cat, suppress_validation_errors=suppress_validation_errors, extension_interpreters=extension_interpreters)
47
47
 
48
- def to_url(self, use_short_notation:None|bool=None, uppercase_only=False) -> str:
48
+ def to_url(self, use_short_notation:None|bool=None, uppercase_only=False, include_extensions:bool=True) -> str:
49
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)
50
+ return PACID_Serializer.to_url(self, use_short_notation=use_short_notation, uppercase_only=uppercase_only, include_extensions=include_extensions)
51
51
 
52
52
  def to_json(self, indent=None) -> str:
53
53
  if not indent:
@@ -69,7 +69,7 @@ class PAC_Parser():
69
69
  extensions = cls._parse_extensions(ext_str)
70
70
  if extensions and extension_interpreters:
71
71
  for i, e in enumerate(extensions):
72
- if interpreter := extension_interpreters.get(e.type):
72
+ if interpreter := extension_interpreters.get(e.type or ''):
73
73
  extensions[i] = interpreter.from_extension(e)
74
74
  pac_id.extensions = extensions
75
75
 
@@ -83,6 +83,8 @@ class PAC_Parser():
83
83
  def _parse_pac_id(cls,id_str:str) -> "PAC_ID":
84
84
  # m = re.match('(HTTPS://)?(PAC.)?(?P<issuer>.+?\..+?)/(?P<identifier>.*)', id_str)
85
85
  m = re.match('(HTTPS://)?(PAC.)?(?P<issuer>.+?)/(?P<identifier>.*)', id_str)
86
+ if not m:
87
+ raise LabFREED_ValidationError(f'{id_str} does not match the pattern expected for PAC-ID')
86
88
  d = m.groupdict()
87
89
 
88
90
  id_segments = list()
@@ -127,7 +129,7 @@ class PAC_Parser():
127
129
 
128
130
  defaults = MappingProxyType(
129
131
  {
130
- 0: { 'name': 'N', 'type': 'N'},
132
+ 0: { 'name': 'N', 'type': 'TEXT'},
131
133
  1: { 'name': 'SUM', 'type': 'TREX'}
132
134
  }
133
135
  )
@@ -146,8 +148,6 @@ class PAC_Parser():
146
148
  if defaults:
147
149
  name = defaults.get(i).get('name')
148
150
  type = defaults.get(i).get('type')
149
- else:
150
- raise ValueError('extension number {i}, must have name and type')
151
151
 
152
152
  #convert to subtype if they were given
153
153
  e = Extension.create(name=name, type=type, data=data)
@@ -9,7 +9,7 @@ class PACID_Serializer():
9
9
  '''Represents a PAC-ID including it's extensions'''
10
10
 
11
11
  @classmethod
12
- def to_url(cls, pac:PAC_ID, use_short_notation:bool|None=None, uppercase_only=False) -> str:
12
+ def to_url(cls, pac:PAC_ID, use_short_notation:bool|None=None, uppercase_only=False, include_extensions:bool=True) -> str:
13
13
  """Serializes the PAC-ID including extensions.
14
14
 
15
15
  Args:
@@ -24,8 +24,12 @@ class PACID_Serializer():
24
24
  """
25
25
  identifier_str = cls._serialize_identifier(pac, use_short_notation=use_short_notation)
26
26
 
27
- use_short_notation_for_extensions = True if use_short_notation is None else use_short_notation
28
- extensions_str = cls._serialize_extensions(pac.extensions, use_short_notation=use_short_notation_for_extensions)
27
+ if include_extensions:
28
+ use_short_notation_for_extensions = True if use_short_notation is None else use_short_notation
29
+ extensions_str = cls._serialize_extensions(pac.extensions, use_short_notation=use_short_notation_for_extensions)
30
+ else:
31
+ extensions_str = ""
32
+
29
33
  out = f"HTTPS://PAC.{pac.issuer}{identifier_str}{extensions_str}"
30
34
 
31
35
  if uppercase_only:
@@ -75,8 +79,10 @@ class PACID_Serializer():
75
79
  continue
76
80
  else:
77
81
  short_notation = False
78
-
79
- out += f'*{e.name}${e.type}/{e.data}'
82
+ if e.name and e.type:
83
+ out += f'*{e.name}${e.type}/{e.data}'
84
+ else:
85
+ out += f'*{e.data}'
80
86
  return out
81
87
 
82
88
 
@@ -1,6 +1,6 @@
1
1
  from enum import Enum
2
2
  import re
3
- from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMsgLevel, _quote_texts
3
+ from labfreed.labfreed_infrastructure import ValidationMsgLevel, _quote_texts
4
4
 
5
5
 
6
6
 
@@ -1,8 +1,5 @@
1
1
 
2
- from enum import Enum
3
- import logging
4
2
  import re
5
- import traceback
6
3
 
7
4
  from pydantic import Field, model_validator
8
5
  from labfreed.labfreed_infrastructure import LabFREED_BaseModel, ValidationMessage, ValidationMsgLevel
@@ -1,4 +1,3 @@
1
- from enum import Enum
2
1
  import json
3
2
  import re
4
3
  from typing import Self
@@ -9,11 +8,7 @@ import jsonpath_ng.ext as jsonpath
9
8
 
10
9
  from labfreed.pac_id_resolver.services import Service, ServiceGroup
11
10
  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)
11
+ from labfreed.pac_id_resolver.cit_common import ( ServiceType)
17
12
 
18
13
 
19
14
  __all__ = [
@@ -1,8 +1,6 @@
1
1
  from functools import lru_cache
2
2
  import logging
3
- import traceback
4
3
  from typing import Self
5
- import yaml
6
4
  from requests import get
7
5
 
8
6
 
@@ -32,7 +30,7 @@ def cit_from_str(s:str, origin:str='') -> CIT_v1|CIT_v2:
32
30
  cit2 = None
33
31
  try:
34
32
  cit1 = CIT_v1.from_csv(s, origin)
35
- cit_version = 'v1'
33
+ cit_version = 'v1' # noqa: F841
36
34
  except Exception:
37
35
  cit1 = None
38
36
 
@@ -66,11 +64,18 @@ class PAC_ID_Resolver():
66
64
  self._cits = cits
67
65
 
68
66
 
69
- def resolve(self, pac_url:PAC_ID|str, check_service_status=True, use_issuer_cit=True) -> list[ServiceGroup]:
67
+ def resolve(self, pac_id:PAC_ID|str, check_service_status=True, use_issuer_cit=True) -> list[ServiceGroup]:
70
68
  '''Resolve a PAC-ID'''
71
- if isinstance(pac_url, str):
72
- pac_id = PAC_CAT.from_url(pac_url)
73
- pac_id_catless = PAC_ID.from_url(pac_url, try_pac_cat=False)
69
+ if isinstance(pac_id, str):
70
+ pac_id = PAC_CAT.from_url(pac_id)
71
+ pac_id_catless = PAC_ID.from_url(pac_id, try_pac_cat=False)
72
+
73
+ # it's likely to h
74
+ if isinstance(pac_id, PAC_ID):
75
+ pac_id_catless = PAC_ID.from_url(pac_id.to_url(), try_pac_cat=False)
76
+ else:
77
+ raise ValueError('pac_id is invalid. Should be a PAC-ID in url form or a PAC-ID object')
78
+
74
79
 
75
80
  cits = self._cits.copy()
76
81
  if use_issuer_cit:
@@ -6,7 +6,6 @@ import requests
6
6
 
7
7
  from concurrent.futures import ThreadPoolExecutor, as_completed
8
8
 
9
- import requests
10
9
  from rich import print
11
10
  from rich.table import Table
12
11