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,2198 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from tricc_oo.converters.utils import *
|
|
5
|
+
from tricc_oo.models import *
|
|
6
|
+
from tricc_oo.visitors.tricc import *
|
|
7
|
+
from tricc_oo.visitors.utils import PROCESSES
|
|
8
|
+
from tricc_oo.converters.datadictionnary import lookup_codesystems_code
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("default")
|
|
11
|
+
ONE_QUESTION_AT_A_TIME = False
|
|
12
|
+
|
|
13
|
+
TRICC_TRUE_VALUE = 'true'
|
|
14
|
+
TRICC_FALSE_VALUE = 'false'
|
|
15
|
+
|
|
16
|
+
def merge_node(from_node,to_node):
|
|
17
|
+
if from_node.activity != to_node.activity:
|
|
18
|
+
logger.critical("Cannot merge nodes from different activities")
|
|
19
|
+
elif issubclass(from_node.__class__, TriccNodeCalculateBase) and issubclass(to_node.__class__, TriccNodeCalculateBase):
|
|
20
|
+
for e in to_node.activity.edges:
|
|
21
|
+
if e.target == from_node.id:
|
|
22
|
+
e.target = to_node.id
|
|
23
|
+
else:
|
|
24
|
+
logger.critical("Cannot merge not calculate nodes ")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_max_version(dict):
|
|
28
|
+
max_version = None
|
|
29
|
+
for id, sim_node in dict.items():
|
|
30
|
+
if max_version is None or max_version.version < sim_node.version :
|
|
31
|
+
max_version = sim_node
|
|
32
|
+
return max_version
|
|
33
|
+
|
|
34
|
+
def get_versions(name, iterable):
|
|
35
|
+
return [n for n in iterable if version_filter(name)(n)]
|
|
36
|
+
|
|
37
|
+
def version_filter(name):
|
|
38
|
+
return lambda item: hasattr(item, 'name') and ( (isinstance(item, TriccNodeEnd) and name == item.get_reference()) or item.name == name ) and not isinstance(item, TriccNodeSelectOption)
|
|
39
|
+
|
|
40
|
+
def get_last_version(name, processed_nodes, _list=None):
|
|
41
|
+
max_version = None
|
|
42
|
+
if isinstance(_list, dict):
|
|
43
|
+
_list = _list[name].values() if name in _list else []
|
|
44
|
+
if _list is None:
|
|
45
|
+
if isinstance(processed_nodes, OrderedSet):
|
|
46
|
+
return processed_nodes.find_last(version_filter(name))
|
|
47
|
+
else:
|
|
48
|
+
_list = get_versions(name, processed_nodes)
|
|
49
|
+
if _list:
|
|
50
|
+
for sim_node in _list:
|
|
51
|
+
# get the max version while not taking a node that have a next node before next calc
|
|
52
|
+
if ((max_version is None
|
|
53
|
+
or max_version.activity.path_len < sim_node.activity.path_len
|
|
54
|
+
or max_version.path_len < sim_node.path_len
|
|
55
|
+
or (max_version.path_len == sim_node.path_len and hash(max_version.id) < hash(sim_node.id))
|
|
56
|
+
) ):
|
|
57
|
+
max_version = sim_node
|
|
58
|
+
if not max_version:
|
|
59
|
+
already_processed = list(filter(lambda p_node: hasattr(p_node, 'name') and p_node.name == name , _list))
|
|
60
|
+
if already_processed:
|
|
61
|
+
max_version = sorted(filtered, key=lambda x: x.path_len, reverse=False)[0]
|
|
62
|
+
|
|
63
|
+
return max_version
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# main function to retrieve the expression from the tree
|
|
67
|
+
# node is the node to calculate
|
|
68
|
+
# processed_nodes are the list of processed nodes
|
|
69
|
+
def get_node_expressions(node, processed_nodes, process=None):
|
|
70
|
+
is_calculate = issubclass(node.__class__, TriccNodeCalculateBase)
|
|
71
|
+
expression = None
|
|
72
|
+
# in case of recursive call processed_nodes will be None
|
|
73
|
+
if processed_nodes is None or is_ready_to_process(node, processed_nodes=processed_nodes):
|
|
74
|
+
expression = get_node_expression(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
|
|
75
|
+
|
|
76
|
+
if is_calculate:
|
|
77
|
+
if expression and (not isinstance(expression, str) or expression != '') and expression is not True :
|
|
78
|
+
num_expression = TriccOperation(
|
|
79
|
+
TriccOperator.CAST_NUMBER,
|
|
80
|
+
[expression]
|
|
81
|
+
)
|
|
82
|
+
elif expression is True or (not expression and is_calculate):
|
|
83
|
+
expression = TriccStatic(1)
|
|
84
|
+
else:
|
|
85
|
+
expression = ''
|
|
86
|
+
if (
|
|
87
|
+
issubclass(node.__class__, TriccNodeCalculateBase)
|
|
88
|
+
and not isinstance(expression, (TriccStatic, TriccReference, TriccOperation))
|
|
89
|
+
and str(expression) != ''
|
|
90
|
+
and not isinstance(node, (TriccNodeWait, TriccNodeActivityEnd, TriccNodeActivityStart, TriccNodeEnd))
|
|
91
|
+
):
|
|
92
|
+
logger.warning("Calculate {0} returning no calculations".format(node.get_name()))
|
|
93
|
+
expression = TriccStatic(True)
|
|
94
|
+
return expression
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def process_calculate(node,processed_nodes, stashed_nodes, calculates, used_calculates,
|
|
98
|
+
warn = False, process=None, **kwargs ):
|
|
99
|
+
# used_calculates dict[name, Dict[id, node]]
|
|
100
|
+
# processed_nodes Dict[id, node]
|
|
101
|
+
# calculates dict[name, Dict[id, node]]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if node not in processed_nodes:
|
|
105
|
+
# generate condition
|
|
106
|
+
if (
|
|
107
|
+
is_ready_to_process(node, processed_nodes,True)
|
|
108
|
+
and process_reference(
|
|
109
|
+
node,
|
|
110
|
+
processed_nodes=processed_nodes,
|
|
111
|
+
calculates=calculates,
|
|
112
|
+
used_calculates=used_calculates,
|
|
113
|
+
replace_reference=False,
|
|
114
|
+
warn = warn,
|
|
115
|
+
codesystems= kwargs.get('codesystems', None)
|
|
116
|
+
)
|
|
117
|
+
):
|
|
118
|
+
if kwargs.get('warn', True):
|
|
119
|
+
logger.debug('Processing relevance for node {0}'.format(node.get_name()))
|
|
120
|
+
node_name = node.name if not isinstance(node, TriccNodeEnd) else node.get_reference()
|
|
121
|
+
last_version = get_last_version(node_name, processed_nodes) if issubclass(node.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase, TriccNodeEnd)) and not isinstance(node, TriccNodeSelectOption) else None
|
|
122
|
+
#last_version = processed_nodes.find_prev(node, lambda item: hasattr(item, 'name') and item.name == node.name)
|
|
123
|
+
if last_version:
|
|
124
|
+
# 0-100 for manually specified instance. 100-200 for auto instance
|
|
125
|
+
node.version = last_version.version + 1
|
|
126
|
+
last_version.last = False
|
|
127
|
+
node.path_len = max(node.path_len, last_version.path_len + 1)
|
|
128
|
+
# FIXME this is for XLS form where only calculate are evaluated for a activity that is not triggered
|
|
129
|
+
if not issubclass(node.__class__, (TriccNodeInputModel)):
|
|
130
|
+
node.last = True
|
|
131
|
+
if (
|
|
132
|
+
issubclass(node.__class__, (TriccNodeDisplayCalculateBase, TriccNodeEnd)) and node.name is not None
|
|
133
|
+
):
|
|
134
|
+
#logger.debug("set last to false for node {} and add its link it to next one".format(last_used_calc.get_name()))
|
|
135
|
+
if node.prev_nodes:
|
|
136
|
+
set_prev_next_node(last_version, node)
|
|
137
|
+
else:
|
|
138
|
+
expression = node.expression or node.expression_reference or node.relevance
|
|
139
|
+
datatype = expression.get_datatype()
|
|
140
|
+
if datatype == 'boolean':
|
|
141
|
+
expression_reference = TriccOperation(
|
|
142
|
+
TriccOperator.OR,
|
|
143
|
+
[TriccOperation(TriccOperator.ISTRUE, [last_version]), expression]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
elif datatype == 'number':
|
|
147
|
+
expression = TriccOperation(
|
|
148
|
+
TriccOperator.PLUS,
|
|
149
|
+
[last_version, expression]
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
expression = TriccOperation(
|
|
153
|
+
TriccOperator.COALESCE,
|
|
154
|
+
[last_version, expression]
|
|
155
|
+
)
|
|
156
|
+
if node.expression:
|
|
157
|
+
node.expression = expression
|
|
158
|
+
elif node.expression_reference:
|
|
159
|
+
node.expression_reference = expression
|
|
160
|
+
elif node.relevance:
|
|
161
|
+
node.relevance = expression
|
|
162
|
+
else:
|
|
163
|
+
node.last = False
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
calc = TriccNodeCalculate(
|
|
167
|
+
id=generate_id(f"save{node.id}"),
|
|
168
|
+
name=node.name,
|
|
169
|
+
path_len=node.path_len+1,
|
|
170
|
+
version=last_version.version + 2,
|
|
171
|
+
expression=TriccOperation(
|
|
172
|
+
TriccOperator.COALESCE,
|
|
173
|
+
[node, last_version, TriccStatic("''")]
|
|
174
|
+
),
|
|
175
|
+
last=True,
|
|
176
|
+
activity=node.activity,
|
|
177
|
+
group=node.group
|
|
178
|
+
)
|
|
179
|
+
node.activity.nodes[calc.id]=calc
|
|
180
|
+
node.activity.calculates.append(calc)
|
|
181
|
+
if issubclass(node.__class__, TriccNodeInputModel):
|
|
182
|
+
node.expression = TriccOperation(
|
|
183
|
+
TriccOperator.COALESCE,
|
|
184
|
+
[
|
|
185
|
+
'$this',
|
|
186
|
+
last_version
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# if has prev, create condition
|
|
193
|
+
if hasattr(node, 'relevance') and (node.relevance is None or isinstance(node.relevance, TriccOperation)):
|
|
194
|
+
node.relevance = get_node_expressions(node, processed_nodes=processed_nodes, process=process)
|
|
195
|
+
# manage not Available
|
|
196
|
+
if isinstance(node, TriccNodeSelectNotAvailable):
|
|
197
|
+
# update the checkbox
|
|
198
|
+
if node.parent:
|
|
199
|
+
if len(node.prev_nodes) == 1:
|
|
200
|
+
prev = list(node.prev_nodes)[0]
|
|
201
|
+
if isinstance(prev, TriccNodeMoreInfo) and prev.parent.name == node.name:
|
|
202
|
+
prev.parent = node
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# managing more info on NotAvaialbee
|
|
206
|
+
parent_empty = TriccOperation(TriccOperator.ISNULL, [node.parent])
|
|
207
|
+
node.relevance = and_join([node.parent.relevance, parent_empty])
|
|
208
|
+
node.required = parent_empty
|
|
209
|
+
node.constraint = parent_empty
|
|
210
|
+
node.constraint_message = "Cannot be selected with a value entered above"
|
|
211
|
+
# update the check box parent : create loop error
|
|
212
|
+
node.parent.required = None # "${{{0}}}=''".format(node.name)
|
|
213
|
+
else:
|
|
214
|
+
logger.warning("not available node {} does't have a single parent".format(node.get_name()))
|
|
215
|
+
elif isinstance(node.relevance, TriccOperation):
|
|
216
|
+
relevance_reference = list(node.relevance.get_references())
|
|
217
|
+
for r in relevance_reference:
|
|
218
|
+
if issubclass(r.__class__, (TriccNodeDisplayCalculateBase )):
|
|
219
|
+
add_used_calculate(node, r, calculates, used_calculates, processed_nodes)
|
|
220
|
+
|
|
221
|
+
generate_calculates(node,calculates, used_calculates,processed_nodes=processed_nodes)
|
|
222
|
+
if last_version and hasattr(node, 'relevance'):
|
|
223
|
+
if isinstance(node, TriccNodeInputModel):
|
|
224
|
+
version_relevance = TriccOperation(
|
|
225
|
+
TriccOperator.ISNULL,
|
|
226
|
+
[last_version]
|
|
227
|
+
)
|
|
228
|
+
elif last_version.relevance:
|
|
229
|
+
version_relevance = not_clean(
|
|
230
|
+
last_version.relevance
|
|
231
|
+
)
|
|
232
|
+
elif last_version.activity.relevance:
|
|
233
|
+
version_relevance = not_clean(
|
|
234
|
+
last_version.activity.relevance,
|
|
235
|
+
)
|
|
236
|
+
else:
|
|
237
|
+
version_relevance = None
|
|
238
|
+
|
|
239
|
+
if version_relevance:
|
|
240
|
+
if getattr(node, 'relevance', None):
|
|
241
|
+
node.relevance = and_join(
|
|
242
|
+
[
|
|
243
|
+
version_relevance,
|
|
244
|
+
node.relevance
|
|
245
|
+
])
|
|
246
|
+
|
|
247
|
+
elif hasattr(node, 'relevance'):
|
|
248
|
+
node.relevance = version_relevance
|
|
249
|
+
|
|
250
|
+
#update_calc_version(calculates,node_name)
|
|
251
|
+
#if hasattr(node, 'next_nodes'):
|
|
252
|
+
#node.next_nodes=reorder_node_list(node.next_nodes, node.group)
|
|
253
|
+
process_reference(
|
|
254
|
+
node,
|
|
255
|
+
processed_nodes=processed_nodes,
|
|
256
|
+
calculates=calculates,
|
|
257
|
+
used_calculates=used_calculates,
|
|
258
|
+
replace_reference=True,
|
|
259
|
+
warn = warn,
|
|
260
|
+
codesystems= kwargs.get('codesystems', None)
|
|
261
|
+
)
|
|
262
|
+
if isinstance(node, (TriccNodeMainStart, TriccNodeActivityStart)):
|
|
263
|
+
process_reference(
|
|
264
|
+
node.activity,
|
|
265
|
+
processed_nodes=processed_nodes,
|
|
266
|
+
calculates=calculates,
|
|
267
|
+
used_calculates=used_calculates,
|
|
268
|
+
replace_reference=True,
|
|
269
|
+
warn = warn,
|
|
270
|
+
codesystems= kwargs.get('codesystems', None)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return True
|
|
274
|
+
# not ready to process or already processed
|
|
275
|
+
|
|
276
|
+
return False
|
|
277
|
+
|
|
278
|
+
def update_calc_version(calculates,name):
|
|
279
|
+
if name in calculates and len(calculates[name])>1:
|
|
280
|
+
ordered_list = sorted(list(calculates[name].values()), key=lambda x:x.path_len)
|
|
281
|
+
i = 1
|
|
282
|
+
len_max=len(calculates[name])
|
|
283
|
+
for elm in ordered_list:
|
|
284
|
+
elm.version=i
|
|
285
|
+
elm.last= (i == len_max)
|
|
286
|
+
i+=1
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def get_max_named_version(calculates,name):
|
|
290
|
+
max = 0
|
|
291
|
+
if name in calculates:
|
|
292
|
+
for node in calculates[name].values():
|
|
293
|
+
if node.version > max:
|
|
294
|
+
max = node.version
|
|
295
|
+
return max
|
|
296
|
+
|
|
297
|
+
def get_count_node(node):
|
|
298
|
+
count_id = generate_id(f"count{node.id}")
|
|
299
|
+
count_name = "cnt_"+count_id
|
|
300
|
+
return TriccNodeCount(
|
|
301
|
+
id = count_id,
|
|
302
|
+
group = node.group,
|
|
303
|
+
activity = node.activity,
|
|
304
|
+
label = "count: "+node.get_name(),
|
|
305
|
+
name = count_name,
|
|
306
|
+
path_len=node.path_len
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
### Function that inject a wait after path that will wait for the nodes
|
|
310
|
+
def get_activity_wait(prev_nodes, nodes_to_wait, next_nodes, replaced_node = None, edge_only = False, activity = None):
|
|
311
|
+
|
|
312
|
+
if issubclass(nodes_to_wait.__class__,TriccBaseModel):
|
|
313
|
+
nodes_to_wait = [nodes_to_wait]
|
|
314
|
+
if issubclass(prev_nodes.__class__,TriccBaseModel):
|
|
315
|
+
prev_nodes = set([prev_nodes])
|
|
316
|
+
elif isinstance(prev_nodes, list):
|
|
317
|
+
prev_nodes = set(prev_nodes)
|
|
318
|
+
|
|
319
|
+
iterator = iter(prev_nodes)
|
|
320
|
+
prev_node = next(iterator)
|
|
321
|
+
path = prev_node if len(prev_nodes) == 1 else get_bridge_path(prev_nodes, activity)
|
|
322
|
+
|
|
323
|
+
activity = activity or prev_node.activity
|
|
324
|
+
calc_node = TriccNodeWait(
|
|
325
|
+
id = generate_id(f"ar{''.join([x.id for x in nodes_to_wait])}{activity.id}"),
|
|
326
|
+
reference = nodes_to_wait,
|
|
327
|
+
activity = activity,
|
|
328
|
+
group = activity,
|
|
329
|
+
path = path
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
#start the wait and the next_nodes from the prev_nodes
|
|
333
|
+
#add the wait as dependency of the next_nodes
|
|
334
|
+
|
|
335
|
+
# add edge between rhombus and node
|
|
336
|
+
|
|
337
|
+
set_prev_next_node(path,calc_node, edge_only=edge_only, activity=activity )
|
|
338
|
+
for next_node in next_nodes:
|
|
339
|
+
#if prev != replaced_node and next_node != replaced_node :
|
|
340
|
+
# set_prev_next_node(prev,next_node,replaced_node)
|
|
341
|
+
#if first:
|
|
342
|
+
#first = False
|
|
343
|
+
set_prev_next_node(calc_node,next_node, edge_only=edge_only,activity=activity)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
return calc_node
|
|
347
|
+
|
|
348
|
+
def get_bridge_path(prev_nodes, node=None,edge_only=False):
|
|
349
|
+
iterator = iter(prev_nodes)
|
|
350
|
+
p_p_node = next(iterator)
|
|
351
|
+
if node is None:
|
|
352
|
+
node = p_p_node
|
|
353
|
+
calc_id = generate_id(f"br{''.join([x.id for x in prev_nodes])}{node.id}")
|
|
354
|
+
calc_name = "path_"+calc_id
|
|
355
|
+
data = {
|
|
356
|
+
'id': calc_id,
|
|
357
|
+
'group': node.group,
|
|
358
|
+
'activity': node.activity,
|
|
359
|
+
'label': "path: " + ( node.get_name()),
|
|
360
|
+
'name': calc_name,
|
|
361
|
+
'path_len': node.path_len + 1 * (node == p_p_node)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if sum([0 if issubclass(n.__class__, (TriccNodeDisplayCalculateBase, TriccNodeRhombus)) else 1 for n in prev_nodes])>0 : #and len(node.prev_nodes)>1:
|
|
365
|
+
calc= TriccNodeDisplayBridge( **data)
|
|
366
|
+
else:
|
|
367
|
+
calc = TriccNodeBridge( **data)
|
|
368
|
+
return calc
|
|
369
|
+
|
|
370
|
+
def inject_bridge_path(node, nodes):
|
|
371
|
+
|
|
372
|
+
prev_nodes = [nodes[n.source] for n in list(filter(lambda x: (x.target == node.id or x.target == node) and x.source in list(nodes.keys()), node.activity.edges))]
|
|
373
|
+
if prev_nodes:
|
|
374
|
+
calc = get_bridge_path(prev_nodes, node,edge_only=True)
|
|
375
|
+
|
|
376
|
+
for e in node.activity.edges:
|
|
377
|
+
if e.target == node.id:
|
|
378
|
+
# if e.source in node.activity.nodes and len(node.activity.nodes[e.source].next_nodes):
|
|
379
|
+
# set_prev_next_node(node.activity[e.source], node, edge_only=True, replaced_node=node)
|
|
380
|
+
# else:
|
|
381
|
+
e.target = calc.id
|
|
382
|
+
|
|
383
|
+
# add edge between bridge and node
|
|
384
|
+
set_prev_next_node(calc,node,edge_only=True, activity=node.activity)
|
|
385
|
+
node.path_len += 1
|
|
386
|
+
return calc
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def inject_node_before(before, node, activity):
|
|
390
|
+
before.group = activity
|
|
391
|
+
before.activity = activity
|
|
392
|
+
activity.nodes[before.id] = before
|
|
393
|
+
nodes = activity.nodes
|
|
394
|
+
prev_nodes = node.prev_nodes.union(set(nodes[n.source] for n in list(filter(lambda x: (x.target == node.id or x.target == node) and x.source in nodes, node.activity.edges))))
|
|
395
|
+
edge_processed = False
|
|
396
|
+
before.path_len = node.path_len
|
|
397
|
+
for e in node.activity.edges:
|
|
398
|
+
if e.target == node.id:
|
|
399
|
+
e.target = before.id
|
|
400
|
+
for p in prev_nodes:
|
|
401
|
+
prev_processed = len(node.next_nodes) > 0
|
|
402
|
+
if node in p.next_nodes:
|
|
403
|
+
p.next_nodes.remove(node)
|
|
404
|
+
p.next_nodes.append(before)
|
|
405
|
+
|
|
406
|
+
# add edge between bridge and node
|
|
407
|
+
set_prev_next_node(before,node,edge_only=not edge_processed, activity=node.activity)
|
|
408
|
+
node.path_len += 1
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def generate_calculates(node,calculates, used_calculates,processed_nodes):
|
|
413
|
+
list_calc = []
|
|
414
|
+
count_node = None
|
|
415
|
+
## add select calcualte
|
|
416
|
+
if issubclass(node.__class__, TriccNodeCalculateBase):
|
|
417
|
+
if isinstance(node, TriccNodeRhombus):
|
|
418
|
+
if (
|
|
419
|
+
(node.expression_reference is None or isinstance(node.expression_reference, TriccOperation))
|
|
420
|
+
and isinstance(node.reference, list)
|
|
421
|
+
and len(node.reference)==1
|
|
422
|
+
and issubclass(node.reference[0].__class__, TriccNodeSelect)
|
|
423
|
+
):
|
|
424
|
+
|
|
425
|
+
count_node = get_count_node(node)
|
|
426
|
+
list_calc.append(count_node)
|
|
427
|
+
set_prev_next_node(node.reference[0],count_node)
|
|
428
|
+
node.path_len+=1
|
|
429
|
+
|
|
430
|
+
if isinstance(node.expression_reference, TriccOperation):
|
|
431
|
+
node.expression_reference.replace_node(node.reference, count_node)
|
|
432
|
+
node.reference[0] = count_node
|
|
433
|
+
# elif isinstance(node.reference, TriccOperation):
|
|
434
|
+
# references = node.reference.get_references()
|
|
435
|
+
# if len(references) == 1 and issubclass(node.reference[0].__class__, TriccNodeSelect):
|
|
436
|
+
# count_node = get_count_node(node)
|
|
437
|
+
# list_calc.append(count_node)
|
|
438
|
+
# set_prev_next_node(references[0],count_node)
|
|
439
|
+
# node.path_len+=1
|
|
440
|
+
# node.reference.replace_node(references[0], count_node)
|
|
441
|
+
if count_node:
|
|
442
|
+
processed_nodes.add(count_node)
|
|
443
|
+
add_calculate(calculates, count_node)
|
|
444
|
+
add_used_calculate(
|
|
445
|
+
node,
|
|
446
|
+
count_node,
|
|
447
|
+
calculates=calculates,
|
|
448
|
+
used_calculates=used_calculates,
|
|
449
|
+
processed_nodes=processed_nodes
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
# if a prev node is a calculate then it must be added in used_calc
|
|
454
|
+
for prev in node.prev_nodes:
|
|
455
|
+
add_used_calculate(
|
|
456
|
+
node,
|
|
457
|
+
prev,
|
|
458
|
+
calculates=calculates,
|
|
459
|
+
used_calculates=used_calculates,
|
|
460
|
+
processed_nodes=processed_nodes
|
|
461
|
+
)
|
|
462
|
+
#if the node have a save
|
|
463
|
+
if hasattr(node, 'save') and node.save is not None and node.save != '':
|
|
464
|
+
# get fragments type.name.icdcode
|
|
465
|
+
calculate_name=node.save
|
|
466
|
+
if not isinstance(node, TriccNodeSelectYesNo) and issubclass(node.__class__, (TriccNodeSelect)):
|
|
467
|
+
calc_node = get_count_node(node)
|
|
468
|
+
calc_node.path_len += 1
|
|
469
|
+
calc_node.name=calculate_name
|
|
470
|
+
calc_node.label = "save select: " +node.get_name()
|
|
471
|
+
else:
|
|
472
|
+
calc_id = generate_id(f"autosave{node.id}")
|
|
473
|
+
calc_node = TriccNodeCalculate(
|
|
474
|
+
name=calculate_name,
|
|
475
|
+
id = calc_id,
|
|
476
|
+
group = node.group,
|
|
477
|
+
activity = node.activity,
|
|
478
|
+
label = "save: " +node.get_name(),
|
|
479
|
+
path_len=node.path_len+ 1
|
|
480
|
+
)
|
|
481
|
+
logger.debug("generate_save_calculate:{}:{} as {}".format(calc_node.tricc_type, node.name if hasattr(node,'name') else node.id, calculate_name))
|
|
482
|
+
if isinstance(node, TriccNodeSelectYesNo):
|
|
483
|
+
yesNode = node.options[0]
|
|
484
|
+
set_prev_next_node(yesNode,calc_node)
|
|
485
|
+
else:
|
|
486
|
+
set_prev_next_node(node,calc_node)
|
|
487
|
+
list_calc.append(calc_node)
|
|
488
|
+
#add_save_calculate(calc_node, calculates, used_calculates,processed_nodes)
|
|
489
|
+
for calc in list_calc:
|
|
490
|
+
node.activity.nodes[calc.id] = calc
|
|
491
|
+
return list_calc
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def add_calculate(calculates, calc_node):
|
|
496
|
+
if issubclass(calc_node.__class__, TriccNodeDisplayCalculateBase):
|
|
497
|
+
if calc_node.name not in calculates:
|
|
498
|
+
calculates[calc_node.name]= {}
|
|
499
|
+
calculates[calc_node.name][calc_node.id] = calc_node
|
|
500
|
+
|
|
501
|
+
def get_option_code_from_label(node, option_label):
|
|
502
|
+
if hasattr(node, 'options'):
|
|
503
|
+
for i in node.options:
|
|
504
|
+
if node.options[i].label.strip() == option_label.strip():
|
|
505
|
+
return node.options[i].name
|
|
506
|
+
logger.critical(f"option with label {option_label} not found in {node.get_name()}")
|
|
507
|
+
else:
|
|
508
|
+
logger.critical(f"node {node.get_name()} has no options")
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def process_reference(node, processed_nodes, calculates, used_calculates=None, replace_reference=False,warn=False, codesystems=None):
|
|
512
|
+
if getattr(node, 'expression_reference', None):
|
|
513
|
+
modified_expression = process_operation_reference(
|
|
514
|
+
node.expression_reference,
|
|
515
|
+
node,
|
|
516
|
+
processed_nodes=processed_nodes,
|
|
517
|
+
calculates=calculates,
|
|
518
|
+
used_calculates=used_calculates,
|
|
519
|
+
replace_reference=replace_reference,
|
|
520
|
+
warn=warn,
|
|
521
|
+
codesystems=codesystems
|
|
522
|
+
)
|
|
523
|
+
if modified_expression is False:
|
|
524
|
+
return False
|
|
525
|
+
elif modified_expression and replace_reference:
|
|
526
|
+
node.reference = list(modified_expression.get_references())
|
|
527
|
+
node.expression_reference = modified_expression
|
|
528
|
+
elif getattr(node, 'reference', None):
|
|
529
|
+
if isinstance(node.reference, list):
|
|
530
|
+
if len(node.reference) == 1 :
|
|
531
|
+
operation = node.reference[0]
|
|
532
|
+
else:
|
|
533
|
+
operation = and_join(
|
|
534
|
+
node.reference
|
|
535
|
+
)
|
|
536
|
+
modified_expression = process_operation_reference(
|
|
537
|
+
operation,
|
|
538
|
+
node,
|
|
539
|
+
processed_nodes=processed_nodes,
|
|
540
|
+
calculates=calculates,
|
|
541
|
+
used_calculates=used_calculates,
|
|
542
|
+
replace_reference=replace_reference,
|
|
543
|
+
warn=warn,
|
|
544
|
+
codesystems=codesystems
|
|
545
|
+
)
|
|
546
|
+
if modified_expression is False:
|
|
547
|
+
return False
|
|
548
|
+
elif modified_expression:
|
|
549
|
+
node.reference = list(modified_expression.get_references())
|
|
550
|
+
if not isinstance(node, TriccNodeWait):
|
|
551
|
+
node.expression_reference = modified_expression
|
|
552
|
+
elif isinstance(node.reference, (TriccOperation, TriccReference)):
|
|
553
|
+
modified_expression = process_operation_reference(
|
|
554
|
+
node.reference,
|
|
555
|
+
node,
|
|
556
|
+
processed_nodes=processed_nodes,
|
|
557
|
+
calculates=calculates,
|
|
558
|
+
used_calculates=used_calculates,
|
|
559
|
+
replace_reference=replace_reference,
|
|
560
|
+
warn=warn,
|
|
561
|
+
codesystems=codesystems
|
|
562
|
+
)
|
|
563
|
+
if modified_expression is False:
|
|
564
|
+
return False
|
|
565
|
+
elif modified_expression and replace_reference:
|
|
566
|
+
node.reference = list(modified_expression.get_references())
|
|
567
|
+
node.expression_reference = modified_expression
|
|
568
|
+
|
|
569
|
+
if isinstance(getattr(node, 'relevance', None), (TriccOperation, TriccReference)):
|
|
570
|
+
modified_expression = process_operation_reference(
|
|
571
|
+
node.relevance,
|
|
572
|
+
node,
|
|
573
|
+
processed_nodes=processed_nodes,
|
|
574
|
+
calculates=calculates,
|
|
575
|
+
used_calculates=used_calculates,
|
|
576
|
+
replace_reference=replace_reference,
|
|
577
|
+
warn=warn,
|
|
578
|
+
codesystems=codesystems
|
|
579
|
+
)
|
|
580
|
+
if modified_expression is False:
|
|
581
|
+
return False
|
|
582
|
+
elif modified_expression and replace_reference:
|
|
583
|
+
node.relevance = modified_expression
|
|
584
|
+
|
|
585
|
+
if isinstance(getattr(node, 'default', None), (TriccOperation, TriccReference)):
|
|
586
|
+
modified_expression = process_operation_reference(
|
|
587
|
+
node.default,
|
|
588
|
+
node,
|
|
589
|
+
processed_nodes=processed_nodes,
|
|
590
|
+
calculates=calculates,
|
|
591
|
+
used_calculates=used_calculates,
|
|
592
|
+
replace_reference=replace_reference,
|
|
593
|
+
warn=warn,
|
|
594
|
+
codesystems=codesystems
|
|
595
|
+
)
|
|
596
|
+
if modified_expression is False:
|
|
597
|
+
return False
|
|
598
|
+
elif modified_expression and replace_reference:
|
|
599
|
+
node.relevance = modified_expression
|
|
600
|
+
|
|
601
|
+
if isinstance(getattr(node, 'expression', None), (TriccOperation, TriccReference)):
|
|
602
|
+
modified_expression = process_operation_reference(
|
|
603
|
+
node.expression,
|
|
604
|
+
node,
|
|
605
|
+
processed_nodes=processed_nodes,
|
|
606
|
+
calculates=calculates,
|
|
607
|
+
used_calculates=used_calculates,
|
|
608
|
+
replace_reference=replace_reference,
|
|
609
|
+
warn=warn,
|
|
610
|
+
codesystems=codesystems
|
|
611
|
+
)
|
|
612
|
+
if modified_expression is False:
|
|
613
|
+
return False
|
|
614
|
+
elif modified_expression and replace_reference:
|
|
615
|
+
node.expression = modified_expression
|
|
616
|
+
|
|
617
|
+
if isinstance(getattr(node, 'applicability', None), (TriccOperation, TriccReference)):
|
|
618
|
+
modified_expression = process_operation_reference(
|
|
619
|
+
node.applicability,
|
|
620
|
+
node,
|
|
621
|
+
processed_nodes=processed_nodes,
|
|
622
|
+
calculates=calculates,
|
|
623
|
+
used_calculates=used_calculates,
|
|
624
|
+
replace_reference=replace_reference,
|
|
625
|
+
warn=warn,
|
|
626
|
+
codesystems=codesystems
|
|
627
|
+
)
|
|
628
|
+
if modified_expression is False:
|
|
629
|
+
return False
|
|
630
|
+
elif modified_expression and replace_reference:
|
|
631
|
+
node.applicability = modified_expression
|
|
632
|
+
return True
|
|
633
|
+
|
|
634
|
+
def process_operation_reference(operation, node, processed_nodes, calculates, used_calculates=None, replace_reference=False,warn=False, codesystems=None):
|
|
635
|
+
modified_operation = None
|
|
636
|
+
node_reference = []
|
|
637
|
+
reference = []
|
|
638
|
+
option_label = None
|
|
639
|
+
ref_list = [r.value for r in operation.get_references() if isinstance(r, TriccReference)]
|
|
640
|
+
real_ref_list = [r for r in operation.get_references() if issubclass(r.__class__, TriccNodeBaseModel)]
|
|
641
|
+
for ref in ref_list:
|
|
642
|
+
if ref.endswith(']'):
|
|
643
|
+
terms = ref[:-1].split('[')
|
|
644
|
+
option_label = terms[1]
|
|
645
|
+
ref = terms[0]
|
|
646
|
+
else:
|
|
647
|
+
option_label = None
|
|
648
|
+
node_in_act = [n for n in node.activity.nodes.values() if n.name == ref and n != node]
|
|
649
|
+
if node_in_act:
|
|
650
|
+
if any(n not in processed_nodes for n in node_in_act):
|
|
651
|
+
return False
|
|
652
|
+
else:
|
|
653
|
+
last_found = node_in_act[0]
|
|
654
|
+
else:
|
|
655
|
+
last_found = get_last_version(name=ref, processed_nodes=processed_nodes)
|
|
656
|
+
if last_found is None:
|
|
657
|
+
if codesystems:
|
|
658
|
+
concept = lookup_codesystems_code(codesystems, ref)
|
|
659
|
+
if not concept:
|
|
660
|
+
logger.critical(f"reference {ref} not found in the project")
|
|
661
|
+
exit(1)
|
|
662
|
+
else:
|
|
663
|
+
if warn:
|
|
664
|
+
logger.debug(f"reference {ref}::{concept.display} not yet processed {node.get_name()}")
|
|
665
|
+
|
|
666
|
+
elif warn:
|
|
667
|
+
logger.debug(f"reference {ref} not found for a calculate {node.get_name()}")
|
|
668
|
+
return False
|
|
669
|
+
else:
|
|
670
|
+
node_reference.append(last_found)
|
|
671
|
+
reference.append(TriccReference(ref))
|
|
672
|
+
if replace_reference:
|
|
673
|
+
if isinstance(operation, (TriccOperation)):
|
|
674
|
+
if modified_operation is None:
|
|
675
|
+
modified_operation = operation.copy(keep_node=True)
|
|
676
|
+
modified_operation.replace_node(TriccReference(ref), last_found)
|
|
677
|
+
elif operation == TriccReference(ref):
|
|
678
|
+
modified_operation = last_found
|
|
679
|
+
if option_label:
|
|
680
|
+
# Resolve human-readable label
|
|
681
|
+
option_code = get_option_code_from_label(last_found, option_label)
|
|
682
|
+
if option_code:
|
|
683
|
+
modified_operation = replace_code_reference(operation, old=f"{ref}[{option_label}]", new=option_code )
|
|
684
|
+
else:
|
|
685
|
+
if warn:
|
|
686
|
+
logger.warning(f"Could not resolve label '{option_label}' for reference {ref}")
|
|
687
|
+
return False
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
node.path_len = max(node.path_len, last_found.path_len)
|
|
691
|
+
for ref in real_ref_list:
|
|
692
|
+
if is_prev_processed(ref, node, processed_nodes=processed_nodes, local=False) is False:
|
|
693
|
+
return False
|
|
694
|
+
|
|
695
|
+
if used_calculates is not None:
|
|
696
|
+
for ref_nodes in node_reference:
|
|
697
|
+
if issubclass(ref_nodes.__class__, TriccNodeCalculateBase):
|
|
698
|
+
add_used_calculate(node, ref_nodes, calculates, used_calculates, processed_nodes=processed_nodes)
|
|
699
|
+
return modified_operation
|
|
700
|
+
|
|
701
|
+
def replace_code_reference(expression, old, new):
|
|
702
|
+
if isinstance(expression, str):
|
|
703
|
+
return expression_reference.replace(old, f"'{new}'")
|
|
704
|
+
if isinstance(expression, TriccOperation):
|
|
705
|
+
expression.replace_node(TriccReference(old), TriccStatic(new))
|
|
706
|
+
return expression
|
|
707
|
+
#add_used_calculate(node, calc_node, calculates, used_calculates, processed_nodes)
|
|
708
|
+
|
|
709
|
+
def add_used_calculate(node, prev_node, calculates, used_calculates, processed_nodes):
|
|
710
|
+
if issubclass(prev_node.__class__, TriccNodeDisplayCalculateBase):
|
|
711
|
+
if prev_node in processed_nodes:
|
|
712
|
+
# if not a verison, index will equal -1
|
|
713
|
+
if prev_node.name not in calculates :
|
|
714
|
+
logger.debug("node {} refered before being processed".format(node.get_name()))
|
|
715
|
+
return False
|
|
716
|
+
max_version = prev_node#get_max_version(calculates[node_clean_name])
|
|
717
|
+
if prev_node.name not in used_calculates:
|
|
718
|
+
used_calculates[prev_node.name] = {}
|
|
719
|
+
#save the max version only once
|
|
720
|
+
if max_version.id not in used_calculates[prev_node.name]:
|
|
721
|
+
used_calculates[prev_node.name][max_version.id] = max_version
|
|
722
|
+
else:
|
|
723
|
+
logger.debug("process_calculate_version_requirement: failed for {0} , prev Node {1} ".format(node.get_name(), prev_node.get_name()))
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def get_select_not_available_options(node,group,label):
|
|
727
|
+
return {0:TriccNodeSelectOption(
|
|
728
|
+
id = generate_id(f"notavaialble{node.id}"),
|
|
729
|
+
name="1",
|
|
730
|
+
label=label,
|
|
731
|
+
select = node,
|
|
732
|
+
group = group,
|
|
733
|
+
list_name = node.list_name
|
|
734
|
+
)}
|
|
735
|
+
|
|
736
|
+
def get_select_yes_no_options(node, group):
|
|
737
|
+
yes = TriccNodeSelectOption(
|
|
738
|
+
id = generate_id(f'yes{node.id}'),
|
|
739
|
+
name=f"{TRICC_TRUE_VALUE}",
|
|
740
|
+
label="Yes",
|
|
741
|
+
select = node,
|
|
742
|
+
group = group,
|
|
743
|
+
list_name = node.list_name
|
|
744
|
+
)
|
|
745
|
+
no = TriccNodeSelectOption(
|
|
746
|
+
id = generate_id(f'no{node.id}'),
|
|
747
|
+
name=f"{TRICC_FALSE_VALUE}",
|
|
748
|
+
label="No",
|
|
749
|
+
select = node,
|
|
750
|
+
group = group,
|
|
751
|
+
list_name = node.list_name
|
|
752
|
+
)
|
|
753
|
+
return {0:yes, 1:no }
|
|
754
|
+
|
|
755
|
+
# walkthough all node in an iterative way, the same node might be parsed 2 times
|
|
756
|
+
# therefore to avoid double processing the nodes variable saves the node already processed
|
|
757
|
+
# there 2 strategies : process it the first time or the last time (wait that all the previuous node are processed)
|
|
758
|
+
|
|
759
|
+
def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, stashed_nodes, path_len, recursive=False, warn = False,
|
|
760
|
+
node_path = [], process=None, **kwargs):
|
|
761
|
+
ended_activity = False
|
|
762
|
+
# logger.debug("walkthrough::{}::{}".format(callback.__name__, node.get_name()))
|
|
763
|
+
|
|
764
|
+
path_len = max(node.activity.path_len, *[0,*[getattr(n,'path_len',0) + 1 for n in node.activity.prev_nodes]]) + 1
|
|
765
|
+
if hasattr(node, 'prev_nodes'):
|
|
766
|
+
path_len = max(path_len, *[0,*[getattr(n,'path_len',0)+ 1 for n in node.prev_nodes]])
|
|
767
|
+
if hasattr(node, 'get_references'):
|
|
768
|
+
references = node.get_references()
|
|
769
|
+
if references:
|
|
770
|
+
path_len = max(path_len, *[0,*[getattr(n,'path_len',0) + 1 for n in references]])
|
|
771
|
+
node.path_len = max(node.path_len, path_len)
|
|
772
|
+
if (
|
|
773
|
+
callback(
|
|
774
|
+
node,
|
|
775
|
+
processed_nodes=processed_nodes,
|
|
776
|
+
stashed_nodes=stashed_nodes,
|
|
777
|
+
warn = warn,
|
|
778
|
+
node_path=node_path,
|
|
779
|
+
process=process,
|
|
780
|
+
**kwargs
|
|
781
|
+
)
|
|
782
|
+
):
|
|
783
|
+
node_path.append(node)
|
|
784
|
+
# node processing succeed
|
|
785
|
+
if not isinstance(node, TriccNodeActivity) and node not in processed_nodes:
|
|
786
|
+
processed_nodes.add(node)
|
|
787
|
+
if warn:
|
|
788
|
+
logger.debug("{}::{}: processed ({})".format(callback.__name__, node.get_name(), len(processed_nodes)))
|
|
789
|
+
if isinstance(node, (TriccNodeEnd, TriccNodeActivityEnd)) and node.activity not in processed_nodes:
|
|
790
|
+
end_nodes = node.activity.get_end_nodes()
|
|
791
|
+
if all([e in processed_nodes for e in end_nodes]):
|
|
792
|
+
processed_nodes.add(node.activity)
|
|
793
|
+
ended_activity = True
|
|
794
|
+
if warn:
|
|
795
|
+
logger.debug("{}::{}: processed ({})".format(callback.__name__, node.activity.get_name(), len(processed_nodes)))
|
|
796
|
+
elif node in stashed_nodes:
|
|
797
|
+
stashed_nodes.remove(node)
|
|
798
|
+
# logger.debug("{}::{}: unstashed ({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
|
|
799
|
+
# put the stached node from that group first
|
|
800
|
+
# if has next, walkthrough them (support options)
|
|
801
|
+
# if len(stashed_nodes)>1:
|
|
802
|
+
if not recursive:
|
|
803
|
+
reorder_node_list(stashed_nodes, node.group, processed_nodes)
|
|
804
|
+
if isinstance(node, (TriccNodeActivityStart, TriccNodeMainStart)):
|
|
805
|
+
if getattr(node, 'process', None):
|
|
806
|
+
if process is None:
|
|
807
|
+
process = [node.process]
|
|
808
|
+
else:
|
|
809
|
+
process[0] = node.process
|
|
810
|
+
if recursive:
|
|
811
|
+
for gp in node.activity.groups.values():
|
|
812
|
+
walktrhough_tricc_node_processed_stached(
|
|
813
|
+
gp,
|
|
814
|
+
callback,
|
|
815
|
+
processed_nodes=processed_nodes,
|
|
816
|
+
stashed_nodes=stashed_nodes,
|
|
817
|
+
path_len=path_len,
|
|
818
|
+
recursive=recursive,
|
|
819
|
+
warn = warn,
|
|
820
|
+
node_path = node_path.copy(),
|
|
821
|
+
**kwargs
|
|
822
|
+
)
|
|
823
|
+
for c in node.activity.calculates:
|
|
824
|
+
if len(c.prev_nodes)== 0:
|
|
825
|
+
walktrhough_tricc_node_processed_stached(
|
|
826
|
+
c,
|
|
827
|
+
callback,
|
|
828
|
+
processed_nodes=processed_nodes,
|
|
829
|
+
stashed_nodes=stashed_nodes,
|
|
830
|
+
path_len=path_len,
|
|
831
|
+
recursive=recursive,
|
|
832
|
+
warn = warn,
|
|
833
|
+
node_path = node_path.copy(),
|
|
834
|
+
**kwargs
|
|
835
|
+
)
|
|
836
|
+
else:
|
|
837
|
+
stashed_nodes += [c for c in node.activity.calculates if len(c.prev_nodes)== 0]
|
|
838
|
+
stashed_nodes += node.activity.groups.values()
|
|
839
|
+
elif issubclass(node.__class__, TriccNodeSelect):
|
|
840
|
+
for option in node.options.values():
|
|
841
|
+
option.path_len = max(path_len, option.path_len)
|
|
842
|
+
callback(option, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, warn = warn, node_path=node_path,**kwargs)
|
|
843
|
+
if option not in processed_nodes:
|
|
844
|
+
processed_nodes.add(option)
|
|
845
|
+
if warn:
|
|
846
|
+
logger.debug(
|
|
847
|
+
"{}::{}: processed ({})".format(callback.__name__, option.get_name(), len(processed_nodes)))
|
|
848
|
+
walkthrough_tricc_option(node, callback, processed_nodes, stashed_nodes, path_len + 1, recursive,
|
|
849
|
+
warn = warn,node_path = node_path, **kwargs)
|
|
850
|
+
if isinstance(node, TriccNodeActivity):
|
|
851
|
+
if node.root not in processed_nodes:
|
|
852
|
+
if node.root is not None:
|
|
853
|
+
node.root.path_len = max(path_len, node.root.path_len)
|
|
854
|
+
if getattr(node.root, 'process', None):
|
|
855
|
+
if process is None:
|
|
856
|
+
process = [node.root.process]
|
|
857
|
+
else:
|
|
858
|
+
process[0] = node.root.process
|
|
859
|
+
if recursive:
|
|
860
|
+
walktrhough_tricc_node_processed_stached(node.root, callback, processed_nodes, stashed_nodes, path_len,
|
|
861
|
+
recursive, warn = warn,node_path = node_path.copy(),**kwargs)
|
|
862
|
+
# for gp in node.groups:
|
|
863
|
+
# walktrhough_tricc_node_processed_stached(gp, callback, processed_nodes, stashed_nodes, path_len,
|
|
864
|
+
# recursive, warn = warn,**kwargs)
|
|
865
|
+
# if node.calculates:
|
|
866
|
+
# for c in node.calculates:
|
|
867
|
+
# walktrhough_tricc_node_processed_stached(c, callback, processed_nodes, stashed_nodes, path_len,
|
|
868
|
+
# recursive, warn = warn,**kwargs)
|
|
869
|
+
elif node.root not in stashed_nodes:
|
|
870
|
+
#stashed_nodes.insert(0,node.root)
|
|
871
|
+
stashed_nodes.insert_at_top(node.root)
|
|
872
|
+
# if node.calculates:
|
|
873
|
+
# stashed_nodes += node.calculates
|
|
874
|
+
# for gp in node.groups:
|
|
875
|
+
# stashed_nodes.add(gp)
|
|
876
|
+
# # stashed_nodes.insert(0,gp)
|
|
877
|
+
return
|
|
878
|
+
elif ended_activity:
|
|
879
|
+
for next_node in node.next_nodes:
|
|
880
|
+
if next_node not in stashed_nodes:
|
|
881
|
+
#stashed_nodes.insert(0,next_node)
|
|
882
|
+
if recursive:
|
|
883
|
+
walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes, stashed_nodes, path_len,
|
|
884
|
+
recursive, warn = warn,node_path = node_path.copy(),**kwargs)
|
|
885
|
+
else:
|
|
886
|
+
stashed_nodes.insert_at_top(next_node)
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
elif hasattr(node, 'next_nodes') and len(node.next_nodes) > 0 and not isinstance(node, TriccNodeActivity):
|
|
890
|
+
if recursive:
|
|
891
|
+
walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes, path_len + 1, recursive,
|
|
892
|
+
warn = warn,node_path = node_path,**kwargs)
|
|
893
|
+
else:
|
|
894
|
+
for nn in node.next_nodes:
|
|
895
|
+
if nn not in stashed_nodes:
|
|
896
|
+
stashed_nodes.insert_at_top(nn)
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
else:
|
|
900
|
+
if node not in processed_nodes and node not in stashed_nodes:
|
|
901
|
+
if node not in stashed_nodes:
|
|
902
|
+
stashed_nodes.insert_at_bottom(node)
|
|
903
|
+
if warn:
|
|
904
|
+
logger.debug("{}::{}: stashed({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn = False, node_path = [], **kwargs):
|
|
908
|
+
|
|
909
|
+
if not recursive:
|
|
910
|
+
for next_node in node.next_nodes:
|
|
911
|
+
if next_node not in stashed_nodes:
|
|
912
|
+
stashed_nodes.insert_at_top(next_node)
|
|
913
|
+
else:
|
|
914
|
+
list_next = set(node.next_nodes)
|
|
915
|
+
for next_node in list_next:
|
|
916
|
+
if not isinstance(node, (TriccNodeActivityEnd, TriccNodeEnd)):
|
|
917
|
+
if next_node not in processed_nodes:
|
|
918
|
+
walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes, stashed_nodes,
|
|
919
|
+
path_len + 1,recursive, warn = warn,node_path = node_path.copy(), **kwargs)
|
|
920
|
+
else:
|
|
921
|
+
logger.critical(
|
|
922
|
+
"{}::end node of {} has a next node".format(callback.__name__, node.activity.get_name()))
|
|
923
|
+
exit(1)
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
def walkthrough_tricc_option(node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn = False,node_path = [], **kwargs):
|
|
927
|
+
if not recursive:
|
|
928
|
+
for option in node.options.values():
|
|
929
|
+
if hasattr(option, 'next_nodes') and len(option.next_nodes) > 0:
|
|
930
|
+
for next_node in option.next_nodes:
|
|
931
|
+
if next_node not in stashed_nodes:
|
|
932
|
+
stashed_nodes.insert_at_top(next_node)
|
|
933
|
+
#stashed_nodes.insert(0,next_node)
|
|
934
|
+
else:
|
|
935
|
+
list_option = []
|
|
936
|
+
while not all(elem in list_option for elem in list(node.options.values())):
|
|
937
|
+
for option in node.options.values():
|
|
938
|
+
if option not in list_option:
|
|
939
|
+
list_option.append(option)
|
|
940
|
+
# then walk the options
|
|
941
|
+
if hasattr(option, 'next_nodes') and len(option.next_nodes) > 0:
|
|
942
|
+
list_next = set(option.next_nodes)
|
|
943
|
+
for next_node in list_next:
|
|
944
|
+
if next_node not in processed_nodes:
|
|
945
|
+
walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes,
|
|
946
|
+
stashed_nodes, path_len + 1, recursive,
|
|
947
|
+
warn = warn,
|
|
948
|
+
node_path = node_path.copy(), **kwargs)
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
def get_data_for_log(node):
|
|
952
|
+
return "{}:{}|{} {}:{}".format(
|
|
953
|
+
node.group.get_name() if node.group is not None else node.activity.get_name(),
|
|
954
|
+
node.group.instance if node.group is not None else node.activity.instance ,
|
|
955
|
+
node.__class__,
|
|
956
|
+
node.get_name(),
|
|
957
|
+
node.instance)
|
|
958
|
+
|
|
959
|
+
def stashed_node_func(node, callback, recursive=False, **kwargs):
|
|
960
|
+
processed_nodes = kwargs.pop('processed_nodes', OrderedSet())
|
|
961
|
+
stashed_nodes = kwargs.pop('stashed_nodes', OrderedSet())
|
|
962
|
+
process = kwargs.pop('process', ['main'])
|
|
963
|
+
path_len = 0
|
|
964
|
+
|
|
965
|
+
walktrhough_tricc_node_processed_stached(
|
|
966
|
+
node,
|
|
967
|
+
callback,
|
|
968
|
+
processed_nodes,
|
|
969
|
+
stashed_nodes,
|
|
970
|
+
path_len,
|
|
971
|
+
recursive,
|
|
972
|
+
process=process,
|
|
973
|
+
**kwargs)
|
|
974
|
+
# callback( node, **kwargs)
|
|
975
|
+
## MANAGE STASHED NODES
|
|
976
|
+
prev_stashed_nodes = stashed_nodes.copy()
|
|
977
|
+
loop_count = 0
|
|
978
|
+
len_prev_processed_nodes = 0
|
|
979
|
+
while len(stashed_nodes) > 0:
|
|
980
|
+
loop_count = check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes,
|
|
981
|
+
loop_count)
|
|
982
|
+
prev_stashed_nodes = stashed_nodes.copy()
|
|
983
|
+
len_prev_processed_nodes = len(processed_nodes)
|
|
984
|
+
if len(stashed_nodes) > 0:
|
|
985
|
+
s_node = stashed_nodes.pop()
|
|
986
|
+
# remove duplicates
|
|
987
|
+
if s_node in stashed_nodes:
|
|
988
|
+
stashed_nodes.remove(s_node)
|
|
989
|
+
if kwargs.get('warn', True):
|
|
990
|
+
logger.debug("{}:: {}: unstashed for processing ({})".format(callback.__name__, s_node.__class__,
|
|
991
|
+
get_data_for_log(s_node),
|
|
992
|
+
len(stashed_nodes)))
|
|
993
|
+
warn = loop_count >= (9 * len(stashed_nodes )+1)
|
|
994
|
+
walktrhough_tricc_node_processed_stached(
|
|
995
|
+
s_node,
|
|
996
|
+
callback,
|
|
997
|
+
processed_nodes,
|
|
998
|
+
stashed_nodes,
|
|
999
|
+
path_len,
|
|
1000
|
+
recursive,
|
|
1001
|
+
warn=warn,
|
|
1002
|
+
process=process,
|
|
1003
|
+
**kwargs)
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
# check if the all the prev nodes are processed
|
|
1007
|
+
def is_ready_to_process(in_node, processed_nodes, strict=True, local=False):
|
|
1008
|
+
if isinstance(in_node, TriccNodeSelectOption):
|
|
1009
|
+
node = in_node.select
|
|
1010
|
+
elif (
|
|
1011
|
+
isinstance(in_node, (TriccNodeActivityStart, TriccNodeMainStart)) ):
|
|
1012
|
+
# check before
|
|
1013
|
+
return True
|
|
1014
|
+
else:
|
|
1015
|
+
node = in_node
|
|
1016
|
+
if hasattr(node, 'prev_nodes'):
|
|
1017
|
+
# ensure the previous node of the select are processed, not the option prev nodes
|
|
1018
|
+
for prev_node in node.prev_nodes:
|
|
1019
|
+
if is_prev_processed(prev_node, node, processed_nodes, local) is False:
|
|
1020
|
+
return False
|
|
1021
|
+
return True
|
|
1022
|
+
|
|
1023
|
+
def is_prev_processed(prev_node, node, processed_nodes, local):
|
|
1024
|
+
if hasattr(prev_node, 'select'):
|
|
1025
|
+
return is_prev_processed(prev_node.select, node, processed_nodes, local)
|
|
1026
|
+
if prev_node not in processed_nodes and (not local):
|
|
1027
|
+
if isinstance(prev_node, TriccNodeExclusive):
|
|
1028
|
+
iterator = iter(prev_node.prev_nodes)
|
|
1029
|
+
p_n_node = next(iterator)
|
|
1030
|
+
logger.debug("is_ready_to_process:failed:via_excl: {} - {} > {} {}:{}".format(
|
|
1031
|
+
get_data_for_log(p_n_node),
|
|
1032
|
+
prev_node.get_name(),
|
|
1033
|
+
node.__class__, node.get_name(), node.instance))
|
|
1034
|
+
|
|
1035
|
+
else:
|
|
1036
|
+
logger.debug("is_ready_to_process:failed: {} -> {} {}:{}".format(
|
|
1037
|
+
get_data_for_log(prev_node),
|
|
1038
|
+
node.__class__, node.get_name(), node.instance))
|
|
1039
|
+
|
|
1040
|
+
logger.debug("prev node node {}:{} for node {} not in processed".format(prev_node.__class__,
|
|
1041
|
+
prev_node.get_name(),
|
|
1042
|
+
node.get_name()))
|
|
1043
|
+
return False
|
|
1044
|
+
return True
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
def print_trace(node, prev_node, processed_nodes, stashed_nodes, history = []):
|
|
1049
|
+
|
|
1050
|
+
if node != prev_node:
|
|
1051
|
+
if node in processed_nodes:
|
|
1052
|
+
logger.warning("print trace :: node {} was the last not processed ({})".format(
|
|
1053
|
+
get_data_for_log(prev_node), node.id, ">".join(history)))
|
|
1054
|
+
#processed_nodes.add(prev_node)
|
|
1055
|
+
return False
|
|
1056
|
+
elif node in history:
|
|
1057
|
+
logger.critical("print trace :: CYCLE node {} found in history ({})".format(
|
|
1058
|
+
get_data_for_log(prev_node), ">".join(history)))
|
|
1059
|
+
exit(1)
|
|
1060
|
+
elif node in stashed_nodes:
|
|
1061
|
+
# logger.debug("print trace :: node {}::{} in stashed".format(node.__class__,node.get_name()))
|
|
1062
|
+
return False
|
|
1063
|
+
# else:
|
|
1064
|
+
# logger.debug("print trace :: node {} not processed/stashed".format(node.get_name()))
|
|
1065
|
+
return True
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def reverse_walkthrough(in_node, next_node, callback, processed_nodes, stashed_nodes, history = []):
|
|
1069
|
+
# transform dead-end nodes
|
|
1070
|
+
if next_node == in_node and next_node not in stashed_nodes:
|
|
1071
|
+
# workaround fir loop
|
|
1072
|
+
return False
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
if isinstance(in_node, TriccNodeSelectOption):
|
|
1076
|
+
node = in_node.select
|
|
1077
|
+
elif isinstance(in_node, TriccNodeActivityStart):
|
|
1078
|
+
node = in_node.activity
|
|
1079
|
+
else:
|
|
1080
|
+
node = in_node
|
|
1081
|
+
if callback(node, next_node, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes):
|
|
1082
|
+
history.append(node)
|
|
1083
|
+
if isinstance(in_node, TriccNodeActivity):
|
|
1084
|
+
prev_nodes = set(in_node.get_end_nodes())
|
|
1085
|
+
for prev in prev_nodes:
|
|
1086
|
+
reverse_walkthrough(prev, next_node, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history=history)
|
|
1087
|
+
if hasattr(node, 'prev_nodes'):
|
|
1088
|
+
if node.prev_nodes:
|
|
1089
|
+
for prev in node.prev_nodes:
|
|
1090
|
+
reverse_walkthrough(prev, node, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history=history)
|
|
1091
|
+
elif node in node.activity.calculates:
|
|
1092
|
+
reverse_walkthrough(prev, node.activity.root, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history=history)
|
|
1093
|
+
|
|
1094
|
+
if issubclass(node.__class__, TriccRhombusMixIn):
|
|
1095
|
+
if isinstance(node.reference, list):
|
|
1096
|
+
for ref in node.reference:
|
|
1097
|
+
reverse_walkthrough(ref, node, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history= history)
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
def get_prev_node_by_name(processed_nodes, name, node):
|
|
1103
|
+
# look for the node in the same activity
|
|
1104
|
+
last_calc = get_last_version(
|
|
1105
|
+
name,
|
|
1106
|
+
processed_nodes
|
|
1107
|
+
)
|
|
1108
|
+
if last_calc:
|
|
1109
|
+
return last_calc
|
|
1110
|
+
|
|
1111
|
+
filtered = list(
|
|
1112
|
+
filter(lambda p_node:
|
|
1113
|
+
hasattr(p_node,'name')
|
|
1114
|
+
and p_node.name == name
|
|
1115
|
+
and p_node.instance == node.instance
|
|
1116
|
+
and p_node.path_len <= node.path_len, processed_nodes
|
|
1117
|
+
))
|
|
1118
|
+
if len(filtered) == 0:
|
|
1119
|
+
filtered = list(filter(lambda p_node: hasattr(p_node, 'name') and p_node.name == name , processed_nodes))
|
|
1120
|
+
if len(filtered) > 0:
|
|
1121
|
+
return sorted(filtered, key=lambda x: x.path_len, reverse=False)[0]
|
|
1122
|
+
|
|
1123
|
+
MIN_LOOP_COUNT = 10
|
|
1124
|
+
|
|
1125
|
+
def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes, loop_count):
|
|
1126
|
+
loop_out = {}
|
|
1127
|
+
|
|
1128
|
+
if len(stashed_nodes) == len(prev_stashed_nodes):
|
|
1129
|
+
# to avoid checking the details
|
|
1130
|
+
if loop_count<=0:
|
|
1131
|
+
if loop_count < -MIN_LOOP_COUNT:
|
|
1132
|
+
loop_count = MIN_LOOP_COUNT+1
|
|
1133
|
+
else:
|
|
1134
|
+
loop_count -= 1
|
|
1135
|
+
if loop_count > MIN_LOOP_COUNT:
|
|
1136
|
+
if set(stashed_nodes) == set(prev_stashed_nodes) and len(processed_nodes) == len_prev_processed_nodes:
|
|
1137
|
+
loop_count += 1
|
|
1138
|
+
if loop_count > max(MIN_LOOP_COUNT, 11 * len(prev_stashed_nodes) + 1):
|
|
1139
|
+
logger.critical("Stashed node list was unchanged: loop likely or unresolved dependence")
|
|
1140
|
+
waited, looped = get_all_dependant(stashed_nodes, stashed_nodes, processed_nodes)
|
|
1141
|
+
logger.debug(f"{len(looped)} nodes waiting stashed nodes")
|
|
1142
|
+
logger.info("unresolved reference")
|
|
1143
|
+
for es_node in [n for n in stashed_nodes if isinstance(n, TriccReference)]:
|
|
1144
|
+
logger.info("Stashed node {}:{}|{} {}".format(
|
|
1145
|
+
es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
|
|
1146
|
+
es_node.activity.instance if hasattr(es_node,'activity') else '',
|
|
1147
|
+
es_node.__class__,
|
|
1148
|
+
es_node.get_name()))
|
|
1149
|
+
logger.info("looped nodes")
|
|
1150
|
+
for dep_list in looped:
|
|
1151
|
+
for d in looped[dep_list]:
|
|
1152
|
+
if d.get_name() == dep_list:
|
|
1153
|
+
logger.critical("[{}] depends on itself".format(
|
|
1154
|
+
dep_list,
|
|
1155
|
+
))
|
|
1156
|
+
logger.error("[{}] depends on [{}]".format(
|
|
1157
|
+
dep_list, str(d)
|
|
1158
|
+
))
|
|
1159
|
+
if dep_list in waited:
|
|
1160
|
+
for d in waited[dep_list]:
|
|
1161
|
+
logger.warning("[{}] depends on [{}]".format(
|
|
1162
|
+
dep_list, str(d)
|
|
1163
|
+
))
|
|
1164
|
+
|
|
1165
|
+
#reverse_walkthrough(es_node, es_node, print_trace, processed_nodes, stashed_nodes)
|
|
1166
|
+
logger.info("waited nodes")
|
|
1167
|
+
for dep_list in waited:
|
|
1168
|
+
if dep_list not in looped:
|
|
1169
|
+
for d in waited[dep_list]:
|
|
1170
|
+
logger.warning("[{}] depends on [{}]".format(
|
|
1171
|
+
dep_list, d.get_name()
|
|
1172
|
+
))
|
|
1173
|
+
|
|
1174
|
+
if len(stashed_nodes) == len(prev_stashed_nodes):
|
|
1175
|
+
exit(1)
|
|
1176
|
+
else:
|
|
1177
|
+
loop_count = 0
|
|
1178
|
+
else:
|
|
1179
|
+
loop_count = 0
|
|
1180
|
+
return loop_count
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
def add_to_tree(tree, n, d):
|
|
1184
|
+
n_str = str(n)
|
|
1185
|
+
if n_str not in tree:
|
|
1186
|
+
tree[n_str] = []
|
|
1187
|
+
if d not in tree[n_str]:
|
|
1188
|
+
tree[n_str].append(d)
|
|
1189
|
+
return tree
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None , looped=None):
|
|
1193
|
+
if looped is None:
|
|
1194
|
+
looped = {}
|
|
1195
|
+
if waited is None:
|
|
1196
|
+
waited = {}
|
|
1197
|
+
for n in loop:
|
|
1198
|
+
dependant = OrderedSet()
|
|
1199
|
+
i=0
|
|
1200
|
+
#logger.critical(f"{i}: {n.__class__}::{n.get_name()}::{getattr(n,'instance','')}::{process_reference(n, processed_nodes, [])}")
|
|
1201
|
+
i += 1
|
|
1202
|
+
if hasattr(n, 'prev_nodes') and n.prev_nodes:
|
|
1203
|
+
dependant = dependant | n.prev_nodes
|
|
1204
|
+
if hasattr(n, 'get_references'):
|
|
1205
|
+
dependant = dependant | (n.get_references() or OrderedSet())
|
|
1206
|
+
if not isinstance(dependant, list):
|
|
1207
|
+
pass
|
|
1208
|
+
for d in dependant:
|
|
1209
|
+
if isinstance(d, TriccNodeSelectOption):
|
|
1210
|
+
d = d.select
|
|
1211
|
+
if d not in waited and d not in looped:
|
|
1212
|
+
if isinstance(d, TriccReference):
|
|
1213
|
+
if not any(n.name == d.value for n in processed_nodes):
|
|
1214
|
+
if not any(n.name == d.value for n in stashed_nodes):
|
|
1215
|
+
waited = add_to_tree(waited, n, d)
|
|
1216
|
+
else :
|
|
1217
|
+
looped = add_to_tree(looped, n, d)
|
|
1218
|
+
|
|
1219
|
+
elif d not in processed_nodes:
|
|
1220
|
+
if d in stashed_nodes:
|
|
1221
|
+
looped = add_to_tree(looped, n, d)
|
|
1222
|
+
else :
|
|
1223
|
+
waited = add_to_tree(waited, n, d)
|
|
1224
|
+
if depth < MAX_DRILL:
|
|
1225
|
+
return get_all_dependant(waited, stashed_nodes, processed_nodes, depth+1, waited , looped)
|
|
1226
|
+
|
|
1227
|
+
return waited, looped
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
MAX_DRILL = 1
|
|
1231
|
+
|
|
1232
|
+
def get_last_end_node(processed_nodes, process=None):
|
|
1233
|
+
end_name = 'tricc_end_'
|
|
1234
|
+
if process:
|
|
1235
|
+
end_name += process
|
|
1236
|
+
return get_last_version(end_name, processed_nodes)
|
|
1237
|
+
|
|
1238
|
+
# Set the source next node to target and clean next nodes of replace node
|
|
1239
|
+
def set_prev_next_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
|
|
1240
|
+
activity = activity or source_node.activity
|
|
1241
|
+
source_id, source_node = get_node_from_id(activity, source_node, edge_only)
|
|
1242
|
+
target_id, target_node = get_node_from_id(activity, target_node, edge_only)
|
|
1243
|
+
# if it is end node, attached it to the activity/page
|
|
1244
|
+
if not edge_only:
|
|
1245
|
+
set_prev_node(source_node, target_node, replaced_node, edge_only)
|
|
1246
|
+
set_next_node(source_node, target_node, replaced_node, edge_only)
|
|
1247
|
+
|
|
1248
|
+
if activity and not any([(e.source == source_id) and ( e.target == target_id) for e in activity.edges]):
|
|
1249
|
+
label = "continue" if issubclass(source_node.__class__, TriccNodeSelectYesNo) else None
|
|
1250
|
+
activity.edges.append(TriccEdge(id = generate_id(), source = source_id, target = target_id, value = label))
|
|
1251
|
+
|
|
1252
|
+
def remove_prev_next(prev_node, next_node, activity=None):
|
|
1253
|
+
activity = activity or prev_node.activity
|
|
1254
|
+
if hasattr(prev_node, 'next_nodes') and next_node in prev_node.next_nodes:
|
|
1255
|
+
prev_node.next_nodes.remove(next_node)
|
|
1256
|
+
if hasattr(next_node, 'prev_nodes') and prev_node in next_node.prev_nodes:
|
|
1257
|
+
next_node.prev_nodes.remove(prev_node)
|
|
1258
|
+
|
|
1259
|
+
for e in list(activity.edges):
|
|
1260
|
+
if (e.target == getattr(next_node, 'id', next_node) and e.source == getattr(prev_node, 'id', prev_node)):
|
|
1261
|
+
activity.edges.remove(e)
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
def set_next_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
|
|
1266
|
+
activity = activity or source_node.activity
|
|
1267
|
+
replace_target = None
|
|
1268
|
+
if not edge_only:
|
|
1269
|
+
if replaced_node is not None and hasattr(source_node, 'path') and replaced_node == source_node.path:
|
|
1270
|
+
source_node.path = target_node
|
|
1271
|
+
elif replaced_node is not None and hasattr(source_node, 'next_nodes') and replaced_node in source_node.next_nodes:
|
|
1272
|
+
replace_target = True
|
|
1273
|
+
source_node.next_nodes.remove(replaced_node)
|
|
1274
|
+
if hasattr(replaced_node, 'prev_nodes') and source_node in replaced_node.prev_nodes:
|
|
1275
|
+
replaced_node.prev_nodes.remove(source_node)
|
|
1276
|
+
#if replaced_node is not None and hasattr(target_node, 'next_nodes') and replaced_node in target_node.next_nodes:
|
|
1277
|
+
# target_node.next_nodes.remove(replaced_node)
|
|
1278
|
+
if target_node not in source_node.next_nodes:
|
|
1279
|
+
source_node.next_nodes.add(target_node)
|
|
1280
|
+
# if rhombus in next_node of prev node and next node as ref
|
|
1281
|
+
if replaced_node is not None:
|
|
1282
|
+
rhombus_list = list(filter(lambda x: issubclass(x.__class__, TriccRhombusMixIn), source_node.next_nodes))
|
|
1283
|
+
for rhm in rhombus_list:
|
|
1284
|
+
if isinstance(rhm.reference, list):
|
|
1285
|
+
if replaced_node in rhm.reference:
|
|
1286
|
+
rhm.reference.remove(replaced_node)
|
|
1287
|
+
rhm.reference.append(target_node)
|
|
1288
|
+
if target_node.id not in activity.nodes:
|
|
1289
|
+
activity.nodes[target_node.id] = target_node
|
|
1290
|
+
if replaced_node and replaced_node in replaced_node.activity.calculates:
|
|
1291
|
+
replaced_node.activity.calculates.remove(replaced_node)
|
|
1292
|
+
if replaced_node and replace_target:
|
|
1293
|
+
if replaced_node.id in replaced_node.activity.nodes:
|
|
1294
|
+
del replaced_node.activity.nodes[replaced_node.id]
|
|
1295
|
+
next_edges = set([
|
|
1296
|
+
e for e in replaced_node.activity.edges if (e.target == replaced_node.id or e.target == replaced_node)
|
|
1297
|
+
] + [
|
|
1298
|
+
e for e in activity.edges if (e.target == replaced_node.id or e.target == replaced_node)
|
|
1299
|
+
])
|
|
1300
|
+
if len(next_edges)==0:
|
|
1301
|
+
for e in next_edges:
|
|
1302
|
+
e.target = target_node.id
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
|
|
1306
|
+
# Set the target_node prev node to source and clean prev nodes of replace_node
|
|
1307
|
+
def set_prev_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
|
|
1308
|
+
activity = activity or source_node.activity
|
|
1309
|
+
replace_source = False
|
|
1310
|
+
# update the prev node of the target not if not an end node
|
|
1311
|
+
# update directly the prev node of the target
|
|
1312
|
+
if replaced_node is not None and hasattr(target_node, 'path') and replaced_node == target_node.path:
|
|
1313
|
+
target_node.path = source_node
|
|
1314
|
+
if replaced_node is not None and hasattr(target_node, 'prev_nodes') and replaced_node in target_node.prev_nodes:
|
|
1315
|
+
replace_source = True
|
|
1316
|
+
target_node.prev_nodes.remove(replaced_node)
|
|
1317
|
+
if hasattr(replaced_node, 'next_nodes') and source_node in replaced_node.next_nodes:
|
|
1318
|
+
replaced_node.next_nodes.remove(source_node)
|
|
1319
|
+
#if replaced_node is not None and hasattr(source_node, 'prev_nodes') and replaced_node in source_node.prev_nodes:
|
|
1320
|
+
# source_node.prev_nodes.remove(replaced_node)
|
|
1321
|
+
if source_node not in target_node.prev_nodes:
|
|
1322
|
+
target_node.prev_nodes.add(source_node)
|
|
1323
|
+
if source_node.id not in activity.nodes:
|
|
1324
|
+
activity.nodes[source_node.id] = source_node
|
|
1325
|
+
if replaced_node and replace_source:
|
|
1326
|
+
if replaced_node.id in replaced_node.activity.nodes:
|
|
1327
|
+
del replaced_node.activity.nodes[replaced_node.id]
|
|
1328
|
+
next_edges = set([
|
|
1329
|
+
e for e in replaced_node.activity.edges if (e.source == replaced_node.id or e.source == replaced_node)
|
|
1330
|
+
] + [
|
|
1331
|
+
e for e in activity.edges if (e.source == replaced_node.id or e.source == replaced_node)
|
|
1332
|
+
])
|
|
1333
|
+
if len(next_edges)==0:
|
|
1334
|
+
for e in next_edges:
|
|
1335
|
+
e.target = target_node.id
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
def replace_node(old, new, page = None):
|
|
1339
|
+
if page is None:
|
|
1340
|
+
page = old.activity
|
|
1341
|
+
logger.debug("replacing node {} with node {} from page {}".format(old.get_name(), new.get_name(), page.get_name()))
|
|
1342
|
+
# list_node used to avoid updating a list in the loop
|
|
1343
|
+
list_nodes = []
|
|
1344
|
+
for prev_node in old.prev_nodes:
|
|
1345
|
+
list_nodes.append(prev_node)
|
|
1346
|
+
for prev_node in list_nodes:
|
|
1347
|
+
set_prev_next_node(prev_node, new, old)
|
|
1348
|
+
old.prev_nodes = set()
|
|
1349
|
+
list_nodes = []
|
|
1350
|
+
for next_node in old.next_nodes:
|
|
1351
|
+
list_nodes.append(next_node)
|
|
1352
|
+
for next_node in list_nodes:
|
|
1353
|
+
set_prev_next_node(new, next_node, old)
|
|
1354
|
+
old.next_nodes = set()
|
|
1355
|
+
if old in page.nodes:
|
|
1356
|
+
del page.nodes[old.id]
|
|
1357
|
+
page.nodes[new.id] = new
|
|
1358
|
+
|
|
1359
|
+
for edge in page.edges:
|
|
1360
|
+
if edge.source == old.id:
|
|
1361
|
+
edge.source = new.id
|
|
1362
|
+
if edge.target == old.id:
|
|
1363
|
+
edge.target = new.id
|
|
1364
|
+
|
|
1365
|
+
def replace_prev_next_node(prev_node, next_node, old_node, force = False):
|
|
1366
|
+
replace_prev_node(prev_node, next_node, old_node)
|
|
1367
|
+
replace_next_node(prev_node, next_node, old_node)
|
|
1368
|
+
|
|
1369
|
+
def replace_prev_node(prev_node, next_node, old_node, force = False):
|
|
1370
|
+
#create a copy pf the list
|
|
1371
|
+
list_nodes = list(next_node.prev_nodes)
|
|
1372
|
+
# replace in case old node is found
|
|
1373
|
+
for p_n_node in list_nodes:
|
|
1374
|
+
if p_n_node == old_node or force:
|
|
1375
|
+
set_prev_next_node(prev_node, next_node, old_node)
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
def replace_next_node(prev_node,next_node,old_node):
|
|
1379
|
+
list_nodes = list(prev_node.next_nodes)
|
|
1380
|
+
for n_p_node in list_nodes:
|
|
1381
|
+
if n_p_node == old_node :
|
|
1382
|
+
set_prev_next_node(prev_node, next_node, old_node)
|
|
1383
|
+
|
|
1384
|
+
def reorder_node_list(list_node, group, processed_nodes):
|
|
1385
|
+
active_activities = set(n.activity for n in processed_nodes)
|
|
1386
|
+
|
|
1387
|
+
# Define a lambda to assign numeric priorities
|
|
1388
|
+
def filter_logic(l_node):
|
|
1389
|
+
|
|
1390
|
+
if (
|
|
1391
|
+
isinstance(l_node, TriccNodeWait)
|
|
1392
|
+
and any(isinstance(rn, TriccNodeActivity) and any(sn.activity == rn for sn in list_node) for rn in l_node.reference)
|
|
1393
|
+
):
|
|
1394
|
+
return 7
|
|
1395
|
+
elif group is not None and hasattr(l_node, 'group') and l_node.group and l_node.group.id == group.id:
|
|
1396
|
+
return 0 # Highest priority: Same group
|
|
1397
|
+
elif issubclass(l_node.__class__, TriccRhombusMixIn) :
|
|
1398
|
+
return 6
|
|
1399
|
+
elif hasattr(group, 'group') and group.group and l_node.group and l_node.group.id == group.group.id:
|
|
1400
|
+
return 1 # Second priority: Parent group
|
|
1401
|
+
elif not isinstance(l_node.activity.root, TriccNodeActivityStart) and l_node.activity in active_activities:
|
|
1402
|
+
return 2 # Third priority: Active activities
|
|
1403
|
+
elif not isinstance(l_node.activity.root, TriccNodeActivityStart):
|
|
1404
|
+
return 3 # Third priority: Active activities
|
|
1405
|
+
elif l_node.activity in active_activities:
|
|
1406
|
+
return 4 # Third priority: Active activities
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
else:
|
|
1410
|
+
return 5 # Lowest priority: Others
|
|
1411
|
+
|
|
1412
|
+
# Sort list_node in place using filter_logic as the key
|
|
1413
|
+
list_node.sort(key=filter_logic, reverse=False)
|
|
1414
|
+
return None
|
|
1415
|
+
|
|
1416
|
+
def loop_info(loop, **kwargs):
|
|
1417
|
+
logger.critical("dependency details")
|
|
1418
|
+
for n in loop:
|
|
1419
|
+
i=0
|
|
1420
|
+
logger.critical(f"{i}: {n.__class__}::{n.get_name()}")
|
|
1421
|
+
i += 1
|
|
1422
|
+
|
|
1423
|
+
|
|
1424
|
+
def has_loop(node, processed_nodes, stashed_nodes, warn , node_path=[], action_on_loop=loop_info,action_on_other=None, **kwargs):
|
|
1425
|
+
next_nodes = get_extended_next_nodes(node)
|
|
1426
|
+
for next_node in next_nodes:
|
|
1427
|
+
if next_node in node_path:
|
|
1428
|
+
loop_start_key = node_path.index(next_node)
|
|
1429
|
+
loop = node_path[loop_start_key:]
|
|
1430
|
+
loop.append(node)
|
|
1431
|
+
loop.append(next_node)
|
|
1432
|
+
action_on_loop(loop, **kwargs)
|
|
1433
|
+
return False
|
|
1434
|
+
if callable(action_on_other):
|
|
1435
|
+
action_on_other(next_node, **kwargs)
|
|
1436
|
+
return True
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
def get_extended_next_nodes(node):
|
|
1441
|
+
|
|
1442
|
+
nodes = node.next_nodes if hasattr(node,'next_nodes') else set()
|
|
1443
|
+
if issubclass(node.__class__, TriccNodeSelect ):
|
|
1444
|
+
for o in node.options.values():
|
|
1445
|
+
nodes = nodes | o.next_nodes
|
|
1446
|
+
if isinstance(node, ( TriccNodeActivity) ):
|
|
1447
|
+
nodes = nodes | node.root.next_nodes
|
|
1448
|
+
return nodes
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
# calculate or retrieve a node expression
|
|
1452
|
+
def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=False, negate=False, process=None):
|
|
1453
|
+
# in case of calculate we only use the select multiple if none is not selected
|
|
1454
|
+
expression = None
|
|
1455
|
+
negate_expression = None
|
|
1456
|
+
node = in_node
|
|
1457
|
+
if isinstance(node, (TriccNodeActivityStart,TriccNodeMainStart, TriccNodeActivityEnd)):
|
|
1458
|
+
if is_prev and is_calculate:
|
|
1459
|
+
expression = get_node_expression(
|
|
1460
|
+
node.activity,
|
|
1461
|
+
processed_nodes=processed_nodes,
|
|
1462
|
+
is_calculate=is_calculate,
|
|
1463
|
+
is_prev=is_prev,
|
|
1464
|
+
negate=negate,
|
|
1465
|
+
process=process
|
|
1466
|
+
)
|
|
1467
|
+
elif isinstance(node, (TriccNodeActivityStart)):
|
|
1468
|
+
return None
|
|
1469
|
+
|
|
1470
|
+
elif isinstance(node, TriccNodeWait):
|
|
1471
|
+
if is_prev:
|
|
1472
|
+
# the wait don't do any calculation with the reference it is only use to wait until the reference are valid
|
|
1473
|
+
return get_node_expression(
|
|
1474
|
+
node.path,
|
|
1475
|
+
processed_nodes=processed_nodes,
|
|
1476
|
+
is_calculate=is_calculate,
|
|
1477
|
+
is_prev=True,
|
|
1478
|
+
process=process
|
|
1479
|
+
)
|
|
1480
|
+
else:
|
|
1481
|
+
#it is a empty calculate
|
|
1482
|
+
return None
|
|
1483
|
+
elif isinstance(node, TriccNodeRhombus):
|
|
1484
|
+
# if is_prev:
|
|
1485
|
+
# expression = TriccOperation(
|
|
1486
|
+
# TriccOperator.ISTRUE,
|
|
1487
|
+
# [node]
|
|
1488
|
+
# )
|
|
1489
|
+
# else:
|
|
1490
|
+
expression = get_rhombus_terms(node, processed_nodes, process=process) # if issubclass(node.__class__, TricNodeDisplayCalulate) else TRICC_CALC_EXPRESSION.format(get_export_name(node)) #
|
|
1491
|
+
negate_expression = not_clean(expression)
|
|
1492
|
+
if node.path is None :
|
|
1493
|
+
if len(node.prev_nodes) == 1:
|
|
1494
|
+
node.path = list(node.prev_nodes)[0]
|
|
1495
|
+
elif len(node.prev_nodes) > 1:
|
|
1496
|
+
logger.critical(f"missing path for Rhombus {node.get_name()}")
|
|
1497
|
+
exit(1)
|
|
1498
|
+
prev_exp = get_node_expression(
|
|
1499
|
+
node.path,
|
|
1500
|
+
processed_nodes=processed_nodes,
|
|
1501
|
+
is_calculate=is_calculate,
|
|
1502
|
+
is_prev=True,
|
|
1503
|
+
process=process)
|
|
1504
|
+
if prev_exp and expression:
|
|
1505
|
+
expression = and_join([prev_exp, expression])
|
|
1506
|
+
negate_expression = and_join([
|
|
1507
|
+
prev_exp,
|
|
1508
|
+
negate_expression
|
|
1509
|
+
])
|
|
1510
|
+
|
|
1511
|
+
elif prev_exp:
|
|
1512
|
+
|
|
1513
|
+
logger.error(f"useless rhombus {node.get_name()}")
|
|
1514
|
+
expression = prev_exp
|
|
1515
|
+
negate_expression = prev_exp
|
|
1516
|
+
logger.critical(f"Rhombus without expression {node.get_name()}")
|
|
1517
|
+
elif is_prev and issubclass(node.__class__, TriccNodeDisplayCalculateBase):
|
|
1518
|
+
expression = TriccOperation(TriccOperator.ISTRUE, [node])
|
|
1519
|
+
elif hasattr(node, 'expression_reference') and isinstance(node.expression_reference, TriccOperation):
|
|
1520
|
+
# if issubclass(node.__class__, TriccNodeDisplayCalculateBase):
|
|
1521
|
+
# expression = TriccOperation(
|
|
1522
|
+
# TriccOperator.CAST_NUMBER,
|
|
1523
|
+
# [node.expression_reference])
|
|
1524
|
+
# else:
|
|
1525
|
+
expression = node.expression_reference
|
|
1526
|
+
elif not is_prev and hasattr(node, 'relevance') and isinstance(node.relevance, TriccOperation):
|
|
1527
|
+
expression = node.relevance
|
|
1528
|
+
elif is_prev and isinstance(node, TriccNodeSelectOption):
|
|
1529
|
+
if negate:
|
|
1530
|
+
negate_expression = get_selected_option_expression(node, negate)
|
|
1531
|
+
else:
|
|
1532
|
+
expression = get_selected_option_expression(node, negate)
|
|
1533
|
+
#TODO remove that and manage it on the "Save" part
|
|
1534
|
+
elif is_prev and isinstance(node, TriccNodeSelectNotAvailable):
|
|
1535
|
+
expression = TriccOperation(
|
|
1536
|
+
TriccOperator.SELECTED,
|
|
1537
|
+
[
|
|
1538
|
+
node,
|
|
1539
|
+
TriccStatic(1)
|
|
1540
|
+
]
|
|
1541
|
+
)
|
|
1542
|
+
elif issubclass(node.__class__, TriccNodeCalculateBase):
|
|
1543
|
+
if negate:
|
|
1544
|
+
negate_expression = get_calculation_terms(node, processed_nodes=processed_nodes, is_calculate=is_calculate, negate=True, process=process)
|
|
1545
|
+
else:
|
|
1546
|
+
expression = get_calculation_terms(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
|
|
1547
|
+
elif ONE_QUESTION_AT_A_TIME and is_prev and not is_calculate and hasattr(node, 'required') and node.required:
|
|
1548
|
+
expression = get_required_node_expression(node)
|
|
1549
|
+
|
|
1550
|
+
if expression is None:
|
|
1551
|
+
expression = get_prev_node_expression(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
|
|
1552
|
+
# in_node not in processed_nodes is need for calculates that can but run after the end of the activity
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
if isinstance(node, TriccNodeActivity):
|
|
1556
|
+
|
|
1557
|
+
|
|
1558
|
+
if node.base_instance is not None and not is_prev:
|
|
1559
|
+
activity = node
|
|
1560
|
+
expression_inputs = []
|
|
1561
|
+
past_instances = [
|
|
1562
|
+
n for n in processed_nodes if getattr(n.base_instance, 'id', None) == node.base_instance.id
|
|
1563
|
+
]
|
|
1564
|
+
for past_instance in past_instances:
|
|
1565
|
+
add_sub_expression(
|
|
1566
|
+
expression_inputs,
|
|
1567
|
+
get_node_expression(
|
|
1568
|
+
past_instance,
|
|
1569
|
+
processed_nodes=processed_nodes,
|
|
1570
|
+
is_calculate=False,
|
|
1571
|
+
is_prev=True,
|
|
1572
|
+
process=process
|
|
1573
|
+
)
|
|
1574
|
+
)
|
|
1575
|
+
|
|
1576
|
+
if isinstance(node.applicability,(TriccStatic,TriccOperation, TriccReference)):
|
|
1577
|
+
if expression:
|
|
1578
|
+
expression = and_join([node.applicability, expression])
|
|
1579
|
+
else:
|
|
1580
|
+
expression = node.applicability
|
|
1581
|
+
if expression and expression_inputs:
|
|
1582
|
+
add_sub_expression(expression_inputs, expression)
|
|
1583
|
+
expression = nand_join(expression, or_join(expression_inputs))
|
|
1584
|
+
elif expression_inputs:
|
|
1585
|
+
expression = negate_term(or_join(expression_inputs))
|
|
1586
|
+
if not is_prev:
|
|
1587
|
+
end_expressions = []
|
|
1588
|
+
f_end_expression = get_end_expression(processed_nodes)
|
|
1589
|
+
if f_end_expression:
|
|
1590
|
+
end_expressions.append(f_end_expression)
|
|
1591
|
+
if process[0] in PROCESSES:
|
|
1592
|
+
for p in PROCESSES[PROCESSES.index(process[0])+1:]:
|
|
1593
|
+
p_end_expression = get_end_expression(processed_nodes, p)
|
|
1594
|
+
if p_end_expression:
|
|
1595
|
+
end_expressions.append(p_end_expression)
|
|
1596
|
+
if node.applicability:
|
|
1597
|
+
end_expressions.append(node.applicability)
|
|
1598
|
+
if end_expressions:
|
|
1599
|
+
if expression:
|
|
1600
|
+
end_expressions.append(expression)
|
|
1601
|
+
if len(end_expressions) == 1:
|
|
1602
|
+
expression = end_expressions[0]
|
|
1603
|
+
else:
|
|
1604
|
+
expression = and_join(end_expressions)
|
|
1605
|
+
|
|
1606
|
+
|
|
1607
|
+
|
|
1608
|
+
if negate:
|
|
1609
|
+
if negate_expression is not None:
|
|
1610
|
+
return negate_expression
|
|
1611
|
+
elif expression is not None:
|
|
1612
|
+
return negate_term(expression)
|
|
1613
|
+
else:
|
|
1614
|
+
logger.critical("exclusive can not negate None from {}".format(node.get_name()))
|
|
1615
|
+
# exit(1)
|
|
1616
|
+
else:
|
|
1617
|
+
return expression
|
|
1618
|
+
|
|
1619
|
+
def get_end_expression(processed_nodes, process=None):
|
|
1620
|
+
end_node = get_last_end_node(processed_nodes, process)
|
|
1621
|
+
if end_node:
|
|
1622
|
+
return TriccOperation(
|
|
1623
|
+
TriccOperator.ISNOTTRUE,
|
|
1624
|
+
[end_node]
|
|
1625
|
+
)
|
|
1626
|
+
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
|
|
1630
|
+
def export_proposed_diags(activity, diags=None, **kwargs):
|
|
1631
|
+
if diags is None:
|
|
1632
|
+
diags = []
|
|
1633
|
+
for node in activity.nodes.values():
|
|
1634
|
+
if isinstance(node, TriccNodeActivity):
|
|
1635
|
+
diags = export_proposed_diags(node, diags, **kwargs)
|
|
1636
|
+
if isinstance(node, TriccNodeProposedDiagnosis):
|
|
1637
|
+
if node.last is not False\
|
|
1638
|
+
and not any([diag.name == node.name for diag in diags]):
|
|
1639
|
+
diags.append(node)
|
|
1640
|
+
return diags
|
|
1641
|
+
|
|
1642
|
+
|
|
1643
|
+
def get_accept_diagnostic_node(code, display, severity, activity):
|
|
1644
|
+
node = TriccNodeAcceptDiagnostic(
|
|
1645
|
+
id=generate_id("pre_final." + code),
|
|
1646
|
+
name="pre_final." + code,
|
|
1647
|
+
label=display,
|
|
1648
|
+
list_name="acc_rej",
|
|
1649
|
+
activity=activity,
|
|
1650
|
+
group=activity,
|
|
1651
|
+
severity=severity
|
|
1652
|
+
)
|
|
1653
|
+
node.options = get_select_accept_reject_options(node, node.activity)
|
|
1654
|
+
return node
|
|
1655
|
+
|
|
1656
|
+
def get_diagnostic_node(code, display, severity, activity):
|
|
1657
|
+
node = TriccNodeAcceptDiagnostic(
|
|
1658
|
+
id=generate_id("final." + code),
|
|
1659
|
+
name="final." + code,
|
|
1660
|
+
label=display,
|
|
1661
|
+
list_name="acc_rej",
|
|
1662
|
+
activity=activity,
|
|
1663
|
+
group=activity,
|
|
1664
|
+
severity=severity
|
|
1665
|
+
)
|
|
1666
|
+
node.options = get_select_accept_reject_options(node, node.activity)
|
|
1667
|
+
return node
|
|
1668
|
+
|
|
1669
|
+
def get_select_accept_reject_options(node, group):
|
|
1670
|
+
yes = TriccNodeSelectOption(
|
|
1671
|
+
id = generate_id(f'accept{node.id}'),
|
|
1672
|
+
name=f"{TRICC_TRUE_VALUE}",
|
|
1673
|
+
label="Accept",
|
|
1674
|
+
select = node,
|
|
1675
|
+
group = group,
|
|
1676
|
+
list_name = node.list_name
|
|
1677
|
+
)
|
|
1678
|
+
no = TriccNodeSelectOption(
|
|
1679
|
+
id = generate_id(f'reject{node.id}'),
|
|
1680
|
+
name=f"{TRICC_FALSE_VALUE}",
|
|
1681
|
+
label="Reject",
|
|
1682
|
+
select = node,
|
|
1683
|
+
group = group,
|
|
1684
|
+
list_name = node.list_name
|
|
1685
|
+
)
|
|
1686
|
+
return {0:yes, 1:no }
|
|
1687
|
+
|
|
1688
|
+
def create_determine_diagnosis_activity(diags):
|
|
1689
|
+
start = TriccNodeActivityStart(
|
|
1690
|
+
id=generate_id('start.determine-diagnosis'),
|
|
1691
|
+
name="start.determine-diagnosis"
|
|
1692
|
+
)
|
|
1693
|
+
|
|
1694
|
+
|
|
1695
|
+
activity = TriccNodeActivity(
|
|
1696
|
+
id=generate_id('activity-determine-diagnosis'),
|
|
1697
|
+
name='determine-diagnosis',
|
|
1698
|
+
label='Diagnosis',
|
|
1699
|
+
root=start,
|
|
1700
|
+
)
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
start.activity = activity
|
|
1704
|
+
start.group = activity
|
|
1705
|
+
diags_conf = []
|
|
1706
|
+
r_diags_conf = []
|
|
1707
|
+
end = TriccNodeActivityEnd(
|
|
1708
|
+
id=generate_id("end.determine-diagnosis"),
|
|
1709
|
+
name="end.determine-diagnosis",
|
|
1710
|
+
activity=activity,
|
|
1711
|
+
group=activity,
|
|
1712
|
+
)
|
|
1713
|
+
activity.nodes[end.id]=end
|
|
1714
|
+
for proposed in diags:
|
|
1715
|
+
d = get_diagnostic_node(proposed.name, proposed.label, proposed.severity, activity)
|
|
1716
|
+
diags_conf.append(d)
|
|
1717
|
+
r = TriccNodeRhombus(
|
|
1718
|
+
id=generate_id(f"proposed-rhombus{proposed.id}"),
|
|
1719
|
+
expression_reference=TriccOperation(
|
|
1720
|
+
TriccOperator.ISTRUE,
|
|
1721
|
+
[TriccReference(proposed.name)]
|
|
1722
|
+
),
|
|
1723
|
+
reference=[TriccReference(proposed.name)],
|
|
1724
|
+
activity=activity,
|
|
1725
|
+
group=activity )
|
|
1726
|
+
r_diags_conf.append(r)
|
|
1727
|
+
set_prev_next_node(start, r, edge_only=False)
|
|
1728
|
+
set_prev_next_node(r, d, edge_only=False)
|
|
1729
|
+
set_prev_next_node(d, end, edge_only=False)
|
|
1730
|
+
activity.nodes[d.options[0].id] = d.options[0]
|
|
1731
|
+
activity.nodes[d.options[1].id] = d.options[1]
|
|
1732
|
+
activity.nodes[d.id]=d
|
|
1733
|
+
activity.nodes[r.id]=r
|
|
1734
|
+
# fallback
|
|
1735
|
+
f = TriccNodeSelectMultiple(
|
|
1736
|
+
name="tricc.manual.diag",
|
|
1737
|
+
label="Add a diagnostic",
|
|
1738
|
+
list_name='manual_diag',
|
|
1739
|
+
id=generate_id("tricc.manual.diag"),
|
|
1740
|
+
activity=activity,
|
|
1741
|
+
group=activity,
|
|
1742
|
+
required=TriccStatic(False),
|
|
1743
|
+
|
|
1744
|
+
)
|
|
1745
|
+
options = [
|
|
1746
|
+
TriccNodeSelectOption(
|
|
1747
|
+
id=generate_id(d.name),
|
|
1748
|
+
name=d.name,
|
|
1749
|
+
label=d.label,
|
|
1750
|
+
list_name=f.list_name,
|
|
1751
|
+
relevance=d.activity.applicability,
|
|
1752
|
+
select=f
|
|
1753
|
+
) for d in diags
|
|
1754
|
+
]
|
|
1755
|
+
f.options=dict(zip(range(0, len(options)), options))
|
|
1756
|
+
wait2 = get_activity_wait([activity.root], diags_conf, [f], edge_only=False)
|
|
1757
|
+
activity.nodes[wait2.id]=wait2
|
|
1758
|
+
activity.nodes[f.id]=f
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
return activity
|
|
1762
|
+
|
|
1763
|
+
def get_prev_node_expression( node, processed_nodes, is_calculate=False, excluded_name=None, process=None):
|
|
1764
|
+
expression = None
|
|
1765
|
+
if node is None:
|
|
1766
|
+
pass
|
|
1767
|
+
# when getting the prev node, we calculate the
|
|
1768
|
+
if hasattr(node, 'expression_inputs') and len(node.expression_inputs) > 0:
|
|
1769
|
+
expression_inputs = node.expression_inputs
|
|
1770
|
+
expression_inputs = clean_or_list(expression_inputs)
|
|
1771
|
+
else:
|
|
1772
|
+
expression_inputs = []
|
|
1773
|
+
for prev_node in node.prev_nodes:
|
|
1774
|
+
if excluded_name is None or prev_node != excluded_name or (
|
|
1775
|
+
isinstance(excluded_name, str) and hasattr(prev_node, 'name') and prev_node.name != excluded_name): # or isinstance(prev_node, TriccNodeActivityEnd):
|
|
1776
|
+
# the rhombus should calculate only reference
|
|
1777
|
+
add_sub_expression(expression_inputs, get_node_expression(
|
|
1778
|
+
prev_node,
|
|
1779
|
+
processed_nodes=processed_nodes,
|
|
1780
|
+
is_calculate=is_calculate,
|
|
1781
|
+
is_prev=True,
|
|
1782
|
+
process=process))
|
|
1783
|
+
# avoid void is there is not conditions to avoid looping too much itme
|
|
1784
|
+
# expression_inputs = clean_or_list(
|
|
1785
|
+
# [
|
|
1786
|
+
# get_tricc_operation_operand(e)
|
|
1787
|
+
# if isinstance(expression, TriccOperation)
|
|
1788
|
+
# else e
|
|
1789
|
+
# for e in expression_inputs])
|
|
1790
|
+
|
|
1791
|
+
expression = None
|
|
1792
|
+
if len(expression_inputs) == 1:
|
|
1793
|
+
expression = expression_inputs[0]
|
|
1794
|
+
|
|
1795
|
+
elif expression_inputs:
|
|
1796
|
+
expression = TriccOperation(
|
|
1797
|
+
TriccOperator.OR,
|
|
1798
|
+
expression_inputs
|
|
1799
|
+
)
|
|
1800
|
+
# if isinstance(node, TriccNodeExclusive):
|
|
1801
|
+
# expression = TRICC_NEGATE.format(expression)
|
|
1802
|
+
# only used for activityStart
|
|
1803
|
+
else:
|
|
1804
|
+
expression = TriccStatic(True)
|
|
1805
|
+
return expression
|
|
1806
|
+
|
|
1807
|
+
def get_activity_end_terms( node, processed_nodes, process=None):
|
|
1808
|
+
end_nodes = node.get_end_nodes()
|
|
1809
|
+
expression_inputs = []
|
|
1810
|
+
for end_node in end_nodes:
|
|
1811
|
+
add_sub_expression(
|
|
1812
|
+
expression_inputs,
|
|
1813
|
+
get_node_expression(
|
|
1814
|
+
end_node,
|
|
1815
|
+
processed_nodes=processed_nodes,
|
|
1816
|
+
is_calculate=False,
|
|
1817
|
+
is_prev=True,
|
|
1818
|
+
process=process))
|
|
1819
|
+
|
|
1820
|
+
return or_join(expression_inputs)
|
|
1821
|
+
|
|
1822
|
+
def get_count_terms( node, processed_nodes, is_calculate, negate=False, process=None):
|
|
1823
|
+
terms = []
|
|
1824
|
+
for prev_node in node.prev_nodes:
|
|
1825
|
+
operation_none = TriccOperation(
|
|
1826
|
+
TriccOperator.SELECTED,
|
|
1827
|
+
[
|
|
1828
|
+
prev_node,
|
|
1829
|
+
TriccStatic('opt_none')
|
|
1830
|
+
]
|
|
1831
|
+
)
|
|
1832
|
+
if isinstance(prev_node, TriccNodeSelectMultiple):
|
|
1833
|
+
if negate:
|
|
1834
|
+
terms.append()
|
|
1835
|
+
#terms.append(TRICC_SELECT_MULTIPLE_CALC_NONE_EXPRESSION.format(get_export_name(prev_node)))
|
|
1836
|
+
else:
|
|
1837
|
+
terms.append(TriccOperation(
|
|
1838
|
+
TriccOperator.MINUS,
|
|
1839
|
+
[
|
|
1840
|
+
TriccOperation(
|
|
1841
|
+
TriccOperator.NATIVE,
|
|
1842
|
+
[
|
|
1843
|
+
'count-selected',
|
|
1844
|
+
prev_node
|
|
1845
|
+
]
|
|
1846
|
+
),TriccOperation(
|
|
1847
|
+
TriccOperator.CAST_NUMBER,
|
|
1848
|
+
[
|
|
1849
|
+
operation_none
|
|
1850
|
+
]
|
|
1851
|
+
)
|
|
1852
|
+
]))
|
|
1853
|
+
#terms.append(TRICC_SELECT_MULTIPLE_CALC_EXPRESSION.format(get_export_name(prev_node)))
|
|
1854
|
+
elif isinstance(prev_node, (TriccNodeSelectYesNo, TriccNodeSelectNotAvailable)):
|
|
1855
|
+
terms.append(TriccOperation(
|
|
1856
|
+
TriccOperator.SELECTED,
|
|
1857
|
+
[
|
|
1858
|
+
prev_node,
|
|
1859
|
+
TriccStatic('1')
|
|
1860
|
+
]
|
|
1861
|
+
))
|
|
1862
|
+
#terms.append(TRICC_SELECTED_EXPRESSION.format(get_export_name(prev_node), '1'))
|
|
1863
|
+
elif isinstance(prev_node, TriccNodeSelectOption):
|
|
1864
|
+
terms.append(get_selected_option_expression(prev_node, negate))
|
|
1865
|
+
else:
|
|
1866
|
+
if negate:
|
|
1867
|
+
terms.append(
|
|
1868
|
+
TriccOperation(
|
|
1869
|
+
TriccOperator.CAST_NUMBER,
|
|
1870
|
+
[
|
|
1871
|
+
TriccOperation(
|
|
1872
|
+
TriccOperator.NATIVE,
|
|
1873
|
+
[
|
|
1874
|
+
TriccOperation(
|
|
1875
|
+
TriccOperator.CAST_NUMBER,
|
|
1876
|
+
[
|
|
1877
|
+
get_node_expression(
|
|
1878
|
+
prev_node,
|
|
1879
|
+
processed_nodes=processed_nodes,
|
|
1880
|
+
is_calculate=False,
|
|
1881
|
+
is_prev=True,
|
|
1882
|
+
process=process)
|
|
1883
|
+
]),
|
|
1884
|
+
TriccStatic('0')
|
|
1885
|
+
]
|
|
1886
|
+
)
|
|
1887
|
+
]
|
|
1888
|
+
)
|
|
1889
|
+
)
|
|
1890
|
+
else:
|
|
1891
|
+
terms.append(
|
|
1892
|
+
TriccOperation(
|
|
1893
|
+
TriccOperator.CAST_NUMBER,
|
|
1894
|
+
[
|
|
1895
|
+
get_node_expression(
|
|
1896
|
+
prev_node,
|
|
1897
|
+
processed_nodes=processed_nodes,
|
|
1898
|
+
is_calculate=False,
|
|
1899
|
+
is_prev=True,
|
|
1900
|
+
process=process)
|
|
1901
|
+
]
|
|
1902
|
+
))
|
|
1903
|
+
if len(terms) == 1:
|
|
1904
|
+
return TriccOperation(
|
|
1905
|
+
TriccOperator.CAST_NUMBER,
|
|
1906
|
+
[terms[0]]
|
|
1907
|
+
)
|
|
1908
|
+
elif len(terms) > 0:
|
|
1909
|
+
return TriccOperation(
|
|
1910
|
+
TriccOperator.PLUS,
|
|
1911
|
+
[
|
|
1912
|
+
TriccOperation(
|
|
1913
|
+
TriccOperator.CAST_NUMBER,
|
|
1914
|
+
[term]
|
|
1915
|
+
) for term in terms
|
|
1916
|
+
]
|
|
1917
|
+
)
|
|
1918
|
+
|
|
1919
|
+
|
|
1920
|
+
def get_add_terms( node, processed_nodes, is_calculate=False, negate=False, process=None):
|
|
1921
|
+
if negate:
|
|
1922
|
+
logger.warning("negate not supported for Add node {}".format(node.get_name()))
|
|
1923
|
+
terms = []
|
|
1924
|
+
for prev_node in node.prev_nodes:
|
|
1925
|
+
if issubclass(prev_node, TriccNodeNumber) or isinstance(node, TriccNodeCount):
|
|
1926
|
+
terms.append(
|
|
1927
|
+
TriccOperation(
|
|
1928
|
+
TriccOperator.COALESCE,
|
|
1929
|
+
[
|
|
1930
|
+
prev_node,
|
|
1931
|
+
TriccStatic(0)
|
|
1932
|
+
]
|
|
1933
|
+
)
|
|
1934
|
+
)
|
|
1935
|
+
else:
|
|
1936
|
+
terms.append(
|
|
1937
|
+
TriccOperation(
|
|
1938
|
+
TriccOperator.CAST_NUMBER,
|
|
1939
|
+
[
|
|
1940
|
+
get_node_expression(
|
|
1941
|
+
prev_node,
|
|
1942
|
+
processed_nodes=processed_nodes,
|
|
1943
|
+
is_calculate=False,
|
|
1944
|
+
is_prev=True,
|
|
1945
|
+
process=process)
|
|
1946
|
+
]
|
|
1947
|
+
)
|
|
1948
|
+
)
|
|
1949
|
+
if len(terms) > 0:
|
|
1950
|
+
operation = terms[0]
|
|
1951
|
+
if len(terms) > 1:
|
|
1952
|
+
for term in terms[1:]:
|
|
1953
|
+
operation = TriccOperation(
|
|
1954
|
+
TriccOperator.ADD,
|
|
1955
|
+
[
|
|
1956
|
+
operation,
|
|
1957
|
+
term
|
|
1958
|
+
]
|
|
1959
|
+
)
|
|
1960
|
+
return operation
|
|
1961
|
+
|
|
1962
|
+
def get_rhombus_terms( node, processed_nodes, is_calculate=False, negate=False, process=None):
|
|
1963
|
+
expression = None
|
|
1964
|
+
left_term = None
|
|
1965
|
+
operator = None
|
|
1966
|
+
if node.reference is not None:
|
|
1967
|
+
if isinstance(node.reference, set):
|
|
1968
|
+
node.reference = list(node.reference)
|
|
1969
|
+
# calcualte the expression only for select muzltiple and fake calculate
|
|
1970
|
+
if issubclass(node.reference.__class__, (list,OrderedSet)):
|
|
1971
|
+
if node.expression_reference is None and len(node.reference) == 1:
|
|
1972
|
+
ref = node.reference[0]
|
|
1973
|
+
if issubclass(ref.__class__, TriccNodeBaseModel):
|
|
1974
|
+
if isinstance(ref, TriccNodeActivity):
|
|
1975
|
+
expression = get_activity_end_terms(ref, processed_nodes, process=process)
|
|
1976
|
+
elif issubclass(ref.__class__, TriccNodeFakeCalculateBase):
|
|
1977
|
+
expression = get_node_expression(
|
|
1978
|
+
ref,
|
|
1979
|
+
processed_nodes=processed_nodes,
|
|
1980
|
+
is_calculate=True,
|
|
1981
|
+
is_prev=True,
|
|
1982
|
+
process=process
|
|
1983
|
+
)
|
|
1984
|
+
else:
|
|
1985
|
+
expression = ref
|
|
1986
|
+
elif issubclass(ref.__class__, TriccReference):
|
|
1987
|
+
expression = ref
|
|
1988
|
+
else:
|
|
1989
|
+
logger.critical('reference {0} was not found in the previous nodes of node {1}'.format(node.reference,
|
|
1990
|
+
node.get_name()))
|
|
1991
|
+
exit(1)
|
|
1992
|
+
elif node.expression_reference is not None and node.expression_reference != '':
|
|
1993
|
+
if isinstance(node.expression_reference, TriccOperation):
|
|
1994
|
+
return node.expression_reference
|
|
1995
|
+
elif isinstance(node.expression_reference, str):
|
|
1996
|
+
expression = node.expression_reference.format(*get_list_names(node.reference))
|
|
1997
|
+
else:
|
|
1998
|
+
logger.critical('expression_reference {0} unsuported type {1}'.format(node.expression_reference, node.expression_reference.__class__.__name__))
|
|
1999
|
+
exit(1)
|
|
2000
|
+
|
|
2001
|
+
else:
|
|
2002
|
+
logger.warning("missing expression for node {}".format(node.get_name()))
|
|
2003
|
+
else:
|
|
2004
|
+
logger.critical('reference {0} is not a list {1}'.format(node.reference, node.get_name()))
|
|
2005
|
+
exit(1)
|
|
2006
|
+
else:
|
|
2007
|
+
logger.critical('reference empty for Rhombis {}'.format( node.get_name()))
|
|
2008
|
+
exit(1)
|
|
2009
|
+
|
|
2010
|
+
if expression is not None:
|
|
2011
|
+
if isinstance(expression, TriccOperation):
|
|
2012
|
+
return expression
|
|
2013
|
+
elif issubclass(expression.__class__ , TriccNodeCalculateBase):
|
|
2014
|
+
return TriccOperation(
|
|
2015
|
+
TriccOperator.CAST_NUMBER,
|
|
2016
|
+
[
|
|
2017
|
+
get_node_expression(
|
|
2018
|
+
expression,
|
|
2019
|
+
processed_nodes=processed_nodes,
|
|
2020
|
+
is_calculate=True,
|
|
2021
|
+
is_prev=True,
|
|
2022
|
+
process=process
|
|
2023
|
+
)
|
|
2024
|
+
])
|
|
2025
|
+
elif issubclass(expression.__class__ , (TriccOperation) ):
|
|
2026
|
+
return expression
|
|
2027
|
+
elif issubclass(expression.__class__ , (TriccNodeDisplayModel, TriccReference)):
|
|
2028
|
+
return TriccOperation(
|
|
2029
|
+
TriccOperator.ISTRUE,
|
|
2030
|
+
[
|
|
2031
|
+
expression
|
|
2032
|
+
]
|
|
2033
|
+
)
|
|
2034
|
+
else:
|
|
2035
|
+
if left_term is not None and re.search(" (\+)|(\-)|(or)|(and) ", expression):
|
|
2036
|
+
expression = "({0}){1}".format(expression, left_term)
|
|
2037
|
+
else:
|
|
2038
|
+
expression = "{0}{1}".format(expression, left_term)
|
|
2039
|
+
else:
|
|
2040
|
+
logger.critical("Rhombus reference was not found for node {}, reference {}".format(
|
|
2041
|
+
node.get_name(),
|
|
2042
|
+
node.reference
|
|
2043
|
+
))
|
|
2044
|
+
exit(1)
|
|
2045
|
+
|
|
2046
|
+
return expression
|
|
2047
|
+
# function that generate the calculation terms return by calculate node
|
|
2048
|
+
# @param node calculate node to assess
|
|
2049
|
+
# @param processed_nodes list of node already processed, importnat because only processed node could be use
|
|
2050
|
+
# @param is_calculate used when this funciton is called in the evaluation of another calculate
|
|
2051
|
+
# @param negate use to retriece the negation of a calculation
|
|
2052
|
+
def get_calculation_terms( node, processed_nodes, is_calculate=False, negate=False, process=None):
|
|
2053
|
+
# returns something directly only if the negate is managed
|
|
2054
|
+
expression = None
|
|
2055
|
+
if isinstance(node, TriccNodeAdd):
|
|
2056
|
+
return get_add_terms(node, False, negate, process=process)
|
|
2057
|
+
elif isinstance(node, TriccNodeCount):
|
|
2058
|
+
return get_count_terms(node, False, negate, process=process)
|
|
2059
|
+
elif isinstance(node, TriccNodeRhombus):
|
|
2060
|
+
return get_rhombus_terms(
|
|
2061
|
+
node,
|
|
2062
|
+
processed_nodes=processed_nodes,
|
|
2063
|
+
is_calculate=False,
|
|
2064
|
+
negate=negate,
|
|
2065
|
+
process=process
|
|
2066
|
+
)
|
|
2067
|
+
elif isinstance(node, ( TriccNodeWait)):
|
|
2068
|
+
# just use to force order of question
|
|
2069
|
+
expression = None
|
|
2070
|
+
# in case of calulate expression evaluation, we need to get the relevance of the activity
|
|
2071
|
+
# because calculate are not the the activity group
|
|
2072
|
+
elif isinstance(node, (TriccNodeActivityStart)) and is_calculate:
|
|
2073
|
+
expression = get_prev_node_expression(node.activity, processed_nodes=processed_nodes, is_calculate=is_calculate, negate=negate, process=process)
|
|
2074
|
+
elif isinstance(node, (TriccNodeActivityStart, TriccNodeActivityEnd)):
|
|
2075
|
+
# the group have the relevance for the activity, not needed to replicate it
|
|
2076
|
+
expression = None #return get_prev_node_expression(node.activity, processed_nodes, is_calculate=False, excluded_name=None)
|
|
2077
|
+
elif isinstance(node, TriccNodeExclusive):
|
|
2078
|
+
if len(node.prev_nodes) == 1:
|
|
2079
|
+
iterator = iter(node.prev_nodes)
|
|
2080
|
+
node_to_negate = next(iterator)
|
|
2081
|
+
if isinstance(node_to_negate, TriccNodeExclusive):
|
|
2082
|
+
logger.critical("2 exclusives cannot be on a row")
|
|
2083
|
+
exit(1)
|
|
2084
|
+
elif issubclass(node_to_negate.__class__, TriccNodeCalculateBase):
|
|
2085
|
+
return get_node_expression(
|
|
2086
|
+
node_to_negate,
|
|
2087
|
+
processed_nodes=processed_nodes,
|
|
2088
|
+
is_prev=True,
|
|
2089
|
+
negate=True,
|
|
2090
|
+
process=process
|
|
2091
|
+
)
|
|
2092
|
+
elif isinstance(node_to_negate, TriccNodeActivity):
|
|
2093
|
+
return get_node_expression(
|
|
2094
|
+
node_to_negate,
|
|
2095
|
+
processed_nodes=processed_nodes,
|
|
2096
|
+
is_calculate=False,
|
|
2097
|
+
is_prev=True,
|
|
2098
|
+
negate=True,
|
|
2099
|
+
process=process
|
|
2100
|
+
)
|
|
2101
|
+
else:
|
|
2102
|
+
logger.critical(f"exclusive node {node.get_name()}\
|
|
2103
|
+
does not depend of a calculate but on\
|
|
2104
|
+
{node_to_negate.__class__}::{node_to_negate.get_name()}")
|
|
2105
|
+
|
|
2106
|
+
else:
|
|
2107
|
+
logger.critical("exclusive node {} has no ou too much parent".format(node.get_name()))
|
|
2108
|
+
|
|
2109
|
+
if isinstance(node.expression_reference, (TriccOperation, TriccStatic)):
|
|
2110
|
+
expression = node.expression_reference
|
|
2111
|
+
elif node.reference is not None and node.expression_reference is not None :
|
|
2112
|
+
expression = get_prev_node_expression(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
|
|
2113
|
+
ref_expression = node.expression_reference.format(*[get_export_name(ref) for ref in node.reference])
|
|
2114
|
+
if expression is not None and expression != '':
|
|
2115
|
+
expression = and_join([expression,ref_expression])
|
|
2116
|
+
else:
|
|
2117
|
+
expression = ref_expression
|
|
2118
|
+
elif expression is None:
|
|
2119
|
+
expression = get_prev_node_expression(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
|
|
2120
|
+
|
|
2121
|
+
# manage the generic negation
|
|
2122
|
+
if negate:
|
|
2123
|
+
|
|
2124
|
+
return negate_term(expression)
|
|
2125
|
+
else:
|
|
2126
|
+
return expression
|
|
2127
|
+
|
|
2128
|
+
# Function that add element to array is not None or ''
|
|
2129
|
+
def add_sub_expression(array, sub):
|
|
2130
|
+
if isinstance(sub, TriccOperation) or sub:
|
|
2131
|
+
not_sub = negate_term(sub)
|
|
2132
|
+
if not_sub in array:
|
|
2133
|
+
# avoid having 2 conditions that are complete opposites
|
|
2134
|
+
array.remove(not_sub)
|
|
2135
|
+
array.append(TriccStatic(True))
|
|
2136
|
+
else:
|
|
2137
|
+
array.append(sub)
|
|
2138
|
+
# elif sub is None:
|
|
2139
|
+
# array.append(TriccStatic(True))
|
|
2140
|
+
|
|
2141
|
+
|
|
2142
|
+
|
|
2143
|
+
# function that negate terms
|
|
2144
|
+
# @param expression to negate
|
|
2145
|
+
def negate_term(expression):
|
|
2146
|
+
|
|
2147
|
+
return not_clean(expression)
|
|
2148
|
+
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
# if the node is "required" then we can take the fact that it has value for the next elements
|
|
2153
|
+
def get_required_node_expression(node):
|
|
2154
|
+
return TriccOperation(
|
|
2155
|
+
operator=TriccOperator.EXISTS,
|
|
2156
|
+
reference=[
|
|
2157
|
+
node
|
|
2158
|
+
]
|
|
2159
|
+
)
|
|
2160
|
+
|
|
2161
|
+
|
|
2162
|
+
# Get a selected option
|
|
2163
|
+
def get_selected_option_expression(option_node, negate):
|
|
2164
|
+
|
|
2165
|
+
selected = TriccOperation(
|
|
2166
|
+
TriccOperator.SELECTED,
|
|
2167
|
+
[
|
|
2168
|
+
option_node.select,
|
|
2169
|
+
TriccStatic(option_node.name)
|
|
2170
|
+
]
|
|
2171
|
+
)
|
|
2172
|
+
|
|
2173
|
+
if negate:
|
|
2174
|
+
return TriccOperation(
|
|
2175
|
+
operator=TriccOperator.AND,
|
|
2176
|
+
resource=[
|
|
2177
|
+
TriccOperation(
|
|
2178
|
+
operator=TriccOperator.NOT,
|
|
2179
|
+
resource=[
|
|
2180
|
+
selected
|
|
2181
|
+
]
|
|
2182
|
+
),TriccOperation(
|
|
2183
|
+
operator=TriccOperator.NATIVE,
|
|
2184
|
+
resource=[
|
|
2185
|
+
'count-selected',
|
|
2186
|
+
option_node.select
|
|
2187
|
+
]
|
|
2188
|
+
)
|
|
2189
|
+
])
|
|
2190
|
+
|
|
2191
|
+
else:
|
|
2192
|
+
return selected
|
|
2193
|
+
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
|
|
2197
|
+
|
|
2198
|
+
|