tricc-oo 1.5.13__py3-none-any.whl → 1.6.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. tests/build.py +20 -28
  2. tests/test_build.py +260 -0
  3. tests/test_cql.py +48 -109
  4. tests/to_ocl.py +15 -17
  5. tricc_oo/__init__.py +0 -6
  6. tricc_oo/converters/codesystem_to_ocl.py +51 -40
  7. tricc_oo/converters/cql/cqlLexer.py +1 -0
  8. tricc_oo/converters/cql/cqlListener.py +1 -0
  9. tricc_oo/converters/cql/cqlParser.py +1 -0
  10. tricc_oo/converters/cql/cqlVisitor.py +1 -0
  11. tricc_oo/converters/cql_to_operation.py +129 -123
  12. tricc_oo/converters/datadictionnary.py +45 -54
  13. tricc_oo/converters/drawio_type_map.py +146 -65
  14. tricc_oo/converters/tricc_to_xls_form.py +58 -28
  15. tricc_oo/converters/utils.py +4 -4
  16. tricc_oo/converters/xml_to_tricc.py +296 -235
  17. tricc_oo/models/__init__.py +2 -1
  18. tricc_oo/models/base.py +333 -305
  19. tricc_oo/models/calculate.py +66 -51
  20. tricc_oo/models/lang.py +26 -27
  21. tricc_oo/models/ocl.py +146 -161
  22. tricc_oo/models/ordered_set.py +15 -19
  23. tricc_oo/models/tricc.py +149 -89
  24. tricc_oo/parsers/xml.py +15 -30
  25. tricc_oo/serializers/planuml.py +4 -6
  26. tricc_oo/serializers/xls_form.py +110 -153
  27. tricc_oo/strategies/input/base_input_strategy.py +28 -32
  28. tricc_oo/strategies/input/drawio.py +59 -71
  29. tricc_oo/strategies/output/base_output_strategy.py +151 -65
  30. tricc_oo/strategies/output/dhis2_form.py +908 -0
  31. tricc_oo/strategies/output/fhir_form.py +377 -0
  32. tricc_oo/strategies/output/html_form.py +224 -0
  33. tricc_oo/strategies/output/openmrs_form.py +694 -0
  34. tricc_oo/strategies/output/spice.py +106 -127
  35. tricc_oo/strategies/output/xls_form.py +322 -244
  36. tricc_oo/strategies/output/xlsform_cdss.py +627 -142
  37. tricc_oo/strategies/output/xlsform_cht.py +252 -125
  38. tricc_oo/strategies/output/xlsform_cht_hf.py +13 -24
  39. tricc_oo/visitors/tricc.py +1424 -1033
  40. tricc_oo/visitors/utils.py +16 -16
  41. tricc_oo/visitors/xform_pd.py +91 -89
  42. {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/METADATA +128 -84
  43. tricc_oo-1.6.8.dist-info/RECORD +52 -0
  44. tricc_oo-1.6.8.dist-info/licenses/LICENSE +373 -0
  45. {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/top_level.txt +0 -0
  46. tricc_oo-1.5.13.dist-info/RECORD +0 -46
  47. {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/WHEEL +0 -0
@@ -1,65 +1,119 @@
1
1
  import re
2
2
  import logging
3
-
4
- from tricc_oo.converters.utils import *
5
- from tricc_oo.models import *
6
- from tricc_oo.visitors.tricc import *
3
+ import requests
4
+ import base64
5
+
6
+
7
+ from tricc_oo.converters.utils import generate_id
8
+ from tricc_oo.models.base import (
9
+ TriccBaseModel, TriccNodeType,
10
+ TriccOperator, TriccOperation, TriccStatic, TriccReference, not_clean,
11
+ and_join, or_join, clean_or_list, nand_join, TriccEdge
12
+ )
13
+ from tricc_oo.models.ordered_set import OrderedSet
14
+ from tricc_oo.models.calculate import (
15
+ TriccNodeDisplayBridge,
16
+ TriccNodeBridge,
17
+ TriccNodeWait,
18
+ TriccNodeCalculate,
19
+ TriccNodeRhombus,
20
+ TriccNodeDisplayCalculateBase,
21
+ TriccNodeExclusive,
22
+ TriccNodeProposedDiagnosis,
23
+ TriccNodeCount,
24
+ TriccNodeAdd,
25
+ TriccNodeFakeCalculateBase,
26
+ TriccRhombusMixIn,
27
+ TriccNodeInput,
28
+ TriccNodeActivityEnd,
29
+ TriccNodeActivityStart,
30
+ TriccNodeEnd,
31
+ TriccNodeDiagnosis,
32
+ get_node_from_id,
33
+
34
+ )
35
+ from tricc_oo.models.tricc import (
36
+ TriccNodeCalculateBase, TriccNodeActivity, TriccNodeBaseModel, TriccNodeNumber,
37
+ TriccNodeSelectMultiple,
38
+ TriccNodeSelectOne,
39
+ TriccNodeSelectOption,
40
+ TriccNodeSelectYesNo,
41
+ TriccNodeInputModel,
42
+ TriccNodeSelect,
43
+ TriccNodeSelectNotAvailable,
44
+ TriccNodeMoreInfo,
45
+ TriccNodeDisplayModel,
46
+ TriccNodeMainStart,
47
+ TriccNodeAcceptDiagnostic,
48
+ TRICC_FALSE_VALUE,
49
+ TRICC_TRUE_VALUE,
50
+ )
7
51
  from tricc_oo.visitors.utils import PROCESSES
52
+ from tricc_oo.converters.cql_to_operation import transform_cql_to_operation
8
53
  from tricc_oo.converters.datadictionnary import lookup_codesystems_code
54
+ from tricc_oo.converters.tricc_to_xls_form import get_list_names, get_export_name
9
55
 
10
56
  logger = logging.getLogger("default")
11
57
  ONE_QUESTION_AT_A_TIME = False
12
58
 
13
- TRICC_TRUE_VALUE = 'true'
14
- TRICC_FALSE_VALUE = 'false'
15
59
 
16
- def merge_node(from_node,to_node):
60
+ def merge_node(from_node, to_node):
17
61
  if from_node.activity != to_node.activity:
18
62
  logger.critical("Cannot merge nodes from different activities")
19
- elif issubclass(from_node.__class__, TriccNodeCalculateBase) and issubclass(to_node.__class__, TriccNodeCalculateBase):
63
+ elif issubclass(from_node.__class__, TriccNodeCalculateBase) and issubclass(
64
+ to_node.__class__, TriccNodeCalculateBase
65
+ ):
20
66
  for e in to_node.activity.edges:
21
67
  if e.target == from_node.id:
22
68
  e.target = to_node.id
23
69
  else:
24
70
  logger.critical("Cannot merge not calculate nodes ")
25
-
71
+
26
72
 
27
73
  def get_max_version(dict):
28
74
  max_version = None
29
75
  for id, sim_node in dict.items():
30
- if max_version is None or max_version.version < sim_node.version :
76
+ if max_version is None or max_version.version < sim_node.version:
31
77
  max_version = sim_node
32
78
  return max_version
33
79
 
80
+
34
81
  def get_versions(name, iterable):
35
82
  return [n for n in iterable if version_filter(name)(n)]
36
83
 
84
+
37
85
  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)
86
+ return (
87
+ lambda item: hasattr(item, "name")
88
+ and ((isinstance(item, TriccNodeEnd) and name == item.get_reference()) or item.name == name)
89
+ and not isinstance(item, TriccNodeSelectOption)
90
+ )
39
91
 
40
- def get_last_version(name, processed_nodes, _list=None):
92
+
93
+ def get_last_version(name, processed_nodes, _list=None):
41
94
  max_version = None
42
95
  if isinstance(_list, dict):
43
96
  _list = _list[name].values() if name in _list else []
44
97
  if _list is None:
45
98
  if isinstance(processed_nodes, OrderedSet):
46
99
  return processed_nodes.find_last(version_filter(name))
47
- else:
100
+ else:
48
101
  _list = get_versions(name, processed_nodes)
49
102
  if _list:
50
- for sim_node in _list:
103
+ for sim_node in _list:
51
104
  # 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
105
+ if (
106
+ max_version is None
107
+ or max_version.activity.path_len < sim_node.activity.path_len
108
+ or max_version.path_len < sim_node.path_len
55
109
  or (max_version.path_len == sim_node.path_len and hash(max_version.id) < hash(sim_node.id))
56
- ) ):
110
+ ):
57
111
  max_version = sim_node
58
112
  if not max_version:
59
- already_processed = list(filter(lambda p_node: hasattr(p_node, 'name') and p_node.name == name , _list))
113
+ already_processed = list(filter(lambda p_node: hasattr(p_node, "name") and p_node.name == name, _list))
60
114
  if already_processed:
61
- max_version = sorted(filtered, key=lambda x: x.path_len, reverse=False)[0]
62
-
115
+ max_version = sorted(already_processed, key=lambda x: x.path_len, reverse=False)[0]
116
+
63
117
  return max_version
64
118
 
65
119
 
@@ -67,324 +121,316 @@ def get_last_version(name, processed_nodes, _list=None):
67
121
  # node is the node to calculate
68
122
  # processed_nodes are the list of processed nodes
69
123
  def get_node_expressions(node, processed_nodes, process=None):
70
- get_overall_exp = issubclass(node.__class__, TriccNodeCalculateBase) and not issubclass(node.__class__, (TriccNodeDisplayBridge,TriccNodeBridge))
124
+ get_overall_exp = issubclass(
125
+ node.__class__,
126
+ (TriccNodeDisplayCalculateBase, TriccNodeProposedDiagnosis, TriccNodeDiagnosis)
127
+ ) and not isinstance(node, (TriccNodeDisplayBridge))
71
128
  expression = None
72
129
  # in case of recursive call processed_nodes will be None
73
130
  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, get_overall_exp=get_overall_exp, process=process)
75
-
76
- # if get_overall_exp:
77
- # if expression and (not isinstance(expression, str) or expression != '') and expression is not TriccStatic(True) :
78
- # num_expression = TriccOperation(
79
- # TriccOperator.CAST_NUMBER,
80
- # [expression]
81
- # )
82
- # elif expression is TriccStatic(True) or (not expression and get_overall_exp):
83
- # expression = TriccStatic(True)
84
- # else:
85
- # expression = None
131
+ expression = get_node_expression(
132
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process
133
+ )
86
134
  if (
87
- issubclass(node.__class__, TriccNodeCalculateBase)
88
- and not isinstance(expression, (TriccStatic, TriccReference, TriccOperation))
89
- and str(expression) != ''
135
+ issubclass(node.__class__, TriccNodeCalculateBase)
136
+ and not isinstance(expression, (TriccStatic, TriccReference, TriccOperation))
137
+ and str(expression) != ""
90
138
  and not isinstance(node, (TriccNodeWait, TriccNodeActivityEnd, TriccNodeActivityStart, TriccNodeEnd))
91
139
  ):
92
140
  logger.warning("Calculate {0} returning no calculations".format(node.get_name()))
93
141
  expression = TriccStatic(True)
94
142
  return expression
95
143
 
144
+
96
145
  def set_last_version_false(node, processed_nodes):
97
- if isinstance(node, (TriccNodeDiagnosis, TriccNodeSelectOption)):
146
+ if isinstance(node, (TriccNodeSelectOption)):
98
147
  return
99
148
  node_name = node.name if not isinstance(node, TriccNodeEnd) else node.get_reference()
100
- #last_version = get_last_version(node_name, processed_nodes) if issubclass(node.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase, TriccNodeEnd)) and not isinstance(node, TriccNodeSelectOption) else None
101
149
  last_version = processed_nodes.find_prev(node, version_filter(node_name))
102
- if last_version and getattr(node, 'process', '') != 'pause':
103
- # 0-100 for manually specified instance. 100-200 for auto instance
150
+ if last_version and getattr(node, "process", "") != "pause":
151
+ # 0-100 for manually specified instance. 100-200 for auto instance
104
152
  node.version = get_next_version(node.name, processed_nodes, last_version.version, 0)
105
153
  last_version.last = False
106
154
  node.path_len = max(node.path_len, last_version.path_len + 1)
107
155
  return last_version
108
-
156
+
157
+
109
158
  def get_version_inheritance(node, last_version, processed_nodes):
110
- # FIXME this is for XLS form where only calculate are evaluated for a activity that is not triggered
111
- if not issubclass(node.__class__, (TriccNodeInputModel)):
112
- node.last = True
113
- if (
114
- issubclass(node.__class__, (TriccNodeDisplayCalculateBase, TriccNodeEnd)) and node.name is not None
115
- ):
116
- #logger.debug("set last to false for node {} and add its link it to next one".format(last_used_calc.get_name()))
117
- if node.prev_nodes:
118
- set_prev_next_node(last_version, node)
119
- else:
120
- expression = node.expression or node.expression_reference or getattr(node, 'relevance', None)
121
- expression = merge_expression(expression, last_version)
122
- if node.expression:
123
- node.expression = expression
124
- elif node.expression_reference:
125
- node.expression_reference = expression
126
- elif node.relevance:
127
- node.relevance = expression
128
- else:
129
- node.last = False
130
- calc = TriccNodeCalculate(
131
- id=generate_id(f"save{node.id}"),
132
- name=node.name,
133
- path_len=node.path_len+1,
134
- #version=get_next_version(node.name, processed_nodes, node.version+2),
135
- expression= merge_expression(node, last_version),
136
- label= f"merge{node.id}",
137
- last=True,
138
- activity=node.activity,
139
- group=node.group
140
- )
141
- node.activity.nodes[calc.id]=calc
142
- node.activity.calculates.append(calc)
143
- #set_last_version_false(calc, processed_nodes)
144
- processed_nodes.add(calc)
145
- if issubclass(node.__class__, TriccNodeInputModel):
146
- node.expression = TriccOperation(
147
- TriccOperator.COALESCE,
148
- [
149
- '$this',
150
- last_version
151
- ]
152
- )
153
-
159
+ # FIXME this is for XLS form where only calculate are evaluated
160
+ # for a activity that is not triggered
161
+ if not issubclass(node.__class__, (TriccNodeInputModel)):
162
+ node.last = True
163
+ if issubclass(node.__class__, (TriccNodeDisplayCalculateBase, TriccNodeEnd)) and node.name is not None:
164
+ # logger.debug("set last to false for node {}
165
+ # and add its link it to next one".format(last_used_calc.get_name()))
166
+ if node.prev_nodes:
167
+ set_prev_next_node(last_version, node)
168
+ else:
169
+ expression = node.expression or node.expression_reference or getattr(node, "relevance", None)
170
+ expression = merge_expression(expression, last_version)
171
+ if node.expression:
172
+ node.expression = expression
173
+ elif node.expression_reference:
174
+ node.expression_reference = expression
175
+ elif node.relevance:
176
+ node.relevance = expression
177
+ else:
178
+ node.last = False
179
+
180
+ # Create a calculate node that coalesces the previous saved value with the current node value
181
+ calc_id = generate_id(f"save_{node.save}")
182
+ calc = TriccNodeCalculate(
183
+ id=calc_id,
184
+ name=node.save,
185
+ path_len=node.path_len + 1,
186
+ expression_reference=TriccOperation(
187
+ TriccOperator.COALESCE,
188
+ [TriccReference(node.save), last_version],
189
+ ),
190
+ reference=[TriccReference(node.name)],
191
+ activity=node.activity,
192
+ group=node.group,
193
+ label=f"Save calculation for {node.label}",
194
+ last=True,
195
+ )
196
+ node.activity.nodes[calc.id] = calc
197
+ node.activity.calculates.append(calc)
198
+ # set_last_version_false(calc, processed_nodes)
199
+ processed_nodes.add(calc)
200
+ if issubclass(node.__class__, TriccNodeInputModel):
201
+ node.expression = TriccOperation(TriccOperator.COALESCE, ["$this", last_version])
202
+
203
+
154
204
  def merge_expression(expression, last_version):
155
205
  datatype = expression.get_datatype()
156
- if datatype == 'boolean':
157
- expression = or_join(
158
- [TriccOperation(TriccOperator.ISTRUE, [last_version]), expression]
159
- )
160
-
161
- elif datatype == 'number':
162
- expression = TriccOperation(
163
- TriccOperator.PLUS,
164
- [last_version, expression]
165
- )
206
+ if datatype == "boolean":
207
+ expression = or_join([TriccOperation(TriccOperator.ISTRUE, [last_version]), expression])
208
+
209
+ elif datatype == "number":
210
+ expression = TriccOperation(TriccOperator.PLUS, [last_version, expression])
166
211
  else:
167
- expression = TriccOperation(
168
- TriccOperator.COALESCE,
169
- [last_version, expression]
170
- )
212
+ expression = TriccOperation(TriccOperator.COALESCE, [last_version, expression])
171
213
  return expression
172
214
 
173
- def process_calculate(node,processed_nodes, stashed_nodes, calculates, used_calculates,
174
- warn = False, process=None, **kwargs ):
175
- # used_calculates dict[name, Dict[id, node]]
176
- # processed_nodes Dict[id, node]
177
- # calculates dict[name, Dict[id, node]]
178
-
179
-
215
+
216
+ def load_calculate(
217
+ node, processed_nodes, stashed_nodes, calculates, used_calculates, warn=False, process=None, **kwargs
218
+ ):
219
+ # used_calculates dict[name, Dict[id, node]]
220
+ # processed_nodes Dict[id, node]
221
+ # calculates dict[name, Dict[id, node]]
222
+
180
223
  if node not in processed_nodes:
181
224
  # generate condition
