tricc-oo 1.0.2__py3-none-any.whl → 1.4.15__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.
Files changed (66) hide show
  1. tests/build.py +213 -0
  2. tests/test_cql.py +197 -0
  3. tests/to_ocl.py +51 -0
  4. {tricc → tricc_oo}/__init__.py +3 -1
  5. tricc_oo/converters/codesystem_to_ocl.py +169 -0
  6. tricc_oo/converters/cql/cqlLexer.py +822 -0
  7. tricc_oo/converters/cql/cqlListener.py +1632 -0
  8. tricc_oo/converters/cql/cqlParser.py +11204 -0
  9. tricc_oo/converters/cql/cqlVisitor.py +913 -0
  10. tricc_oo/converters/cql_to_operation.py +402 -0
  11. tricc_oo/converters/datadictionnary.py +115 -0
  12. tricc_oo/converters/drawio_type_map.py +222 -0
  13. tricc_oo/converters/tricc_to_xls_form.py +61 -0
  14. tricc_oo/converters/utils.py +65 -0
  15. tricc_oo/converters/xml_to_tricc.py +1003 -0
  16. tricc_oo/models/__init__.py +4 -0
  17. tricc_oo/models/base.py +732 -0
  18. tricc_oo/models/calculate.py +216 -0
  19. tricc_oo/models/ocl.py +281 -0
  20. tricc_oo/models/ordered_set.py +125 -0
  21. tricc_oo/models/tricc.py +418 -0
  22. tricc_oo/parsers/xml.py +138 -0
  23. tricc_oo/serializers/__init__.py +0 -0
  24. tricc_oo/serializers/xls_form.py +745 -0
  25. tricc_oo/strategies/__init__.py +0 -0
  26. tricc_oo/strategies/input/__init__.py +0 -0
  27. tricc_oo/strategies/input/base_input_strategy.py +111 -0
  28. tricc_oo/strategies/input/drawio.py +317 -0
  29. tricc_oo/strategies/output/base_output_strategy.py +148 -0
  30. tricc_oo/strategies/output/spice.py +365 -0
  31. tricc_oo/strategies/output/xls_form.py +697 -0
  32. tricc_oo/strategies/output/xlsform_cdss.py +189 -0
  33. tricc_oo/strategies/output/xlsform_cht.py +200 -0
  34. tricc_oo/strategies/output/xlsform_cht_hf.py +334 -0
  35. tricc_oo/visitors/__init__.py +0 -0
  36. tricc_oo/visitors/tricc.py +2198 -0
  37. tricc_oo/visitors/utils.py +17 -0
  38. tricc_oo/visitors/xform_pd.py +264 -0
  39. tricc_oo-1.4.15.dist-info/METADATA +219 -0
  40. tricc_oo-1.4.15.dist-info/RECORD +46 -0
  41. {tricc_oo-1.0.2.dist-info → tricc_oo-1.4.15.dist-info}/WHEEL +1 -1
  42. tricc_oo-1.4.15.dist-info/top_level.txt +2 -0
  43. tricc/converters/mc_to_tricc.py +0 -542
  44. tricc/converters/tricc_to_xls_form.py +0 -553
  45. tricc/converters/utils.py +0 -44
  46. tricc/converters/xml_to_tricc.py +0 -740
  47. tricc/models/tricc.py +0 -1093
  48. tricc/parsers/xml.py +0 -81
  49. tricc/serializers/xls_form.py +0 -364
  50. tricc/strategies/input/base_input_strategy.py +0 -80
  51. tricc/strategies/input/drawio.py +0 -246
  52. tricc/strategies/input/medalcreator.py +0 -168
  53. tricc/strategies/output/base_output_strategy.py +0 -92
  54. tricc/strategies/output/xls_form.py +0 -308
  55. tricc/strategies/output/xlsform_cdss.py +0 -46
  56. tricc/strategies/output/xlsform_cht.py +0 -106
  57. tricc/visitors/tricc.py +0 -375
  58. tricc_oo-1.0.2.dist-info/LICENSE +0 -78
  59. tricc_oo-1.0.2.dist-info/METADATA +0 -229
  60. tricc_oo-1.0.2.dist-info/RECORD +0 -26
  61. tricc_oo-1.0.2.dist-info/top_level.txt +0 -2
  62. venv/bin/vba_extract.py +0 -78
  63. {tricc → tricc_oo}/converters/__init__.py +0 -0
  64. {tricc → tricc_oo}/models/lang.py +0 -0
  65. {tricc/serializers → tricc_oo/parsers}/__init__.py +0 -0
  66. {tricc → tricc_oo}/serializers/planuml.py +0 -0
