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.
- tests/build.py +213 -0
- tests/test_cql.py +197 -0
- tests/to_ocl.py +51 -0
- {tricc → tricc_oo}/__init__.py +3 -1
- tricc_oo/converters/codesystem_to_ocl.py +169 -0
- tricc_oo/converters/cql/cqlLexer.py +822 -0
- tricc_oo/converters/cql/cqlListener.py +1632 -0
- tricc_oo/converters/cql/cqlParser.py +11204 -0
- tricc_oo/converters/cql/cqlVisitor.py +913 -0
- tricc_oo/converters/cql_to_operation.py +402 -0
- tricc_oo/converters/datadictionnary.py +115 -0
- tricc_oo/converters/drawio_type_map.py +222 -0
- tricc_oo/converters/tricc_to_xls_form.py +61 -0
- tricc_oo/converters/utils.py +65 -0
- tricc_oo/converters/xml_to_tricc.py +1003 -0
- tricc_oo/models/__init__.py +4 -0
- tricc_oo/models/base.py +732 -0
- tricc_oo/models/calculate.py +216 -0
- tricc_oo/models/ocl.py +281 -0
- tricc_oo/models/ordered_set.py +125 -0
- tricc_oo/models/tricc.py +418 -0
- tricc_oo/parsers/xml.py +138 -0
- tricc_oo/serializers/__init__.py +0 -0
- tricc_oo/serializers/xls_form.py +745 -0
- tricc_oo/strategies/__init__.py +0 -0
- tricc_oo/strategies/input/__init__.py +0 -0
- tricc_oo/strategies/input/base_input_strategy.py +111 -0
- tricc_oo/strategies/input/drawio.py +317 -0
- tricc_oo/strategies/output/base_output_strategy.py +148 -0
- tricc_oo/strategies/output/spice.py +365 -0
- tricc_oo/strategies/output/xls_form.py +697 -0
- tricc_oo/strategies/output/xlsform_cdss.py +189 -0
- tricc_oo/strategies/output/xlsform_cht.py +200 -0
- tricc_oo/strategies/output/xlsform_cht_hf.py +334 -0
- tricc_oo/visitors/__init__.py +0 -0
- tricc_oo/visitors/tricc.py +2198 -0
- tricc_oo/visitors/utils.py +17 -0
- tricc_oo/visitors/xform_pd.py +264 -0
- tricc_oo-1.4.15.dist-info/METADATA +219 -0
- tricc_oo-1.4.15.dist-info/RECORD +46 -0
- {tricc_oo-1.0.2.dist-info → tricc_oo-1.4.15.dist-info}/WHEEL +1 -1
- tricc_oo-1.4.15.dist-info/top_level.txt +2 -0
- tricc/converters/mc_to_tricc.py +0 -542
- tricc/converters/tricc_to_xls_form.py +0 -553
- tricc/converters/utils.py +0 -44
- tricc/converters/xml_to_tricc.py +0 -740
- tricc/models/tricc.py +0 -1093
- tricc/parsers/xml.py +0 -81
- tricc/serializers/xls_form.py +0 -364
- tricc/strategies/input/base_input_strategy.py +0 -80
- tricc/strategies/input/drawio.py +0 -246
- tricc/strategies/input/medalcreator.py +0 -168
- tricc/strategies/output/base_output_strategy.py +0 -92
- tricc/strategies/output/xls_form.py +0 -308
- tricc/strategies/output/xlsform_cdss.py +0 -46
- tricc/strategies/output/xlsform_cht.py +0 -106
- tricc/visitors/tricc.py +0 -375
- tricc_oo-1.0.2.dist-info/LICENSE +0 -78
- tricc_oo-1.0.2.dist-info/METADATA +0 -229
- tricc_oo-1.0.2.dist-info/RECORD +0 -26
- tricc_oo-1.0.2.dist-info/top_level.txt +0 -2
- venv/bin/vba_extract.py +0 -78
- {tricc → tricc_oo}/converters/__init__.py +0 -0
- {tricc → tricc_oo}/models/lang.py +0 -0
- {tricc/serializers → tricc_oo/parsers}/__init__.py +0 -0
- {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
|