182
- if (
183
- is_ready_to_process(node, processed_nodes,True)
184
- and process_reference(
185
- node,
186
- processed_nodes=processed_nodes,
187
- calculates=calculates,
188
- used_calculates=used_calculates,
189
- replace_reference=False,
190
- warn = warn,
191
- codesystems= kwargs.get('codesystems', None)
192
- )
225
+ if is_ready_to_process(node, processed_nodes, True) and process_reference(
226
+ node,
227
+ processed_nodes=processed_nodes,
228
+ calculates=calculates,
229
+ used_calculates=used_calculates,
230
+ replace_reference=False,
231
+ warn=warn,
232
+ codesystems=kwargs.get("codesystems", None),
193
233
  ):
194
- if kwargs.get('warn', True):
195
- logger.debug('Processing relevance for node {0}'.format(node.get_name()))
234
+ if kwargs.get("warn", True):
235
+ logger.debug("Processing relevance for node {0}".format(node.get_name()))
196
236
  # tricc diagnostic have the same name as proposed diag but will be serialised with different names
197
237
 
198
- last_version = set_last_version_false(node, processed_nodes)
238
+ last_version = set_last_version_false(node, processed_nodes)
199
239
  if last_version:
200
240
  last_version = get_version_inheritance(node, last_version, processed_nodes)
201
241
 
202
- generate_calculates(node,calculates, used_calculates,processed_nodes=processed_nodes, process=process)
203
-
204
-
242
+ generate_calculates(node, calculates, used_calculates, processed_nodes=processed_nodes, process=process)
205
243
 
206
- # if has prev, create condition
207
- if hasattr(node, 'relevance') and (node.relevance is None or isinstance(node.relevance, TriccOperation)):
244
+ # if has prev, create condition
245
+ if hasattr(node, "relevance") and (node.relevance is None or isinstance(node.relevance, TriccOperation)):
208
246
  node.relevance = get_node_expressions(node, processed_nodes=processed_nodes, process=process)
209
247
  # manage not Available
210
248
  if isinstance(node, TriccNodeSelectNotAvailable):
211
249
  # update the checkbox
212
- if node.parent:
250
+ if node.parent:
213
251
  if len(node.prev_nodes) == 1:
214
252
  prev = list(node.prev_nodes)[0]
215
253
  if isinstance(prev, TriccNodeMoreInfo) and prev.parent.name == node.name:
216
254
  prev.parent = node
217
-
218
-
255
+
219
256
  # managing more info on NotAvaialbee
220
257
  parent_empty = TriccOperation(TriccOperator.ISNULL, [node.parent])
221
- node.relevance = and_join([node.parent.relevance, parent_empty])
258
+ node.relevance = and_join([node.parent.relevance, parent_empty])
222
259
  node.required = parent_empty
223
260
  node.constraint = parent_empty
224
261
  node.constraint_message = "Cannot be selected with a value entered above"
225
262
  # update the check box parent : create loop error
226
263
  node.parent.required = None # "${{{0}}}=''".format(node.name)
227
264
  else:
228
- logger.warning("not available node {} does't have a single parent".format(node.get_name()))
265
+ logger.warning("not available node {} does't have a single parent".format(node.get_name()))
229
266
  elif isinstance(node.relevance, TriccOperation):
230
267
  relevance_reference = list(node.relevance.get_references())
231
268
  for r in relevance_reference:
232
- if issubclass(r.__class__, (TriccNodeDisplayCalculateBase )):
269
+ if issubclass(r.__class__, (TriccNodeDisplayCalculateBase)):
233
270
  add_used_calculate(node, r, calculates, used_calculates, processed_nodes)
234
-
235
- if last_version and hasattr(node, 'relevance'):
271
+
272
+ if last_version and hasattr(node, "relevance"):
236
273
  if isinstance(node, TriccNodeInputModel):
237
- version_relevance = TriccOperation(
238
- TriccOperator.ISNULL,
239
- [last_version]
240
- )
274
+ version_relevance = TriccOperation(TriccOperator.ISNULL, [last_version])
241
275
  elif last_version.relevance:
242
- version_relevance = not_clean(
243
- last_version.relevance
244
- )
276
+ version_relevance = not_clean(last_version.relevance)
245
277
  elif last_version.activity.relevance:
246
278
  version_relevance = not_clean(
247
- last_version.activity.relevance,
279
+ last_version.activity.relevance,
248
280
  )
249
281
  else:
250
282
  version_relevance = None
251
-
283
+
252
284
  if version_relevance:
253
- if getattr(node, 'relevance', None):
254
- node.relevance = and_join(
255
- [
256
- version_relevance,
257
- node.relevance
258
- ])
259
-
260
- elif hasattr(node, 'relevance'):
285
+ if getattr(node, "relevance", None):
286
+ node.relevance = and_join([version_relevance, node.relevance])
287
+
288
+ elif hasattr(node, "relevance"):
261
289
  node.relevance = version_relevance
262
290
 
263
- #if hasattr(node, 'next_nodes'):
264
- #node.next_nodes=reorder_node_list(node.next_nodes, node.group)
291
+ # if hasattr(node, 'next_nodes'):
292
+ # node.next_nodes=reorder_node_list(node.next_nodes, node.group)
265
293
  process_reference(
266
- node,
267
- processed_nodes=processed_nodes,
294
+ node,
295
+ processed_nodes=processed_nodes,
268
296
  calculates=calculates,
269
- used_calculates=used_calculates,
270
- replace_reference=True,
271
- warn = warn,
272
- codesystems= kwargs.get('codesystems', None)
297
+ used_calculates=used_calculates,
298
+ replace_reference=True,
299
+ warn=warn,
300
+ codesystems=kwargs.get("codesystems", None),
273
301
  )
274
302
  if isinstance(node, (TriccNodeMainStart, TriccNodeActivityStart)):
275
303
  process_reference(
276
304
  node.activity,
277
- processed_nodes=processed_nodes,
305
+ processed_nodes=processed_nodes,
278
306
  calculates=calculates,
279
- used_calculates=used_calculates,
280
- replace_reference=True,
281
- warn = warn,
282
- codesystems= kwargs.get('codesystems', None)
307
+ used_calculates=used_calculates,
308
+ replace_reference=True,
309
+ warn=warn,
310
+ codesystems=kwargs.get("codesystems", None),
283
311
  )
284
312
 
285
313
  return True
286
314
  # not ready to process or already processed
287
315
 
288
316
  return False
289
-
290
317
 
291
318
 
292
- def get_max_named_version(calculates,name):
319
+ def get_max_named_version(calculates, name):
293
320
  max = 0
294
- if name in calculates:
295
- for node in calculates[name].values():
321
+ if name in calculates:
322
+ for node in calculates[name].values():
296
323
  if node.version > max:
297
324
  max = node.version
298
325
  return max
299
326
 
327
+
300
328
  def get_count_node(node):
301
329
  count_id = generate_id(f"count{node.id}")
302
- count_name = "cnt_"+count_id
330
+ count_name = "cnt_" + count_id
303
331
  return TriccNodeCount(
304
- id = count_id,
305
- group = node.group,
306
- activity = node.activity,
307
- label = "count: "+node.get_name(),
308
- name = count_name,
309
- path_len=node.path_len
332
+ id=count_id,
333
+ group=node.group,
334
+ activity=node.activity,
335
+ label="count: " + node.get_name(),
336
+ name=count_name,
337
+ path_len=node.path_len,
310
338
  )
311
-
312
- ### Function that inject a wait after path that will wait for the nodes
313
- def get_activity_wait(prev_nodes, nodes_to_wait, next_nodes, replaced_node = None, edge_only = False, activity = None):
314
339
 
315
- if issubclass(nodes_to_wait.__class__,TriccBaseModel):
340
+
341
+ # Function that inject a wait after path that will wait for the nodes
342
+
343
+
344
+ def get_activity_wait(prev_nodes, nodes_to_wait, next_nodes, replaced_node=None, edge_only=False, activity=None):
345
+
346
+ if issubclass(nodes_to_wait.__class__, TriccBaseModel):
316
347
  nodes_to_wait = [nodes_to_wait]
317
- if issubclass(prev_nodes.__class__,TriccBaseModel):
348
+ if issubclass(prev_nodes.__class__, TriccBaseModel):
318
349
  prev_nodes = set([prev_nodes])
319
350
  elif isinstance(prev_nodes, list):
320
351
  prev_nodes = set(prev_nodes)
321
-
352
+
322
353
  iterator = iter(prev_nodes)
323
354
  prev_node = next(iterator)
324
355
  path = prev_node if len(prev_nodes) == 1 else get_bridge_path(prev_nodes, activity)
325
-
356
+
326
357
  activity = activity or prev_node.activity
327
358
  calc_node = TriccNodeWait(
328
- id = generate_id(f"ar{''.join([x.id for x in nodes_to_wait])}{activity.id}"),
329
- reference = nodes_to_wait,
330
- activity = activity,
331
- group = activity,
332
- path = path
333
- )
359
+ id=generate_id(f"ar{''.join([x.id for x in nodes_to_wait])}{activity.id}"),
360
+ reference=nodes_to_wait,
361
+ activity=activity,
362
+ group=activity,
363
+ path=path,
364
+ )
334
365
 
335
- #start the wait and the next_nodes from the prev_nodes
336
- #add the wait as dependency of the next_nodes
366
+ # start the wait and the next_nodes from the prev_nodes
367
+ # add the wait as dependency of the next_nodes
337
368
 
338
- # add edge between rhombus and node
369
+ # add edge between rhombus and node
339
370
 
340
- set_prev_next_node(path,calc_node, edge_only=edge_only, activity=activity )
371
+ set_prev_next_node(path, calc_node, edge_only=edge_only, activity=activity)
341
372
  for next_node in next_nodes:
342
- #if prev != replaced_node and next_node != replaced_node :
343
- # set_prev_next_node(prev,next_node,replaced_node)
344
- #if first:
345
- #first = False
346
- set_prev_next_node(calc_node,next_node, edge_only=edge_only,activity=activity)
347
-
348
-
373
+ # if prev != replaced_node and next_node != replaced_node :
374
+ # set_prev_next_node(prev,next_node,replaced_node)
375
+ # if first:
376
+ # first = False
377
+ set_prev_next_node(calc_node, next_node, edge_only=edge_only, activity=activity)
378
+
349
379
  return calc_node
350
-
351
- def get_bridge_path(prev_nodes, node=None,edge_only=False):
380
+
381
+
382
+ def get_bridge_path(prev_nodes, node=None, edge_only=False):
352
383
  iterator = iter(prev_nodes)
353
- p_p_node = next(iterator)
384
+ p_p_node = next(iterator)
354
385
  if node is None:
355
386
  node = p_p_node
356
- calc_id = generate_id(f"br{''.join([x.id for x in prev_nodes])}{node.id}")
357
- calc_name = "path_"+calc_id
387
+ calc_id = generate_id(f"br{''.join([x.id for x in prev_nodes])}{node.id}")
388
+ calc_name = "path_" + calc_id
358
389
  data = {
359
- 'id': calc_id,
360
- 'group': node.group,
361
- 'activity': node.activity,
362
- 'label': "path: " + ( node.get_name()),
363
- 'name': calc_name,
364
- 'path_len': node.path_len + 1 * (node == p_p_node)
390
+ "id": calc_id,
391
+ "group": node.group,
392
+ "activity": node.activity,
393
+ "label": "path: " + (node.get_name()),
394
+ "name": calc_name,
395
+ "path_len": node.path_len + 1 * (node == p_p_node),
365
396
  }
366
-
367
- if len(prev_nodes)>1 and sum([0 if issubclass(n.__class__, (TriccNodeDisplayCalculateBase, TriccNodeRhombus)) else 1 for n in prev_nodes])>0 :
368
- calc= TriccNodeDisplayBridge( **data)
397
+
398
+ if (
399
+ len(prev_nodes) > 1
400
+ and sum(
401
+ [0 if issubclass(n.__class__, (TriccNodeDisplayCalculateBase, TriccNodeRhombus)) else 1 for n in prev_nodes]
402
+ )
403
+ > 0
404
+ ):
405
+ calc = TriccNodeDisplayBridge(**data)
369
406
  else:
370
- calc = TriccNodeBridge( **data)
407
+ calc = TriccNodeBridge(**data)
371
408
  return calc
372
-
409
+
410
+
373
411
  def inject_bridge_path(node, nodes):
374
412
 
375
- 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))]
413
+ prev_nodes = [
414
+ nodes[n.source]
415
+ for n in list(
416
+ filter(
417
+ lambda x: (x.target == node.id or x.target == node) and x.source in list(nodes.keys()),
418
+ node.activity.edges,
419
+ )
420
+ )
421
+ ]
376
422
  if prev_nodes:
377
- calc = get_bridge_path(prev_nodes, node,edge_only=True)
423
+ calc = get_bridge_path(prev_nodes, node, edge_only=True)
378
424
 
379
425
  for e in node.activity.edges:
380
426
  if e.target == node.id:
381
427
  # if e.source in node.activity.nodes and len(node.activity.nodes[e.source].next_nodes):
382
428
  # set_prev_next_node(node.activity[e.source], node, edge_only=True, replaced_node=node)
383
429
  # else:
384
- e.target = calc.id
385
-
430
+ e.target = calc.id
431
+
386
432
  # add edge between bridge and node
387
- set_prev_next_node(calc,node,edge_only=True, activity=node.activity)
433
+ set_prev_next_node(calc, node, edge_only=True, activity=node.activity)
388
434
  node.path_len += 1
389
435
  return calc
390
436
 
@@ -394,45 +440,50 @@ def inject_node_before(before, node, activity):
394
440
  before.activity = activity
395
441
  activity.nodes[before.id] = before
396
442
  nodes = activity.nodes
397
- 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))))
443
+ prev_nodes = node.prev_nodes.union(
444
+ set(
445
+ nodes[n.source]
446
+ for n in list(
447
+ filter(lambda x: (x.target == node.id or x.target == node) and x.source in nodes, node.activity.edges)
448
+ )
449
+ )
450
+ )
398
451
  edge_processed = False
399
452
  before.path_len = node.path_len
400
453
  for e in node.activity.edges:
401
454
  if e.target == node.id:
402
455
  e.target = before.id
403
- for p in prev_nodes:
404
- prev_processed = len(node.next_nodes) > 0
456
+ for p in prev_nodes:
405
457
  if node in p.next_nodes:
406
458
  p.next_nodes.remove(node)
407
459
  p.next_nodes.append(before)
408
460
 
409
461
  # add edge between bridge and node
410
- set_prev_next_node(before,node,edge_only=not edge_processed, activity=node.activity)
462
+ set_prev_next_node(before, node, edge_only=not edge_processed, activity=node.activity)
411
463
  node.path_len += 1
412
464
 
413
-
414
-
415
- def generate_calculates(node,calculates, used_calculates,processed_nodes, process):
465
+
466
+ def generate_calculates(node, calculates, used_calculates, processed_nodes, process):
416
467
  list_calc = []
417
468
  count_node = None
418
- ## add select calcualte
469
+ # add select calcualte
419
470
  if issubclass(node.__class__, TriccNodeCalculateBase):
420
471
  if isinstance(node, TriccNodeRhombus):
421
472
  if (
422
473
  (node.expression_reference is None or isinstance(node.expression_reference, TriccOperation))
423
474
  and isinstance(node.reference, list)
424
- and len(node.reference)==1
475
+ and len(node.reference) == 1
425
476
  and issubclass(node.reference[0].__class__, TriccNodeSelect)
426
477
  ):
427
478
 
428
479
  count_node = get_count_node(node)
429
480
  list_calc.append(count_node)
430
- set_prev_next_node(node.reference[0],count_node)
431
- node.path_len+=1
432
-
481
+ set_prev_next_node(node.reference[0], count_node)
482
+ node.path_len += 1
483
+
433
484
  if isinstance(node.expression_reference, TriccOperation):
434
485
  node.expression_reference.replace_node(node.reference, count_node)
435
- node.reference[0] = count_node
486
+ node.reference[0] = count_node
436
487
  # elif isinstance(node.reference, TriccOperation):
437
488
  # references = node.reference.get_references()
438
489
  # if len(references) == 1 and issubclass(node.reference[0].__class__, TriccNodeSelect):
