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
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from curses.ascii import isalnum, isalpha, isdigit
|
|
5
|
+
|
|
6
|
+
from numpy import isnan
|
|
7
|
+
|
|
8
|
+
from tricc_oo.converters.utils import clean_name, remove_html
|
|
9
|
+
from tricc_oo.converters.cql_to_operation import transform_cql_to_operation
|
|
10
|
+
from tricc_oo.models.tricc import *
|
|
11
|
+
from tricc_oo.models.base import TriccNodeType, OPERATION_LIST
|
|
12
|
+
from tricc_oo.models.ocl import get_data_type
|
|
13
|
+
from tricc_oo.converters.drawio_type_map import TYPE_MAP
|
|
14
|
+
from tricc_oo.parsers.xml import (
|
|
15
|
+
get_edges_list,
|
|
16
|
+
get_mxcell,
|
|
17
|
+
get_elm,
|
|
18
|
+
get_mxcell_parent_list,
|
|
19
|
+
get_tricc_type,
|
|
20
|
+
get_tricc_type_list,
|
|
21
|
+
)
|
|
22
|
+
import hashlib
|
|
23
|
+
from tricc_oo.visitors.tricc import *
|
|
24
|
+
from tricc_oo.converters.datadictionnary import add_concept
|
|
25
|
+
|
|
26
|
+
TRICC_YES_LABEL = ["yes", "oui"]
|
|
27
|
+
TRICC_NO_LABEL = ["no", "non"]
|
|
28
|
+
TRICC_FOLLOW_LABEL = ["follow", "suivre", "continue"]
|
|
29
|
+
NO_LABEL = "NO_LABEL"
|
|
30
|
+
TRICC_LIST_NAME = "list_{0}"
|
|
31
|
+
import logging
|
|
32
|
+
DISPLAY_ATTRIBUTES = [
|
|
33
|
+
'label',
|
|
34
|
+
'hint',
|
|
35
|
+
'help'
|
|
36
|
+
]
|
|
37
|
+
logger = logging.getLogger("default")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_all_nodes(diagram, activity, nodes):
|
|
42
|
+
for tricc_type in TYPE_MAP:
|
|
43
|
+
if TYPE_MAP[tricc_type]["model"]:
|
|
44
|
+
list = get_tricc_type_list(diagram, TYPE_MAP[tricc_type]["objects"], tricc_type)
|
|
45
|
+
add_tricc_base_node(
|
|
46
|
+
diagram,
|
|
47
|
+
nodes,
|
|
48
|
+
TYPE_MAP[tricc_type]["model"],
|
|
49
|
+
list,
|
|
50
|
+
activity,
|
|
51
|
+
attributes=TYPE_MAP[tricc_type]["attributes"],
|
|
52
|
+
mandatory_attributes=TYPE_MAP[tricc_type]["mandatory_attributes"],
|
|
53
|
+
has_options=TYPE_MAP[tricc_type].get('has_options', None)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return nodes
|
|
57
|
+
|
|
58
|
+
def create_activity(diagram, media_path, project):
|
|
59
|
+
|
|
60
|
+
external_id = diagram.attrib.get("id")
|
|
61
|
+
id = get_id( external_id, diagram.attrib.get("id"))
|
|
62
|
+
root = create_root_node(diagram)
|
|
63
|
+
name = diagram.attrib.get("name")
|
|
64
|
+
form_id = diagram.attrib.get("name", None)
|
|
65
|
+
if root is not None:
|
|
66
|
+
activity = TriccNodeActivity(
|
|
67
|
+
root=root,
|
|
68
|
+
name=get_rand_name(f"a{id}"),
|
|
69
|
+
id=id,
|
|
70
|
+
external_id=external_id,
|
|
71
|
+
label=name,
|
|
72
|
+
form_id=form_id,
|
|
73
|
+
)
|
|
74
|
+
if root.relevance is not None:
|
|
75
|
+
activity.applicability=root.relevance
|
|
76
|
+
# activity definition is never instanciated
|
|
77
|
+
if isinstance(root, TriccNodeActivityStart):
|
|
78
|
+
activity.instance = 0
|
|
79
|
+
# add the group on the root node
|
|
80
|
+
root.group = activity
|
|
81
|
+
activity.group = activity
|
|
82
|
+
edges = get_edges(diagram)
|
|
83
|
+
if edges and len(edges) > 0:
|
|
84
|
+
activity.edges = edges
|
|
85
|
+
nodes = get_nodes(diagram, activity)
|
|
86
|
+
for n in nodes.values():
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
issubclass(n.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase))
|
|
90
|
+
and not isinstance(n, (TriccRhombusMixIn, TriccNodeRhombus, TriccNodeDisplayBridge))
|
|
91
|
+
and not n.name.startswith('label_')
|
|
92
|
+
):
|
|
93
|
+
system = n.name.split('.')[0] if '.' in n.name else 'tricc'
|
|
94
|
+
if isinstance(n, TriccNodeSelectOption) and isinstance(n.select, TriccNodeSelectNotAvailable):
|
|
95
|
+
add_concept(project.code_systems, system, n.select.name, n.label, {"datatype": 'Boolean'})
|
|
96
|
+
elif not isinstance(n, TriccNodeSelectNotAvailable):
|
|
97
|
+
add_concept(project.code_systems, system, n.name, n.label, {"datatype": get_data_type(n.tricc_type)})
|
|
98
|
+
if getattr(n,'save', None):
|
|
99
|
+
system = n.save.split('.')[0] if '.' in n.save else 'tricc'
|
|
100
|
+
add_concept(project.code_systems, system, n.save, n.label, {"datatype": get_data_type(n.tricc_type)})
|
|
101
|
+
|
|
102
|
+
groups = get_groups(diagram, nodes, activity)
|
|
103
|
+
if groups and len(groups) > 0:
|
|
104
|
+
activity.groups = groups
|
|
105
|
+
if nodes and len(nodes) > 0:
|
|
106
|
+
activity.nodes = nodes
|
|
107
|
+
|
|
108
|
+
images = process_edges(diagram, media_path, activity, nodes)
|
|
109
|
+
# link back the activity
|
|
110
|
+
activity.root.activity = activity
|
|
111
|
+
manage_dangling_calculate(activity)
|
|
112
|
+
|
|
113
|
+
if activity is not None:
|
|
114
|
+
if activity.root is not None:
|
|
115
|
+
project.pages[activity.id] = activity
|
|
116
|
+
if activity.root.tricc_type == TriccNodeType.start:
|
|
117
|
+
if "main" not in project.start_pages and (
|
|
118
|
+
activity.root.process == "main" or activity.root.process is None
|
|
119
|
+
):
|
|
120
|
+
project.start_pages["main"] = activity
|
|
121
|
+
activity.root.process = 'main'
|
|
122
|
+
elif activity.root.process is not None:
|
|
123
|
+
if activity.root.process not in project.start_pages:
|
|
124
|
+
project.start_pages[activity.root.process] = []
|
|
125
|
+
project.start_pages[activity.root.process].append(activity)
|
|
126
|
+
else:
|
|
127
|
+
logger.warning(
|
|
128
|
+
"Page {0} has a start node but there is already a start node in page {1}".format(
|
|
129
|
+
activity.label, start_page.label
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
if images:
|
|
133
|
+
project.images += images
|
|
134
|
+
for node in list(filter(lambda p_node: isinstance(p_node, TriccNodeSelectNotAvailable),list(activity.nodes.values()))):
|
|
135
|
+
prev_node = None
|
|
136
|
+
prev_edges = list(filter(lambda p_e: p_e.target == node.id,list(activity.edges)))
|
|
137
|
+
if len(prev_edges):
|
|
138
|
+
prev_node = [n for n in activity.nodes.values() if n.id in [p_e.source for p_e in prev_edges]]
|
|
139
|
+
if prev_node:
|
|
140
|
+
node.parent = prev_node[0]
|
|
141
|
+
if not node.parent :
|
|
142
|
+
logger.critical(f"unable to find the parent of the NotApplicable node {node.get_name()}")
|
|
143
|
+
exit(1)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
else:
|
|
148
|
+
return None, None
|
|
149
|
+
logger.warning("root not found for page {0}".format(name))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def manage_dangling_calculate(activity):
|
|
153
|
+
dangling = {}
|
|
154
|
+
for node in activity.nodes.values():
|
|
155
|
+
prev_nodes = [
|
|
156
|
+
activity.nodes[n.source]
|
|
157
|
+
for n in list(
|
|
158
|
+
filter(
|
|
159
|
+
lambda x: (x.target == node.id or x.target == node)
|
|
160
|
+
and (
|
|
161
|
+
x.source in activity.nodes
|
|
162
|
+
or x.source in activity.nodes.values()
|
|
163
|
+
),
|
|
164
|
+
activity.edges,
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
]
|
|
168
|
+
if len(prev_nodes) == 0 and issubclass(node.__class__, TriccNodeCalculateBase):
|
|
169
|
+
dangling[node.id] = node
|
|
170
|
+
if len(dangling) > 0:
|
|
171
|
+
activity.calculates += list(dangling.values())
|
|
172
|
+
# wait = get_activity_wait([activity.root], [activity.root], dangling.values(), edge_only=True)
|
|
173
|
+
# activity.nodes.update(dangling)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def process_edges(diagram, media_path, activity, nodes):
|
|
177
|
+
end_found = False
|
|
178
|
+
images = []
|
|
179
|
+
for edge in activity.edges:
|
|
180
|
+
# enrich nodes
|
|
181
|
+
if edge.target not in nodes:
|
|
182
|
+
activity.unused_edges.append(edge)
|
|
183
|
+
elif edge.source not in nodes and edge.target in nodes:
|
|
184
|
+
enriched, image = enrich_node(diagram, media_path, edge, nodes[edge.target], activity)
|
|
185
|
+
if enriched is None:
|
|
186
|
+
activity.unused_edges.append(edge)
|
|
187
|
+
if image is not None:
|
|
188
|
+
images.append({"file_path": enriched, "image_content": image})
|
|
189
|
+
|
|
190
|
+
elif isinstance(nodes[edge.target], (TriccNodeActivityEnd)) or (isinstance(nodes[edge.target], (TriccNodeEnd)) and isinstance(activity.root, TriccNodeMainStart )):
|
|
191
|
+
end_found = True
|
|
192
|
+
if (
|
|
193
|
+
edge.target in nodes
|
|
194
|
+
and issubclass(nodes[edge.target].__class__, TriccRhombusMixIn)
|
|
195
|
+
and edge.source != nodes[edge.target].path.id
|
|
196
|
+
):
|
|
197
|
+
edge.target = nodes[edge.target].path.id
|
|
198
|
+
# modify edge for selectyesNo
|
|
199
|
+
if edge.source in nodes and isinstance(
|
|
200
|
+
nodes[edge.source], TriccNodeSelectYesNo
|
|
201
|
+
):
|
|
202
|
+
process_yesno_edge(edge, nodes)
|
|
203
|
+
|
|
204
|
+
# create calculate based on edges label
|
|
205
|
+
elif edge.value is not None:
|
|
206
|
+
label = edge.value.strip()
|
|
207
|
+
processed = False
|
|
208
|
+
calc = None
|
|
209
|
+
if (
|
|
210
|
+
label.lower() in TRICC_FOLLOW_LABEL
|
|
211
|
+
):
|
|
212
|
+
if isinstance(nodes[edge.source], TriccNodeRhombus):
|
|
213
|
+
edge.source = nodes[edge.source].path.id
|
|
214
|
+
processed = True
|
|
215
|
+
elif label.lower() in (TRICC_YES_LABEL) or label == "":
|
|
216
|
+
# do nothinbg for yes
|
|
217
|
+
processed = True
|
|
218
|
+
elif re.search(r"^\-?[0-9]+([.,][0-9]+)?$", edge.value.strip()):
|
|
219
|
+
calc = process_factor_edge(edge, nodes)
|
|
220
|
+
elif label.lower() in TRICC_NO_LABEL:
|
|
221
|
+
calc = process_exclusive_edge(edge, nodes)
|
|
222
|
+
elif any(reserved in label.lower() for reserved in ([str(o) for o in list(TriccOperator)] + list(OPERATION_LIST.keys()) + ['$this'])):
|
|
223
|
+
# manage comment
|
|
224
|
+
calc = process_condition_edge(edge, nodes)
|
|
225
|
+
else:
|
|
226
|
+
logger.warning(f"unsupported edge label {label} in {diagram.attrib.get('name', diagram.attrib['id'])}")
|
|
227
|
+
processed = True
|
|
228
|
+
if calc is not None:
|
|
229
|
+
processed = True
|
|
230
|
+
nodes[calc.id] = calc
|
|
231
|
+
# add edge between calc and
|
|
232
|
+
set_prev_next_node(calc, nodes[edge.target], edge_only=True)
|
|
233
|
+
edge.target = calc.id
|
|
234
|
+
if not processed:
|
|
235
|
+
logger.warning(
|
|
236
|
+
"Edge between {0} and {1} with label '{2}' could not be interpreted: {3}".format(
|
|
237
|
+
nodes[edge.source].get_name(),
|
|
238
|
+
nodes[edge.target].get_name(),
|
|
239
|
+
edge.value.strip(),
|
|
240
|
+
"not management found",
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
if not end_found:
|
|
244
|
+
fake_end = TriccNodeActivityEnd(id=generate_id(f"e{activity.name}"), activity=activity, group=activity)
|
|
245
|
+
last_nodes = [
|
|
246
|
+
n for n in list(activity.nodes.values())
|
|
247
|
+
if (
|
|
248
|
+
issubclass(
|
|
249
|
+
n.__class__,
|
|
250
|
+
(
|
|
251
|
+
TriccNodeInputModel,
|
|
252
|
+
TriccNodeText,
|
|
253
|
+
TriccNodeNote,
|
|
254
|
+
)
|
|
255
|
+
) and (
|
|
256
|
+
not any([n.id == e.source for e in activity.edges])
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
]
|
|
260
|
+
if last_nodes:
|
|
261
|
+
for n in last_nodes:
|
|
262
|
+
set_prev_next_node(n, fake_end, edge_only=True)
|
|
263
|
+
activity.nodes[fake_end.id] = fake_end
|
|
264
|
+
# take all last nodes
|
|
265
|
+
else:
|
|
266
|
+
logger.warning(f"Activity {activity.label} have no end, calculated might be included in the end definition")
|
|
267
|
+
last_nodes = [
|
|
268
|
+
n for n in list(activity.nodes.values())
|
|
269
|
+
if (
|
|
270
|
+
not any([n.id == e.source for e in activity.edges])
|
|
271
|
+
)
|
|
272
|
+
]
|
|
273
|
+
if last_nodes:
|
|
274
|
+
for n in last_nodes:
|
|
275
|
+
set_prev_next_node(n, fake_end, edge_only=True)
|
|
276
|
+
activity.nodes[fake_end.id] = fake_end
|
|
277
|
+
else:
|
|
278
|
+
logger.critical(f"cannot guess end for {activity.get_name()}")
|
|
279
|
+
exit(1)
|
|
280
|
+
|
|
281
|
+
return images
|
|
282
|
+
|
|
283
|
+
def get_id(elm_id, activity_id):
|
|
284
|
+
return str(elm_id) if len(elm_id)>8 else str(activity_id) + str(elm_id)
|
|
285
|
+
|
|
286
|
+
def _get_name(name, id, act_id):
|
|
287
|
+
if (
|
|
288
|
+
name is not None
|
|
289
|
+
and (name.endswith(("_", ".")))
|
|
290
|
+
):
|
|
291
|
+
return name + get_id(id, act_id)
|
|
292
|
+
return name
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def get_nodes(diagram, activity):
|
|
296
|
+
nodes = {activity.root.id: activity.root}
|
|
297
|
+
get_all_nodes(diagram, activity, nodes)
|
|
298
|
+
new_nodes = {}
|
|
299
|
+
node_to_remove = []
|
|
300
|
+
activity_end_node = None
|
|
301
|
+
end_node = None
|
|
302
|
+
for node in nodes.values():
|
|
303
|
+
# clean name
|
|
304
|
+
if (
|
|
305
|
+
hasattr(node, "name")
|
|
306
|
+
):
|
|
307
|
+
node.name = _get_name(node.name, node.id, activity.id)
|
|
308
|
+
if issubclass(node.__class__, TriccRhombusMixIn) and node.path is None:
|
|
309
|
+
# generate rhombuse path
|
|
310
|
+
calc = inject_bridge_path(node, {**nodes, **new_nodes})
|
|
311
|
+
if calc:
|
|
312
|
+
node.path = calc
|
|
313
|
+
new_nodes[calc.id] = calc
|
|
314
|
+
else:
|
|
315
|
+
node.path = activity.root
|
|
316
|
+
# add the edge between trhombus and its path
|
|
317
|
+
elif isinstance(node, TriccNodeGoTo):
|
|
318
|
+
# find if the node has next nodes, if yes, add a bridge + Rhoimbus
|
|
319
|
+
path = inject_bridge_path(node, {**nodes, **new_nodes})
|
|
320
|
+
if path:
|
|
321
|
+
new_nodes[path.id] = path
|
|
322
|
+
else:
|
|
323
|
+
logger.critical(f"goto without in edges {node.get_name()}")
|
|
324
|
+
# action after the activity
|
|
325
|
+
next_nodes_id = [e.target for e in activity.edges if e.source == node.id]
|
|
326
|
+
if len(next_nodes_id) > 0:
|
|
327
|
+
|
|
328
|
+
calc = get_activity_wait(
|
|
329
|
+
path, [node], next_nodes_id, node, edge_only=True
|
|
330
|
+
)
|
|
331
|
+
new_nodes[calc.id] = calc
|
|
332
|
+
for goto_next_node in next_nodes_id:
|
|
333
|
+
remove_prev_next(node, goto_next_node, activity)
|
|
334
|
+
elif isinstance(node, TriccNodeActivityEnd):
|
|
335
|
+
if not activity_end_node:
|
|
336
|
+
activity_end_node = node
|
|
337
|
+
else:
|
|
338
|
+
merge_node(node, activity_end_node)
|
|
339
|
+
node_to_remove.append(node.id)
|
|
340
|
+
# add activity relevance to calculate
|
|
341
|
+
elif (
|
|
342
|
+
issubclass(node.__class__, TriccNodeDisplayCalculateBase)
|
|
343
|
+
and not getattr(node, 'relevance', None)
|
|
344
|
+
and node.activity.applicability
|
|
345
|
+
):
|
|
346
|
+
node.applicability = node.activity.applicability
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
nodes.update(new_nodes)
|
|
350
|
+
|
|
351
|
+
for key in node_to_remove:
|
|
352
|
+
del nodes[key]
|
|
353
|
+
edge_list = activity.edges.copy()
|
|
354
|
+
for edge in edge_list:
|
|
355
|
+
if edge.source in node_to_remove or edge.target in node_to_remove:
|
|
356
|
+
activity.edges.remove(edge)
|
|
357
|
+
|
|
358
|
+
return nodes
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def create_root_node(diagram):
|
|
362
|
+
node = None
|
|
363
|
+
elm = get_tricc_type(diagram, "object", TriccNodeType.start)
|
|
364
|
+
if elm is None:
|
|
365
|
+
elm = get_tricc_type(diagram, "UserObject", TriccNodeType.start)
|
|
366
|
+
if elm is not None:
|
|
367
|
+
external_id = elm.attrib.get("id")
|
|
368
|
+
id = get_id( external_id, diagram.attrib.get("id"))
|
|
369
|
+
node = TriccNodeMainStart(
|
|
370
|
+
id=id,
|
|
371
|
+
external_id=external_id,
|
|
372
|
+
parent=elm.attrib.get("parent"),
|
|
373
|
+
name="ms" + id,
|
|
374
|
+
label=elm.attrib.get("label"),
|
|
375
|
+
form_id=elm.attrib.get("form_id"),
|
|
376
|
+
relevance=elm.attrib.get("relevance"),
|
|
377
|
+
process=elm.attrib.get('process', 'main'),
|
|
378
|
+
)
|
|
379
|
+
else:
|
|
380
|
+
elm = get_tricc_type(diagram, "object", TriccNodeType.activity_start)
|
|
381
|
+
if elm is None:
|
|
382
|
+
elm = get_tricc_type(diagram, "UserObject", TriccNodeType.activity_start)
|
|
383
|
+
if elm is not None:
|
|
384
|
+
external_id = elm.attrib.get("id")
|
|
385
|
+
id = get_id( external_id, diagram.attrib.get("id"))
|
|
386
|
+
node = TriccNodeActivityStart(
|
|
387
|
+
id=id,
|
|
388
|
+
external_id=external_id,
|
|
389
|
+
#parent=elm.attrib.get("parent"),
|
|
390
|
+
name="ma" + id,
|
|
391
|
+
label=diagram.attrib.get("name"),
|
|
392
|
+
relevance=elm.attrib.get("relevance"),
|
|
393
|
+
instance=int(
|
|
394
|
+
elm.attrib.get("instance")
|
|
395
|
+
if elm.attrib.get("instance") is not None
|
|
396
|
+
else 1
|
|
397
|
+
),
|
|
398
|
+
)
|
|
399
|
+
load_expressions(node)
|
|
400
|
+
return node
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# converter XML item to object
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def set_additional_attributes(attribute_names, elm, node):
|
|
407
|
+
if not isinstance(attribute_names, list):
|
|
408
|
+
attribute_names = [attribute_names]
|
|
409
|
+
for attributename in attribute_names:
|
|
410
|
+
attribute = elm.attrib.get(attributename)
|
|
411
|
+
if attribute is not None:
|
|
412
|
+
# input expression can add a condition to either relevance (display) or calculate expression
|
|
413
|
+
if attributename == "expression_inputs":
|
|
414
|
+
attribute = [attribute]
|
|
415
|
+
elif attributename == "instance":
|
|
416
|
+
attribute = int(attribute)
|
|
417
|
+
else:
|
|
418
|
+
attribute
|
|
419
|
+
setattr(node, attributename, attribute)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def get_select_options(diagram, select_node, nodes):
|
|
425
|
+
options = {}
|
|
426
|
+
i = 0
|
|
427
|
+
list = get_mxcell_parent_list(diagram, select_node.external_id, TriccNodeType.select_option)
|
|
428
|
+
options_name_list = []
|
|
429
|
+
for elm in list:
|
|
430
|
+
name = elm.attrib.get("name")
|
|
431
|
+
if name in options_name_list and not name.endswith("_"):
|
|
432
|
+
logger.critical(
|
|
433
|
+
"Select question {0} have twice the option name {1}".format(
|
|
434
|
+
select_node.get_name(), name
|
|
435
|
+
)
|
|
436
|
+
)
|
|
437
|
+
else:
|
|
438
|
+
options_name_list.append(name)
|
|
439
|
+
|
|
440
|
+
external_id = elm.attrib.get("id")
|
|
441
|
+
id = get_id(external_id, diagram.attrib.get('id'))
|
|
442
|
+
option = TriccNodeSelectOption(
|
|
443
|
+
id=id,
|
|
444
|
+
label=elm.attrib.get("label"),
|
|
445
|
+
name=name,
|
|
446
|
+
select=select_node,
|
|
447
|
+
list_name=select_node.list_name,
|
|
448
|
+
activity=select_node.activity,
|
|
449
|
+
group=select_node.group,
|
|
450
|
+
)
|
|
451
|
+
set_additional_attributes(["save", "relevance"], elm, option)
|
|
452
|
+
load_expressions(option)
|
|
453
|
+
options[i] = option
|
|
454
|
+
nodes[id] = option
|
|
455
|
+
i += 1
|
|
456
|
+
if len(list) == 0:
|
|
457
|
+
logger.critical("select {} does not have any option".format(select_node.label))
|
|
458
|
+
else:
|
|
459
|
+
return options
|
|
460
|
+
|
|
461
|
+
##TBR START
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def get_max_version(dict):
|
|
465
|
+
max_version = None
|
|
466
|
+
for id, sim_node in dict.items():
|
|
467
|
+
if max_version is None or max_version.version < sim_node.version:
|
|
468
|
+
max_version = sim_node
|
|
469
|
+
return max_version
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def update_calc_version(calculates, name):
|
|
473
|
+
if name in calculates and len(calculates[name]) > 1:
|
|
474
|
+
ordered_list = sorted(list(calculates[name].values()), key=lambda x: x.path_len)
|
|
475
|
+
i = 1
|
|
476
|
+
len_max = len(calculates[name])
|
|
477
|
+
for elm in ordered_list:
|
|
478
|
+
elm.version = i
|
|
479
|
+
elm.last = (i == len_max)
|
|
480
|
+
i += 1
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def get_max_named_version(calculates, name):
|
|
484
|
+
max = 0
|
|
485
|
+
if name in calculates:
|
|
486
|
+
for node in calculates[name].values():
|
|
487
|
+
if node.version > max:
|
|
488
|
+
max = node.version
|
|
489
|
+
return max
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def inject_bridge_path(node, nodes):
|
|
494
|
+
calc_name = "p" + node.id
|
|
495
|
+
calc_id = generate_id(calc_name)
|
|
496
|
+
|
|
497
|
+
data = {
|
|
498
|
+
"id": calc_id,
|
|
499
|
+
"group": node.group,
|
|
500
|
+
"activity": node.activity,
|
|
501
|
+
"label": "path: " + node.get_name(),
|
|
502
|
+
"name": calc_name,
|
|
503
|
+
"path_len": node.path_len,
|
|
504
|
+
}
|
|
505
|
+
prev_nodes = [
|
|
506
|
+
nodes[n.source]
|
|
507
|
+
for n in list(
|
|
508
|
+
filter(
|
|
509
|
+
lambda x: (x.target == node.id or x.target == node)
|
|
510
|
+
and x.source in nodes,
|
|
511
|
+
node.activity.edges,
|
|
512
|
+
)
|
|
513
|
+
)
|
|
514
|
+
]
|
|
515
|
+
if (
|
|
516
|
+
sum(
|
|
517
|
+
[
|
|
518
|
+
(
|
|
519
|
+
0
|
|
520
|
+
if issubclass(
|
|
521
|
+
n.__class__, (TriccNodeDisplayCalculateBase, TriccNodeRhombus)
|
|
522
|
+
)
|
|
523
|
+
else 1
|
|
524
|
+
)
|
|
525
|
+
for n in prev_nodes
|
|
526
|
+
]
|
|
527
|
+
)
|
|
528
|
+
> 0
|
|
529
|
+
): # and len(node.prev_nodes)>1:
|
|
530
|
+
calc = TriccNodeDisplayBridge(**data)
|
|
531
|
+
else:
|
|
532
|
+
calc = TriccNodeBridge(**data)
|
|
533
|
+
|
|
534
|
+
for e in node.activity.edges:
|
|
535
|
+
if e.target == node.id:
|
|
536
|
+
if e.source in node.activity.nodes and len(node.activity.nodes[e.source].next_nodes):
|
|
537
|
+
set_prev_next_node(node.activity[e.source], node, edge_only=False, replaced_node=node)
|
|
538
|
+
else:
|
|
539
|
+
e.target = calc.id
|
|
540
|
+
|
|
541
|
+
# add edge between bridge and node
|
|
542
|
+
set_prev_next_node(calc, node, edge_only=True)
|
|
543
|
+
|
|
544
|
+
node.path_len += 1
|
|
545
|
+
return calc
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def enrich_node(diagram, media_path, edge, node, activity, help_before=False):
|
|
549
|
+
if edge.target == node.id:
|
|
550
|
+
# get node and process type
|
|
551
|
+
type, message = get_message(diagram, edge.source_external_id)
|
|
552
|
+
if type is not None:
|
|
553
|
+
if type == 'help':
|
|
554
|
+
help = TriccNodeMoreInfo(
|
|
555
|
+
id=generate_id(),
|
|
556
|
+
name=f"{node.name}.more_info",
|
|
557
|
+
label=message,
|
|
558
|
+
parent=node,
|
|
559
|
+
required=None,
|
|
560
|
+
)
|
|
561
|
+
#node.help = message
|
|
562
|
+
if help_before:
|
|
563
|
+
inject_node_before(help, node, activity)
|
|
564
|
+
else:
|
|
565
|
+
set_prev_next_node(node, help, edge_only=True, activity=activity)
|
|
566
|
+
activity.nodes[help.id] = help
|
|
567
|
+
return help, None
|
|
568
|
+
|
|
569
|
+
if type in (TriccNodeType.start, TriccNodeType.activity_start):
|
|
570
|
+
return True
|
|
571
|
+
elif hasattr(node, type):
|
|
572
|
+
if message is not None:
|
|
573
|
+
setattr(node, type, message)
|
|
574
|
+
return True, None
|
|
575
|
+
else:
|
|
576
|
+
logger.warning(
|
|
577
|
+
"A attribute box of type {0} and value {1} is attached to an object not compatible {2}".format(
|
|
578
|
+
type, message, node.get_name()
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
return False, None
|
|
582
|
+
else:
|
|
583
|
+
image, payload = get_image(diagram, media_path, edge.source_external_id)
|
|
584
|
+
if image is not None:
|
|
585
|
+
if hasattr(node, "image"):
|
|
586
|
+
node.image = image
|
|
587
|
+
return image, payload
|
|
588
|
+
else:
|
|
589
|
+
logger.warning("image not supported for {} ".format(node.get_name()))
|
|
590
|
+
return None, None
|
|
591
|
+
else:
|
|
592
|
+
logger.warning(f"edge from an unsuported node {edge.source_external_id}")
|
|
593
|
+
|
|
594
|
+
return None, None
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
get_style_dict = lambda style: dict(item.split('=', 1) for item in style.split(';') if '=' in item)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def severity_from_color(color):
|
|
601
|
+
if color == '#fff2cc':
|
|
602
|
+
return 'moderate'
|
|
603
|
+
elif color == '#f8cecc':
|
|
604
|
+
return 'severe'
|
|
605
|
+
else:
|
|
606
|
+
return 'light'
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def add_tricc_base_node(
|
|
611
|
+
diagram, nodes, type, list, group, attributes=[], mandatory_attributes=[], has_options=None
|
|
612
|
+
):
|
|
613
|
+
for elm in list:
|
|
614
|
+
external_id = elm.attrib.get("id")
|
|
615
|
+
id = get_id( external_id, diagram.attrib.get("id"))
|
|
616
|
+
parent = elm.attrib.get("parent")
|
|
617
|
+
node = type(
|
|
618
|
+
external_id=external_id,
|
|
619
|
+
id=id,
|
|
620
|
+
#parent=parent,
|
|
621
|
+
group=group,
|
|
622
|
+
activity=group,
|
|
623
|
+
**set_mandatory_attribute(elm, mandatory_attributes, diagram),
|
|
624
|
+
)
|
|
625
|
+
if has_options:
|
|
626
|
+
node.options = get_select_options(diagram, node, nodes)
|
|
627
|
+
for o in node.options:
|
|
628
|
+
nodes[node.options[o].id] = node.options[o]
|
|
629
|
+
elif type == TriccNodeSelectNotAvailable:
|
|
630
|
+
node.options = get_select_not_available_options(node, group, node.label)
|
|
631
|
+
node.label = NO_LABEL
|
|
632
|
+
nodes[node.options[0].id] = node.options[0]
|
|
633
|
+
elif type == TriccNodeSelectYesNo:
|
|
634
|
+
node.list_name = "yes_no"
|
|
635
|
+
node.options = get_select_yes_no_options(node, group)
|
|
636
|
+
nodes[node.options[0].id] = node.options[0]
|
|
637
|
+
nodes[node.options[1].id] = node.options[1]
|
|
638
|
+
elif type == TriccNodeProposedDiagnosis and getattr(node, 'severity', '') == None:
|
|
639
|
+
mxcell = get_mxcell(diagram, external_id)
|
|
640
|
+
styles = get_style_dict(mxcell.attrib.get('style',''))
|
|
641
|
+
if 'fillColor' in styles and styles['fillColor'] != 'none':
|
|
642
|
+
node.severity = severity_from_color(styles['fillColor'])
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
set_additional_attributes(attributes, elm, node)
|
|
647
|
+
load_expressions(node)
|
|
648
|
+
nodes[id] = node
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def load_expressions(node):
|
|
652
|
+
if getattr(node, 'expression', None):
|
|
653
|
+
node.expression = parse_expression('', node.expression)
|
|
654
|
+
if getattr(node, 'relevance', None):
|
|
655
|
+
node.relevance = parse_expression('', node.relevance)
|
|
656
|
+
if getattr(node, 'default', None):
|
|
657
|
+
node.default = parse_expression('', node.default)
|
|
658
|
+
if getattr(node, 'reference', None):
|
|
659
|
+
if isinstance(node, TriccNodeRhombus):
|
|
660
|
+
node.label = remove_html(node.label)
|
|
661
|
+
node.expression_reference = parse_expression(node.label, node.reference)
|
|
662
|
+
else:
|
|
663
|
+
node.expression_reference = parse_expression('', node.reference)
|
|
664
|
+
|
|
665
|
+
node.reference = node.expression_reference.get_references()
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def parse_expression(label=None, expression=None):
|
|
671
|
+
if expression:
|
|
672
|
+
ref_pattern = r'(\$\{[^\}]+\})'
|
|
673
|
+
# only if simple ref
|
|
674
|
+
if not re.search(ref_pattern, expression):
|
|
675
|
+
operation = transform_cql_to_operation(expression, label)
|
|
676
|
+
if isinstance(operation, TriccReference):
|
|
677
|
+
if label:
|
|
678
|
+
if label[0] == '[' and label[-1] == ']':
|
|
679
|
+
operation = TriccOperation(
|
|
680
|
+
operator=TriccOperator.SELECTED,
|
|
681
|
+
reference=[
|
|
682
|
+
operation,
|
|
683
|
+
TriccReference(operation.value + label)
|
|
684
|
+
]
|
|
685
|
+
)
|
|
686
|
+
else:
|
|
687
|
+
for operator in OPERATION_LIST:
|
|
688
|
+
if operator in label:
|
|
689
|
+
if operator == '==':
|
|
690
|
+
operator = '='
|
|
691
|
+
terms = label.split(operator)
|
|
692
|
+
operation = transform_cql_to_operation(
|
|
693
|
+
f"{expression} {operator} {terms[1].replace('?', '').strip()}",
|
|
694
|
+
label
|
|
695
|
+
)
|
|
696
|
+
break
|
|
697
|
+
# implied is true
|
|
698
|
+
if isinstance(operation, TriccReference):
|
|
699
|
+
operation = TriccOperation(
|
|
700
|
+
operator=TriccOperator.ISTRUE,
|
|
701
|
+
reference=[
|
|
702
|
+
operation,
|
|
703
|
+
]
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
else:
|
|
707
|
+
pass
|
|
708
|
+
|
|
709
|
+
else:
|
|
710
|
+
operation = transform_cql_to_operation(
|
|
711
|
+
expression.replace('${', '"').replace('}', '"'),
|
|
712
|
+
label
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
if operation is None:
|
|
716
|
+
logger.warning(f"unable to parse: {expression} ")
|
|
717
|
+
return expression
|
|
718
|
+
return operation
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def set_mandatory_attribute(elm, mandatory_attributes, diagram=None):
|
|
722
|
+
param = {}
|
|
723
|
+
diagram_id = diagram.attrib.get('id')
|
|
724
|
+
for attributes in mandatory_attributes:
|
|
725
|
+
if attributes == 'name':
|
|
726
|
+
name = elm.attrib.get("name")
|
|
727
|
+
id = elm.attrib.get("id")
|
|
728
|
+
attribute_value = _get_name(name, id, diagram_id)
|
|
729
|
+
elif attributes == 'list_name':
|
|
730
|
+
name = elm.attrib.get("name")
|
|
731
|
+
id = elm.attrib.get("id")
|
|
732
|
+
attribute_value = TRICC_LIST_NAME.format(clean_str(_get_name(name, id, diagram_id), replace_dots= True))
|
|
733
|
+
else:
|
|
734
|
+
attribute_value = elm.attrib.get(attributes)
|
|
735
|
+
if attribute_value is None:
|
|
736
|
+
if elm.attrib.get("label") is not None:
|
|
737
|
+
display_name = elm.attrib.get("label")
|
|
738
|
+
elif elm.attrib.get("name") is not None:
|
|
739
|
+
display_name = elm.attrib.get("name")
|
|
740
|
+
else:
|
|
741
|
+
display_name = elm.attrib.get("id")
|
|
742
|
+
|
|
743
|
+
if attributes == "source":
|
|
744
|
+
if elm.attrib.get("target") is not None:
|
|
745
|
+
logger.critical(
|
|
746
|
+
"the attibute target is {}".format(elm.attrib.get("target"))
|
|
747
|
+
)
|
|
748
|
+
elif attributes == "target":
|
|
749
|
+
if elm.attrib.get("source") is not None:
|
|
750
|
+
logger.critical(
|
|
751
|
+
"the attibute target is {}".format(elm.attrib.get("source"))
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
logger.critical(
|
|
755
|
+
"the attibute {} is mandatory but not found in {} within group {}".format(
|
|
756
|
+
attributes, display_name, diagram.attrib.get('name') if diagram is not None else ""
|
|
757
|
+
)
|
|
758
|
+
)
|
|
759
|
+
exit(1)
|
|
760
|
+
if attributes == "link":
|
|
761
|
+
param[attributes] = clean_link(attribute_value)
|
|
762
|
+
|
|
763
|
+
elif attribute_value is not None:
|
|
764
|
+
if attributes in DISPLAY_ATTRIBUTES:
|
|
765
|
+
param[attributes] = remove_html(attribute_value.strip())
|
|
766
|
+
else:
|
|
767
|
+
param[attributes] = attribute_value.strip() if isinstance(attribute_value, str) else attribute_value
|
|
768
|
+
return param
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def clean_link(link):
|
|
772
|
+
# link have the format "data:page/id,DiagramID"
|
|
773
|
+
link_parts = link.split(",")
|
|
774
|
+
if link_parts[0] == "data:page/id" and len(link_parts) == 2:
|
|
775
|
+
return link_parts[1]
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def get_groups(diagram, nodes, parent_group):
|
|
779
|
+
groups = {}
|
|
780
|
+
list = get_tricc_type_list(diagram, "object", TriccNodeType.page)
|
|
781
|
+
for elm in list:
|
|
782
|
+
add_group(elm, diagram, nodes, groups, parent_group)
|
|
783
|
+
return groups
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
def add_group(elm, diagram, nodes, groups, parent_group):
|
|
787
|
+
external_id = elm.attrib.get("id")
|
|
788
|
+
id = get_id(external_id, diagram.attrib.get("id"))
|
|
789
|
+
if id not in groups:
|
|
790
|
+
group = TriccGroup(
|
|
791
|
+
name=elm.attrib.get("name"),
|
|
792
|
+
label=elm.attrib.get("label"),
|
|
793
|
+
id=id,
|
|
794
|
+
external_id=external_id,
|
|
795
|
+
group=parent_group,
|
|
796
|
+
)
|
|
797
|
+
# get elememt witn parent = id and tricc_type defiend
|
|
798
|
+
list_child = get_tricc_type_list(
|
|
799
|
+
diagram, ["object", "UserObject"], tricc_type=None, parent_id=id
|
|
800
|
+
)
|
|
801
|
+
add_group_to_child(group, diagram, list_child, nodes, groups, parent_group)
|
|
802
|
+
if group is not None:
|
|
803
|
+
groups[group.id] = group
|
|
804
|
+
return group
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def add_group_to_child(group, diagram, list_child, nodes, groups, parent_group):
|
|
808
|
+
for child_elm in list_child:
|
|
809
|
+
if child_elm.attrib.get("tricc_type") == TriccNodeType.container_hint_media:
|
|
810
|
+
list_sub_child = get_tricc_type_list(
|
|
811
|
+
diagram,
|
|
812
|
+
["object", "UserObject"],
|
|
813
|
+
tricc_type=None,
|
|
814
|
+
parent_id=child_elm.attrib.get("id"),
|
|
815
|
+
)
|
|
816
|
+
add_group_to_child(
|
|
817
|
+
group, diagram, list_sub_child, nodes, groups, parent_group
|
|
818
|
+
)
|
|
819
|
+
elif child_elm.attrib.get("tricc_type") == TriccNodeType.page:
|
|
820
|
+
child_group_id = child_elm.attrib.get("id")
|
|
821
|
+
if not child_group_id in groups:
|
|
822
|
+
child_group = add_group(child_elm, diagram, nodes, groups, group)
|
|
823
|
+
else:
|
|
824
|
+
child_group = groups[child_group_id]
|
|
825
|
+
child_group.group = group
|
|
826
|
+
else:
|
|
827
|
+
child_id = child_elm.attrib.get("id")
|
|
828
|
+
if child_id is not None and child_id in nodes:
|
|
829
|
+
nodes[child_id].group = group
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
def get_image(diagram, path, id):
|
|
833
|
+
elm = get_mxcell(diagram, id)
|
|
834
|
+
if elm is not None:
|
|
835
|
+
style = elm.attrib.get("style")
|
|
836
|
+
file_name, payload = add_image_from_style(style, path)
|
|
837
|
+
if file_name is not None:
|
|
838
|
+
return file_name, payload
|
|
839
|
+
return None, None
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def add_image_from_style(style, path):
|
|
843
|
+
image_attrib = None
|
|
844
|
+
if style is not None and "image=data:image/" in style:
|
|
845
|
+
image_attrib = style.split("image=data:image/")
|
|
846
|
+
if image_attrib is not None and len(image_attrib)== 2:
|
|
847
|
+
image_parts = image_attrib[1].split(",")
|
|
848
|
+
if len(image_parts) == 2:
|
|
849
|
+
payload = image_parts[1][:-1]
|
|
850
|
+
image_name = hashlib.md5(payload.encode('utf-8')).hexdigest()
|
|
851
|
+
path = os.path.join(path, "images")
|
|
852
|
+
file_name = os.path.join(path, image_name + "." + image_parts[0])
|
|
853
|
+
if not (
|
|
854
|
+
os.path.isdir(path)
|
|
855
|
+
): # check if it exists, because if it does, error will be raised
|
|
856
|
+
# (later change to make folder complaint to CHT)
|
|
857
|
+
os.makedirs(path, exist_ok=True)
|
|
858
|
+
with open(file_name, "wb") as fh:
|
|
859
|
+
fh.write(base64.decodebytes(payload.encode("ascii")))
|
|
860
|
+
image_path = os.path.basename(file_name)
|
|
861
|
+
return image_path, payload
|
|
862
|
+
return None, None
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
def get_contained_main_node(diagram, id):
|
|
866
|
+
list = get_mxcell_parent_list(diagram, id, media_nodes)
|
|
867
|
+
if isinstance(list, List) and len(list) > 0:
|
|
868
|
+
# use only the first one
|
|
869
|
+
return list[0]
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
def get_message(diagram, id):
|
|
873
|
+
elm = get_elm(diagram, id)
|
|
874
|
+
if elm is not None:
|
|
875
|
+
type = elm.attrib.get("odk_type")
|
|
876
|
+
if type is not None:
|
|
877
|
+
if type.endswith("-message"):
|
|
878
|
+
type = type[:-8]
|
|
879
|
+
return type, elm.attrib.get("label")
|
|
880
|
+
# use only the first one
|
|
881
|
+
return None, None
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def get_edges(diagram):
|
|
885
|
+
edges = []
|
|
886
|
+
list = get_edges_list(diagram)
|
|
887
|
+
for elm in list:
|
|
888
|
+
external_id = elm.attrib.get("id")
|
|
889
|
+
id = get_id(external_id, diagram.attrib.get("id"))
|
|
890
|
+
edge = TriccEdge(
|
|
891
|
+
id=id,
|
|
892
|
+
**set_mandatory_attribute(
|
|
893
|
+
elm, ["source", "parent", "target"], diagram
|
|
894
|
+
),
|
|
895
|
+
)
|
|
896
|
+
edge.source_external_id = edge.source
|
|
897
|
+
edge.target_external_id = edge.target
|
|
898
|
+
edge.source = get_id(edge.source, diagram.attrib.get("id"))
|
|
899
|
+
edge.target = get_id(edge.target, diagram.attrib.get("id"))
|
|
900
|
+
set_additional_attributes(["value"], elm, edge)
|
|
901
|
+
if edge.value is not None:
|
|
902
|
+
edge.value = remove_html(edge.value)
|
|
903
|
+
edges.append(edge)
|
|
904
|
+
return edges
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
## Process edges
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def process_factor_edge(edge, nodes):
|
|
911
|
+
factor = edge.value.strip()
|
|
912
|
+
if factor != 1:
|
|
913
|
+
return TriccNodeCalculate(
|
|
914
|
+
id=edge.id,
|
|
915
|
+
expression_reference=TriccOperation(TriccOperator.MULTIPLIED, [nodes[edge.source],TriccStatic(factor)]),
|
|
916
|
+
reference=[nodes[edge.source]],
|
|
917
|
+
activity=nodes[edge.source].activity,
|
|
918
|
+
group=nodes[edge.source].group,
|
|
919
|
+
label="factor {}".format(factor),
|
|
920
|
+
)
|
|
921
|
+
return None
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def process_condition_edge(edge, nodes):
|
|
925
|
+
label = edge.value.strip()
|
|
926
|
+
node = nodes[edge.source]
|
|
927
|
+
node_ref = f'"{nodes[edge.source].name}"'
|
|
928
|
+
if '$this' in label:
|
|
929
|
+
operation = parse_expression('', expression=label.replace('$this', node_ref ))
|
|
930
|
+
else:
|
|
931
|
+
operation = parse_expression(label, expression=node_ref)
|
|
932
|
+
if operation and isinstance(operation, TriccOperation):
|
|
933
|
+
# insert rhombus
|
|
934
|
+
return TriccNodeRhombus(
|
|
935
|
+
id=edge.id,
|
|
936
|
+
expression_reference=operation,
|
|
937
|
+
reference=operation.get_references(),
|
|
938
|
+
path=nodes[edge.source],
|
|
939
|
+
activity=nodes[edge.source].activity,
|
|
940
|
+
group=nodes[edge.source].group,
|
|
941
|
+
label=label
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
def process_exclusive_edge(edge, nodes):
|
|
949
|
+
error = None
|
|
950
|
+
if issubclass(nodes[edge.source].__class__, TriccNodeCalculateBase):
|
|
951
|
+
# insert Negate
|
|
952
|
+
if not isinstance(nodes[edge.target], TriccNodeExclusive) or not isinstance(
|
|
953
|
+
nodes[edge.source], TriccNodeExclusive
|
|
954
|
+
):
|
|
955
|
+
return TriccNodeExclusive(
|
|
956
|
+
id=edge.id,
|
|
957
|
+
activity=nodes[edge.target].activity,
|
|
958
|
+
group=nodes[edge.target].group,
|
|
959
|
+
)
|
|
960
|
+
else:
|
|
961
|
+
error = "No after or before a exclusice/negate node"
|
|
962
|
+
else:
|
|
963
|
+
error = "label not after a yesno nor a calculate"
|
|
964
|
+
if error is not None:
|
|
965
|
+
logger.warning(
|
|
966
|
+
"Edge between {0} and {1} with label '{2}' could not be interpreted: {3}".format(
|
|
967
|
+
nodes[edge.source].get_name(),
|
|
968
|
+
nodes[edge.target].get_name(),
|
|
969
|
+
edge.value.strip(),
|
|
970
|
+
error,
|
|
971
|
+
)
|
|
972
|
+
)
|
|
973
|
+
return None
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def process_yesno_edge(edge, nodes):
|
|
977
|
+
if edge.value is None:
|
|
978
|
+
logger.critical(
|
|
979
|
+
"yesNo {} node with labelless edges".format(nodes[edge.source].get_name())
|
|
980
|
+
)
|
|
981
|
+
exit(1)
|
|
982
|
+
label = edge.value.strip().lower()
|
|
983
|
+
yes_option = None
|
|
984
|
+
no_option = None
|
|
985
|
+
for option in nodes[edge.source].options.values():
|
|
986
|
+
if option.label.lower() == "yes":
|
|
987
|
+
yes_option = option
|
|
988
|
+
else:
|
|
989
|
+
no_option = option
|
|
990
|
+
if label.lower() in TRICC_FOLLOW_LABEL:
|
|
991
|
+
pass
|
|
992
|
+
elif label.lower() in TRICC_YES_LABEL:
|
|
993
|
+
edge.source = yes_option.id
|
|
994
|
+
edge.source_external_id = None
|
|
995
|
+
elif label.lower() in TRICC_NO_LABEL:
|
|
996
|
+
edge.source = no_option.id
|
|
997
|
+
edge.source_external_id = None
|
|
998
|
+
else:
|
|
999
|
+
logger.warning(
|
|
1000
|
+
"edge {0} is coming from select {1}".format(
|
|
1001
|
+
edge.id, nodes[edge.source].get_name()
|
|
1002
|
+
)
|
|
1003
|
+
)
|