tricc-oo 1.0.1__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.1.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 -194
- 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.1.dist-info/LICENSE +0 -78
- tricc_oo-1.0.1.dist-info/METADATA +0 -229
- tricc_oo-1.0.1.dist-info/RECORD +0 -26
- tricc_oo-1.0.1.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
tricc_oo/models/tricc.py
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import random
|
|
5
|
+
import string
|
|
6
|
+
from enum import Enum, auto
|
|
7
|
+
from typing import Dict, ForwardRef, List, Optional, Union
|
|
8
|
+
from fhir.resources.codesystem import CodeSystem
|
|
9
|
+
from fhir.resources.valueset import ValueSet
|
|
10
|
+
from pydantic import BaseModel, constr
|
|
11
|
+
from strenum import StrEnum
|
|
12
|
+
from .base import *
|
|
13
|
+
from tricc_oo.converters.utils import generate_id
|
|
14
|
+
|
|
15
|
+
class TriccNodeCalculateBase(TriccNodeBaseModel):
|
|
16
|
+
#input: Dict[TriccOperation, TriccNodeBaseModel] = {}
|
|
17
|
+
reference: Union[List[Union[TriccNodeBaseModel,TriccStatic]], Expression, TriccStatic] = None
|
|
18
|
+
expression_reference: Union[str, TriccOperation] = None
|
|
19
|
+
last: bool = None
|
|
20
|
+
datatype: str = 'boolean'
|
|
21
|
+
# to use the enum value of the TriccNodeType
|
|
22
|
+
class Config:
|
|
23
|
+
use_enum_values = True # <--
|
|
24
|
+
|
|
25
|
+
def make_instance(self, instance_nb, activity, **kwargs):
|
|
26
|
+
# shallow copy
|
|
27
|
+
instance = super().make_instance(instance_nb, activity=activity)
|
|
28
|
+
#input = {}
|
|
29
|
+
#instance.input = input
|
|
30
|
+
expression = self.expression.copy() if self.expression is not None else None
|
|
31
|
+
if self.reference:
|
|
32
|
+
instance.reference = [e.copy() if isinstance(e, (TriccReference, TriccOperation)) else (TriccReference(e.name) if hasattr(e, 'name') else e) for e in self.reference]
|
|
33
|
+
else:
|
|
34
|
+
instance.reference = None
|
|
35
|
+
version = self.version + 1
|
|
36
|
+
instance.version = version
|
|
37
|
+
return instance
|
|
38
|
+
|
|
39
|
+
def __init__(self, **data):
|
|
40
|
+
if 'name' not in data:
|
|
41
|
+
data['name'] = get_rand_name(data.get('id', None))
|
|
42
|
+
super().__init__(**data)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def append(self, elm):
|
|
46
|
+
self.reference.append(elm)
|
|
47
|
+
|
|
48
|
+
def get_references(self):
|
|
49
|
+
if isinstance(self.reference, set):
|
|
50
|
+
return self.reference
|
|
51
|
+
elif isinstance(self.reference, list):
|
|
52
|
+
return set(self.reference)
|
|
53
|
+
elif isinstance(self.expression_reference, TriccOperation):
|
|
54
|
+
return self.expression_reference.get_references()
|
|
55
|
+
elif isinstance(self.reference, TriccOperation):
|
|
56
|
+
return self.reference.get_references()
|
|
57
|
+
|
|
58
|
+
elif self.reference:
|
|
59
|
+
return self.reference
|
|
60
|
+
logger.critical("Cannot get reference from a sting")
|
|
61
|
+
def __str__(self):
|
|
62
|
+
return self.get_name()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TriccNodeActivity(TriccNodeBaseModel):
|
|
66
|
+
tricc_type: TriccNodeType = TriccNodeType.activity
|
|
67
|
+
# starting point of the activity
|
|
68
|
+
root: TriccNodeBaseModel
|
|
69
|
+
# edge list
|
|
70
|
+
edges: List[TriccEdge] = []
|
|
71
|
+
# copy of the edge for later restauration
|
|
72
|
+
unused_edges: List[TriccEdge] = []
|
|
73
|
+
# nodes part of that actvity
|
|
74
|
+
nodes: Dict[str, TriccNodeBaseModel] = {}
|
|
75
|
+
# groups
|
|
76
|
+
groups: Dict[str, TriccGroup] = {}
|
|
77
|
+
# save the instance on the base activity
|
|
78
|
+
instances: Dict[int, TriccNodeBaseModel] = {}
|
|
79
|
+
relevance: Optional[Union[Expression, TriccOperation]] = None
|
|
80
|
+
#caclulate that are not part of the any skip logic:
|
|
81
|
+
# - inputs
|
|
82
|
+
# - dangling calculate
|
|
83
|
+
# - case definition
|
|
84
|
+
calculates: List[TriccNodeCalculateBase] = []
|
|
85
|
+
applicability: Optional[Union[Expression, TriccOperation]] = None
|
|
86
|
+
|
|
87
|
+
# redefine
|
|
88
|
+
def make_instance(self, instance_nb, **kwargs):
|
|
89
|
+
from tricc_oo.models.calculate import (
|
|
90
|
+
TriccNodeDisplayBridge,
|
|
91
|
+
TriccNodeBridge,
|
|
92
|
+
|
|
93
|
+
)
|
|
94
|
+
# shallow copy
|
|
95
|
+
if instance_nb in self.instances:
|
|
96
|
+
return self.instances[instance_nb]
|
|
97
|
+
else:
|
|
98
|
+
instance = super().make_instance(instance_nb, activity=None)
|
|
99
|
+
self.instances[instance_nb] = instance
|
|
100
|
+
# instance.base_instance = self
|
|
101
|
+
# we duplicate all the related nodes (not the calculate, duplication is manage in calculate version code)
|
|
102
|
+
nodes = {}
|
|
103
|
+
instance.nodes = nodes
|
|
104
|
+
edges = []
|
|
105
|
+
instance.edges = edges
|
|
106
|
+
unused_edges = []
|
|
107
|
+
instance.edges = unused_edges
|
|
108
|
+
calculates = []
|
|
109
|
+
instance.calculates = calculates
|
|
110
|
+
relevance = None
|
|
111
|
+
instance.relevance = relevance
|
|
112
|
+
groups = {}
|
|
113
|
+
instance.groups = groups
|
|
114
|
+
instance.group = instance
|
|
115
|
+
instance.activity = instance
|
|
116
|
+
for edge in self.edges:
|
|
117
|
+
instance.edges.append(edge.make_instance(instance_nb, activity=instance))
|
|
118
|
+
instance.update_nodes(self.root)
|
|
119
|
+
# we walk throught the nodes and replace them when ready
|
|
120
|
+
for node in list(filter(lambda p_node: isinstance(p_node, (TriccNodeDisplayBridge,TriccNodeBridge)),list(self.nodes.values()) )):
|
|
121
|
+
instance.update_nodes(node)
|
|
122
|
+
for node in list(filter(lambda p_node: p_node != self.root and not isinstance(p_node, (TriccNodeDisplayBridge,TriccNodeBridge)),list(self.nodes.values()) )):
|
|
123
|
+
instance_node = instance.update_nodes(node)
|
|
124
|
+
if node in self.calculates and instance_node:
|
|
125
|
+
instance.calculates.append(instance_node)
|
|
126
|
+
# update parents
|
|
127
|
+
for node in list(filter(lambda p_node: hasattr(p_node, 'parent'),list(instance.nodes.values()))):
|
|
128
|
+
new_parent = list(filter(lambda p_node: p_node.base_instance == node.parent,list(instance.nodes.values())))
|
|
129
|
+
if new_parent:
|
|
130
|
+
node.parent = new_parent[0]
|
|
131
|
+
else:
|
|
132
|
+
logger.error("Parent not found in the activity")
|
|
133
|
+
for group in self.groups.values():
|
|
134
|
+
instance.update_groups(group)
|
|
135
|
+
# update parent group
|
|
136
|
+
for group in self.groups.values():
|
|
137
|
+
instance.update_groups_group(group)
|
|
138
|
+
|
|
139
|
+
return instance
|
|
140
|
+
|
|
141
|
+
def update_groups_group(self, group):
|
|
142
|
+
for instance_group in self.groups.values():
|
|
143
|
+
if instance_group.group == group:
|
|
144
|
+
instance_group.group == instance_group
|
|
145
|
+
elif instance_group.group == self.base_instance:
|
|
146
|
+
instance_group.group == self
|
|
147
|
+
|
|
148
|
+
def update_groups(self, group):
|
|
149
|
+
# create new group
|
|
150
|
+
instance_group = group.make_instance(self.instance, activity=self)
|
|
151
|
+
# update the group in all activity
|
|
152
|
+
for node in list(self.nodes.values()):
|
|
153
|
+
if node.group == group:
|
|
154
|
+
node.group == instance_group
|
|
155
|
+
self.groups[instance_group.id] = instance_group
|
|
156
|
+
|
|
157
|
+
def update_nodes(self, node_origin):
|
|
158
|
+
from tricc_oo.models.calculate import (
|
|
159
|
+
TriccNodeEnd,
|
|
160
|
+
TriccNodeActivityStart,
|
|
161
|
+
TriccNodeMainStart,
|
|
162
|
+
TriccNodeActivityEnd,
|
|
163
|
+
TriccRhombusMixIn
|
|
164
|
+
)
|
|
165
|
+
updated_edges = 0
|
|
166
|
+
node_instance = None
|
|
167
|
+
if not isinstance(node_origin, TriccNodeSelectOption):
|
|
168
|
+
# do not perpetuate the instance number in the underlying activities
|
|
169
|
+
if isinstance(node_origin, TriccNodeActivity):
|
|
170
|
+
node_instance = node_origin.make_instance(node_origin.instance if node_origin.instance<100 else 0 , activity=self)
|
|
171
|
+
else:
|
|
172
|
+
node_instance = node_origin.make_instance(self.instance, activity=self)
|
|
173
|
+
self.nodes[node_instance.id] = node_instance
|
|
174
|
+
if isinstance(node_instance, (TriccNodeActivityEnd)):
|
|
175
|
+
node_instance.set_name()
|
|
176
|
+
# update root
|
|
177
|
+
if isinstance(node_origin, TriccNodeActivityStart) and node_origin == node_origin.activity.root:
|
|
178
|
+
self.root = node_instance
|
|
179
|
+
if issubclass(node_instance.__class__, TriccRhombusMixIn):
|
|
180
|
+
old_path = node_origin.path
|
|
181
|
+
if old_path is not None:
|
|
182
|
+
for n in node_instance.activity.nodes.values():
|
|
183
|
+
if n.base_instance.id == old_path.id:
|
|
184
|
+
node_instance.path = n
|
|
185
|
+
# test next_nodes to check that the instance has already prev/next
|
|
186
|
+
if node_instance.path is None and node_instance.next_nodes:
|
|
187
|
+
logger.critical("new path not found")
|
|
188
|
+
elif len(node_instance.prev_nodes) == 1:
|
|
189
|
+
node.path = list(node_instance.prev_nodes)[0]
|
|
190
|
+
elif not (len(node_instance.reference)== 1 and issubclass(node_instance.reference[0].__class__, TriccNodeInputModel)):
|
|
191
|
+
error.warning("Rhombus without a path")
|
|
192
|
+
exit(1)
|
|
193
|
+
# generate options
|
|
194
|
+
elif issubclass(node_instance.__class__, TriccNodeSelect):
|
|
195
|
+
for key, option_instance in node_instance.options.items():
|
|
196
|
+
updated_edges += self.update_edges(node_origin.options[key], option_instance)
|
|
197
|
+
updated_edges += self.update_edges(node_origin, node_instance)
|
|
198
|
+
if updated_edges == 0:
|
|
199
|
+
node_edge = list(filter(lambda x: (x.source == node_instance.id or x.source == node_instance) , node_instance.activity.edges))
|
|
200
|
+
node_edge_origin = list(filter(lambda x: (x.source == node_origin.id or x.source == node_origin) , node_origin.activity.edges))
|
|
201
|
+
if len(node_edge) == 0 and not issubclass(node_origin.__class__, TriccNodeCalculateBase):
|
|
202
|
+
logger.warning("no edge was updated for node {}::{}::{}::{}".format(node_instance.activity.get_name(),
|
|
203
|
+
node_instance.__class__,
|
|
204
|
+
node_instance.get_name(),
|
|
205
|
+
node_instance.instance))
|
|
206
|
+
return node_instance
|
|
207
|
+
|
|
208
|
+
def update_edges(self, node_origin, node_instance):
|
|
209
|
+
updates = 0
|
|
210
|
+
|
|
211
|
+
for edge in self.edges:
|
|
212
|
+
if edge.source == node_origin.id or edge.source == node_origin:
|
|
213
|
+
edge.source = node_instance.id
|
|
214
|
+
updates += 1
|
|
215
|
+
if edge.target == node_origin.id or edge.target == node_origin:
|
|
216
|
+
edge.target = node_instance.id
|
|
217
|
+
updates += 1
|
|
218
|
+
return updates
|
|
219
|
+
|
|
220
|
+
def get_end_nodes(self):
|
|
221
|
+
from tricc_oo.models.calculate import (
|
|
222
|
+
TriccNodeEnd,
|
|
223
|
+
TriccNodeActivityEnd,
|
|
224
|
+
)
|
|
225
|
+
return list(filter(lambda x: isinstance(x, (TriccNodeActivityEnd)) or (isinstance(x, (TriccNodeEnd)) and isinstance(self.root, TriccNodeMainStart )), self.nodes.values()))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class TriccNodeDisplayModel(TriccNodeBaseModel):
|
|
231
|
+
name: str
|
|
232
|
+
image: Optional[b64] = None
|
|
233
|
+
hint: Optional[Union[str, TriccNodeBaseModel]] = None
|
|
234
|
+
help: Optional[Union[str, TriccNodeBaseModel]] = None
|
|
235
|
+
group: Optional[Union[TriccGroup, TriccNodeActivity]] = None
|
|
236
|
+
relevance: Optional[Union[Expression, TriccOperation]] = None
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# to use the enum value of the TriccNodeType
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class TriccNodeNote(TriccNodeDisplayModel):
|
|
243
|
+
tricc_type: TriccNodeType = TriccNodeType.note
|
|
244
|
+
datatype: str = 'string'
|
|
245
|
+
|
|
246
|
+
class TriccNodeInputModel(TriccNodeDisplayModel):
|
|
247
|
+
required: Optional[Union[Expression, TriccOperation, TriccStatic]] = '1'
|
|
248
|
+
constraint_message: Optional[Union[str, Dict[str,str]]] = None
|
|
249
|
+
constraint: Optional[Expression] = None
|
|
250
|
+
save: Optional[str] = None # contribute to another calculate
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class TriccNodeDate(TriccNodeInputModel):
|
|
254
|
+
tricc_type: TriccNodeType = TriccNodeType.date
|
|
255
|
+
datatype: str = 'date'
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class TriccNodeMainStart(TriccNodeBaseModel):
|
|
259
|
+
tricc_type: TriccNodeType = TriccNodeType.start
|
|
260
|
+
form_id: Optional[str] = None
|
|
261
|
+
process: Optional[str] = None
|
|
262
|
+
relevance: Optional[Union[Expression, TriccOperation]] = None
|
|
263
|
+
datatype: str = 'boolean'
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class TriccNodeLinkIn(TriccNodeBaseModel):
|
|
267
|
+
tricc_type: TriccNodeType = TriccNodeType.link_in
|
|
268
|
+
datatype: str = 'n/a'
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class TriccNodeLinkOut(TriccNodeBaseModel):
|
|
272
|
+
tricc_type: TriccNodeType = TriccNodeType.link_out
|
|
273
|
+
reference: Optional[Union[TriccNodeLinkIn, triccId]] = None
|
|
274
|
+
# no need to copy
|
|
275
|
+
datatype: str = 'n/a'
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class TriccNodeGoTo(TriccNodeBaseModel):
|
|
279
|
+
tricc_type: TriccNodeType = TriccNodeType.goto
|
|
280
|
+
link: Union[TriccNodeActivity, triccId]
|
|
281
|
+
datatype: str = 'n/a'
|
|
282
|
+
|
|
283
|
+
# no need ot copy
|
|
284
|
+
def make_instance(self, instance_nb, activity, **kwargs):
|
|
285
|
+
# shallow copy
|
|
286
|
+
instance = super().make_instance(instance_nb, activity=activity)
|
|
287
|
+
# do not use activity instance for goto
|
|
288
|
+
instance.instance = self.instance
|
|
289
|
+
return instance
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class TriccNodeSelectOption(TriccNodeDisplayModel):
|
|
293
|
+
tricc_type: TriccNodeType = TriccNodeType.select_option
|
|
294
|
+
label: Union[str, Dict[str,str]]
|
|
295
|
+
save: Optional[str] = None
|
|
296
|
+
select: TriccNodeInputModel
|
|
297
|
+
list_name: str
|
|
298
|
+
def get_datatype(self):
|
|
299
|
+
if isnumeric(self.name):
|
|
300
|
+
return 'number'
|
|
301
|
+
else:
|
|
302
|
+
return 'string'
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def make_instance(self, instance_nb, activity, select, **kwargs):
|
|
306
|
+
# shallow copy
|
|
307
|
+
instance = super().make_instance(instance_nb, activity=activity)
|
|
308
|
+
instance.select = select
|
|
309
|
+
return instance
|
|
310
|
+
|
|
311
|
+
def get_name(self):
|
|
312
|
+
name = super().get_name()
|
|
313
|
+
select_name = self.select.get_name()
|
|
314
|
+
return select_name + "::" + name
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class TriccNodeSelect(TriccNodeInputModel):
|
|
318
|
+
filter: Optional[str] = None
|
|
319
|
+
options: Dict[int, TriccNodeSelectOption] = {}
|
|
320
|
+
list_name: str
|
|
321
|
+
def get_datatype(self):
|
|
322
|
+
rtype = set()
|
|
323
|
+
for k,o in options.items():
|
|
324
|
+
rtype.add(o.get_datatype())
|
|
325
|
+
if len(rtype)>1:
|
|
326
|
+
return 'mixed'
|
|
327
|
+
else:
|
|
328
|
+
return rtype.pop()
|
|
329
|
+
|
|
330
|
+
def make_instance(self, instance_nb, activity, **kwargs):
|
|
331
|
+
# shallow copy, no copy of filter and list_name
|
|
332
|
+
instance = super().make_instance(instance_nb, activity=activity)
|
|
333
|
+
instance.options = {}
|
|
334
|
+
for key, option in self.options.items():
|
|
335
|
+
instance.options[key] = option.make_instance(instance_nb, activity=activity, select=instance)
|
|
336
|
+
return instance
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class TriccNodeSelectOne(TriccNodeSelect):
|
|
340
|
+
tricc_type: TriccNodeType = TriccNodeType.select_one
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class TriccNodeSelectYesNo(TriccNodeSelectOne):
|
|
344
|
+
pass
|
|
345
|
+
|
|
346
|
+
class TriccNodeAcceptDiagnostic(TriccNodeSelectOne):
|
|
347
|
+
severity: Optional[str] = None
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class TriccParentMixIn(BaseModel):
|
|
351
|
+
parent: Optional[TriccNodeBaseModel] = None
|
|
352
|
+
|
|
353
|
+
# options: List[TriccNodeSelectOption] = [TriccNodeSelectOption(label='Yes', name='yes'),
|
|
354
|
+
# TriccNodeSelectOption(label='No', name='no')]
|
|
355
|
+
class TriccNodeSelectNotAvailable(TriccNodeSelectOne, TriccParentMixIn):
|
|
356
|
+
...
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class TriccNodeSelectMultiple(TriccNodeSelect):
|
|
360
|
+
tricc_type: TriccNodeType = TriccNodeType.select_multiple
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class TriccNodeNumber(TriccNodeInputModel):
|
|
364
|
+
min: Optional[float] = None
|
|
365
|
+
max: Optional[float] = None
|
|
366
|
+
datatype : str = 'number'
|
|
367
|
+
# no need to copy min max in make isntance
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
class TriccNodeDecimal(TriccNodeNumber):
|
|
371
|
+
tricc_type: TriccNodeType = TriccNodeType.decimal
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class TriccNodeInteger(TriccNodeNumber):
|
|
376
|
+
tricc_type: TriccNodeType = TriccNodeType.integer
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class TriccNodeText(TriccNodeInputModel):
|
|
380
|
+
tricc_type: TriccNodeType = TriccNodeType.text
|
|
381
|
+
datatype : str = 'string'
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class TriccNodeMoreInfo(TriccNodeInputModel, TriccParentMixIn):
|
|
385
|
+
tricc_type: TriccNodeType = TriccNodeType.help
|
|
386
|
+
datatype : str = 'n/a'
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class TriccProject(BaseModel):
|
|
391
|
+
title: str = "My project"
|
|
392
|
+
description: str = ""
|
|
393
|
+
lang_code: str = "en"
|
|
394
|
+
# abstract graph / Scheduling
|
|
395
|
+
#abs_graph: MultiDiGraph = MultiDiGraph()
|
|
396
|
+
#abs_graph_process_start: Dict = {}
|
|
397
|
+
# implementation graph
|
|
398
|
+
#impl_graph: MultiDiGraph = MultiDiGraph()
|
|
399
|
+
#impl_graph_process_start: Dict = {}
|
|
400
|
+
# authored graph
|
|
401
|
+
#graph: MultiDiGraph = MultiDiGraph()
|
|
402
|
+
#graph_process_start: Dict = {}
|
|
403
|
+
# list of context:
|
|
404
|
+
pages: Dict[str, TriccNodeActivity]= {}
|
|
405
|
+
start_pages: Dict[str, TriccNodeActivity] = {}
|
|
406
|
+
images: List[Dict[str,str]] = []
|
|
407
|
+
contexts : Set[triccName] = set()
|
|
408
|
+
# TODO manage trad properly
|
|
409
|
+
def get_keyword_trad(keyword):
|
|
410
|
+
return keyword
|
|
411
|
+
# dict of code_system_id: codesystem
|
|
412
|
+
code_systems: Dict[str, CodeSystem] = {}
|
|
413
|
+
# dict of valueset_id: valueset
|
|
414
|
+
value_sets: Dict[str, ValueSet] = {}
|
|
415
|
+
|
|
416
|
+
#class Config:
|
|
417
|
+
# Allow arbitrary types for validation
|
|
418
|
+
# arbitrary_types_allowed = True
|
tricc_oo/parsers/xml.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import lxml.etree as etree
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
type_name = "odk_type"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def read_drawio(file_content):
|
|
10
|
+
if file_content.startswith('<mxfile host'):
|
|
11
|
+
file_content = "<?xml version='1.0' encoding='utf-8'?>\n" + file_content
|
|
12
|
+
file_content = bytes(bytearray(file_content, encoding="utf-8"))
|
|
13
|
+
root = etree.fromstring(file_content)
|
|
14
|
+
# import xml.etree.cElementTree as ET
|
|
15
|
+
# with open(filepath) as f:
|
|
16
|
+
# add a fake root so etree can work
|
|
17
|
+
# it = itertools.chain('<root>', f, '</root>')
|
|
18
|
+
# etree = ET.fromstringlist(it)
|
|
19
|
+
# get all the pages
|
|
20
|
+
diagram_list = root.findall(".//diagram")
|
|
21
|
+
|
|
22
|
+
return diagram_list
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_container_media(diagram, container_id):
|
|
26
|
+
# get the edge
|
|
27
|
+
return diagram.find(
|
|
28
|
+
f".//object[@{type_name}='container_hint_media' and @id='{container_id}']"
|
|
29
|
+
)
|
|
30
|
+
# get the image node
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_tricc_type(diagram, node_type, tricc_type):
|
|
34
|
+
return diagram.find(f'.//{node_type}[@{type_name}="{str(tricc_type)}"]')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_tricc_type_list(diagram, node_type, tricc_type=None, parent_id=None):
|
|
38
|
+
if tricc_type:
|
|
39
|
+
tricc_type = str(tricc_type)
|
|
40
|
+
|
|
41
|
+
parent_suffix = f"[@parent='{parent_id}']" if parent_id is not None else ""
|
|
42
|
+
if isinstance(tricc_type, list):
|
|
43
|
+
result = []
|
|
44
|
+
for type_ in tricc_type:
|
|
45
|
+
result += get_tricc_type_list(diagram, node_type, type_, parent_id)
|
|
46
|
+
return list(set(result))
|
|
47
|
+
if isinstance(node_type, list):
|
|
48
|
+
result = []
|
|
49
|
+
for type_ in node_type:
|
|
50
|
+
result += get_tricc_type_list(diagram, type_, tricc_type, parent_id)
|
|
51
|
+
return list(set(result))
|
|
52
|
+
elif tricc_type is None:
|
|
53
|
+
child = list(diagram.findall(f".//{node_type}[@{type_name}]{parent_suffix}"))
|
|
54
|
+
if child:
|
|
55
|
+
return child
|
|
56
|
+
else:
|
|
57
|
+
return get_child_through_mxcell(diagram, type_name, node_type, parent_suffix, tricc_type)
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
child = list(
|
|
61
|
+
diagram.findall(
|
|
62
|
+
f'.//{node_type}[@{type_name}="{tricc_type}"]{parent_suffix}'
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
if child:
|
|
66
|
+
return child
|
|
67
|
+
else:
|
|
68
|
+
return get_child_through_mxcell(diagram, type_name, node_type, parent_suffix, tricc_type)
|
|
69
|
+
|
|
70
|
+
def get_child_through_mxcell(diagram, type_name, node_type, parent_suffix, tricc_type):
|
|
71
|
+
child = []
|
|
72
|
+
# try with mxCell
|
|
73
|
+
sub = list(diagram.findall(f".//mxCell{parent_suffix}"))
|
|
74
|
+
for s in sub:
|
|
75
|
+
obj = s.getparent()
|
|
76
|
+
if (
|
|
77
|
+
obj.tag == node_type
|
|
78
|
+
and type_name in obj.attrib
|
|
79
|
+
and (
|
|
80
|
+
not tricc_type
|
|
81
|
+
or tricc_type == obj.attrib.get(type_name, None)
|
|
82
|
+
)
|
|
83
|
+
):
|
|
84
|
+
child.append(obj)
|
|
85
|
+
return child
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# end def
|
|
89
|
+
|
|
90
|
+
def get_mxcell_parent_list(diagram, select_id, tricc_type=None, attrib=None):
|
|
91
|
+
# get the mxcels
|
|
92
|
+
if tricc_type is None:
|
|
93
|
+
if attrib is not None:
|
|
94
|
+
return diagram.findall(f".//mxCell[@parent='{select_id}']/..[@{attrib}]")
|
|
95
|
+
else:
|
|
96
|
+
return diagram.findall(f".//mxCell[@parent='{select_id}']")
|
|
97
|
+
elif isinstance(tricc_type, List):
|
|
98
|
+
result = []
|
|
99
|
+
for type in tricc_type:
|
|
100
|
+
result += get_mxcell_parent_list(diagram, select_id, type)
|
|
101
|
+
return result
|
|
102
|
+
else:
|
|
103
|
+
return diagram.findall(
|
|
104
|
+
f".//mxCell[@parent='{select_id}']/..[@{type_name}='{tricc_type}']"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_elm(diagram, id):
|
|
109
|
+
return diagram.find(f".//*[@id='{id}']")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_mxcell(diagram, id):
|
|
113
|
+
elm = diagram.find(f".//*[@id='{id}']")
|
|
114
|
+
if elm.tag == 'mxCell':
|
|
115
|
+
return elm
|
|
116
|
+
else:
|
|
117
|
+
return diagram.find(f".//*[@id='{id}']/mxCell")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_edges_list(diagram):
|
|
121
|
+
# return list(diagram.findall('.//mxCell[@edge][@source][@target]'))
|
|
122
|
+
# to ensure source and target one can use this xpath above but better trigger a pydantic error if source/target are missing
|
|
123
|
+
return list(
|
|
124
|
+
set(
|
|
125
|
+
diagram.findall(".//mxCell[@edge][@source]")
|
|
126
|
+
+ diagram.findall(".//mxCell[@edge][@target]")
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_select_option_image(diagram, select_option_id):
|
|
132
|
+
# get the edge
|
|
133
|
+
edge = diagram.find(f".//mxCell[@edge and @target='{select_option_id}']")
|
|
134
|
+
# get the image node
|
|
135
|
+
if edge is not None and edge.attrib.get("source") is not None:
|
|
136
|
+
return diagram.find(
|
|
137
|
+
f".//mxCell[@id='{edge.attrib.get('source')}' and not(@{type_name}) and not(@edge)]"
|
|
138
|
+
)
|
|
File without changes
|