@@ -445,68 +496,93 @@ def generate_calculates(node,calculates, used_calculates,processed_nodes, proces
445
496
  processed_nodes.add(count_node)
446
497
  add_calculate(calculates, count_node)
447
498
  add_used_calculate(
448
- node,
449
- count_node,
450
- calculates=calculates,
499
+ node,
500
+ count_node,
501
+ calculates=calculates,
451
502
  used_calculates=used_calculates,
452
- processed_nodes=processed_nodes
503
+ processed_nodes=processed_nodes,
453
504
  )
454
-
455
-
505
+
456
506
  # if a prev node is a calculate then it must be added in used_calc
457
507
  for prev in node.prev_nodes:
458
508
  add_used_calculate(
459
- node,
460
- prev,
461
- calculates=calculates,
462
- used_calculates=used_calculates,
463
- processed_nodes=processed_nodes
509
+ node, prev, calculates=calculates, used_calculates=used_calculates, processed_nodes=processed_nodes
464
510
  )
465
- #if the node have a save
466
- if hasattr(node, 'save') and node.save is not None and node.save != '':
511
+ # if the node have a save
512
+ if hasattr(node, "save") and node.save is not None and node.save != "":
467
513
  # get fragments type.name.icdcode
468
- calculate_name=node.save
514
+ calculate_name = node.save
469
515
  if node.name != calculate_name:
470
516
  calc_id = generate_id(f"autosave{node.id}")
471
517
  if issubclass(node.__class__, TriccNodeSelect) or isinstance(node, TriccNodeSelectNotAvailable):
472
- expression = get_count_terms_details( node, processed_nodes, True, False, process)
518
+ expression = get_count_terms_details(node, processed_nodes, True, False, process)
473
519
  else:
474
- expression = get_node_expression(node,processed_nodes,True,True)
520
+ expression = get_node_expression(node, processed_nodes, True, True)
475
521
  calc_node = TriccNodeCalculate(
476
522
  name=calculate_name,
477
- id = calc_id,
478
- group = node.group,
479
- #version=get_next_version(calculate_name, processed_nodes, node.version+2),
480
- activity = node.activity,
481
- label = "save: " +node.get_name(),
482
- path_len=node.path_len+ 1,
523
+ id=calc_id,
524
+ group=node.group,
525
+ # version=get_next_version(calculate_name, processed_nodes, node.version+2),
526
+ activity=node.activity,
527
+ label="save: " + node.get_name(),
528
+ path_len=node.path_len + 1,
483
529
  last=True,
484
- expression=expression
530
+ expression=expression,
485
531
  )
486
532
  node.activity.calculates.append(calc_node)
487
533
  last_version = set_last_version_false(calc_node, processed_nodes)
488
534
  if last_version:
489
535
  calc_node.expression = merge_expression(calc_node.expression, last_version)
490
536
  processed_nodes.add(calc_node)
491
- logger.debug("generate_save_calculate:{}:{} as {}".format(calc_node.tricc_type, node.name if hasattr(node,'name') else node.id, calculate_name))
492
-
537
+ logger.debug(
538
+ "generate_save_calculate:{}:{} as {}".format(
539
+ calc_node.tricc_type, node.name if hasattr(node, "name") else node.id, calculate_name
540
+ )
541
+ )
542
+
493
543
  list_calc.append(calc_node)
494
- #add_save_calculate(calc_node, calculates, used_calculates,processed_nodes)
544
+ # add_save_calculate(calc_node, calculates, used_calculates,processed_nodes)
495
545
  for calc in list_calc:
496
546
  node.activity.nodes[calc.id] = calc
497
547
  add_calculate(calculates, calc)
498
- return list_calc
499
548
 
549
+ # Add CONTAINS calculations for each option in select multiple (except opt_none)
550
+ if isinstance(node, TriccNodeSelectMultiple):
551
+ for option in node.options.values():
552
+ if not option.name.startswith("opt_"):
553
+ calc_id = generate_id(f"contains_{node.id}_{option.name}")
554
+ expression = TriccOperation(TriccOperator.CONTAINS, [node, TriccStatic(option.name)])
555
+ calc_node = TriccNodeCalculate(
556
+ name=option.name,
557
+ id=calc_id,
558
+ group=node.group,
559
+ activity=node.activity,
560
+ label=f"contains: {node.get_name()} contains '{option.name}'",
561
+ path_len=node.path_len + 1,
562
+ last=True,
563
+ expression=expression,
564
+ )
565
+ node.activity.calculates.append(calc_node)
566
+ last_version = set_last_version_false(calc_node, processed_nodes)
567
+ if last_version:
568
+ calc_node.expression = merge_expression(calc_node.expression, last_version)
569
+ processed_nodes.add(calc_node)
570
+ list_calc.append(calc_node)
571
+ node.activity.nodes[calc_node.id] = calc_node
572
+ add_calculate(calculates, calc_node)
573
+
574
+ return list_calc
500
575
 
501
576
 
502
577
  def add_calculate(calculates, calc_node):
503
578
  if issubclass(calc_node.__class__, TriccNodeDisplayCalculateBase):
504
579
  if calc_node.name not in calculates:
505
- calculates[calc_node.name]= {}
580
+ calculates[calc_node.name] = {}
506
581
  calculates[calc_node.name][calc_node.id] = calc_node
507
582
 
583
+
508
584
  def get_option_code_from_label(node, option_label):
509
- if hasattr(node, 'options'):
585
+ if hasattr(node, "options"):
510
586
  for i in node.options:
511
587
  if node.options[i].label.strip() == option_label.strip():
512
588
  return node.options[i].name
@@ -515,43 +591,95 @@ def get_option_code_from_label(node, option_label):
515
591
  logger.critical(f"node {node.get_name()} has no options")
516
592
 
517
593
 
518
- def process_reference(node, processed_nodes, calculates, used_calculates=None, replace_reference=False,warn=False, codesystems=None):
519
- if getattr(node, 'expression_reference', None):
594
+ # CQL is deined as a cql library and this code will
595
+ # parse the definition and will extract the logic under the define statement
596
+
597
+
598
+ def extract_with_regex(data):
599
+ text = data
600
+ # Pattern to match define statement and capture the name and body
601
+ pattern = r'define\s+"([^"]+)":\s*(.*)'
602
+ match = re.search(pattern, text, re.DOTALL)
603
+
604
+ if match:
605
+ definition_name = match.group(1)
606
+ definition_body = match.group(2).strip()
607
+ return {"name": definition_name, "body": definition_body, "full": match.group(0)}
608
+ return None
609
+
610
+
611
+ def process_reference(
612
+ node, processed_nodes, calculates, used_calculates=None, replace_reference=False, warn=False, codesystems=None
613
+ ):
614
+ # process a remote reference coded as a cql
615
+ if getattr(node, "remote_reference", None):
616
+ remote_reference_url = node.remote_reference
617
+ print(f"Fetching remote reference from {remote_reference_url}")
618
+ response = requests.get(remote_reference_url)
619
+ response_json = response.json()
620
+ cql_content = response_json["content"][0]["data"]
621
+ decode_cql_content = base64.b64decode(cql_content).decode("utf-8")
622
+ definition = extract_with_regex(decode_cql_content)
623
+
624
+ if definition:
625
+ cql_expression = definition["body"]
626
+
627
+ # We use `transform_cql_to_operation` to parse the raw CQL string.
628
+ operation = transform_cql_to_operation(cql_expression, context=f"remote reference for {node.get_name()}")
629
+
630
+ if not operation:
631
+ logger.error(f"Failed to parse remote CQL expression for node {node.get_name()}: {cql_expression}")
632
+ return False
633
+
634
+ # The parsed operation is assigned to `expression_reference`.
635
+ # The original code incorrectly assigned the raw string to `node.reference`
636
+ # and had an unreachable `if isinstance(cql_expression, list):` block.
637
+ node.expression_reference = operation
638
+ node.remote_reference = None
639
+
640
+ # By setting `expression_reference` and clearing `remote_reference`,
641
+ # we can now re-process this node. A recursive call to `process_reference`
642
+ # will now enter the `elif getattr(node, 'expression_reference', None):`
643
+ # block, which will correctly handle the newly parsed expression.
644
+ return process_reference(
645
+ node, processed_nodes, calculates, used_calculates, replace_reference, warn, codesystems
646
+ )
647
+
648
+ elif getattr(node, "expression_reference", None):
520
649
  modified_expression = process_operation_reference(
521
- node.expression_reference,
522
- node,
650
+ node.expression_reference,
651
+ node,
523
652
  processed_nodes=processed_nodes,
524
- calculates=calculates,
525
- used_calculates=used_calculates,
653
+ calculates=calculates,
654
+ used_calculates=used_calculates,
526
655
  replace_reference=replace_reference,
527
- warn=warn,
528
- codesystems=codesystems
656
+ warn=warn,
657
+ codesystems=codesystems,
529
658
  )
530
659
  if modified_expression is False:
531
660
  return False
532
661
  elif modified_expression and replace_reference:
533
662
  node.reference = list(modified_expression.get_references())
534
663
  node.expression_reference = modified_expression
535
- elif getattr(node, 'reference', None):
664
+
665
+ elif getattr(node, "reference", None):
536
666
  reference = node.reference
537
667
  if isinstance(reference, list):
538
668
  if isinstance(node, TriccNodeWait):
539
- reference = [TriccOperation(TriccOperator.ISTRUE,[n]) for n in reference]
540
- if len(node.reference) == 1 :
669
+ reference = [TriccOperation(TriccOperator.ISTRUE, [n]) for n in reference]
670
+ if len(node.reference) == 1:
541
671
  operation = reference[0]
542
672
  else:
543
- operation = and_join(
544
- reference
545
- )
673
+ operation = and_join(reference)
546
674
  modified_expression = process_operation_reference(
547
- operation,
675
+ operation,
548
676
  node,
549
677
  processed_nodes=processed_nodes,
550
- calculates=calculates,
551
- used_calculates=used_calculates,
678
+ calculates=calculates,
679
+ used_calculates=used_calculates,
552
680
  replace_reference=replace_reference,
553
- warn=warn,
554
- codesystems=codesystems
681
+ warn=warn,
682
+ codesystems=codesystems,
555
683
  )
556
684
  if modified_expression is False:
557
685
  return False
@@ -561,14 +689,14 @@ def process_reference(node, processed_nodes, calculates, used_calculates=None,
561
689
  node.expression_reference = modified_expression
562
690
  elif isinstance(node.reference, (TriccOperation, TriccReference)):
563
691
  modified_expression = process_operation_reference(
564
- node.reference,
565
- node,
692
+ node.reference,
693
+ node,
566
694
  processed_nodes=processed_nodes,
567
- calculates=calculates,
568
- used_calculates=used_calculates,
695
+ calculates=calculates,
696
+ used_calculates=used_calculates,
569
697
  replace_reference=replace_reference,
570
- warn=warn,
571
- codesystems=codesystems
698
+ warn=warn,
699
+ codesystems=codesystems,
572
700
  )
573
701
  if modified_expression is False:
574
702
  return False
@@ -576,64 +704,95 @@ def process_reference(node, processed_nodes, calculates, used_calculates=None,
576
704
  node.reference = list(modified_expression.get_references())
577
705
  node.expression_reference = modified_expression
578
706
 
579
- if isinstance(getattr(node, 'relevance', None), (TriccOperation, TriccReference)):
707
+ if isinstance(getattr(node, "relevance", None), (TriccOperation, TriccReference)):
580
708
  modified_expression = process_operation_reference(
581
- node.relevance,
582
- node,
709
+ node.relevance,
710
+ node,
583
711
  processed_nodes=processed_nodes,
584
- calculates=calculates,
585
- used_calculates=used_calculates,
712
+ calculates=calculates,
713
+ used_calculates=used_calculates,
586
714
  replace_reference=replace_reference,
587
- warn=warn,
588
- codesystems=codesystems
715
+ warn=warn,
716
+ codesystems=codesystems,
589
717
  )
590
718
  if modified_expression is False:
591
719
  return False
592
720
  elif modified_expression and replace_reference:
593
721
  node.relevance = modified_expression
594
-
595
- if isinstance(getattr(node, 'default', None), (TriccOperation, TriccReference)):
722
+
723
+ if isinstance(getattr(node, "trigger", None), (TriccOperation, TriccReference)):
724
+ modified_expression = process_operation_reference(
725
+ node.trigger,
726
+ node,
727
+ processed_nodes=processed_nodes,
728
+ calculates=calculates,
729
+ used_calculates=used_calculates,
730
+ replace_reference=replace_reference,
731
+ warn=warn,
732
+ codesystems=codesystems,
733
+ )
734
+ if modified_expression is False:
735
+ return False
736
+ elif modified_expression and replace_reference:
737
+ node.trigger = modified_expression
738
+ if isinstance(getattr(node, "constraint", None), (TriccOperation, TriccReference)):
739
+ modified_expression = process_operation_reference(
740
+ node.constraint,
741
+ node,
742
+ processed_nodes=processed_nodes,
743
+ calculates=calculates,
744
+ used_calculates=used_calculates,
745
+ replace_reference=replace_reference,
746
+ warn=warn,
747
+ codesystems=codesystems,
748
+ )
749
+ if modified_expression is False:
750
+ return False
751
+ elif modified_expression and replace_reference:
752
+ node.constraint = modified_expression
753
+
754
+ if isinstance(getattr(node, "default", None), (TriccOperation, TriccReference)):
596
755
  modified_expression = process_operation_reference(
597
- node.default,
598
- node,
756
+ node.default,
757
+ node,
599
758
  processed_nodes=processed_nodes,
600
- calculates=calculates,
601
- used_calculates=used_calculates,
759
+ calculates=calculates,
760
+ used_calculates=used_calculates,
602
761
  replace_reference=replace_reference,
603
- warn=warn,
604
- codesystems=codesystems
605
- )
762
+ warn=warn,
763
+ codesystems=codesystems,
764
+ )
606
765
  if modified_expression is False:
607
766
  return False
608
767
  elif modified_expression and replace_reference:
609
768
  node.relevance = modified_expression
610
-
611
- if isinstance(getattr(node, 'expression', None), (TriccOperation, TriccReference)):
769
+
770
+ if isinstance(getattr(node, "expression", None), (TriccOperation, TriccReference)):
612
771
  modified_expression = process_operation_reference(
613
- node.expression,
614
- node,
772
+ node.expression,
773
+ node,
615
774
  processed_nodes=processed_nodes,
616
- calculates=calculates,
617
- used_calculates=used_calculates,
775
+ calculates=calculates,
776
+ used_calculates=used_calculates,
618
777
  replace_reference=replace_reference,
619
- warn=warn,
620
- codesystems=codesystems
778
+ warn=warn,
779
+ codesystems=codesystems,
621
780
  )
622
781
  if modified_expression is False:
623
782
  return False
624
783
  elif modified_expression and replace_reference:
625
784
  node.expression = modified_expression
626
-
627
- if isinstance(getattr(node, 'applicability', None), (TriccOperation, TriccReference)):
785
+
786
+ if isinstance(getattr(node, "applicability", None), (TriccOperation, TriccReference)):
628
787
  modified_expression = process_operation_reference(
629
- node.applicability,
630
- node,
788
+ node.applicability,
789
+ node,
631
790
  processed_nodes=processed_nodes,
632
- calculates=calculates,
633
- used_calculates=used_calculates,
791
+ calculates=calculates,
792
+ used_calculates=used_calculates,
634
793
  replace_reference=replace_reference,
635
- warn=warn,
636
- codesystems=codesystems
794
+ warn=warn,
795
+ codesystems=codesystems,
637
796
  )
638
797
  if modified_expression is False:
639
798
  return False
@@ -641,7 +800,17 @@ def process_reference(node, processed_nodes, calculates, used_calculates=None,
641
800
  node.applicability = modified_expression
642
801
  return True
643
802
 
644
- def process_operation_reference(operation, node, processed_nodes, calculates, used_calculates=None, replace_reference=False,warn=False, codesystems=None):
803
+
804
+ def process_operation_reference(
805
+ operation,
806
+ node,
807
+ processed_nodes,
808
+ calculates,
809
+ used_calculates=None,
810
+ replace_reference=False,
811
+ warn=False,
812
+ codesystems=None,
813
+ ):
645
814
  modified_operation = None
646
815
  node_reference = []
647
816
  reference = []
@@ -649,13 +818,13 @@ def process_operation_reference(operation, node, processed_nodes, calculates, us
649
818
  ref_list = [r.value for r in operation.get_references() if isinstance(r, TriccReference)]
650
819
  real_ref_list = [r for r in operation.get_references() if issubclass(r.__class__, TriccNodeBaseModel)]
651
820
  for ref in ref_list:
652
- if ref.endswith(']'):
653
- terms = ref[:-1].split('[')
821
+ if ref.endswith("]"):
822
+ terms = ref[:-1].split("[")
654
823
  option_label = terms[1]
655
824
  ref = terms[0]
656
825
  else:
657
826
  option_label = None
658
- node_in_act = [n for n in node.activity.nodes.values() if n.name == ref and n != node]
827
+ node_in_act = [n for n in node.activity.nodes.values() if n.name == ref and n != node]
659
828
  if node_in_act:
660
829
  if any(n not in processed_nodes for n in node_in_act):
661
830
  return False
@@ -663,16 +832,16 @@ def process_operation_reference(operation, node, processed_nodes, calculates, us
663
832
  last_found = node_in_act[0]
664
833
  else:
665
834
  last_found = get_last_version(name=ref, processed_nodes=processed_nodes)
666
- if last_found is None:
835
+ if last_found is None:
667
836
  if codesystems:
668
- concept = lookup_codesystems_code(codesystems, ref)
837
+ concept = lookup_codesystems_code(codesystems, ref)
669
838
  if not concept:
670
839
  logger.critical(f"reference {ref} not found in the project for{str(node)} ")
671
840
  exit(1)
672
841
  else:
673
842
  if warn:
674
843
  logger.debug(f"reference {ref}::{concept.display} not yet processed {node.get_name()}")
675
-
844
+
676
845
  elif warn:
677
846
  logger.debug(f"reference {ref} not found for a calculate {node.get_name()}")
678
847
  return False
@@ -680,7 +849,9 @@ def process_operation_reference(operation, node, processed_nodes, calculates, us
680
849
  node_reference.append(last_found)
681
850
  reference.append(TriccReference(ref))
682
851
  if replace_reference:
683
- if not issubclass(last_found.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase, TriccNodeInput)):
852
+ if not issubclass(
853
+ last_found.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase, TriccNodeInput)
854
+ ):
684
855
  last_found = get_node_expression(last_found, processed_nodes, is_prev=True)
685
856
  if isinstance(operation, (TriccOperation)):
686
857
  if modified_operation is None:
@@ -692,118 +863,141 @@ def process_operation_reference(operation, node, processed_nodes, calculates, us
692
863
  # Resolve human-readable label
693
864
  option_code = get_option_code_from_label(last_found, option_label)
694
865
  if option_code:
695
- modified_operation = replace_code_reference(operation, old=f"{ref}[{option_label}]", new=option_code )
866
+ modified_operation = replace_code_reference(
867
+ operation, old=f"{ref}[{option_label}]", new=option_code
868
+ )
696
869
  else:
697
870
  if warn:
698
871
  logger.warning(f"Could not resolve label '{option_label}' for reference {ref}")
699
872
  return False
700
- if hasattr(last_found, 'path_len'):
701
- path_len = last_found.path_len
873
+ if hasattr(last_found, "path_len"):
874
+ path_len = last_found.path_len
702
875
  elif isinstance(last_found, TriccOperation):
703
- path_len = max(getattr(n, 'path_len', 0) for n in last_found.get_references())
876
+ path_len = max(getattr(n, "path_len", 0) for n in last_found.get_references())
704
877
  else:
705
878
  path_len = 0
706
879
  node.path_len = max(node.path_len, path_len)
707
880
  for ref in real_ref_list:
708
881
  if is_prev_processed(ref, node, processed_nodes=processed_nodes, local=False) is False:
709
882
  return False
710
-
883
+
711
884
  if used_calculates is not None:
712
885
  for ref_nodes in node_reference:
713
886
  if issubclass(ref_nodes.__class__, TriccNodeCalculateBase):
714
887
  add_used_calculate(node, ref_nodes, calculates, used_calculates, processed_nodes=processed_nodes)
715
888
  return modified_operation
716
889
 
890
+
717
891
  def replace_code_reference(expression, old, new):
718
892
  if isinstance(expression, str):
719
- return expression_reference.replace(old, f"'{new}'")
893
+ return expression.replace(old, f"'{new}'")
720
894
  if isinstance(expression, TriccOperation):
721
895
  expression.replace_node(TriccReference(old), TriccStatic(new))
722
896
  return expression
723
- #add_used_calculate(node, calc_node, calculates, used_calculates, processed_nodes)
897
+
898
+
899
+ # add_used_calculate(node, calc_node, calculates, used_calculates, processed_nodes)
900
+
724
901
 
725
902
  def add_used_calculate(node, prev_node, calculates, used_calculates, processed_nodes):
726
903
  if issubclass(prev_node.__class__, TriccNodeDisplayCalculateBase):
727
904
  if prev_node in processed_nodes:
728
905
  # if not a verison, index will equal -1
729
- if prev_node.name not in calculates :
906
+ if prev_node.name not in calculates:
730
907
  logger.debug("node {} refered before being processed".format(node.get_name()))
731
908
  return False
732
- max_version = prev_node#get_max_version(calculates[node_clean_name])
909
+ max_version = prev_node # get_max_version(calculates[node_clean_name])
733
910
  if prev_node.name not in used_calculates:
734
911
  used_calculates[prev_node.name] = {}
735
- #save the max version only once
912
+ # save the max version only once
736
913
  if max_version.id not in used_calculates[prev_node.name]:
737
914
  used_calculates[prev_node.name][max_version.id] = max_version
738
915
  else:
739
- logger.debug("process_calculate_version_requirement: failed for {0} , prev Node {1} ".format(node.get_name(), prev_node.get_name()))
740
-
741
-
742
- def get_select_not_available_options(node,group,label):
743
- return {0:TriccNodeSelectOption(
744
- id = generate_id(f"notavaialble{node.id}"),
745
- name="1",
746
- label=label,
747
- select = node,
748
- group = group,
749
- list_name = node.list_name
750
- )}
751
-
916
+ logger.debug(
917
+ "load_calculate_version_requirement: failed for {0} , prev Node {1} ".format(
918
+ node.get_name(), prev_node.get_name()
919
+ )
920
+ )
921
+
922
+
923
+ def get_select_not_available_options(node, group, label):
924
+ return {
925
+ 0: TriccNodeSelectOption(
926
+ id=generate_id(f"notavaialble{node.id}"),
927
+ name="1",
928
+ label=label,
929
+ select=node,
930
+ group=group,
931
+ list_name=node.list_name,
932
+ )
933
+ }
934
+
935
+
752
936
  def get_select_yes_no_options(node, group):
753
937
  yes = TriccNodeSelectOption(
754
- id = generate_id(f'yes{node.id}'),
755
- name=f"{TRICC_TRUE_VALUE}",
756
- label="Yes",
757
- select = node,
758
- group = group,
759
- list_name = node.list_name
760
- )
938
+ id=generate_id(f"yes{node.id}"),
939
+ name=f"{TRICC_TRUE_VALUE}",
940
+ label="Yes",
941
+ select=node,
942
+ group=group,
943
+ list_name=node.list_name,
944
+ )
761
945
  no = TriccNodeSelectOption(
762
- id = generate_id(f'no{node.id}'),
763
- name=f"{TRICC_FALSE_VALUE}",
764
- label="No",
765
- select = node,
766
- group = group,
767
- list_name = node.list_name
768
- )
769
- return {0:yes, 1:no }
946
+ id=generate_id(f"no{node.id}"),
947
+ name=f"{TRICC_FALSE_VALUE}",
948
+ label="No",
949
+ select=node,
950
+ group=group,
951
+ list_name=node.list_name,
952
+ )
953
+ return {0: yes, 1: no}
954
+
770
955
 
771
- # walkthough all node in an iterative way, the same node might be parsed 2 times
956
+ # walkthough all node in an iterative way, the same node might be parsed 2 times
772
957
  # therefore to avoid double processing the nodes variable saves the node already processed
773
958
  # there 2 strategies : process it the first time or the last time (wait that all the previuous node are processed)
774
959
 
775
- def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, stashed_nodes, path_len, recursive=False, warn = False,
776
- node_path = [], process=None, **kwargs):
960
+
961
+ def walktrhough_tricc_node_processed_stached(
962
+ node,
963
+ callback,
964
+ processed_nodes,
965
+ stashed_nodes,
966
+ path_len,
967
+ recursive=False,
968
+ warn=False,
969
+ node_path=[],
970
+ process=None,
971
+ **kwargs,
972
+ ):
777
973
  ended_activity = False
778
974
  # logger.debug("walkthrough::{}::{}".format(callback.__name__, node.get_name()))
779
-
780
- path_len = max(node.activity.path_len, *[0,*[getattr(n,'path_len',0) + 1 for n in node.activity.prev_nodes]]) + 1
781
- if hasattr(node, 'prev_nodes'):
782
- path_len = max(path_len, *[0,*[getattr(n,'path_len',0)+ 1 for n in node.prev_nodes]])
783
- if hasattr(node, 'get_references'):
975
+
976
+ path_len = max(node.activity.path_len, *[0, *[getattr(n, "path_len", 0) + 1 for n in node.activity.prev_nodes]]) + 1
977
+ if hasattr(node, "prev_nodes"):
978
+ path_len = max(path_len, *[0, *[getattr(n, "path_len", 0) + 1 for n in node.prev_nodes]])
979
+ if hasattr(node, "get_references"):
784
980
  references = node.get_references()
785
981
  if references:
786
- path_len = max(path_len, *[0,*[getattr(n,'path_len',0) + 1 for n in references]])
982
+ path_len = max(path_len, *[0, *[getattr(n, "path_len", 0) + 1 for n in references]])
787
983
  node.path_len = max(node.path_len, path_len)
788
984
  prev_process = process[0] if process else None
789
- if isinstance(node, TriccNodeActivity) and getattr(node.root, 'process', None):
985
+ if isinstance(node, TriccNodeActivity) and getattr(node.root, "process", None):
790
986
  if process is None:
791
987
  process = [node.root.process]
792
988
  else:
793
989
  process[0] = node.root.process
794
- if (
795
- callback(
796
- node,
797
- processed_nodes=processed_nodes,
798
- stashed_nodes=stashed_nodes,
799
- warn = warn,
800
- node_path=node_path,
801
- process=process,
802
- **kwargs
803
- )
990
+ if callback(
991
+ node,
992
+ processed_nodes=processed_nodes,
993
+ stashed_nodes=stashed_nodes,
994
+ warn=warn,
995
+ node_path=node_path,
996
+ process=process,
997
+ **kwargs,
804
998
  ):
805
999
  node_path.append(node)
806
- # node processing succeed
1000
+ # node processing succeed
807
1001
  if not isinstance(node, TriccNodeActivity) and node not in processed_nodes:
808
1002
  processed_nodes.add(node)
809
1003
  if warn:
@@ -814,7 +1008,11 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
814
1008
  processed_nodes.add(node.activity)
815
1009
  ended_activity = True
816
1010
  if warn:
817
- logger.debug("{}::{}: processed ({})".format(callback.__name__, node.activity.get_name(), len(processed_nodes)))
1011
+ logger.debug(
1012
+ "{}::{}: processed ({})".format(
1013
+ callback.__name__, node.activity.get_name(), len(processed_nodes)
1014
+ )
1015
+ )
818
1016
  elif node in stashed_nodes:
819
1017
  stashed_nodes.remove(node)
820
1018
  # logger.debug("{}::{}: unstashed ({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
@@ -825,90 +1023,118 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
825
1023
  if recursive:
826
1024
  for gp in node.activity.groups.values():
827
1025
  walktrhough_tricc_node_processed_stached(
828
- gp,
1026
+ gp,
829
1027
  callback,
830
- processed_nodes=processed_nodes,
831
- stashed_nodes=stashed_nodes,
1028
+ processed_nodes=processed_nodes,
1029
+ stashed_nodes=stashed_nodes,
832
1030
  path_len=path_len,
833
1031
  recursive=recursive,
834
- warn = warn,
835
- node_path = node_path.copy(),
836
- **kwargs
1032
+ warn=warn,
1033
+ node_path=node_path.copy(),
1034
+ **kwargs,
837
1035
  )
838
1036
  for c in node.activity.calculates:
839
- if len(c.prev_nodes)== 0:
1037
+ if len(c.prev_nodes) == 0:
840
1038
  walktrhough_tricc_node_processed_stached(
841
1039
  c,
842
1040
  callback,
843
- processed_nodes=processed_nodes,
844
- stashed_nodes=stashed_nodes,
1041
+ processed_nodes=processed_nodes,
1042
+ stashed_nodes=stashed_nodes,
845
1043
  path_len=path_len,
846
1044
  recursive=recursive,
847
- warn = warn,
848
- node_path = node_path.copy(),
849
- **kwargs
850
- )
1045
+ warn=warn,
1046
+ node_path=node_path.copy(),
1047
+ **kwargs,
1048
+ )
851
1049
  else:
852
- stashed_nodes += [c for c in node.activity.calculates if len(c.prev_nodes)== 0]
1050
+ stashed_nodes += [c for c in node.activity.calculates if len(c.prev_nodes) == 0]
853
1051
  stashed_nodes += node.activity.groups.values()
854
1052
  elif issubclass(node.__class__, TriccNodeSelect):
855
1053
  for option in node.options.values():
856
- option.path_len = max(path_len, option.path_len)
857
- callback(option, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, warn = warn, node_path=node_path,**kwargs)
1054
+ option.path_len = max(path_len, option.path_len)
1055
+ callback(
1056
+ option,
1057
+ processed_nodes=processed_nodes,
1058
+ stashed_nodes=stashed_nodes,
1059
+ warn=warn,
1060
+ node_path=node_path,
1061
+ **kwargs,
1062
+ )
858
1063
  if option not in processed_nodes:
859
1064
  processed_nodes.add(option)
860
1065
  if warn:
861
1066
  logger.debug(
862
- "{}::{}: processed ({})".format(callback.__name__, option.get_name(), len(processed_nodes)))
863
- walkthrough_tricc_option(node, callback, processed_nodes, stashed_nodes, path_len + 1, recursive,
864
- warn = warn,node_path = node_path, **kwargs)
1067
+ "{}::{}: processed ({})".format(callback.__name__, option.get_name(), len(processed_nodes))
1068
+ )
1069
+ walkthrough_tricc_option(
1070
+ node,
1071
+ callback,
1072
+ processed_nodes,
1073
+ stashed_nodes,
1074
+ path_len + 1,
1075
+ recursive,
1076
+ warn=warn,
1077
+ node_path=node_path,
1078
+ **kwargs,
1079
+ )
865
1080
  if isinstance(node, TriccNodeActivity):
866
1081
  if node.root not in processed_nodes:
867
1082
  if node.root is not None:
868
- node.root.path_len = max(path_len, node.root.path_len)
1083
+ node.root.path_len = max(path_len, node.root.path_len)
869
1084
  if recursive:
870
- walktrhough_tricc_node_processed_stached(node.root, callback, processed_nodes, stashed_nodes, path_len,
871
- recursive, warn = warn,node_path = node_path.copy(),**kwargs)
872
- # for gp in node.groups:
873
- # walktrhough_tricc_node_processed_stached(gp, callback, processed_nodes, stashed_nodes, path_len,
874
- # recursive, warn = warn,**kwargs)
875
- # if node.calculates:
876
- # for c in node.calculates:
877
- # walktrhough_tricc_node_processed_stached(c, callback, processed_nodes, stashed_nodes, path_len,
878
- # recursive, warn = warn,**kwargs)
1085
+ walktrhough_tricc_node_processed_stached(
1086
+ node.root,
1087
+ callback,
1088
+ processed_nodes,
1089
+ stashed_nodes,
1090
+ path_len,
1091
+ recursive,
1092
+ warn=warn,
1093
+ node_path=node_path.copy(),
1094
+ **kwargs,
1095
+ )
879
1096
  elif node.root not in stashed_nodes:
880
- #stashed_nodes.insert(0,node.root)
881
1097
  stashed_nodes.insert_at_top(node.root)
882
- # if node.calculates:
883
- # stashed_nodes += node.calculates
884
- # for gp in node.groups:
885
- # stashed_nodes.add(gp)
886
- # # stashed_nodes.insert(0,gp)
887
1098
  return
888
1099
  elif ended_activity:
889
1100
  for next_node in node.next_nodes:
890
1101
  if next_node not in stashed_nodes:
891
- #stashed_nodes.insert(0,next_node)
1102
+ # stashed_nodes.insert(0,next_node)
892
1103
  if recursive:
893
- walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes, stashed_nodes, path_len,
894
- recursive, warn = warn,node_path = node_path.copy(),**kwargs)
1104
+ walktrhough_tricc_node_processed_stached(
1105
+ next_node,
1106
+ callback,
1107
+ processed_nodes,
1108
+ stashed_nodes,
1109
+ path_len,
1110
+ recursive,
1111
+ warn=warn,
1112
+ node_path=node_path.copy(),
1113
+ **kwargs,
1114
+ )
895
1115
  else:
896
1116
  stashed_nodes.insert_at_top(next_node)
897
-
898
-
899
- elif hasattr(node, 'next_nodes') and len(node.next_nodes) > 0 and not isinstance(node, TriccNodeActivity):
1117
+
1118
+ elif hasattr(node, "next_nodes") and len(node.next_nodes) > 0 and not isinstance(node, TriccNodeActivity):
900
1119
  if recursive:
901
- walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes, path_len + 1, recursive,
902
- warn = warn,node_path = node_path,**kwargs)
1120
+ walkthrough_tricc_next_nodes(
1121
+ node,
1122
+ callback,
1123
+ processed_nodes,
1124
+ stashed_nodes,
1125
+ path_len + 1,
1126
+ recursive,
1127
+ warn=warn,
1128
+ node_path=node_path,
1129
+ **kwargs,
1130
+ )
903
1131
  else:
904
1132
  for nn in node.next_nodes:
905
1133
  if nn not in stashed_nodes:
906
1134
  stashed_nodes.insert_at_top(nn)
907
1135
  if not recursive:
908
1136
  reorder_node_list(stashed_nodes, node.group, processed_nodes)
909
-
910
-
911
-
1137
+
912
1138
  else:
913
1139
  if prev_process and process and prev_process != process[0]:
914
1140
  process[0] = prev_process
@@ -919,8 +1145,10 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
919
1145
  logger.debug("{}::{}: stashed({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
920
1146
 
921
1147
 
922
- def walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn = False, node_path = [], **kwargs):
923
-
1148
+ def walkthrough_tricc_next_nodes(
1149
+ node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn=False, node_path=[], **kwargs
1150
+ ):
1151
+
924
1152
  if not recursive:
925
1153
  for next_node in node.next_nodes:
926
1154
  if next_node not in stashed_nodes:
@@ -930,73 +1158,100 @@ def walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes,
930
1158
  for next_node in list_next:
931
1159
  if not isinstance(node, (TriccNodeActivityEnd, TriccNodeEnd)):
932
1160
  if next_node not in processed_nodes:
933
- walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes, stashed_nodes,
934
- path_len + 1,recursive, warn = warn,node_path = node_path.copy(), **kwargs)
1161
+ walktrhough_tricc_node_processed_stached(
1162
+ next_node,
1163
+ callback,
1164
+ processed_nodes,
1165
+ stashed_nodes,
1166
+ path_len + 1,
1167
+ recursive,
1168
+ warn=warn,
1169
+ node_path=node_path.copy(),
1170
+ **kwargs,
1171
+ )
935
1172
  else:
936
1173
  logger.critical(
937
- "{}::end node of {} has a next node".format(callback.__name__, node.activity.get_name()))
1174
+ "{}::end node of {} has a next node".format(callback.__name__, node.activity.get_name())
1175
+ )
938
1176
  exit(1)
939
1177
 
940
1178
 
941
- def walkthrough_tricc_option(node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn = False,node_path = [], **kwargs):
1179
+ def walkthrough_tricc_option(
1180
+ node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn=False, node_path=[], **kwargs
1181
+ ):
942
1182
  if not recursive:
943
1183
  for option in node.options.values():
944
- if hasattr(option, 'next_nodes') and len(option.next_nodes) > 0:
1184
+ if hasattr(option, "next_nodes") and len(option.next_nodes) > 0:
945
1185
  for next_node in option.next_nodes:
946
1186
  if next_node not in stashed_nodes:
947
1187
  stashed_nodes.insert_at_top(next_node)
948
- #stashed_nodes.insert(0,next_node)
1188
+ # stashed_nodes.insert(0,next_node)
949
1189
  else:
950
1190
  list_option = []
951
1191
  while not all(elem in list_option for elem in list(node.options.values())):
952
1192
  for option in node.options.values():
953
1193
  if option not in list_option:
954
1194
  list_option.append(option)
955
- # then walk the options
956
- if hasattr(option, 'next_nodes') and len(option.next_nodes) > 0:
1195
+ # then walk the options
1196
+ if hasattr(option, "next_nodes") and len(option.next_nodes) > 0:
957
1197
  list_next = set(option.next_nodes)
958
1198
  for next_node in list_next:
959
1199
  if next_node not in processed_nodes:
960
- walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes,
961
- stashed_nodes, path_len + 1, recursive,
962
- warn = warn,
963
- node_path = node_path.copy(), **kwargs)
1200
+ walktrhough_tricc_node_processed_stached(
1201
+ next_node,
1202
+ callback,
1203
+ processed_nodes,
1204
+ stashed_nodes,
1205
+ path_len + 1,
1206
+ recursive,
1207
+ warn=warn,
1208
+ node_path=node_path.copy(),
1209
+ **kwargs,
1210
+ )
1211
+
964
1212
 
965
1213
  def get_next_version(name, processed_nodes, version=0, min=100):
966
- return max(version, min,*[(getattr(n,'version',None) or getattr(n,'instance',None) or 0) for n in get_versions(name, processed_nodes)])+1
1214
+ return (
1215
+ max(
1216
+ version,
1217
+ min,
1218
+ *[
1219
+ (getattr(n, "version", None) or getattr(n, "instance", None) or 0)
1220
+ for n in get_versions(name, processed_nodes)
1221
+ ],
1222
+ )
1223
+ + 1
1224
+ )
967
1225
 
968
1226
 
969
1227
  def get_data_for_log(node):
970
1228
  return "{}:{}|{} {}:{}".format(
971
1229
  node.group.get_name() if node.group is not None else node.activity.get_name(),
972
- node.group.instance if node.group is not None else node.activity.instance ,
1230
+ node.group.instance if node.group is not None else node.activity.instance,
973
1231
  node.__class__,
974
1232
  node.get_name(),
975
- node.instance)
1233
+ node.instance,
1234
+ )
1235
+
976
1236
 
977
1237
  def stashed_node_func(node, callback, recursive=False, **kwargs):
978
- processed_nodes = kwargs.pop('processed_nodes', OrderedSet())
979
- stashed_nodes = kwargs.pop('stashed_nodes', OrderedSet())
980
- process = kwargs.pop('process', ['main'])
1238
+ processed_nodes = kwargs.pop("processed_nodes", OrderedSet())
1239
+ stashed_nodes = kwargs.pop("stashed_nodes", OrderedSet())
1240
+ process = kwargs.pop("process", ["main"])
981
1241
  path_len = 0
982
-
1242
+
983
1243
  walktrhough_tricc_node_processed_stached(
984
- node,
985
- callback,
986
- processed_nodes,
987
- stashed_nodes,
988
- path_len,
989
- recursive,
990
- process=process,
991
- **kwargs)
1244
+ node, callback, processed_nodes, stashed_nodes, path_len, recursive, process=process, **kwargs
1245
+ )
992
1246
  # callback( node, **kwargs)
993
- ## MANAGE STASHED NODES
1247
+ # MANAGE STASHED NODES
994
1248
  prev_stashed_nodes = stashed_nodes.copy()
995
1249
  loop_count = 0
996
1250
  len_prev_processed_nodes = 0
997
1251
  while len(stashed_nodes) > 0:
998
- loop_count = check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes,
999
- loop_count)
1252
+ loop_count = check_stashed_loop(
1253
+ stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes, loop_count
1254
+ )
1000
1255
  prev_stashed_nodes = stashed_nodes.copy()
1001
1256
  len_prev_processed_nodes = len(processed_nodes)
1002
1257
  if len(stashed_nodes) > 0:
@@ -1004,11 +1259,13 @@ def stashed_node_func(node, callback, recursive=False, **kwargs):
1004
1259
  # remove duplicates
1005
1260
  if s_node in stashed_nodes:
1006
1261
  stashed_nodes.remove(s_node)
1007
- if kwargs.get('warn', True):
1008
- logger.debug("{}:: {}: unstashed for processing ({})".format(callback.__name__, s_node.__class__,
1009
- get_data_for_log(s_node),
1010
- len(stashed_nodes)))
1011
- warn = loop_count >= (9 * len(stashed_nodes )+1)
1262
+ if kwargs.get("warn", True):
1263
+ logger.debug(
1264
+ "{}:: {}: unstashed for processing ({})::{}".format(
1265
+ callback.__name__, s_node.__class__, get_data_for_log(s_node), len(stashed_nodes)
1266
+ )
1267
+ )
1268
+ warn = loop_count >= (9 * len(stashed_nodes) + 1)
1012
1269
  walktrhough_tricc_node_processed_stached(
1013
1270
  s_node,
1014
1271
  callback,
@@ -1018,77 +1275,87 @@ def stashed_node_func(node, callback, recursive=False, **kwargs):
1018
1275
  recursive,
1019
1276
  warn=warn,
1020
1277
  process=process,
1021
- **kwargs)
1278
+ **kwargs,
1279
+ )
1022
1280
 
1023
1281
 
1024
1282
  # check if the all the prev nodes are processed
1025
1283
  def is_ready_to_process(in_node, processed_nodes, strict=True, local=False):
1026
1284
  if isinstance(in_node, TriccNodeSelectOption):
1027
1285
  node = in_node.select
1028
- elif (
1029
- isinstance(in_node, (TriccNodeActivityStart, TriccNodeMainStart)) ):
1286
+ elif isinstance(in_node, (TriccNodeActivityStart, TriccNodeMainStart)):
1030
1287
  # check before
1031
1288
  return True
1032
1289
  else:
1033
1290
  node = in_node
1034
- if hasattr(node, 'prev_nodes'):
1291
+ if hasattr(node, "prev_nodes"):
1035
1292
  # ensure the previous node of the select are processed, not the option prev nodes
1036
1293
  for prev_node in node.prev_nodes:
1037
1294
  if is_prev_processed(prev_node, node, processed_nodes, local) is False:
1038
1295
  return False
1039
1296
  return True
1040
-
1297
+
1298
+
1041
1299
  def is_prev_processed(prev_node, node, processed_nodes, local):
1042
- if hasattr(prev_node, 'select'):
1043
- return is_prev_processed(prev_node.select, node, processed_nodes, local)
1300
+ if hasattr(prev_node, "select"):
1301
+ return is_prev_processed(prev_node.select, node, processed_nodes, local)
1044
1302
  if prev_node not in processed_nodes and (not local):
1045
1303
  if isinstance(prev_node, TriccNodeExclusive):
1046
1304
  iterator = iter(prev_node.prev_nodes)
1047
1305
  p_n_node = next(iterator)
1048
- logger.debug("is_ready_to_process:failed:via_excl: {} - {} > {} {}:{}".format(
1049
- get_data_for_log(p_n_node),
1050
- prev_node.get_name(),
1051
- node.__class__, node.get_name(), node.instance))
1306
+ logger.debug(
1307
+ "is_ready_to_process:failed:via_excl: {} - {} > {} {}:{}".format(
1308
+ get_data_for_log(p_n_node), prev_node.get_name(), node.__class__, node.get_name(), node.instance
1309
+ )
1310
+ )
1052
1311
 
1053
1312
  else:
1054
- logger.debug("is_ready_to_process:failed: {} -> {} {}:{}".format(
1055
- get_data_for_log(prev_node),
1056
- node.__class__, node.get_name(), node.instance))
1313
+ logger.debug(
1314
+ "is_ready_to_process:failed: {} -> {} {}:{}".format(
1315
+ get_data_for_log(prev_node), node.__class__, node.get_name(), node.instance
1316
+ )
1317
+ )
1057
1318
 
1058
- logger.debug("prev node node {}:{} for node {} not in processed".format(prev_node.__class__,
1059
- prev_node.get_name(),
1060
- node.get_name()))
1319
+ logger.debug(
1320
+ "prev node node {}:{} for node {} not in processed".format(
1321
+ prev_node.__class__, prev_node.get_name(), node.get_name()
1322
+ )
1323
+ )
1061
1324
  return False
1062
1325
  return True
1063
1326
 
1064
1327
 
1328
+ def print_trace(node, prev_node, processed_nodes, stashed_nodes, history=[]):
1065
1329
 
1066
- def print_trace(node, prev_node, processed_nodes, stashed_nodes, history = []):
1067
-
1068
1330
  if node != prev_node:
1069
1331
  if node in processed_nodes:
1070
- logger.warning("print trace :: node {} was the last not processed ({})".format(
1071
- get_data_for_log(prev_node), node.id, ">".join(history)))
1072
- #processed_nodes.add(prev_node)
1332
+ logger.warning(
1333
+ "print trace :: node {} was the last not processed ({}):{}".format(
1334
+ get_data_for_log(prev_node), node.id, ">".join(history)
1335
+ )
1336
+ )
1337
+ # processed_nodes.add(prev_node)
1073
1338
  return False
1074
1339
  elif node in history:
1075
- logger.critical("print trace :: CYCLE node {} found in history ({})".format(
1076
- get_data_for_log(prev_node), ">".join(history)))
1340
+ logger.critical(
1341
+ "print trace :: CYCLE node {} found in history ({})".format(
1342
+ get_data_for_log(prev_node), ">".join(history)
1343
+ )
1344
+ )
1077
1345
  exit(1)
1078
1346
  elif node in stashed_nodes:
1079
1347
  # logger.debug("print trace :: node {}::{} in stashed".format(node.__class__,node.get_name()))
1080
1348
  return False
1081
1349
  # else:
1082
- # logger.debug("print trace :: node {} not processed/stashed".format(node.get_name()))
1350
+ # logger.debug("print trace :: node {} not processed/stashed".format(node.get_name()))
1083
1351
  return True
1084
1352
 
1085
1353
 
1086
- def reverse_walkthrough(in_node, next_node, callback, processed_nodes, stashed_nodes, history = []):
1354
+ def reverse_walkthrough(in_node, next_node, callback, processed_nodes, stashed_nodes, history=[]):
1087
1355
  # transform dead-end nodes
1088
1356
  if next_node == in_node and next_node not in stashed_nodes:
1089
1357
  # workaround fir loop
1090
1358
  return False
1091
-
1092
1359
 
1093
1360
  if isinstance(in_node, TriccNodeSelectOption):
1094
1361
  node = in_node.select