@@ -0,0 +1,216 @@
1
+
2
+ import logging
3
+ import random
4
+ import string
5
+ from enum import Enum, auto
6
+ from typing import Dict, ForwardRef, List, Optional, Union
7
+
8
+ from pydantic import BaseModel, constr
9
+ from strenum import StrEnum
10
+ from .base import *
11
+ from .tricc import *
12
+ from tricc_oo.converters.utils import generate_id, get_rand_name
13
+
14
+
15
+
16
+
17
+
18
+
19
+ class TriccNodeDisplayCalculateBase(TriccNodeCalculateBase):
20
+ save: Optional[str] = None # contribute to another calculate
21
+ hint: Optional[str] = None # for diagnostic display
22
+ help: Optional[str] = None # for diagnostic display
23
+ applicability: Optional[Union[Expression, TriccOperation]] = None
24
+ # no need to copy save
25
+ def to_fake(self):
26
+ data = vars(self)
27
+ del data['hint']
28
+ del data['help']
29
+ del data['save']
30
+ fake = TriccNodeFakeCalculateBase(**data)
31
+ replace_node(self,fake)
32
+ return fake
33
+ def __str__(self):
34
+ return self.get_name()
35
+
36
+ def __repr__(self):
37
+ return self.get_name()
38
+
39
+ class TriccNodeCalculate(TriccNodeDisplayCalculateBase):
40
+ tricc_type: TriccNodeType = TriccNodeType.calculate
41
+
42
+
43
+ class TriccNodeAdd(TriccNodeDisplayCalculateBase):
44
+ tricc_type: TriccNodeType = TriccNodeType.add
45
+ datatype: str = 'number'
46
+
47
+
48
+ class TriccNodeCount(TriccNodeDisplayCalculateBase):
49
+ tricc_type: TriccNodeType = TriccNodeType.count
50
+ datatype: str = 'number'
51
+
52
+
53
+ class TriccNodeProposedDiagnosis(TriccNodeDisplayCalculateBase):
54
+ tricc_type: TriccNodeType = TriccNodeType.proposed_diagnosis
55
+ severity: str = None
56
+
57
+ class TriccNodeFakeCalculateBase(TriccNodeCalculateBase):
58
+ ...
59
+
60
+ class TriccNodeInput(TriccNodeFakeCalculateBase):
61
+ tricc_type: TriccNodeType = TriccNodeType.input
62
+
63
+ class TriccNodeDisplayBridge(TriccNodeDisplayCalculateBase):
64
+ tricc_type: TriccNodeType = TriccNodeType.bridge
65
+
66
+
67
+ class TriccNodeBridge(TriccNodeFakeCalculateBase):
68
+ tricc_type: TriccNodeType = TriccNodeType.bridge
69
+
70
+ class TriccRhombusMixIn():
71
+
72
+ def make_mixin_instance(self, instance, instance_nb, activity, **kwargs):
73
+ # shallow copy
74
+ reference = []
75
+ expression_reference = None
76
+ instance.path = None
77
+ if isinstance(self.expression_reference, (str, TriccOperation)):
78
+ expression_reference = self.expression_reference.copy()
79
+ reference = list(expression_reference.get_references())
80
+ if isinstance(self.reference, (str, TriccOperation)):
81
+ expression_reference = self.reference.copy()
82
+ reference = list(expression_reference.get_references())
83
+ elif isinstance(self.reference, list):
84
+ for ref in self.reference:
85
+ if issubclass(ref.__class__, TriccBaseModel):
86
+ pass
87
+ # get the reference
88
+ if self.activity == ref.activity:
89
+ for sub_node in activity.nodes.values():
90
+ if sub_node.base_instance == ref:
91
+ reference.append(sub_node)
92
+ else: # ref from outside
93
+ reference.append(ref)
94
+ logger.warning("new instance of a rhombus use the reference of the base one")
95
+ elif isinstance(ref, TriccReference):
96
+ reference.append(ref)
97
+ elif isinstance(ref, str):
98
+ logger.debug("passing raw reference {} on node {}".format(ref, self.get_name()))
99
+ reference.append(ref)
100
+ else:
101
+ logger.critical("unexpected reference {} in node {}".format(ref, self.get_name()))
102
+ exit(1)
103
+ instance.reference = reference
104
+ instance.expression_reference = expression_reference
105
+ instance.name = get_rand_name(self.id)
106
+ return instance
107
+
108
+
109
+
110
+
111
+ class TriccNodeRhombus(TriccNodeCalculateBase,TriccRhombusMixIn):
112
+ tricc_type: TriccNodeType = TriccNodeType.rhombus
113
+ path: Optional[TriccNodeBaseModel] = None
114
+ reference: Union[List[TriccNodeBaseModel], Expression, TriccOperation, TriccReference, List[TriccReference]]
115
+
116
+ def make_instance(self, instance_nb, activity, **kwargs):
117
+ instance = super(TriccNodeRhombus, self).make_instance(instance_nb, activity, **kwargs)
118
+ instance = self.make_mixin_instance(instance, instance_nb, activity, **kwargs)
119
+ return instance
120
+
121
+
122
+ def __init__(self, **data):
123
+ data['name'] = get_rand_name(data.get('id', None))
124
+ super().__init__(**data)
125
+
126
+
127
+
128
+
129
+
130
+ class TriccNodeDiagnosis(TriccNodeDisplayCalculateBase):
131
+ tricc_type: TriccNodeType = TriccNodeType.diagnosis
132
+ severity: str = None
133
+ def __init__(self, **data):
134
+ data['reference'] = f'"final.{data["name"]}" is true'
135
+ super().__init__(**data)
136
+
137
+ # rename rhombus
138
+ self.name = get_rand_name(f"d{data.get('id', None)}")
139
+
140
+ class TriccNodeExclusive(TriccNodeFakeCalculateBase):
141
+ tricc_type: TriccNodeType = TriccNodeType.exclusive
142
+
143
+ def get_node_from_id(activity, node, edge_only):
144
+ node_id = getattr(node,'id',node)
145
+ if not isinstance(node_id, str):
146
+ logger.critical("can set prev_next only with string or node")
147
+ exit(1)
148
+ if issubclass(node.__class__, TriccBaseModel):
149
+ return node_id, node
150
+ elif node_id in activity.nodes:
151
+ node = activity.nodes[node_id]
152
+ elif not edge_only:
153
+ logger.critical(f"cannot find {node_id} in {activiy.get_name()}")
154
+ exit(1)
155
+ return node_id, node
156
+
157
+ class TriccNodeWait(TriccNodeFakeCalculateBase, TriccRhombusMixIn):
158
+ tricc_type: TriccNodeType = TriccNodeType.wait
159
+ path: Optional[TriccNodeBaseModel] = None
160
+ reference: Union[List[TriccNodeBaseModel], Expression, TriccOperation]
161
+
162
+ def make_instance(self, instance_nb, activity, **kwargs):
163
+ instance = super(TriccNodeWait, self).make_instance(instance_nb, activity, **kwargs)
164
+ instance = self.make_mixin_instance(instance, instance_nb, activity, **kwargs)
165
+ return instance
166
+
167
+
168
+ class TriccNodeActivityEnd(TriccNodeFakeCalculateBase):
169
+ tricc_type: TriccNodeType = TriccNodeType.activity_end
170
+
171
+ def __init__(self, **data):
172
+ super().__init__(**data)
173
+ # FOR END
174
+ self.set_name()
175
+
176
+ def set_name(self):
177
+ self.name = ACTIVITY_END_NODE_FORMAT.format(self.activity.id)
178
+
179
+
180
+ class TriccNodeEnd(TriccNodeDisplayCalculateBase):
181
+ tricc_type: TriccNodeType = TriccNodeType.end
182
+ process: str = None
183
+ def __init__(self, **data):
184
+ if data.get('name', None) is None:
185
+ data['name'] = 'tricc_end_' + data.get('process', '')
186
+ super().__init__(**data)
187
+ # FOR END
188
+
189
+
190
+
191
+ def set_name(self):
192
+ if self.name is None:
193
+ self.name = self.get_reference()
194
+ #self.name = END_NODE_FORMAT.format(self.activity.id)
195
+
196
+ def get_reference(self):
197
+ return 'tricc_end_' + (self.process or '')
198
+
199
+ class TriccNodeActivityStart(TriccNodeFakeCalculateBase):
200
+ tricc_type: TriccNodeType = TriccNodeType.activity_start
201
+ relevance: Optional[Union[Expression, TriccOperation]] = None
202
+
203
+
204
+ def get_node_from_list(in_nodes, node_id):
205
+ nodes = list(filter(lambda x: x.id == node_id, in_nodes))
206
+ if len(nodes) > 0:
207
+ return nodes[0]
208
+
209
+ # qualculate that saves quantity, or we may merge integer/decimals
210
+ class TriccNodeQuantity(TriccNodeDisplayCalculateBase):
211
+ tricc_type: TriccNodeType = TriccNodeType.quantity
212
+
213
+
214
+
215
+
216
+ TriccNodeCalculate.update_forward_refs()
tricc_oo/models/ocl.py ADDED
@@ -0,0 +1,281 @@
1
+ # https://docs.openconceptlab.org/
2
+
3
+ from enum import Enum
4
+ from typing import Dict, List, Optional, Union, Literal, Annotated
5
+ from xml.dom import HIERARCHY_REQUEST_ERR
6
+ from xmlrpc.client import Boolean
7
+ from pydantic import BaseModel, constr, AnyHttpUrl, StringConstraints
8
+ from ocldev.oclconstants import OclConstants, OclConstants as OclConstantsBase
9
+
10
+ OCLId = Annotated[
11
+ str,
12
+ StringConstraints(pattern=r"^.+$")
13
+ ]
14
+ OCLName = Annotated[
15
+ str,
16
+ StringConstraints(pattern=r"^.+$")
17
+ ]
18
+ OCLShortName = Annotated[
19
+ str,
20
+ StringConstraints(pattern=r"^.+$")
21
+ ]
22
+ OCLLocale = Annotated[
23
+ str,
24
+ StringConstraints(pattern=r"^[a-zA-Z\-]{2,7}$")
25
+ ]
26
+ Uri = Annotated[
27
+ str,
28
+ StringConstraints(pattern=r"^.+$")
29
+ ]
30
+ OCLMapCode = Annotated[
31
+ str,
32
+ StringConstraints(pattern=r"^.+$")
33
+ ]
34
+
35
+
36
+
37
+ class OclConstants(OclConstantsBase):
38
+ #OCL Access type
39
+ ACCESS_TYPE_VIEW = 'View'
40
+ ACCESS_TYPE_EDIT = 'Edit'
41
+ ACCESS_TYPE_NONE = 'None'
42
+ ACCESS_TYPES = [
43
+ ACCESS_TYPE_EDIT,
44
+ ACCESS_TYPE_VIEW,
45
+ ACCESS_TYPE_NONE
46
+ ]
47
+ #https://www.hl7.org/fhir/valueset-codesystem-hierarchy-meaning.html
48
+ HIERARCHY_MEANING_IS_A = 'is-a'
49
+ HIERARCHY_MEANING_GROUP_BY = 'grouped-by'
50
+ HIERARCHY_MEANING_PART_OF = 'part-of'
51
+ HIERARCHY_MEANING_CLASSIFIED_WITH = ' classified-with'
52
+ HIERARCHY_MEANINGS = [
53
+ HIERARCHY_MEANING_IS_A,
54
+ HIERARCHY_MEANING_GROUP_BY,
55
+ HIERARCHY_MEANING_PART_OF,
56
+ HIERARCHY_MEANING_CLASSIFIED_WITH
57
+ ]
58
+ SOURCE_TYPE_DICTIONARY = "Dictionary"
59
+ SOURCE_TYPE_REFERENCE = "Reference"
60
+ SOURCE_TYPE_EXTERNAL_DICTIONARY = "ExternalDictionary"
61
+ SOURCE_TYPES = [
62
+ SOURCE_TYPE_DICTIONARY,
63
+ SOURCE_TYPE_REFERENCE,
64
+ SOURCE_TYPE_EXTERNAL_DICTIONARY
65
+ ]
66
+ # MAP type found for fever/malaria on OCL app
67
+ MAP_TYPE_SAME_AS = "SAME-AS"
68
+ MAP_TYPE_PART_OF = "PART-OF"
69
+ MAP_TYPE_Q_AND_A = "Q-AND-A"
70
+ MAP_TYPE_NARROWER_THAN = "NARROWER-THAN"
71
+ MAP_TYPE_CONCEPT_SET = "CONCEPT-SET"
72
+ MAP_TYPE_SYSTEM = "SYSTEM"
73
+ MAP_TYPE_BROADER_THAN = "BROADER-THAN"
74
+ MAP_TYPE_HAS_ANSWER = "HAS-ANSWER"
75
+ MAP_TYPE_HAS_ELEMENT = "HAS-ELEMENT"
76
+ MAP_TYPE_MAP_TO = "MAP-TO"
77
+ MAP_TYPES = [
78
+ MAP_TYPE_SAME_AS,
79
+ MAP_TYPE_PART_OF,
80
+ MAP_TYPE_Q_AND_A,
81
+ MAP_TYPE_NARROWER_THAN,
82
+ MAP_TYPE_CONCEPT_SET,
83
+ MAP_TYPE_SYSTEM,
84
+ MAP_TYPE_BROADER_THAN,
85
+ MAP_TYPE_HAS_ANSWER,
86
+ MAP_TYPE_HAS_ELEMENT,
87
+ MAP_TYPE_MAP_TO,
88
+ ]
89
+ DATA_TYPE_BOOLEAN = 'Boolean'
90
+ DATA_TYPE_COMPLEX = "Complex"
91
+ DATA_TYPE_STRUCTURED_NUMERIC = "Structured-Numeric"
92
+ DATA_TYPE_RULE = "Rule"
93
+ DATA_TYPE_DATETIME = "Datetime"
94
+ DATA_TYPE_TIME = "Time"
95
+ DATA_TYPE_DATE = "Date"
96
+ DATA_TYPE_DOCUMENT = "Document"
97
+ DATA_TYPE_CODED = "Coded"
98
+ DATA_TYPE_STRING = "String"
99
+ DATA_TYPE_TEXT = "Text"
100
+ DATA_TYPE_NA = "N/A"
101
+ DATA_TYPE_NUMERIC = "Numeric"
102
+ DATA_TYPE_NONE = "None"
103
+ DATA_TYPES =[
104
+ DATA_TYPE_BOOLEAN,
105
+ DATA_TYPE_CODED,
106
+ DATA_TYPE_STRING,
107
+ DATA_TYPE_TEXT,
108
+ DATA_TYPE_NA,
109
+ DATA_TYPE_NUMERIC,
110
+ DATA_TYPE_NONE,
111
+ DATA_TYPE_COMPLEX,
112
+ DATA_TYPE_STRUCTURED_NUMERIC,
113
+ DATA_TYPE_RULE,
114
+ DATA_TYPE_DATETIME,
115
+ DATA_TYPE_TIME,
116
+ DATA_TYPE_DATE,
117
+ DATA_TYPE_DOCUMENT
118
+ ]
119
+ DESCRIPTION_TYPE_DEFINITION = "Definition"
120
+ DESCRIPTION_TYPE_NONE = "None"
121
+ DESCRIPTION_TYPES = [
122
+ DESCRIPTION_TYPE_DEFINITION,
123
+ DESCRIPTION_TYPE_NONE
124
+
125
+ ]
126
+ NAME_TYPE_INDEX_TERM = "Index-Term"
127
+ NAME_TYPE_SHORT = "Short"
128
+ NAME_TYPE_FULLY_SPECIFIED = "Fully-Specified"
129
+ NAME_TYPE_NONE = "None"
130
+ NAME_TYPES = [
131
+ NAME_TYPE_INDEX_TERM,
132
+ NAME_TYPE_SHORT,
133
+ NAME_TYPE_FULLY_SPECIFIED,
134
+ NAME_TYPE_NONE
135
+ ]
136
+ OCLRessourceType= Literal[tuple(OclConstants.RESOURCE_TYPES)]
137
+
138
+
139
+ def get_data_type(tricc_type):
140
+ if tricc_type.lower() in ('integer', 'decimal', 'add', 'count'):
141
+ return OclConstants.DATA_TYPE_NUMERIC
142
+ elif tricc_type.lower() in ('activity', 'page'):
143
+ return OclConstants.DATA_TYPE_DOCUMENT
144
+ elif tricc_type.lower() in ('select_one'):
145
+ return OclConstants.DATA_TYPE_CODED
146
+ elif tricc_type.lower() in ('calculate', 'diagnosis', 'proposed_diagnosis'):
147
+ return OclConstants.DATA_TYPE_BOOLEAN
148
+ found_type = [ t for t in OclConstants.DATA_TYPES if t.lower() == tricc_type.lower()]
149
+ if found_type:
150
+ return found_type[0]
151
+ return OclConstants.DATA_TYPE_NA
152
+
153
+ class OCLBaseModel(BaseModel):
154
+ type:OCLRessourceType
155
+ id:OCLId
156
+ external_id:str = None
157
+ public_access:Literal[tuple(OclConstants.ACCESS_TYPES)] = OclConstants.ACCESS_TYPE_VIEW
158
+ extras:Dict[str,Union[str,Dict[str,str]]] = {}
159
+ url: Union[AnyHttpUrl,Uri] = None
160
+ # enriched data for get
161
+ class OclGet(BaseModel):
162
+ created_on:str = None
163
+ created_by:str = None
164
+ updated_on:str = None
165
+ updated_by:str = None
166
+
167
+ class OCLBaseModelBrowsable(OCLBaseModel):
168
+ name:OCLName
169
+ description:str = None
170
+ website:AnyHttpUrl = None
171
+
172
+
173
+ class OCLDetailedName(BaseModel):
174
+ name: str
175
+ external_id:str = None
176
+ locale:OCLLocale
177
+ locale_preferred:Boolean = None
178
+ name_type:Literal[tuple(OclConstants.NAME_TYPES)] = OclConstants.NAME_TYPE_SHORT
179
+
180
+ class OCLDetailedDescription(BaseModel):
181
+ description:str
182
+ external_id:str = None
183
+ locale:OCLLocale
184
+ locale_preferred:Boolean = None
185
+ description_type:Literal[tuple(OclConstants.DESCRIPTION_TYPES)] = OclConstants.DESCRIPTION_TYPE_DEFINITION
186
+
187
+ class OCLConcept(OCLBaseModel):
188
+ type:OCLRessourceType = OclConstants.RESOURCE_TYPE_CONCEPT
189
+ uuid: str = None
190
+ concept_class: str
191
+ datatype:Literal[tuple(OclConstants.DATA_TYPES)] = OclConstants.DATA_TYPE_NONE
192
+ names:List[OCLDetailedName]
193
+ descriptions: List[OCLDetailedDescription] = []
194
+ retired:Boolean = False
195
+ # not for create
196
+ versions: str = None # TODO version
197
+ source:OCLId = None
198
+ owner:OCLId = None
199
+ owner_type:Literal[tuple(OclConstants.OWNER_TYPE_TO_STEM)] = None
200
+ owner_url:Union[AnyHttpUrl,Uri] = None
201
+ versions_url:Union[AnyHttpUrl,Uri] = None
202
+ source_url:Union[AnyHttpUrl,Uri] = None
203
+ owner_url:Union[AnyHttpUrl,Uri] = None
204
+ mappings_url:Union[AnyHttpUrl,Uri] = None
205
+
206
+ class OCLMapping(BaseModel):
207
+ type:OCLRessourceType = OclConstants.RESOURCE_TYPE_MAPPING
208
+ uuid: str = None
209
+ retired:Boolean = False
210
+ map_type: Literal[tuple(OclConstants.MAP_TYPES)]
211
+ from_concept_url:Union[AnyHttpUrl,Uri]
212
+ from_source_url:Uri = None
213
+ from_concept_code:str = None
214
+ from_concept_name:str = None
215
+ to_concept_url:Union[AnyHttpUrl,Uri] = None
216
+ to_source:str = None
217
+ to_concept_code:str = None
218
+ to_source_owner:OCLId = None
219
+ to_source_owner_type:Literal[tuple(OclConstants.OWNER_TYPE_TO_STEM)] = None
220
+ #for bulk
221
+ source:OCLId = None
222
+ owner:OCLId = None
223
+ owner_type:Literal[tuple(OclConstants.OWNER_TYPE_TO_STEM.values())] = None
224
+
225
+ class OCLCollection(OCLBaseModelBrowsable):
226
+ #TODO https://docs.openconceptlab.org/en/latest/oclapi/apireference/collections.html
227
+ pass
228
+
229
+ class OCLUser(OCLBaseModelBrowsable):
230
+ #TODO https://docs.openconceptlab.org/en/latest/oclapi/apireference/users.html
231
+ pass
232
+
233
+ class OCLMappingInternal(OCLMapping):
234
+ to_concept_url:Union[AnyHttpUrl,Uri]
235
+ # when there is not URL
236
+
237
+ class OCLMappingExternal(OCLMapping):
238
+ to_source_url:Uri
239
+ to_concept_code:str
240
+ to_concept_name:str = None
241
+
242
+ class OCLOrganisation(OCLBaseModelBrowsable):
243
+ type:OCLRessourceType = OclConstants.RESOURCE_TYPE_ORGANIZATION
244
+ company:OCLName
245
+ logo_url:AnyHttpUrl
246
+ location:OCLName
247
+ text:str
248
+
249
+
250
+
251
+ class OCLSource(OCLBaseModelBrowsable):
252
+ type:OCLRessourceType = OclConstants.RESOURCE_TYPE_SOURCE
253
+ short_code:OCLShortName
254
+ full_name:OCLName
255
+ source_type:Literal[tuple(OclConstants.SOURCE_TYPES)] = OclConstants.SOURCE_TYPE_DICTIONARY
256
+ default_locale:OCLLocale = 'en'
257
+ supported_locales:List[OCLLocale] = ['en']
258
+ custom_validation_schema:str = 'None'
259
+ # not for create
260
+ owner:OCLId = None
261
+ owner_type:Literal[tuple(OclConstants.OWNER_TYPE_TO_STEM)] = None
262
+ owner_url:Union[AnyHttpUrl,Uri] = None
263
+ #FHIR
264
+ hierarchy_meaning:Literal[tuple(OclConstants.HIERARCHY_MEANINGS)] = None
265
+ hierarchy_root_url:Union[AnyHttpUrl,Uri] = None
266
+ meta:str = None
267
+ canonical_url:Union[AnyHttpUrl,Uri] = None
268
+ internal_reference_id:OCLId = None
269
+ #collection_reference:Uri
270
+ versions_url:Union[AnyHttpUrl,Uri] = None
271
+ concepts_url:Union[AnyHttpUrl,Uri] = None
272
+ mappings_url:Union[AnyHttpUrl,Uri] = None
273
+
274
+ versions: str = None # TODO version
275
+ active_concepts:int = 0
276
+ active_mappings:int = 0
277
+
278
+ class OCLSourceVersion(OCLBaseModelBrowsable):
279
+ released: Boolean = None
280
+ previous_version :str
281
+ parent_version :str
@@ -0,0 +1,125 @@
1
+ from collections import OrderedDict
2
+ from collections.abc import Iterable, Sequence
3
+ from pydantic import BaseModel, GetCoreSchemaHandler
4
+ from pydantic_core import CoreSchema
5
+
6
+ class OrderedSet(Sequence):
7
+ def __init__(self, iterable=None):
8
+ self._od = OrderedDict.fromkeys(iterable or [])
9
+
10
+ def copy(self):
11
+ return OrderedSet(list(self._od.keys()))
12
+
13
+ def add(self, item):
14
+ self.insert_at_bottom(item)
15
+
16
+ def remove(self, item):
17
+ del self._od[item]
18
+
19
+ def pop(self):
20
+ return self._od.popitem(last=False)[0]
21
+
22
+ def insert_at_top(self, item):
23
+ # Add item if not already present
24
+ self.insert_at_bottom(item)
25
+ # Move item to the top
26
+ self._od.move_to_end(item, last=False)
27
+
28
+ def insert_at_bottom(self, item):
29
+ if item not in self._od:
30
+ self._od[item] = None
31
+ def __contains__(self, item):
32
+ return item in self._od
33
+
34
+ def __iter__(self):
35
+ return iter(self._od)
36
+
37
+ def __len__(self):
38
+ return len(self._od)
39
+
40
+ def __repr__(self):
41
+ return f"{type(self).__name__}({list(self._od.keys())})"
42
+
43
+ def _add_items(self, items):
44
+ for item in items:
45
+ if item not in self:
46
+ self.insert_at_bottom(item)
47
+ def __eq__(self, other):
48
+ if not isinstance(other, self.__class__):
49
+ return False
50
+ else:
51
+ return self._od.keys() == other._od.keys()
52
+
53
+ # Union method (| operator)
54
+ def __or__(self, other):
55
+ if not isinstance(other, Iterable):
56
+ raise TypeError(f"Unsupported operand type(s) for |: 'OrderedSet' and '{type(other).__name__}'")
57
+ new_set = self.copy()
58
+ new_set._add_items(other)
59
+ return new_set
60
+
61
+ def union(self, other):
62
+ return self.__or__(other)
63
+ def __or__(self, other):
64
+ if not isinstance(other, Iterable):
65
+ raise TypeError("Unsupported operand type(s) for |: 'OrderedSet' and '{}'".format(type(other)))
66
+ new_set = self.copy()
67
+ new_set._add_items(other)
68
+ return new_set
69
+
70
+ def __iadd__(self, other):
71
+ if not isinstance(other, Iterable):
72
+ raise TypeError("Unsupported operand type(s) for +=: 'OrderedSet' and '{}'".format(type(other)))
73
+ self._add_items(other)
74
+ return self
75
+
76
+ def get(self, index):
77
+ return self.__getitem__(index)
78
+
79
+ def __getitem__(self, index):
80
+ try:
81
+ return list(self._od.keys())[index]
82
+ except IndexError:
83
+ raise IndexError("Index out of range") from None
84
+
85
+ def sort(self, key=None, reverse=False):
86
+ sorted_keys = sorted(self._od.keys(), key=key, reverse=reverse)
87
+ self._od = OrderedDict.fromkeys(sorted_keys)
88
+
89
+ def find_last(self, filter: callable):
90
+ # Iterate over items in reverse order
91
+ for item in reversed(list(self._od.keys())):
92
+ if filter(item):
93
+ return item
94
+ return None # Return None if no matching item is found
95
+
96
+ def find_first(self, filter: callable):
97
+ for item in list(self._od.keys()):
98
+ if filter(item):
99
+ return item
100
+ return None # Return None if no matching item is found
101
+
102
+
103
+ def find_prev(self, obj, filter: callable):
104
+ # Get the list of keys (items) in the OrderedSet
105
+ keys = list(self._od.keys())
106
+
107
+ # If the object is not in the OrderedSet, start from the end
108
+ if obj not in self._od:
109
+ start_index = len(keys) - 1
110
+ else:
111
+ # Find the index of the given object
112
+ start_index = keys.index(obj)
113
+
114
+ # Iterate backward from the start_index
115
+ for i in range(start_index - 1, -1, -1):
116
+ item = keys[i]
117
+ if filter(item):
118
+ return item
119
+
120
+ return None # Return None if no matching item is found before the object
121
+
122
+ @classmethod
123
+ def __get_pydantic_core_schema__(cls, source_type: type, handler: GetCoreSchemaHandler) -> CoreSchema:
124
+ # Define how Pydantic should handle this type
125
+ return handler.generate_schema(list) # Treat it as a list for validation purposes