@@ -1101,53 +1368,79 @@ def reverse_walkthrough(in_node, next_node, callback, processed_nodes, stashed_n
1101
1368
  if isinstance(in_node, TriccNodeActivity):
1102
1369
  prev_nodes = set(in_node.get_end_nodes())
1103
1370
  for prev in prev_nodes:
1104
- reverse_walkthrough(prev, next_node, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history=history)
1105
- if hasattr(node, 'prev_nodes'):
1371
+ reverse_walkthrough(
1372
+ prev,
1373
+ next_node,
1374
+ callback,
1375
+ processed_nodes=processed_nodes,
1376
+ stashed_nodes=stashed_nodes,
1377
+ history=history,
1378
+ )
1379
+ if hasattr(node, "prev_nodes"):
1106
1380
  if node.prev_nodes:
1107
1381
  for prev in node.prev_nodes:
1108
- reverse_walkthrough(prev, node, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history=history)
1382
+ reverse_walkthrough(
1383
+ prev,
1384
+ node,
1385
+ callback,
1386
+ processed_nodes=processed_nodes,
1387
+ stashed_nodes=stashed_nodes,
1388
+ history=history,
1389
+ )
1109
1390
  elif node in node.activity.calculates:
1110
- reverse_walkthrough(prev, node.activity.root, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history=history)
1391
+ reverse_walkthrough(
1392
+ prev,
1393
+ node.activity.root,
1394
+ callback,
1395
+ processed_nodes=processed_nodes,
1396
+ stashed_nodes=stashed_nodes,
1397
+ history=history,
1398
+ )
1111
1399
 
1112
1400
  if issubclass(node.__class__, TriccRhombusMixIn):
1113
1401
  if isinstance(node.reference, list):
1114
1402
  for ref in node.reference:
1115
- reverse_walkthrough(ref, node, callback, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, history= history)
1116
-
1117
-
1403
+ reverse_walkthrough(
1404
+ ref,
1405
+ node,
1406
+ callback,
1407
+ processed_nodes=processed_nodes,
1408
+ stashed_nodes=stashed_nodes,
1409
+ history=history,
1410
+ )
1118
1411
 
1119
1412
 
1120
1413
  def get_prev_node_by_name(processed_nodes, name, node):
1121
- # look for the node in the same activity
1122
- last_calc = get_last_version(
1123
- name,
1124
- processed_nodes
1125
- )
1414
+ # look for the node in the same activity
1415
+ last_calc = get_last_version(name, processed_nodes)
1126
1416
  if last_calc:
1127
1417
  return last_calc
1128
-
1418
+
1129
1419
  filtered = list(
1130
- filter(lambda p_node:
1131
- hasattr(p_node,'name')
1132
- and p_node.name == name
1133
- and p_node.instance == node.instance
1134
- and p_node.path_len <= node.path_len, processed_nodes
1135
- ))
1420
+ filter(
1421
+ lambda p_node: hasattr(p_node, "name")
1422
+ and p_node.name == name
1423
+ and p_node.instance == node.instance
1424
+ and p_node.path_len <= node.path_len,
1425
+ processed_nodes,
1426
+ )
1427
+ )
1136
1428
  if len(filtered) == 0:
1137
- filtered = list(filter(lambda p_node: hasattr(p_node, 'name') and p_node.name == name , processed_nodes))
1429
+ filtered = list(filter(lambda p_node: hasattr(p_node, "name") and p_node.name == name, processed_nodes))
1138
1430
  if len(filtered) > 0:
1139
1431
  return sorted(filtered, key=lambda x: x.path_len, reverse=False)[0]
1140
1432
 
1433
+
1141
1434
  MIN_LOOP_COUNT = 10
1142
1435
 
1436
+
1143
1437
  def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes, loop_count):
1144
- loop_out = {}
1145
-
1438
+
1146
1439
  if len(stashed_nodes) == len(prev_stashed_nodes):
1147
- # to avoid checking the details
1148
- if loop_count<=0:
1440
+ # to avoid checking the details
1441
+ if loop_count <= 0:
1149
1442
  if loop_count < -MIN_LOOP_COUNT:
1150
- loop_count = MIN_LOOP_COUNT+1
1443
+ loop_count = MIN_LOOP_COUNT + 1
1151
1444
  else:
1152
1445
  loop_count -= 1
1153
1446
  if loop_count > MIN_LOOP_COUNT:
@@ -1155,51 +1448,56 @@ def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_p
1155
1448
  loop_count += 1
1156
1449
  if loop_count > max(MIN_LOOP_COUNT, 11 * len(prev_stashed_nodes) + 1):
1157
1450
  logger.critical("Stashed node list was unchanged: loop likely or unresolved dependence")
1158
- waited, looped = get_all_dependant(stashed_nodes, stashed_nodes, processed_nodes)
1451
+ waited, looped = get_all_dependant(stashed_nodes, stashed_nodes, processed_nodes)
1159
1452
  logger.debug(f"{len(looped)} nodes waiting stashed nodes")
1160
1453
  logger.info("unresolved reference")
1161
1454
  for es_node in [n for n in stashed_nodes if isinstance(n, TriccReference)]:
1162
- logger.info("Stashed node {}:{}|{} {}".format(
1163
- es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1164
- es_node.activity.instance if hasattr(es_node,'activity') else '',
1165
- es_node.__class__,
1166
- es_node.get_name()))
1167
- for es_node in [node for node_list in looped.values() for node in node_list if isinstance(node, TriccReference)]:
1168
- logger.info("looped node {}:{}|{} {}".format(
1169
- es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1170
- es_node.activity.instance if hasattr(es_node,'activity') else '',
1171
- es_node.__class__,
1172
- es_node.get_name()))
1173
- for es_node in [node for node_list in waited.values() for node in node_list if isinstance(node, TriccReference)]:
1174
- logger.info("waited node {}:{}|{} {}".format(
1175
- es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1176
- es_node.activity.instance if hasattr(es_node,'activity') else '',
1177
- es_node.__class__,
1178
- es_node.get_name()))
1455
+ logger.info(
1456
+ "Stashed node {}:{}|{} {}".format(
1457
+ es_node.activity.get_name() if hasattr(es_node, "activity") else "",
1458
+ es_node.activity.instance if hasattr(es_node, "activity") else "",
1459
+ es_node.__class__,
1460
+ es_node.get_name(),
1461
+ )
1462
+ )
1463
+ for es_node in [
1464
+ node for node_list in looped.values() for node in node_list if isinstance(node, TriccReference)
1465
+ ]:
1466
+ logger.info(
1467
+ "looped node {}:{}|{} {}".format(
1468
+ es_node.activity.get_name() if hasattr(es_node, "activity") else "",
1469
+ es_node.activity.instance if hasattr(es_node, "activity") else "",
1470
+ es_node.__class__,
1471
+ es_node.get_name(),
1472
+ )
1473
+ )
1474
+ for es_node in [
1475
+ node for node_list in waited.values() for node in node_list if isinstance(node, TriccReference)
1476
+ ]:
1477
+ logger.info(
1478
+ "waited node {}:{}|{} {}".format(
1479
+ es_node.activity.get_name() if hasattr(es_node, "activity") else "",
1480
+ es_node.activity.instance if hasattr(es_node, "activity") else "",
1481
+ es_node.__class__,
1482
+ es_node.get_name(),
1483
+ )
1484
+ )
1179
1485
  logger.info("looped nodes")
1180
1486
  for dep_list in looped:
1181
1487
  for d in looped[dep_list]:
1182
1488
  if str(d) in looped:
1183
- logger.critical("[{}] depends on [{}]".format(
1184
- dep_list, str(d)
1185
- ))
1489
+ logger.critical("[{}] depends on [{}]".format(dep_list, str(d)))
1186
1490
  else:
1187
- logger.error("[{}] depends on [{}]".format(
1188
- dep_list, str(d)
1189
- ))
1491
+ logger.error("[{}] depends on [{}]".format(dep_list, str(d)))
1190
1492
  if dep_list in waited:
1191
1493
  for d in waited[dep_list]:
1192
- logger.warning("[{}] depends on [{}]".format(
1193
- dep_list, str(d)
1194
- ))
1494
+ logger.warning("[{}] depends on [{}]".format(dep_list, str(d)))
1195
1495
  logger.info("waited nodes")
1196
1496
  for dep_list in waited:
1197
1497
  if dep_list not in looped:
1198
1498
  for d in waited[dep_list]:
1199
- logger.warning("[{}] depends on [{}]".format(
1200
- dep_list, d.get_name()
1201
- ))
1202
-
1499
+ logger.warning("[{}] depends on [{}]".format(dep_list, d.get_name()))
1500
+
1203
1501
  if len(stashed_nodes) == len(prev_stashed_nodes):
1204
1502
  exit(1)
1205
1503
  else:
@@ -1208,6 +1506,7 @@ def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_p
1208
1506
  loop_count = 0
1209
1507
  return loop_count
1210
1508
 
1509
+
1211
1510
  def add_to_tree(tree, n, d):
1212
1511
  n_str = str(n)
1213
1512
  if n_str not in tree:
@@ -1216,10 +1515,10 @@ def add_to_tree(tree, n, d):
1216
1515
  tree[n_str].append(d)
1217
1516
  return tree
1218
1517
 
1219
-
1220
- def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None , looped=None, path = None):
1518
+
1519
+ def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None, looped=None, path=None):
1221
1520
  if path is None:
1222
- path =[]
1521
+ path = []
1223
1522
  if looped is None:
1224
1523
  looped = {}
1225
1524
  if waited is None:
@@ -1229,50 +1528,55 @@ def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None
1229
1528
  cur_path = path.copy()
1230
1529
  cur_path.append(n)
1231
1530
  dependant = OrderedSet()
1232
- i=0
1233
- #logger.critical(f"{i}: {n.__class__}::{n.get_name()}::{getattr(n,'instance','')}::{process_reference(n, processed_nodes, [])}")
1234
- i += 1
1235
- if hasattr(n, 'prev_nodes') and n.prev_nodes:
1236
- dependant = dependant | n.prev_nodes
1237
- if hasattr(n, 'get_references'):
1238
- dependant = dependant | (n.get_references() or OrderedSet())
1531
+ if hasattr(n, "prev_nodes") and n.prev_nodes:
1532
+ dependant = dependant | n.prev_nodes
1533
+ if hasattr(n, "get_references"):
1534
+ dependant = dependant | (n.get_references() or OrderedSet())
1239
1535
  if not isinstance(dependant, list):
1240
1536
  pass
1241
1537
  for d in dependant:
1242
1538
  if d in path:
1243
- logger.warning(f"loop {str(d)} already in path {'::'.join(map(str, path))} ")
1539
+ logger.warning(
1540
+ f"loop {str(d)} already in path {'::'.join(map(str, path))} "
1541
+ )
1244
1542
  if isinstance(d, TriccNodeSelectOption):
1245
1543
  d = d.select
1246
-
1544
+
1247
1545
  if isinstance(d, TriccReference):
1248
1546
  if not any(n.name == d.value for n in processed_nodes):
1249
1547
  if not any(n.name == d.value for n in stashed_nodes):
1250
1548
  waited = add_to_tree(waited, n, d)
1251
- else :
1549
+ else:
1252
1550
  looped = add_to_tree(looped, n, d)
1253
-
1551
+
1254
1552
  elif d not in processed_nodes:
1255
1553
  if d in stashed_nodes:
1256
1554
  looped = add_to_tree(looped, n, d)
1257
- else :
1555
+ else:
1258
1556
  waited = add_to_tree(waited, n, d)
1259
1557
  all_dependant = all_dependant.union(dependant)
1260
1558
  if depth < MAX_DRILL:
1261
- waited, looped = get_all_dependant(all_dependant, stashed_nodes, processed_nodes, depth+1, waited , looped, path=cur_path)
1262
-
1559
+ waited, looped = get_all_dependant(
1560
+ all_dependant, stashed_nodes, processed_nodes, depth + 1, waited, looped, path=cur_path
1561
+ )
1562
+
1263
1563
  return waited, looped
1264
1564
 
1265
1565
 
1266
1566
  MAX_DRILL = 3
1267
1567
 
1568
+
1268
1569
  def get_last_end_node(processed_nodes, process=None):
1269
- end_name = 'tricc_end_'
1570
+ end_name = "tricc_end_"
1270
1571
  if process:
1271
1572
  end_name += process
1272
1573
  return get_last_version(end_name, processed_nodes)
1273
1574
 
1575
+
1274
1576
  # Set the source next node to target and clean next nodes of replace node
1275
- def set_prev_next_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
1577
+
1578
+
1579
+ def set_prev_next_node(source_node, target_node, replaced_node=None, edge_only=False, activity=None):
1276
1580
  activity = activity or source_node.activity
1277
1581
  source_id, source_node = get_node_from_id(activity, source_node, edge_only)
1278
1582
  target_id, target_node = get_node_from_id(activity, target_node, edge_only)
@@ -1280,36 +1584,44 @@ def set_prev_next_node(source_node, target_node, replaced_node=None, edge_only =
1280
1584
  if not edge_only:
1281
1585
  set_prev_node(source_node, target_node, replaced_node, edge_only)
1282
1586
  set_next_node(source_node, target_node, replaced_node, edge_only)
1283
-
1284
- if activity and not any([(e.source == source_id) and ( e.target == target_id) for e in activity.edges]):
1285
- label = "continue" if issubclass(source_node.__class__, TriccNodeSelect) else None
1286
- activity.edges.append(TriccEdge(id = generate_id(), source = source_id, target = target_id, value = label))
1587
+
1588
+ if activity and not any([(e.source == source_id) and (e.target == target_id) for e in activity.edges]):
1589
+ if issubclass(source_node.__class__, TriccNodeSelect):
1590
+ label = "continue"
1591
+ elif isinstance(source_node, TriccNodeRhombus):
1592
+ label = "yes"
1593
+ else:
1594
+ label = None
1595
+ activity.edges.append(TriccEdge(id=generate_id(), source=source_id, target=target_id, value=label))
1596
+
1287
1597
 
1288
1598
  def remove_prev_next(prev_node, next_node, activity=None):
1289
1599
  activity = activity or prev_node.activity
1290
- if hasattr(prev_node, 'next_nodes') and next_node in prev_node.next_nodes:
1600
+ if hasattr(prev_node, "next_nodes") and next_node in prev_node.next_nodes:
1291
1601
  prev_node.next_nodes.remove(next_node)
1292
- if hasattr(next_node, 'prev_nodes') and prev_node in next_node.prev_nodes:
1602
+ if hasattr(next_node, "prev_nodes") and prev_node in next_node.prev_nodes:
1293
1603
  next_node.prev_nodes.remove(prev_node)
1294
-
1604
+
1295
1605
  for e in list(activity.edges):
1296
- if (e.target == getattr(next_node, 'id', next_node) and e.source == getattr(prev_node, 'id', prev_node)):
1606
+ if e.target == getattr(next_node, "id", next_node) and e.source == getattr(prev_node, "id", prev_node):
1297
1607
  activity.edges.remove(e)
1298
-
1299
-
1300
-
1301
- def set_next_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
1608
+
1609
+
1610
+ def set_next_node(source_node, target_node, replaced_node=None, edge_only=False, activity=None):
1302
1611
  activity = activity or source_node.activity
1303
1612
  replace_target = None
1304
- if not edge_only:
1305
- if replaced_node is not None and hasattr(source_node, 'path') and replaced_node == source_node.path:
1613
+ if not edge_only:
1614
+ if replaced_node is not None and hasattr(source_node, "path") and replaced_node == source_node.path:
1306
1615
  source_node.path = target_node
1307
- elif replaced_node is not None and hasattr(source_node, 'next_nodes') and replaced_node in source_node.next_nodes:
1616
+ elif (
1617
+ replaced_node is not None and hasattr(source_node, "next_nodes") and replaced_node in source_node.next_nodes
1618
+ ):
1308
1619
  replace_target = True
1309
1620
  source_node.next_nodes.remove(replaced_node)
1310
- if hasattr(replaced_node, 'prev_nodes') and source_node in replaced_node.prev_nodes:
1621
+ if hasattr(replaced_node, "prev_nodes") and source_node in replaced_node.prev_nodes:
1311
1622
  replaced_node.prev_nodes.remove(source_node)
1312
- #if replaced_node is not None and hasattr(target_node, 'next_nodes') and replaced_node in target_node.next_nodes:
1623
+ # if replaced_node is not None and hasattr(target_node, 'next_nodes')
1624
+ # and replaced_node in target_node.next_nodes:
1313
1625
  # target_node.next_nodes.remove(replaced_node)
1314
1626
  if target_node not in source_node.next_nodes:
1315
1627
  source_node.next_nodes.add(target_node)
@@ -1328,31 +1640,34 @@ def set_next_node(source_node, target_node, replaced_node=None, edge_only = Fals
1328
1640
  if replaced_node and replace_target:
1329
1641
  if replaced_node.id in replaced_node.activity.nodes:
1330
1642
  del replaced_node.activity.nodes[replaced_node.id]
1331
- next_edges = set([
1332
- e for e in replaced_node.activity.edges if (e.target == replaced_node.id or e.target == replaced_node)
1333
- ] + [
1334
- e for e in activity.edges if (e.target == replaced_node.id or e.target == replaced_node)
1335
- ])
1336
- if len(next_edges)==0:
1337
- for e in next_edges:
1643
+ next_edges = set(
1644
+ [
1645
+ e for e in replaced_node.activity.edges
1646
+ if (e.target == replaced_node.id or e.target == replaced_node)
1647
+ ] + [
1648
+ e for e in activity.edges
1649
+ if (e.target == replaced_node.id or e.target == replaced_node)
1650
+ ]
1651
+ )
1652
+ if len(next_edges) == 0:
1653
+ for e in next_edges:
1338
1654
  e.target = target_node.id
1339
1655
 
1340
-
1341
1656
 
1342
1657
  # Set the target_node prev node to source and clean prev nodes of replace_node
1343
- def set_prev_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
1658
+ def set_prev_node(source_node, target_node, replaced_node=None, edge_only=False, activity=None):
1344
1659
  activity = activity or source_node.activity
1345
1660
  replace_source = False
1346
1661
  # update the prev node of the target not if not an end node
1347
1662
  # update directly the prev node of the target
1348
- if replaced_node is not None and hasattr(target_node, 'path') and replaced_node == target_node.path:
1663
+ if replaced_node is not None and hasattr(target_node, "path") and replaced_node == target_node.path:
1349
1664
  target_node.path = source_node
1350
- if replaced_node is not None and hasattr(target_node, 'prev_nodes') and replaced_node in target_node.prev_nodes:
1665
+ if replaced_node is not None and hasattr(target_node, "prev_nodes") and replaced_node in target_node.prev_nodes:
1351
1666
  replace_source = True
1352
1667
  target_node.prev_nodes.remove(replaced_node)
1353
- if hasattr(replaced_node, 'next_nodes') and source_node in replaced_node.next_nodes:
1668
+ if hasattr(replaced_node, "next_nodes") and source_node in replaced_node.next_nodes:
1354
1669
  replaced_node.next_nodes.remove(source_node)
1355
- #if replaced_node is not None and hasattr(source_node, 'prev_nodes') and replaced_node in source_node.prev_nodes:
1670
+ # if replaced_node is not None and hasattr(source_node, 'prev_nodes') and replaced_node in source_node.prev_nodes:
1356
1671
  # source_node.prev_nodes.remove(replaced_node)
1357
1672
  if source_node not in target_node.prev_nodes:
1358
1673
  target_node.prev_nodes.add(source_node)
@@ -1360,18 +1675,17 @@ def set_prev_node(source_node, target_node, replaced_node=None, edge_only = Fals
1360
1675
  activity.nodes[source_node.id] = source_node
1361
1676
  if replaced_node and replace_source:
1362
1677
  if replaced_node.id in replaced_node.activity.nodes:
1363
- del replaced_node.activity.nodes[replaced_node.id]
1364
- next_edges = set([
1365
- e for e in replaced_node.activity.edges if (e.source == replaced_node.id or e.source == replaced_node)
1366
- ] + [
1367
- e for e in activity.edges if (e.source == replaced_node.id or e.source == replaced_node)
1368
- ])
1369
- if len(next_edges)==0:
1370
- for e in next_edges:
1678
+ del replaced_node.activity.nodes[replaced_node.id]
1679
+ next_edges = set(
1680
+ [e for e in replaced_node.activity.edges if (e.source == replaced_node.id or e.source == replaced_node)]
1681
+ + [e for e in activity.edges if (e.source == replaced_node.id or e.source == replaced_node)]
1682
+ )
1683
+ if len(next_edges) == 0:
1684
+ for e in next_edges:
1371
1685
  e.target = target_node.id
1372
-
1373
1686
 
1374
- def replace_node(old, new, page = None):
1687
+
1688
+ def replace_node(old, new, page=None):
1375
1689
  if page is None:
1376
1690
  page = old.activity
1377
1691
  logger.debug("replacing node {} with node {} from page {}".format(old.get_name(), new.get_name(), page.get_name()))
@@ -1398,23 +1712,25 @@ def replace_node(old, new, page = None):
1398
1712
  if edge.target == old.id:
1399
1713
  edge.target = new.id
1400
1714
 
1401
- def replace_prev_next_node(prev_node, next_node, old_node, force = False):
1715
+
1716
+ def replace_prev_next_node(prev_node, next_node, old_node, force=False):
1402
1717
  replace_prev_node(prev_node, next_node, old_node)
1403
1718
  replace_next_node(prev_node, next_node, old_node)
1404
1719
 
1405
- def replace_prev_node(prev_node, next_node, old_node, force = False):
1406
- #create a copy pf the list
1720
+
1721
+ def replace_prev_node(prev_node, next_node, old_node, force=False):
1722
+ # create a copy pf the list
1407
1723
  list_nodes = list(next_node.prev_nodes)
1408
1724
  # replace in case old node is found
1409
1725
  for p_n_node in list_nodes:
1410
1726
  if p_n_node == old_node or force:
1411
1727
  set_prev_next_node(prev_node, next_node, old_node)
1412
-
1413
-
1414
- def replace_next_node(prev_node,next_node,old_node):
1728
+
1729
+
1730
+ def replace_next_node(prev_node, next_node, old_node):
1415
1731
  list_nodes = list(prev_node.next_nodes)
1416
1732
  for n_p_node in list_nodes:
1417
- if n_p_node == old_node :
1733
+ if n_p_node == old_node:
1418
1734
  set_prev_next_node(prev_node, next_node, old_node)
1419
1735
 
1420
1736
 
@@ -1424,9 +1740,11 @@ PARENT_GROUP_PRIORITY = 6000
1424
1740
  ACTIVE_ACTIVITY_PRIORITY = 5000
1425
1741
  NON_START_ACTIVITY_PRIORITY = 4000
1426
1742
  ACTIVE_ACTIVITY_LOWER_PRIORITY = 3000
1743
+ FLOW_CALCULATE_NODE_PRIORITY = 6500
1427
1744
  RHOMBUS_PRIORITY = 1000
1428
1745
  DEFAULT_PRIORITY = 2000
1429
1746
 
1747
+
1430
1748
  def reorder_node_list(node_list, group, processed_nodes):
1431
1749
  # Cache active activities for O(1) lookup
1432
1750
  active_activities = {n.activity for n in processed_nodes}
@@ -1449,6 +1767,12 @@ def reorder_node_list(node_list, group, processed_nodes):
1449
1767
  # Check for non main activities
1450
1768
  elif activity and isinstance(activity.root, TriccNodeActivityStart):
1451
1769
  priority += NON_START_ACTIVITY_PRIORITY
1770
+ # Check for display calculate and end nodes with prev_nodes
1771
+ elif (
1772
+ issubclass(node.__class__, TriccNodeDisplayCalculateBase) or
1773
+ isinstance(node, TriccNodeEnd)
1774
+ ) and not isinstance(node, TriccNodeActivityEnd) and hasattr(node, 'prev_nodes') and len(node.prev_nodes) > 0:
1775
+ priority += FLOW_CALCULATE_NODE_PRIORITY
1452
1776
  # Check for active activities (lower priority)
1453
1777
  elif activity and activity in active_activities:
1454
1778
  priority += ACTIVE_ACTIVITY_LOWER_PRIORITY
@@ -1462,49 +1786,51 @@ def reorder_node_list(node_list, group, processed_nodes):
1462
1786
 
1463
1787
  # Sort in place, highest priority first
1464
1788
  node_list.sort(key=get_priority, reverse=True)
1465
-
1789
+
1790
+
1466
1791
  def loop_info(loop, **kwargs):
1467
1792
  logger.critical("dependency details")
1468
1793
  for n in loop:
1469
- i=0
1794
+ i = 0
1470
1795
  logger.critical(f"{i}: {n.__class__}::{n.get_name()}")
1471
1796
  i += 1
1472
1797
 
1473
1798
 
1474
- def has_loop(node, processed_nodes, stashed_nodes, warn , node_path=[], action_on_loop=loop_info,action_on_other=None, **kwargs):
1799
+ def has_loop(
1800
+ node, processed_nodes, stashed_nodes, warn, node_path=[], action_on_loop=loop_info, action_on_other=None, **kwargs
1801
+ ):
1475
1802
  next_nodes = get_extended_next_nodes(node)
1476
- for next_node in next_nodes:
1803
+ for next_node in next_nodes:
1477
1804
  if next_node in node_path:
1478
1805
  loop_start_key = node_path.index(next_node)
1479
1806
  loop = node_path[loop_start_key:]
1480
1807
  loop.append(node)
1481
1808
  loop.append(next_node)
1482
1809
  action_on_loop(loop, **kwargs)
1483
- return False
1810
+ return False
1484
1811
  if callable(action_on_other):
1485
1812
  action_on_other(next_node, **kwargs)
1486
1813
  return True
1487
-
1488
-
1814
+
1489
1815
 
1490
1816
  def get_extended_next_nodes(node):
1491
-
1492
- nodes = node.next_nodes if hasattr(node,'next_nodes') else set()
1493
- if issubclass(node.__class__, TriccNodeSelect ):
1817
+
1818
+ nodes = node.next_nodes if hasattr(node, "next_nodes") else set()
1819
+ if issubclass(node.__class__, TriccNodeSelect):
1494
1820
  for o in node.options.values():
1495
1821
  nodes = nodes | o.next_nodes
1496
- if isinstance(node, ( TriccNodeActivity) ):
1822
+ if isinstance(node, (TriccNodeActivity)):
1497
1823
  nodes = nodes | node.root.next_nodes
1498
1824
  return nodes
1499
-
1825
+
1500
1826
 
1501
1827
  # calculate or retrieve a node expression
1502
- def get_node_expression( in_node, processed_nodes, get_overall_exp=False, is_prev=False, negate=False, process=None):
1828
+ def get_node_expression(in_node, processed_nodes, get_overall_exp=False, is_prev=False, negate=False, process=None):
1503
1829
  # in case of calculate we only use the select multiple if none is not selected
1504
1830
  expression = None
1505
1831
  negate_expression = None
1506
1832
  node = in_node
1507
- if isinstance(node, (TriccNodeActivityStart,TriccNodeMainStart)):
1833
+ if isinstance(node, (TriccNodeActivityStart, TriccNodeMainStart)):
1508
1834
  if is_prev and get_overall_exp:
1509
1835
  expression = get_node_expression(
1510
1836
  node.activity,
@@ -1512,13 +1838,13 @@ def get_node_expression( in_node, processed_nodes, get_overall_exp=False, is_pre
1512
1838
  get_overall_exp=True,
1513
1839
  is_prev=is_prev,
1514
1840
  negate=negate,
1515
- process=process
1841
+ process=process,
1516
1842
  )
1517
1843
  if isinstance(node, TriccNodeMainStart):
1518
- expression = get_applicability_expression(node.activity, processed_nodes, process, expression )
1844
+ expression = get_applicability_expression(node.activity, processed_nodes, process, expression)
1519
1845
  elif isinstance(node, (TriccNodeActivityStart)):
1520
1846
  return TriccStatic(True)
1521
-
1847
+
1522
1848
  elif isinstance(node, TriccNodeWait):
1523
1849
  if is_prev:
1524
1850
  # the wait don't do any calculation with the reference it is only use to wait until the reference are valid
@@ -1527,86 +1853,88 @@ def get_node_expression( in_node, processed_nodes, get_overall_exp=False, is_pre
1527
1853
  processed_nodes=processed_nodes,
1528
1854
  get_overall_exp=get_overall_exp,
1529
1855
  is_prev=True,
1530
- process=process
1531
- )
1856
+ process=process,
1857
+ )
1532
1858
  else:
1533
- #it is a empty calculate
1859
+ # it is a empty calculate
1534
1860
  return None
1535
1861
  elif isinstance(node, TriccNodeRhombus):
1536
- # if is_prev:
1537
- # expression = TriccOperation(
1538
- # TriccOperator.ISTRUE,
1539
- # [node]
1540
- # )
1541
- # else:
1542
- expression = get_rhombus_terms(node, processed_nodes, process=process) # if issubclass(node.__class__, TricNodeDisplayCalulate) else TRICC_CALC_EXPRESSION.format(get_export_name(node)) #
1862
+ expression = get_rhombus_terms(node, processed_nodes, process=process)
1543
1863
  negate_expression = not_clean(expression)
1544
- if node.path is None :
1864
+ if node.path is None:
1545
1865
  if len(node.prev_nodes) == 1:
1546
1866
  node.path = list(node.prev_nodes)[0]
1547
1867
  elif len(node.prev_nodes) > 1:
1548
1868
  logger.critical(f"missing path for Rhombus {node.get_name()}")
1549
1869
  exit(1)
1550
1870
  prev_exp = get_node_expression(
1551
- node.path,
1552
- processed_nodes=processed_nodes,
1553
- get_overall_exp=get_overall_exp,
1554
- is_prev=True,
1555
- process=process)
1871
+ node.path, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, is_prev=True, process=process
1872
+ )
1556
1873
  if prev_exp and expression:
1557
1874
  expression = and_join([prev_exp, expression])
1558
- negate_expression = and_join([
1559
- prev_exp,
1560
- negate_expression
1561
- ])
1875
+ negate_expression = and_join([prev_exp, negate_expression])
1562
1876
 
1563
1877
  elif prev_exp:
1564
-
1878
+
1565
1879
  logger.error(f"useless rhombus {node.get_name()}")
1566
1880
  expression = prev_exp
1567
1881
  negate_expression = prev_exp
1568
1882
  logger.critical(f"Rhombus without expression {node.get_name()}")
1569
1883
  elif is_prev and issubclass(node.__class__, TriccNodeDisplayCalculateBase):
1570
1884
  expression = TriccOperation(TriccOperator.ISTRUE, [node])
1571
- elif hasattr(node, 'expression_reference') and isinstance(node.expression_reference, TriccOperation):
1885
+ prev_exp_overall = get_node_expression(
1886
+ node,
1887
+ processed_nodes=processed_nodes,
1888
+ get_overall_exp=False,
1889
+ is_prev=False,
1890
+ process=process,
1891
+ negate=negate
1892
+ )
1893
+ if prev_exp_overall in [TriccStatic(True), TriccStatic(False)]:
1894
+ expression = prev_exp_overall
1895
+ elif hasattr(node, "expression_reference") and isinstance(node.expression_reference, TriccOperation):
1572
1896
  # if issubclass(node.__class__, TriccNodeDisplayCalculateBase):
1573
1897
  # expression = TriccOperation(
1574
1898
  # TriccOperator.CAST_NUMBER,
1575
1899
  # [node.expression_reference])
1576
- # else:
1577
- expression = node.expression_reference
1900
+ # else:
1901
+ expression = node.expression_reference
1578
1902
  elif is_prev and isinstance(node, TriccNodeSelectOption):
1579
1903
  if negate:
1580
1904
  negate_expression = get_selected_option_expression(node, negate)
1581
1905
  else:
1582
1906
  expression = get_selected_option_expression(node, negate)
1583
- #TODO remove that and manage it on the "Save" part
1907
+ # TODO remove that and manage it on the "Save" part
1584
1908
  elif is_prev and isinstance(node, TriccNodeSelectNotAvailable):
1585
- expression = TriccOperation(
1586
- TriccOperator.SELECTED,
1587
- [
1588
- node,
1589
- TriccStatic(1)
1590
- ]
1591
- )
1909
+ expression = TriccOperation(TriccOperator.SELECTED, [node, TriccStatic(1)])
1592
1910
  elif issubclass(node.__class__, TriccNodeCalculateBase):
1593
1911
  if negate:
1594
- negate_expression = get_calculation_terms(node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, negate=True, process=process)
1912
+ negate_expression = get_calculation_terms(
1913
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, negate=True, process=process
1914
+ )
1595
1915
  else:
1596
- expression = get_calculation_terms(node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process)
1597
-
1598
- elif (not is_prev or not ONE_QUESTION_AT_A_TIME) and hasattr(node, 'relevance') and isinstance(node.relevance, (TriccOperation, TriccStatic)):
1599
- expression = node.relevance
1600
- elif ONE_QUESTION_AT_A_TIME and is_prev and not get_overall_exp and hasattr(node, 'required') and node.required:
1916
+ expression = get_calculation_terms(
1917
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process
1918
+ )
1919
+
1920
+ elif (
1921
+ (not is_prev or not ONE_QUESTION_AT_A_TIME)
1922
+ and hasattr(node, "relevance")
1923
+ and isinstance(node.relevance, (TriccOperation, TriccStatic))
1924
+ ):
1925
+ expression = node.relevance
1926
+ elif ONE_QUESTION_AT_A_TIME and is_prev and not get_overall_exp and hasattr(node, "required") and node.required:
1601
1927
  expression = get_required_node_expression(node)
1602
-
1928
+
1603
1929
  if expression is None:
1604
- expression = get_prev_node_expression(node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process)
1605
- # in_node not in processed_nodes is need for calculates that can but run after the end of the activity
1606
- #if isinstance(node, TriccNodeActivitiy) and not prev:
1607
- # expression = get_applicability_expression(node, processed_nodes, process, expression)
1608
- # expression = get_prev_instance_skip_expression(node, processed_nodes, process, expression)
1609
- # expression = get_process_skip_expression(node, processed_nodes, process, expression)
1930
+ expression = get_prev_node_expression(
1931
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process
1932
+ )
1933
+ # in_node not in processed_nodes is need for calculates that can but run after the end of the activity
1934
+ # if isinstance(node, TriccNodeActivitiy) and not prev:
1935
+ # expression = get_applicability_expression(node, processed_nodes, process, expression)
1936
+ # expression = get_prev_instance_skip_expression(node, processed_nodes, process, expression)
1937
+ # expression = get_process_skip_expression(node, processed_nodes, process, expression)
1610
1938
  if negate:
1611
1939
  if negate_expression is not None:
1612
1940
  return negate_expression
@@ -1617,79 +1945,66 @@ def get_node_expression( in_node, processed_nodes, get_overall_exp=False, is_pre
1617
1945
  # exit(1)
1618
1946
  else:
1619
1947
  return expression
1620
-
1948
+
1949
+
1621
1950
  def get_applicability_expression(node, processed_nodes, process, expression=None):
1622
- if isinstance(node.applicability,(TriccStatic,TriccOperation, TriccReference)):
1951
+ if isinstance(node.applicability, (TriccStatic, TriccOperation, TriccReference)):
1623
1952
  if expression:
1624
1953
  expression = and_join([node.applicability, expression])
1625
1954
  else:
1626
1955
  expression = node.applicability
1627
-
1956
+
1628
1957
  return expression
1629
-
1630
1958
 
1631
1959
 
1632
-
1633
-
1634
1960
  def get_prev_instance_skip_expression(node, processed_nodes, process, expression=None):
1635
1961
  if node.base_instance is not None:
1636
- activity = node
1637
1962
  expression_inputs = []
1638
- past_instances = [
1639
- n for n in processed_nodes if getattr(n.base_instance, 'id', None) == node.base_instance.id
1640
- ]
1963
+ past_instances = [n for n in processed_nodes if getattr(n.base_instance, "id", None) == node.base_instance.id]
1641
1964
  for past_instance in past_instances:
1642
1965
  add_sub_expression(
1643
- expression_inputs,
1966
+ expression_inputs,
1644
1967
  get_node_expression(
1645
- past_instance,
1646
- processed_nodes=processed_nodes,
1647
- get_overall_exp=True,
1648
- is_prev=True,
1649
- process=process
1650
- )
1968
+ past_instance, processed_nodes=processed_nodes, get_overall_exp=True, is_prev=True, process=process
1969
+ ),
1651
1970
  )
1652
1971
  if expression and expression_inputs:
1653
- add_sub_expression(expression_inputs, expression)
1654
1972
  expression = nand_join(expression, or_join(expression_inputs))
1655
1973
  elif expression_inputs:
1656
- expression = negate_term(or_join(expression_inputs))
1974
+ expression = negate_term(or_join(expression_inputs))
1657
1975
  return expression
1658
1976
 
1659
1977
 
1660
1978
  # end def
1661
1979
  def get_process_skip_expression(node, processed_nodes, process, expression=None):
1662
-
1663
- end_expressions = []
1664
- f_end_expression = get_end_expression(processed_nodes)
1665
- if f_end_expression:
1666
- end_expressions.append(f_end_expression)
1667
- b_end_expression = get_end_expression(processed_nodes, 'pause')
1668
- if b_end_expression:
1669
- end_expressions.append(b_end_expression)
1670
- if process[0] in PROCESSES:
1671
- for p in PROCESSES[PROCESSES.index(process[0])+1:]:
1672
- p_end_expression = get_end_expression(processed_nodes, p)
1673
- if p_end_expression:
1674
- end_expressions.append(p_end_expression)
1675
- if end_expressions:
1676
- if expression:
1677
- end_expressions.append(expression)
1678
- if len(end_expressions) == 1:
1679
- expression = end_expressions[0]
1680
- else:
1681
- expression = and_join(end_expressions)
1980
+ list_ends = OrderedSet(filter(lambda x: issubclass(x.__class__, TriccNodeEnd), processed_nodes))
1981
+ if list_ends:
1982
+ end_expressions = []
1983
+ f_end_expression = get_end_expression(list_ends)
1984
+ if f_end_expression:
1985
+ end_expressions.append(f_end_expression)
1986
+ b_end_expression = get_end_expression(list_ends, "pause")
1987
+ if b_end_expression:
1988
+ end_expressions.append(b_end_expression)
1989
+ if process[0] in PROCESSES:
1990
+ for p in PROCESSES[PROCESSES.index(process[0]) + 1:]:
1991
+ p_end_expression = get_end_expression(list_ends, p)
1992
+ if p_end_expression:
1993
+ end_expressions.append(p_end_expression)
1994
+ if end_expressions:
1995
+ if expression:
1996
+ end_expressions.append(expression)
1997
+ if len(end_expressions) == 1:
1998
+ expression = end_expressions[0]
1999
+ else:
2000
+ expression = and_join(end_expressions)
1682
2001
  return expression
1683
-
2002
+
2003
+
1684
2004
  def get_end_expression(processed_nodes, process=None):
1685
2005
  end_node = get_last_end_node(processed_nodes, process)
1686
2006
  if end_node:
1687
- return TriccOperation(
1688
- TriccOperator.ISNOTTRUE,
1689
- [end_node]
1690
- )
1691
-
1692
-
2007
+ return TriccOperation(TriccOperator.ISNOTTRUE, [end_node])
1693
2008
 
1694
2009
 
1695
2010
  def export_proposed_diags(activity, diags=None, **kwargs):
@@ -1699,11 +2014,10 @@ def export_proposed_diags(activity, diags=None, **kwargs):
1699
2014
  if isinstance(node, TriccNodeActivity):
1700
2015
  diags = export_proposed_diags(node, diags, **kwargs)
1701
2016
  if isinstance(node, TriccNodeProposedDiagnosis):
1702
- if node.last is not False\
1703
- and not any([diag.name == node.name for diag in diags]):
1704
- diags.append(node)
2017
+ if node.last is not False and not any([diag.name == node.name for diag in diags]):
2018
+ diags.append(node)
1705
2019
  return diags
1706
-
2020
+
1707
2021
 
1708
2022
  def get_accept_diagnostic_node(code, display, severity, priority, activity):
1709
2023
  node = TriccNodeAcceptDiagnostic(
@@ -1714,12 +2028,13 @@ def get_accept_diagnostic_node(code, display, severity, priority, activity):
1714
2028
  activity=activity,
1715
2029
  group=activity,
1716
2030
  severity=severity,
1717
- priority=priority
2031
+ priority=priority,
1718
2032
  )
1719
2033
  node.options = get_select_accept_reject_options(node, node.activity)
1720
2034
  return node
1721
2035
 
1722
- def get_diagnostic_node(code, display, severity, priority, activity):
2036
+
2037
+ def get_diagnostic_node(code, display, severity, priority, activity, option):
1723
2038
  node = TriccNodeCalculate(
1724
2039
  id=generate_id("final." + code),
1725
2040
  name="final." + code,
@@ -1727,51 +2042,48 @@ def get_diagnostic_node(code, display, severity, priority, activity):
1727
2042
  activity=activity,
1728
2043
  group=activity,
1729
2044
  priority=priority,
1730
- expression_reference=or_join([
1731
- TriccOperation(TriccOperator.ISTRUE, [TriccReference("pre_final." + code)]),
1732
- TriccOperation(
1733
- TriccOperator.SELECTED,
1734
- [TriccReference('tricc.manual.diag'), TriccStatic(code)]
1735
- )
1736
- ])
2045
+ expression_reference=or_join(
2046
+ [
2047
+ TriccOperation(TriccOperator.ISTRUE, [TriccReference("pre_final." + code)]),
2048
+ TriccOperation(TriccOperator.SELECTED, [TriccReference("tricc.manual.diag"), TriccStatic(option)]),
2049
+ ]
2050
+ ),
1737
2051
  )
1738
2052
  return node
1739
2053
 
2054
+
1740
2055
  def get_select_accept_reject_options(node, group):
1741
2056
  yes = TriccNodeSelectOption(
1742
- id = generate_id(f'accept{node.id}'),
1743
- name=f"{TRICC_TRUE_VALUE}",
1744
- label="Accept",
1745
- select = node,
1746
- group = group,
1747
- list_name = node.list_name
1748
- )
2057
+ id=generate_id(f"accept{node.id}"),
2058
+ name=f"{TRICC_TRUE_VALUE}",
2059
+ label="Accept",
2060
+ select=node,
2061
+ group=group,
2062
+ list_name=node.list_name,
2063
+ )
1749
2064
  no = TriccNodeSelectOption(
1750
- id = generate_id(f'reject{node.id}'),
1751
- name=f"{TRICC_FALSE_VALUE}",
1752
- label="Reject",
1753
- select = node,
1754
- group = group,
1755
- list_name = node.list_name
1756
- )
1757
- return {0:yes, 1:no }
2065
+ id=generate_id(f"reject{node.id}"),
2066
+ name=f"{TRICC_FALSE_VALUE}",
2067
+ label="Reject",
2068
+ select=node,
2069
+ group=group,
2070
+ list_name=node.list_name,
2071
+ )
2072
+ return {0: yes, 1: no}
2073
+
1758
2074
 
1759
2075
  def create_determine_diagnosis_activity(diags):
1760
2076
  start = TriccNodeMainStart(
1761
- id=generate_id('start.determine-diagnosis'),
1762
- name="start.determine-diagnosis",
1763
- process='determine-diagnosis'
2077
+ id=generate_id("start.determine-diagnosis"), name="start.determine-diagnosis", process="determine-diagnosis"
1764
2078
  )
1765
2079
 
1766
-
1767
2080
  activity = TriccNodeActivity(
1768
- id=generate_id('activity-determine-diagnosis'),
1769
- name='determine-diagnosis',
1770
- label='Diagnosis',
2081
+ id=generate_id("activity-determine-diagnosis"),
2082
+ name="determine-diagnosis",
2083
+ label="Classifications",
1771
2084
  root=start,
1772
2085
  )
1773
2086
 
1774
-
1775
2087
  start.activity = activity
1776
2088
  start.group = activity
1777
2089
  diags_conf = []
@@ -1781,33 +2093,40 @@ def create_determine_diagnosis_activity(diags):
1781
2093
  activity=activity,
1782
2094
  group=activity,
1783
2095
  )
1784
- activity.nodes[end.id]=end
1785
-
2096
+ activity.nodes[end.id] = end
2097
+
1786
2098
  f = TriccNodeSelectMultiple(
1787
2099
  name="tricc.manual.diag",
1788
- label="Add diagnosis",
1789
- list_name='manual_diag',
2100
+ label="Add classifications",
2101
+ list_name="manual_diag",
1790
2102
  id=generate_id("tricc.manual.diag"),
1791
2103
  activity=activity,
1792
2104
  group=activity,
1793
2105
  required=TriccStatic(False),
1794
-
1795
2106
  )
2107
+ options = []
1796
2108
  for proposed in diags:
2109
+ option = TriccNodeSelectOption(
2110
+ id=generate_id(proposed.name),
2111
+ name=proposed.name,
2112
+ label=proposed.label,
2113
+ list_name=f.list_name,
2114
+ relevance=proposed.activity.applicability,
2115
+ select=f,
2116
+ )
2117
+ options.append(option)
1797
2118
  d = get_accept_diagnostic_node(proposed.name, proposed.label, proposed.severity, proposed.priority, activity)
1798
- c = get_diagnostic_node(proposed.name, proposed.label, proposed.severity, proposed.priority, activity)
2119
+ c = get_diagnostic_node(proposed.name, proposed.label, proposed.severity, proposed.priority, activity, option)
1799
2120
  diags_conf.append(d)
1800
2121
  r = TriccNodeRhombus(
1801
2122
  path=start,
1802
2123
  id=generate_id(f"proposed-rhombus{proposed.id}"),
1803
- expression_reference=TriccOperation(
1804
- TriccOperator.ISTRUE,
1805
- [TriccReference(proposed.name)]
1806
- ),
2124
+ expression_reference=TriccOperation(TriccOperator.ISTRUE, [TriccReference(proposed.name)]),
1807
2125
  reference=[TriccReference(proposed.name)],
1808
2126
  activity=activity,
1809
2127
  priority=proposed.priority,
1810
- group=activity)
2128
+ group=activity,
2129
+ )
1811
2130
  activity.calculates.append(r)
1812
2131
  activity.calculates.append(c)
1813
2132
  set_prev_next_node(r, d, edge_only=False)
@@ -1815,234 +2134,202 @@ def create_determine_diagnosis_activity(diags):
1815
2134
  wait2 = get_activity_wait([activity.root], diags_conf, [f], edge_only=False)
1816
2135
  activity.nodes[d.options[0].id] = d.options[0]
1817
2136
  activity.nodes[d.options[1].id] = d.options[1]
1818
- activity.nodes[d.id]=d
1819
- activity.nodes[r.id]=r
1820
- activity.nodes[c.id]=c
1821
- activity.nodes[f.id]=f
1822
- activity.nodes[wait2.id]=wait2
2137
+ activity.nodes[d.id] = d
2138
+ activity.nodes[r.id] = r
2139
+ activity.nodes[c.id] = c
2140
+ activity.nodes[f.id] = f
2141
+ activity.nodes[wait2.id] = wait2
1823
2142
  # fallback
1824
2143
 
1825
- options = [
1826
- TriccNodeSelectOption(
1827
- id=generate_id(d.name),
1828
- name=d.name,
1829
- label=d.label,
1830
- list_name=f.list_name,
1831
- relevance=d.activity.applicability,
1832
- select=f
1833
- ) for d in diags
1834
- ]
1835
- f.options=dict(zip(range(0, len(options)), options))
1836
- activity.nodes[f.id]=f
2144
+ f.options = dict(zip(range(0, len(options)), options))
2145
+ activity.nodes[f.id] = f
1837
2146
  set_prev_next_node(f, end, edge_only=False)
1838
-
2147
+
1839
2148
  return activity
1840
-
1841
- def get_prev_node_expression( node, processed_nodes, get_overall_exp=False, excluded_name=None, process=None):
2149
+
2150
+
2151
+ def get_prev_node_expression(node, processed_nodes, get_overall_exp=False, excluded_name=None, process=None):
1842
2152
  expression = None
1843
2153
  if node is None:
1844
2154
  pass
1845
2155
  # when getting the prev node, we calculate the
1846
- if hasattr(node, 'expression_inputs') and len(node.expression_inputs) > 0:
2156
+ if hasattr(node, "expression_inputs") and len(node.expression_inputs) > 0:
1847
2157
  expression_inputs = node.expression_inputs
1848
2158
  expression_inputs = clean_or_list(expression_inputs)
1849
2159
  else:
1850
- expression_inputs = []
2160
+ expression_inputs = []
1851
2161
  prev_activities = {}
1852
2162
  for prev_node in node.prev_nodes:
1853
2163
  if prev_node.activity.id not in prev_activities:
1854
- prev_activities[prev_node.activity.id]=[]
2164
+ prev_activities[prev_node.activity.id] = []
1855
2165
  prev_activities[prev_node.activity.id].append(prev_node)
1856
-
2166
+
1857
2167
  for act_id in prev_activities:
1858
2168
  act_expression_inputs = []
1859
2169
  for prev_node in prev_activities[act_id]:
1860
- if excluded_name is None or prev_node != excluded_name or (
1861
- isinstance(excluded_name, str) and hasattr(prev_node, 'name') and prev_node.name != excluded_name): # or isinstance(prev_node, TriccNodeActivityEnd):
2170
+ if (
2171
+ excluded_name is None
2172
+ or prev_node != excluded_name
2173
+ or (
2174
+ # or isinstance(prev_node, TriccNodeActivityEnd):
2175
+ isinstance(excluded_name, str)
2176
+ and hasattr(prev_node, "name")
2177
+ and prev_node.name != excluded_name
2178
+ )
2179
+ ):
1862
2180
  # the rhombus should calculate only reference
1863
2181
  sub = get_node_expression(
1864
2182
  prev_node,
1865
2183
  processed_nodes=processed_nodes,
1866
2184
  get_overall_exp=get_overall_exp,
1867
2185
  is_prev=True,
1868
- process=process)
1869
- if isinstance(node, TriccNodeActivity) or get_overall_exp:
1870
- add_sub_expression(act_expression_inputs, sub )
2186
+ process=get_overall_exp,
2187
+ )
2188
+ if isinstance(node, TriccNodeActivity) or get_overall_exp:
2189
+ add_sub_expression(act_expression_inputs, sub)
1871
2190
  else:
1872
- add_sub_expression(expression_inputs, sub )
1873
-
2191
+ add_sub_expression(expression_inputs, sub)
2192
+
1874
2193
  if act_expression_inputs:
1875
2194
  act_sub = or_join(act_expression_inputs)
1876
- if act_sub == TriccStatic(True):
1877
- act_sub = get_node_expression(
2195
+ # if there is condition fallback on the calling activity condition
2196
+ if act_sub == TriccStatic(True):
2197
+ act_sub = get_node_expression(
1878
2198
  prev_node.activity,
1879
2199
  processed_nodes=processed_nodes,
1880
- get_overall_exp=True,
2200
+ get_overall_exp=get_overall_exp,
1881
2201
  is_prev=True,
1882
2202
  negate=False,
1883
- process=process
2203
+ process=process,
1884
2204
  )
1885
- add_sub_expression(expression_inputs, act_sub )
2205
+ add_sub_expression(expression_inputs, act_sub)
1886
2206
  # avoid void is there is not conditions to avoid looping too much itme
1887
2207
  # expression_inputs = clean_or_list(
1888
2208
  # [
1889
- # get_tricc_operation_operand(e)
1890
- # if isinstance(expression, TriccOperation)
1891
- # else e
2209
+ # get_tricc_operation_operand(e)
2210
+ # if isinstance(expression, TriccOperation)
2211
+ # else e
1892
2212
  # for e in expression_inputs])
1893
-
2213
+
1894
2214
  if expression_inputs:
1895
- expression = or_join(
1896
- expression_inputs
1897
- )
2215
+ expression = or_join(expression_inputs)
1898
2216
  # if isinstance(node, TriccNodeExclusive):
1899
2217
  # expression = TRICC_NEGATE.format(expression)
1900
- # only used for activityStart
2218
+ # only used for activityStart
1901
2219
  else:
1902
2220
  expression = TriccStatic(True)
1903
2221
  return expression
1904
2222
 
1905
- def get_activity_end_terms( node, processed_nodes, process=None):
2223
+
2224
+ def get_activity_end_terms(node, processed_nodes, process=None):
1906
2225
  end_nodes = node.get_end_nodes()
1907
2226
  expression_inputs = []
1908
2227
  for end_node in end_nodes:
1909
2228
  add_sub_expression(
1910
2229
  expression_inputs,
1911
2230
  get_node_expression(
1912
- end_node,
1913
- processed_nodes=processed_nodes,
1914
- get_overall_exp=False,
1915
- is_prev=True,
1916
- process=process))
2231
+ end_node, processed_nodes=processed_nodes, get_overall_exp=False, is_prev=True, process=process
2232
+ ),
2233
+ )
2234
+
2235
+ return or_join(expression_inputs)
1917
2236
 
1918
- return or_join(expression_inputs)
1919
2237
 
1920
- def get_count_terms( node, processed_nodes, get_overall_exp, negate=False, process=None):
2238
+ def get_count_terms(node, processed_nodes, get_overall_exp, negate=False, process=None):
1921
2239
  terms = []
1922
-
2240
+
1923
2241
  for prev_node in node.prev_nodes:
1924
- term = get_count_terms_details( prev_node, processed_nodes, get_overall_exp, negate, process)
2242
+ term = get_count_terms_details(prev_node, processed_nodes, get_overall_exp, negate, process)
1925
2243
  if term:
1926
2244
  terms.append(term)
1927
2245
  if len(terms) == 1:
1928
- return TriccOperation(
1929
- TriccOperator.CAST_NUMBER,
1930
- [terms[0]]
1931
- )
2246
+ return TriccOperation(TriccOperator.CAST_NUMBER, [terms[0]])
1932
2247
  elif len(terms) > 0:
1933
- return TriccOperation(
1934
- TriccOperator.PLUS,
1935
- [
1936
- TriccOperation(
1937
- TriccOperator.CAST_NUMBER,
1938
- [term]
1939
- ) for term in terms
1940
- ]
1941
- )
1942
-
1943
- def get_count_terms_details( prev_node, processed_nodes, get_overall_exp, negate=False, process=None):
1944
- operation_none = TriccOperation(
1945
- TriccOperator.SELECTED,
1946
- [
1947
- prev_node,
1948
- TriccStatic('opt_none')
1949
- ]
1950
- )
1951
- if isinstance(prev_node, TriccNodeSelectYesNo):
2248
+ return TriccOperation(TriccOperator.PLUS, [TriccOperation(TriccOperator.CAST_NUMBER, [term]) for term in terms])
2249
+
2250
+
2251
+ def get_none_option(node):
2252
+ if hasattr(node, "options"):
2253
+ for opt in node.options.values():
2254
+ if opt.name == "opt_none":
2255
+ return opt
2256
+ return None
2257
+
2258
+
2259
+ def get_count_terms_details(prev_node, processed_nodes, get_overall_exp, negate=False, process=None):
2260
+ opt_none = get_none_option(prev_node)
2261
+ if opt_none:
2262
+ operation_none = TriccOperation(TriccOperator.SELECTED, [prev_node, TriccStatic(opt_none)])
2263
+ else:
2264
+ operation_none = TriccOperation(TriccOperator.SELECTED, [prev_node, TriccStatic("opt_none")])
2265
+ if isinstance(prev_node, TriccNodeSelectYesNo):
2266
+ return TriccOperation(TriccOperator.SELECTED, [prev_node, TriccStatic(prev_node.options[0])])
2267
+ elif issubclass(prev_node.__class__, TriccNodeSelect):
2268
+ if negate:
2269
+ return
2270
+ # terms.append(TRICC_SELECT_MULTIPLE_CALC_NONE_EXPRESSION.format(get_export_name(prev_node)))
2271
+ else:
1952
2272
  return TriccOperation(
1953
- TriccOperator.SELECTED,
2273
+ TriccOperator.MINUS,
1954
2274
  [
1955
- prev_node,
1956
- TriccStatic(prev_node.options[0].name)
1957
- ]
2275
+ TriccOperation(TriccOperator.COUNT, [prev_node]),
2276
+ TriccOperation(TriccOperator.CAST_NUMBER, [operation_none]),
2277
+ ],
1958
2278
  )
1959
- elif issubclass(prev_node.__class__, TriccNodeSelect):
1960
- if negate:
1961
- return
1962
- #terms.append(TRICC_SELECT_MULTIPLE_CALC_NONE_EXPRESSION.format(get_export_name(prev_node)))
1963
- else:
1964
- return TriccOperation(
1965
- TriccOperator.MINUS,
1966
- [
1967
- TriccOperation(
1968
- TriccOperator.NATIVE,
1969
- [
1970
- 'count-selected',
1971
- prev_node
1972
- ]
1973
- ),TriccOperation(
1974
- TriccOperator.CAST_NUMBER,
1975
- [
1976
- operation_none
1977
- ]
1978
- )
1979
- ])
1980
- #terms.append(TRICC_SELECT_MULTIPLE_CALC_EXPRESSION.format(get_export_name(prev_node)))
1981
- elif isinstance(prev_node, (TriccNodeSelectNotAvailable)):
2279
+ # terms.append(TRICC_SELECT_MULTIPLE_CALC_EXPRESSION.format(get_export_name(prev_node)))
2280
+ elif isinstance(prev_node, (TriccNodeSelectNotAvailable)):
2281
+ return TriccOperation(TriccOperator.SELECTED, [prev_node, TriccStatic("1")])
2282
+ # terms.append(TRICC_SELECTED_EXPRESSION.format(get_export_name(prev_node), '1'))
2283
+ elif isinstance(prev_node, TriccNodeSelectOption):
2284
+ return get_selected_option_expression(prev_node, negate)
2285
+ else:
2286
+ if negate:
1982
2287
  return TriccOperation(
1983
- TriccOperator.SELECTED,
2288
+ TriccOperator.CAST_NUMBER,
1984
2289
  [
1985
- prev_node,
1986
- TriccStatic('1')
1987
- ]
1988
- )
1989
- #terms.append(TRICC_SELECTED_EXPRESSION.format(get_export_name(prev_node), '1'))
1990
- elif isinstance(prev_node, TriccNodeSelectOption):
1991
- return get_selected_option_expression(prev_node, negate)
1992
- else:
1993
- if negate:
1994
- return TriccOperation(
1995
- TriccOperator.CAST_NUMBER,
2290
+ TriccOperation(
2291
+ TriccOperator.NATIVE,
1996
2292
  [
1997
2293
  TriccOperation(
1998
- TriccOperator.NATIVE,
2294
+ TriccOperator.CAST_NUMBER,
1999
2295
  [
2000
- TriccOperation(
2001
- TriccOperator.CAST_NUMBER,
2002
- [
2003
- get_node_expression(
2004
- prev_node,
2005
- processed_nodes=processed_nodes,
2006
- get_overall_exp=True,
2007
- is_prev=True,
2008
- process=process)
2009
- ]),
2010
- TriccStatic('0')
2011
- ]
2012
- )
2013
- ]
2296
+ get_node_expression(
2297
+ prev_node,
2298
+ processed_nodes=processed_nodes,
2299
+ get_overall_exp=get_overall_exp,
2300
+ is_prev=True,
2301
+ process=process,
2302
+ )
2303
+ ],
2304
+ ),
2305
+ TriccStatic("0"),
2306
+ ],
2014
2307
  )
2015
-
2016
- else:
2017
- return TriccOperation(
2018
- TriccOperator.CAST_NUMBER,
2019
- [
2020
- get_node_expression(
2021
- prev_node,
2022
- processed_nodes=processed_nodes,
2023
- get_overall_exp=True,
2024
- is_prev=True,
2025
- process=process)
2026
- ]
2308
+ ],
2309
+ )
2310
+
2311
+ else:
2312
+ return TriccOperation(
2313
+ TriccOperator.CAST_NUMBER,
2314
+ [
2315
+ get_node_expression(
2316
+ prev_node,
2317
+ processed_nodes=processed_nodes,
2318
+ get_overall_exp=get_overall_exp,
2319
+ is_prev=True,
2320
+ process=process
2027
2321
  )
2028
-
2029
-
2030
-
2031
- def get_add_terms( node, processed_nodes, get_overall_exp=False, negate=False, process=None):
2322
+ ],
2323
+ )
2324
+
2325
+
2326
+ def get_add_terms(node, processed_nodes, get_overall_exp=False, negate=False, process=None):
2032
2327
  if negate:
2033
2328
  logger.warning("negate not supported for Add node {}".format(node.get_name()))
2034
2329
  terms = []
2035
2330
  for prev_node in node.prev_nodes:
2036
2331
  if issubclass(prev_node, TriccNodeNumber) or isinstance(node, TriccNodeCount):
2037
- terms.append(
2038
- TriccOperation(
2039
- TriccOperator.COALESCE,
2040
- [
2041
- prev_node,
2042
- TriccStatic(0)
2043
- ]
2044
- )
2045
- )
2332
+ terms.append(TriccOperation(TriccOperator.COALESCE, [prev_node, TriccStatic(0)]))
2046
2333
  else:
2047
2334
  terms.append(
2048
2335
  TriccOperation(
@@ -2051,34 +2338,29 @@ def get_add_terms( node, processed_nodes, get_overall_exp=False, negate=False, p
2051
2338
  get_node_expression(
2052
2339
  prev_node,
2053
2340
  processed_nodes=processed_nodes,
2054
- get_overall_exp=True,
2055
- is_prev=True,
2056
- process=process)
2057
- ]
2341
+ get_overall_exp=get_overall_exp,
2342
+ is_prev=True,
2343
+ process=process,
2344
+ )
2345
+ ],
2058
2346
  )
2059
2347
  )
2060
2348
  if len(terms) > 0:
2061
2349
  operation = terms[0]
2062
2350
  if len(terms) > 1:
2063
2351
  for term in terms[1:]:
2064
- operation = TriccOperation(
2065
- TriccOperator.ADD,
2066
- [
2067
- operation,
2068
- term
2069
- ]
2070
- )
2352
+ operation = TriccOperation(TriccOperator.ADD, [operation, term])
2071
2353
  return operation
2072
-
2073
- def get_rhombus_terms( node, processed_nodes, get_overall_exp=False, negate=False, process=None):
2354
+
2355
+
2356
+ def get_rhombus_terms(node, processed_nodes, get_overall_exp=False, negate=False, process=None):
2074
2357
  expression = None
2075
2358
  left_term = None
2076
- operator = None
2077
2359
  if node.reference is not None:
2078
2360
  if isinstance(node.reference, set):
2079
2361
  node.reference = list(node.reference)
2080
2362
  # calcualte the expression only for select muzltiple and fake calculate
2081
- if issubclass(node.reference.__class__, (list,OrderedSet)):
2363
+ if issubclass(node.reference.__class__, (list, OrderedSet)):
2082
2364
  if node.expression_reference is None and len(node.reference) == 1:
2083
2365
  ref = node.reference[0]
2084
2366
  if issubclass(ref.__class__, TriccNodeBaseModel):
@@ -2086,81 +2368,83 @@ def get_rhombus_terms( node, processed_nodes, get_overall_exp=False, negate=Fals
2086
2368
  expression = get_activity_end_terms(ref, processed_nodes, process=process)
2087
2369
  elif issubclass(ref.__class__, TriccNodeFakeCalculateBase):
2088
2370
  expression = get_node_expression(
2089
- ref,
2090
- processed_nodes=processed_nodes,
2091
- get_overall_exp=True,
2092
- is_prev=True,
2093
- process=process
2371
+ ref, processed_nodes=processed_nodes, get_overall_exp=True, is_prev=True, process=process
2094
2372
  )
2095
2373
  else:
2096
2374
  expression = ref
2097
2375
  elif issubclass(ref.__class__, TriccReference):
2098
2376
  expression = ref
2099
2377
  else:
2100
- logger.critical('reference {0} was not found in the previous nodes of node {1}'.format(node.reference,
2101
- node.get_name()))
2378
+ logger.critical(
2379
+ "reference {0} was not found in the previous nodes of node {1}".format(
2380
+ node.reference, node.get_name()
2381
+ )
2382
+ )
2102
2383
  exit(1)
2103
- elif node.expression_reference is not None and node.expression_reference != '':
2104
- if isinstance(node.expression_reference, TriccOperation):
2384
+ elif node.expression_reference is not None and node.expression_reference != "":
2385
+ if isinstance(node.expression_reference, (TriccOperation, TriccReference, TriccStatic)):
2105
2386
  return node.expression_reference
2106
2387
  elif isinstance(node.expression_reference, str):
2107
2388
  expression = node.expression_reference.format(*get_list_names(node.reference))
2108
2389
  else:
2109
- logger.critical('expression_reference {0} unsuported type {1}'.format(node.expression_reference, node.expression_reference.__class__.__name__))
2390
+ logger.critical(
2391
+ "expression_reference {0} unsuported type {1}".format(
2392
+ node.expression_reference, node.expression_reference.__class__.__name__
2393
+ )
2394
+ )
2110
2395
  exit(1)
2111
2396
 
2112
2397
  else:
2113
2398
  logger.warning("missing expression for node {}".format(node.get_name()))
2114
2399
  else:
2115
- logger.critical('reference {0} is not a list {1}'.format(node.reference, node.get_name()))
2400
+ logger.critical("reference {0} is not a list {1}".format(node.reference, node.get_name()))
2116
2401
  exit(1)
2117
2402
  else:
2118
- logger.critical('reference empty for Rhombis {}'.format( node.get_name()))
2403
+ logger.critical("reference empty for Rhombis {}".format(node.get_name()))
2119
2404
  exit(1)
2120
2405
 
2121
2406
  if expression is not None:
2122
2407
  if isinstance(expression, (TriccOperation, TriccStatic)):
2123
2408
  return expression
2124
- elif issubclass(expression.__class__ , TriccNodeCalculateBase):
2409
+ elif issubclass(expression.__class__, TriccNodeCalculateBase):
2125
2410
  return TriccOperation(
2126
2411
  TriccOperator.CAST_NUMBER,
2127
2412
  [
2128
2413
  get_node_expression(
2129
2414
  expression,
2130
2415
  processed_nodes=processed_nodes,
2131
- get_overall_exp=True,
2416
+ get_overall_exp=get_overall_exp,
2132
2417
  is_prev=True,
2133
2418
  process=process
2134
2419
  )
2135
- ])
2136
- elif issubclass(expression.__class__ , (TriccOperation) ):
2137
- return expression
2138
- elif issubclass(expression.__class__ , (TriccNodeDisplayModel, TriccReference)):
2139
- return TriccOperation(
2140
- TriccOperator.ISTRUE,
2141
- [
2142
- expression
2143
- ]
2420
+ ],
2144
2421
  )
2422
+ elif issubclass(expression.__class__, (TriccOperation)):
2423
+ return expression
2424
+ elif issubclass(expression.__class__, (TriccNodeDisplayModel, TriccReference)):
2425
+ return TriccOperation(TriccOperator.ISTRUE, [expression])
2145
2426
  else:
2146
2427
  if left_term is not None and re.search(" (+)|(-)|(or)|(and) ", expression):
2147
2428
  expression = "({0}){1}".format(expression, left_term)
2148
2429
  else:
2149
2430
  expression = "{0}{1}".format(expression, left_term)
2150
2431
  else:
2151
- logger.critical("Rhombus reference was not found for node {}, reference {}".format(
2152
- node.get_name(),
2153
- node.reference
2154
- ))
2432
+ logger.critical(
2433
+ "Rhombus reference was not found for node {}, reference {}".format(node.get_name(), node.reference)
2434
+ )
2155
2435
  exit(1)
2156
2436
 
2157
2437
  return expression
2438
+
2439
+
2158
2440
  # function that generate the calculation terms return by calculate node
2159
2441
  # @param node calculate node to assess
2160
2442
  # @param processed_nodes list of node already processed, importnat because only processed node could be use
2161
2443
  # @param get_overall_exp used when this funciton is called in the evaluation of another calculate
2162
2444
  # @param negate use to retriece the negation of a calculation
2163
- def get_calculation_terms( node, processed_nodes, get_overall_exp=False, negate=False, process=None):
2445
+
2446
+
2447
+ def get_calculation_terms(node, processed_nodes, get_overall_exp=False, negate=False, process=None):
2164
2448
  # returns something directly only if the negate is managed
2165
2449
  expression = None
2166
2450
  if isinstance(node, TriccNodeAdd):
@@ -2169,22 +2453,24 @@ def get_calculation_terms( node, processed_nodes, get_overall_exp=False, negate=
2169
2453
  return get_count_terms(node, False, negate, process=process)
2170
2454
  elif isinstance(node, TriccNodeRhombus):
2171
2455
  return get_rhombus_terms(
2172
- node,
2173
- processed_nodes=processed_nodes,
2174
- get_overall_exp=False,
2175
- negate=negate,
2176
- process=process
2456
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, negate=negate, process=process
2177
2457
  )
2178
- elif isinstance(node, ( TriccNodeWait)):
2458
+ elif isinstance(node, (TriccNodeWait)):
2179
2459
  # just use to force order of question
2180
2460
  expression = None
2181
- # in case of calulate expression evaluation, we need to get the relevance of the activity
2461
+ # in case of calulate expression evaluation, we need to get the relevance of the activity
2182
2462
  # because calculate are not the the activity group
2183
2463
  elif isinstance(node, (TriccNodeActivityStart)) and get_overall_exp:
2184
- expression = get_prev_node_expression(node.activity, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, negate=negate, process=process)
2464
+ expression = get_prev_node_expression(
2465
+ node.activity,
2466
+ processed_nodes=processed_nodes,
2467
+ get_overall_exp=get_overall_exp,
2468
+ negate=negate,
2469
+ process=process,
2470
+ )
2185
2471
  elif isinstance(node, (TriccNodeActivityStart, TriccNodeActivityEnd)):
2186
2472
  # the group have the relevance for the activity, not needed to replicate it
2187
- expression = None #return get_prev_node_expression(node.activity, processed_nodes, get_overall_exp=False, excluded_name=None)
2473
+ expression = None
2188
2474
  elif isinstance(node, TriccNodeExclusive):
2189
2475
  if len(node.prev_nodes) == 1:
2190
2476
  iterator = iter(node.prev_nodes)
@@ -2196,48 +2482,57 @@ def get_calculation_terms( node, processed_nodes, get_overall_exp=False, negate=
2196
2482
  return get_node_expression(
2197
2483
  node_to_negate,
2198
2484
  processed_nodes=processed_nodes,
2199
- get_overall_exp=True,
2485
+ get_overall_exp=get_overall_exp,
2200
2486
  is_prev=True,
2201
2487
  negate=True,
2202
- process=process
2203
- )
2488
+ process=process,
2489
+ )
2204
2490
  elif isinstance(node_to_negate, TriccNodeActivity):
2205
2491
  return get_node_expression(
2206
2492
  node_to_negate,
2207
2493
  processed_nodes=processed_nodes,
2208
- get_overall_exp=True,
2494
+ get_overall_exp=get_overall_exp,
2209
2495
  is_prev=True,
2210
2496
  negate=True,
2211
- process=process
2497
+ process=process,
2212
2498
  )
2213
2499
  else:
2214
- logger.critical(f"exclusive node {node.get_name()}\
2500
+ logger.critical(
2501
+ f"exclusive node {node.get_name()}\
2215
2502
  does not depend of a calculate but on\
2216
- {node_to_negate.__class__}::{node_to_negate.get_name()}")
2503
+ {node_to_negate.__class__}::{node_to_negate.get_name()}"
2504
+ )
2217
2505
 
2218
2506
  else:
2219
2507
  logger.critical("exclusive node {} has no ou too much parent".format(node.get_name()))
2220
-
2508
+
2221
2509
  if isinstance(node.expression_reference, (TriccOperation, TriccStatic)):
2222
2510
  expression = node.expression_reference
2223
- elif node.reference is not None and node.expression_reference is not None :
2224
- expression = get_prev_node_expression(node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process)
2511
+ elif node.reference is not None and node.expression_reference is not None:
2512
+ expression = get_prev_node_expression(
2513
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process
2514
+ )
2225
2515
  ref_expression = node.expression_reference.format(*[get_export_name(ref) for ref in node.reference])
2226
- if expression is not None and expression != '':
2227
- expression = and_join([expression,ref_expression])
2516
+ if expression is not None and expression != "":
2517
+ expression = and_join([expression, ref_expression])
2228
2518
  else:
2229
2519
  expression = ref_expression
2230
2520
  elif expression is None:
2231
- expression = get_prev_node_expression(node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process)
2232
-
2521
+ expression = get_prev_node_expression(
2522
+ node, processed_nodes=processed_nodes, get_overall_exp=get_overall_exp, process=process
2523
+ )
2524
+
2233
2525
  # manage the generic negation
2234
2526
  if negate:
2235
-
2527
+
2236
2528
  return negate_term(expression)
2237
2529
  else:
2238
2530
  return expression
2239
-
2531
+
2532
+
2240
2533
  # Function that add element to array is not None or ''
2534
+
2535
+
2241
2536
  def add_sub_expression(array, sub):
2242
2537
  if isinstance(sub, (TriccOperation, TriccStatic)):
2243
2538
  not_sub = negate_term(sub)
@@ -2251,62 +2546,158 @@ def add_sub_expression(array, sub):
2251
2546
  pass
2252
2547
  # elif sub is None:
2253
2548
  # array.append(TriccStatic(True))
2254
-
2255
-
2256
2549
 
2257
2550
  # function that negate terms
2551
+
2552
+
2258
2553
  # @param expression to negate
2259
- def negate_term(expression):
2260
-
2261
- return not_clean(expression)
2262
2554
 
2263
2555
 
2556
+ def negate_term(expression):
2557
+
2558
+ return not_clean(expression)
2264
2559
 
2265
2560
 
2266
2561
  # if the node is "required" then we can take the fact that it has value for the next elements
2267
2562
  def get_required_node_expression(node):
2268
- return TriccOperation(
2269
- operator=TriccOperator.EXISTS,
2270
- reference=[
2271
- node
2272
- ]
2273
- )
2563
+ return TriccOperation(operator=TriccOperator.EXISTS, reference=[node])
2274
2564
 
2275
2565
 
2276
2566
  # Get a selected option
2277
2567
  def get_selected_option_expression(option_node, negate):
2278
-
2279
- selected = TriccOperation(
2280
- TriccOperator.SELECTED,
2281
- [
2282
- option_node.select,
2283
- TriccStatic(option_node.name)
2284
- ]
2285
- )
2286
-
2568
+ if isinstance(option_node.select, TriccNodeSelectOne):
2569
+ return get_selected_option_expression_single(option_node, negate)
2570
+ else:
2571
+ return get_selected_option_expression_multiple(option_node, negate)
2572
+
2573
+
2574
+ def get_selected_option_expression_single(option_node, negate):
2575
+
2576
+ if not negate:
2577
+ return TriccOperation(TriccOperator.EQUAL, [option_node.select, option_node])
2578
+
2579
+
2580
+ def get_selected_option_expression_multiple(option_node, negate):
2581
+
2582
+ selected = TriccOperation(TriccOperator.SELECTED, [option_node.select, TriccStatic(option_node)])
2583
+
2287
2584
  if negate:
2288
2585
  return TriccOperation(
2289
2586
  operator=TriccOperator.AND,
2290
2587
  resource=[
2291
- TriccOperation(
2292
- operator=TriccOperator.NOT,
2293
- resource=[
2294
- selected
2295
- ]
2296
- ),TriccOperation(
2297
- operator=TriccOperator.NATIVE,
2298
- resource=[
2299
- 'count-selected',
2300
- option_node.select
2301
- ]
2302
- )
2303
- ])
2304
-
2588
+ TriccOperation(operator=TriccOperator.NOT, resource=[selected]),
2589
+ TriccOperation(operator=TriccOperator.ISNOTNULL, resource=[option_node.select]),
2590
+ ],
2591
+ )
2592
+
2305
2593
  else:
2306
2594
  return selected
2307
2595
 
2308
2596
 
2309
-
2597
+ def generate_calculate(node, processed_nodes, **kwargs):
2598
+ # For calculations, set calculate in questionOptions
2599
+ # Check if node is ready to be processed (similar to XLS form strategy)
2600
+ if not is_ready_to_process(node, processed_nodes, strict=True):
2601
+ return False
2310
2602
 
2603
+ # Process references to ensure dependencies are handled
2604
+ if not process_reference(
2605
+ node, processed_nodes, {}, replace_reference=True, codesystems=kwargs.get("codesystems", None)
2606
+ ):
2607
+ return False
2608
+
2609
+ if node not in processed_nodes:
2610
+ if kwargs.get("warn", True):
2611
+ logger.debug("generation of calculate for node {}".format(node.get_name()))
2612
+ if (
2613
+ hasattr(node, "expression")
2614
+ and (node.expression is None)
2615
+ and issubclass(node.__class__, TriccNodeCalculateBase)
2616
+ ):
2617
+ node.expression = get_node_expressions(
2618
+ node, processed_nodes, process=kwargs.get("process", "main ")
2619
+ )
2620
+ # continue walk
2621
+ if issubclass(
2622
+ node.__class__,
2623
+ (
2624
+ TriccNodeDisplayModel,
2625
+ TriccNodeDisplayCalculateBase,
2626
+ TriccNodeEnd,
2627
+ ),
2628
+ ):
2629
+ set_last_version_false(node, processed_nodes)
2630
+ return True
2311
2631
 
2312
2632
 
2633
+ def generate_base(node, processed_nodes, **kwargs):
2634
+ # Generate question for OpenMRS O3 schema
2635
+ # Handle activity nodes by processing their inner content
2636
+ # Check if node is ready to be processed (similar to XLS form strategy)
2637
+ if not is_ready_to_process(node, processed_nodes, strict=False):
2638
+ return False
2639
+
2640
+ # Process references to ensure dependencies are handled
2641
+ if not process_reference(
2642
+ node, processed_nodes, {}, replace_reference=False, codesystems=kwargs.get("codesystems", None)
2643
+ ):
2644
+ return False
2645
+ if node not in processed_nodes:
2646
+ if issubclass(node.__class__, TriccRhombusMixIn) and isinstance(node.reference, str):
2647
+ logger.warning("node {} still using the reference string".format(node.get_name()))
2648
+ if issubclass(node.__class__, TriccNodeInputModel):
2649
+ # we don't overright if define in the diagram
2650
+ if node.constraint is None:
2651
+ if isinstance(node, TriccNodeSelectMultiple):
2652
+ none_opt = get_none_option(node)
2653
+ if none_opt:
2654
+ node.constraint = or_join(
2655
+ [
2656
+ TriccOperation(
2657
+ TriccOperator.EQUAL,
2658
+ ["$this", TriccStatic(none_opt)],
2659
+ ),
2660
+ TriccOperation(
2661
+ TriccOperator.NOT,
2662
+ [
2663
+ TriccOperation(
2664
+ TriccOperator.SELECTED,
2665
+ ["$this", TriccStatic(none_opt)],
2666
+ )
2667
+ ],
2668
+ ),
2669
+ ]
2670
+ ) # '.=\'opt_none\' or not(selected(.,\'opt_none\'))'
2671
+ node.constraint_message = "**None** cannot be selected together with choice."
2672
+ elif node.tricc_type in (
2673
+ TriccNodeType.integer,
2674
+ TriccNodeType.decimal,
2675
+ ):
2676
+ constraints = []
2677
+ constraints_min = ""
2678
+ constraints_max = ""
2679
+ if node.min is not None and node.min != "":
2680
+ node.min = float(node.min)
2681
+ if int(node.min) == node.min:
2682
+ node.min = int(node.min)
2683
+ constraints.append(
2684
+ TriccOperation(TriccOperator.MORE_OR_EQUAL, ["$this", TriccStatic(node.min)])
2685
+ )
2686
+ constraints_min = "The minimum value is {0}.".format(node.min)
2687
+ if node.max is not None and node.max != "":
2688
+ node.max = float(node.max)
2689
+ if int(node.max) == node.max:
2690
+ node.max = int(node.max)
2691
+ constraints.append(
2692
+ TriccOperation(TriccOperator.LESS_OR_EQUAL, ["$this", TriccStatic(node.max)])
2693
+ )
2694
+ constraints_max = "The maximum value is {0}.".format(node.max)
2695
+ if len(constraints) > 1:
2696
+ node.constraint = TriccOperation(TriccOperator.AND, constraints)
2697
+ node.constraint_message = (constraints_min + " " + constraints_max).strip()
2698
+ elif len(constraints) == 1:
2699
+ node.constraint = constraints[0]
2700
+ node.constraint_message = (constraints_min + " " + constraints_max).strip()
2701
+ # continue walk
2702
+ return True
2703
+ return False