tricc-oo 1.0.1__py3-none-any.whl → 1.4.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tests/build.py +213 -0
- tests/test_cql.py +197 -0
- tests/to_ocl.py +51 -0
- {tricc → tricc_oo}/__init__.py +3 -1
- tricc_oo/converters/codesystem_to_ocl.py +169 -0
- tricc_oo/converters/cql/cqlLexer.py +822 -0
- tricc_oo/converters/cql/cqlListener.py +1632 -0
- tricc_oo/converters/cql/cqlParser.py +11204 -0
- tricc_oo/converters/cql/cqlVisitor.py +913 -0
- tricc_oo/converters/cql_to_operation.py +402 -0
- tricc_oo/converters/datadictionnary.py +115 -0
- tricc_oo/converters/drawio_type_map.py +222 -0
- tricc_oo/converters/tricc_to_xls_form.py +61 -0
- tricc_oo/converters/utils.py +65 -0
- tricc_oo/converters/xml_to_tricc.py +1003 -0
- tricc_oo/models/__init__.py +4 -0
- tricc_oo/models/base.py +732 -0
- tricc_oo/models/calculate.py +216 -0
- tricc_oo/models/ocl.py +281 -0
- tricc_oo/models/ordered_set.py +125 -0
- tricc_oo/models/tricc.py +418 -0
- tricc_oo/parsers/xml.py +138 -0
- tricc_oo/serializers/__init__.py +0 -0
- tricc_oo/serializers/xls_form.py +745 -0
- tricc_oo/strategies/__init__.py +0 -0
- tricc_oo/strategies/input/__init__.py +0 -0
- tricc_oo/strategies/input/base_input_strategy.py +111 -0
- tricc_oo/strategies/input/drawio.py +317 -0
- tricc_oo/strategies/output/base_output_strategy.py +148 -0
- tricc_oo/strategies/output/spice.py +365 -0
- tricc_oo/strategies/output/xls_form.py +697 -0
- tricc_oo/strategies/output/xlsform_cdss.py +189 -0
- tricc_oo/strategies/output/xlsform_cht.py +200 -0
- tricc_oo/strategies/output/xlsform_cht_hf.py +334 -0
- tricc_oo/visitors/__init__.py +0 -0
- tricc_oo/visitors/tricc.py +2198 -0
- tricc_oo/visitors/utils.py +17 -0
- tricc_oo/visitors/xform_pd.py +264 -0
- tricc_oo-1.4.15.dist-info/METADATA +219 -0
- tricc_oo-1.4.15.dist-info/RECORD +46 -0
- {tricc_oo-1.0.1.dist-info → tricc_oo-1.4.15.dist-info}/WHEEL +1 -1
- tricc_oo-1.4.15.dist-info/top_level.txt +2 -0
- tricc/converters/mc_to_tricc.py +0 -542
- tricc/converters/tricc_to_xls_form.py +0 -553
- tricc/converters/utils.py +0 -44
- tricc/converters/xml_to_tricc.py +0 -740
- tricc/models/tricc.py +0 -1093
- tricc/parsers/xml.py +0 -81
- tricc/serializers/xls_form.py +0 -364
- tricc/strategies/input/base_input_strategy.py +0 -80
- tricc/strategies/input/drawio.py +0 -246
- tricc/strategies/input/medalcreator.py +0 -168
- tricc/strategies/output/base_output_strategy.py +0 -92
- tricc/strategies/output/xls_form.py +0 -194
- tricc/strategies/output/xlsform_cdss.py +0 -46
- tricc/strategies/output/xlsform_cht.py +0 -106
- tricc/visitors/tricc.py +0 -375
- tricc_oo-1.0.1.dist-info/LICENSE +0 -78
- tricc_oo-1.0.1.dist-info/METADATA +0 -229
- tricc_oo-1.0.1.dist-info/RECORD +0 -26
- tricc_oo-1.0.1.dist-info/top_level.txt +0 -2
- venv/bin/vba_extract.py +0 -78
- {tricc → tricc_oo}/converters/__init__.py +0 -0
- {tricc → tricc_oo}/models/lang.py +0 -0
- {tricc/serializers → tricc_oo/parsers}/__init__.py +0 -0
- {tricc → tricc_oo}/serializers/planuml.py +0 -0
tricc/models/tricc.py
DELETED
|
@@ -1,1093 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import random
|
|
5
|
-
import string
|
|
6
|
-
from enum import Enum, auto
|
|
7
|
-
from typing import Dict, ForwardRef, List, Optional, Union
|
|
8
|
-
|
|
9
|
-
from pydantic import BaseModel, constr
|
|
10
|
-
from strenum import StrEnum
|
|
11
|
-
|
|
12
|
-
from tricc.converters.utils import generate_id
|
|
13
|
-
|
|
14
|
-
logger = logging.getLogger("default")
|
|
15
|
-
|
|
16
|
-
Expression = constr(regex="^[^\\/]+$")
|
|
17
|
-
|
|
18
|
-
triccId = constr(regex="^.+$")
|
|
19
|
-
triccIdList = constr(regex="^.+$")
|
|
20
|
-
|
|
21
|
-
b64 = constr(regex="[^-A-Za-z0-9+/=]|=[^=]|={3,}$")
|
|
22
|
-
|
|
23
|
-
TriccEdge = ForwardRef('TriccEdge')
|
|
24
|
-
# data:page/id,UkO_xCL5ZjyshJO9Bexg
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
ACTIVITY_END_NODE_FORMAT = "aend_{}"
|
|
28
|
-
END_NODE_FORMAT = "end_{}"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class TriccNodeType(StrEnum):
|
|
32
|
-
#replace with auto ?
|
|
33
|
-
note = 'note'
|
|
34
|
-
calculate = 'calculate'
|
|
35
|
-
select_multiple = 'select_multiple'
|
|
36
|
-
select_one = 'select_one'
|
|
37
|
-
decimal = 'decimal'
|
|
38
|
-
integer = 'integer'
|
|
39
|
-
text = 'text'
|
|
40
|
-
date = 'date'
|
|
41
|
-
rhombus = 'rhombus' # fetch data
|
|
42
|
-
goto = 'goto' #: start the linked activity within the target activity
|
|
43
|
-
start = 'start' #: main start of the algo
|
|
44
|
-
activity_start = 'activity_start' #: start of an activity (link in)
|
|
45
|
-
link_in = 'link_in'
|
|
46
|
-
link_out = 'link_out'
|
|
47
|
-
count = 'count' #: count the number of valid input
|
|
48
|
-
add = 'add' # add counts
|
|
49
|
-
container_hint_media = 'container_hint_media' # DEPRECATED
|
|
50
|
-
activity = 'activity'
|
|
51
|
-
select_yesno = 'select_one yesno' # NOT YET SUPPORTED
|
|
52
|
-
select_option = 'select_option'
|
|
53
|
-
hint = 'hint-message'
|
|
54
|
-
help = 'help-message'
|
|
55
|
-
exclusive = 'not'
|
|
56
|
-
end = 'end'
|
|
57
|
-
activity_end = 'activity_end'
|
|
58
|
-
edge = 'edge'
|
|
59
|
-
page = 'page'
|
|
60
|
-
not_available = 'not_available'
|
|
61
|
-
quantity = 'quantity'
|
|
62
|
-
bridge = 'bridge'
|
|
63
|
-
wait = 'wait'
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class TriccOperation(str, Enum):
|
|
67
|
-
_and = 'and'
|
|
68
|
-
_or = 'or'
|
|
69
|
-
_not = 'not'
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
media_nodes = [
|
|
73
|
-
TriccNodeType.note,
|
|
74
|
-
TriccNodeType.select_multiple,
|
|
75
|
-
TriccNodeType.select_one,
|
|
76
|
-
TriccNodeType.decimal,
|
|
77
|
-
TriccNodeType.integer,
|
|
78
|
-
TriccNodeType.text,
|
|
79
|
-
]
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class TriccBaseModel(BaseModel):
|
|
83
|
-
id: triccId
|
|
84
|
-
tricc_type: TriccNodeType
|
|
85
|
-
#parent: Optional[triccId]#TODO: used ?
|
|
86
|
-
instance: int = 1
|
|
87
|
-
base_instance: Optional[TriccBaseModel]
|
|
88
|
-
|
|
89
|
-
def make_instance(self, nb_instance, **kwargs):
|
|
90
|
-
instance = self.copy()
|
|
91
|
-
# change the id to avoid collision of name
|
|
92
|
-
instance.id = generate_id()
|
|
93
|
-
instance.instance = int(nb_instance)
|
|
94
|
-
instance.base_instance = self
|
|
95
|
-
|
|
96
|
-
# assign the defualt group
|
|
97
|
-
# if activity is not None and self.group == activity.base_instance:
|
|
98
|
-
# instance.group = instance
|
|
99
|
-
return instance
|
|
100
|
-
|
|
101
|
-
def __eq__(self, other):
|
|
102
|
-
if isinstance(other, self.__class__):
|
|
103
|
-
return self.id == other.id
|
|
104
|
-
else:
|
|
105
|
-
return False
|
|
106
|
-
|
|
107
|
-
def __ne__(self, other):
|
|
108
|
-
return not self.__eq__(other)
|
|
109
|
-
|
|
110
|
-
def __hash__(self):
|
|
111
|
-
hash_value = hash(self.id)
|
|
112
|
-
return hash_value
|
|
113
|
-
|
|
114
|
-
def get_name(self):
|
|
115
|
-
return id
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
class TriccEdge(TriccBaseModel):
|
|
119
|
-
tricc_type: TriccNodeType = TriccNodeType.edge
|
|
120
|
-
source: Union[triccId, TriccNodeBaseModel]
|
|
121
|
-
target: Union[triccId, TriccNodeBaseModel]
|
|
122
|
-
value: Optional[str]
|
|
123
|
-
|
|
124
|
-
def make_instance(self, instance_nb, activity=None):
|
|
125
|
-
instance = super().make_instance(instance_nb, activity=activity)
|
|
126
|
-
#if issubclass(self.source.__class__, TriccBaseModel):
|
|
127
|
-
instance.source = self.source if isinstance(self.source, str) else self.source.copy() #TODO should we copy the nodes ?
|
|
128
|
-
#if issubclass(self.target.__class__, TriccBaseModel):
|
|
129
|
-
instance.target = self.target if isinstance(self.target, str) else self.target.copy()
|
|
130
|
-
return instance
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
class TriccGroup(TriccBaseModel):
|
|
134
|
-
tricc_type: TriccNodeType = TriccNodeType.page
|
|
135
|
-
group: Optional[TriccBaseModel]
|
|
136
|
-
name: Optional[str]
|
|
137
|
-
export_name:Optional[str]
|
|
138
|
-
label: Optional[Union[str, Dict[str,str]]]
|
|
139
|
-
relevance: Optional[Expression]
|
|
140
|
-
path_len: int = 0
|
|
141
|
-
prev_nodes: List[TriccBaseModel] = []
|
|
142
|
-
def __init__(self, **data):
|
|
143
|
-
super().__init__(**data)
|
|
144
|
-
if self.name is None:
|
|
145
|
-
self.name = generate_id()
|
|
146
|
-
|
|
147
|
-
def get_name(self):
|
|
148
|
-
|
|
149
|
-
if self.label is not None:
|
|
150
|
-
name = self.label[self.label.keys()[0]] if isinstance(self.label, Dict) else self.label
|
|
151
|
-
if len(name) < 50:
|
|
152
|
-
return name
|
|
153
|
-
else:
|
|
154
|
-
return name[:50]
|
|
155
|
-
else:
|
|
156
|
-
return self.name
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class TriccNodeBaseModel(TriccBaseModel):
|
|
160
|
-
path_len: int = 0
|
|
161
|
-
group: Optional[Union[TriccGroup, TriccNodeActivity]]
|
|
162
|
-
name: Optional[str]
|
|
163
|
-
export_name:Optional[str]
|
|
164
|
-
label: Optional[Union[str, Dict[str,str]]]
|
|
165
|
-
next_nodes: List[TriccNodeBaseModel] = []
|
|
166
|
-
prev_nodes: List[TriccNodeBaseModel] = []
|
|
167
|
-
expression: Optional[Expression] # will be generated based on the input
|
|
168
|
-
expression_inputs: List[Expression] = []
|
|
169
|
-
activity: Optional[TriccNodeActivity]
|
|
170
|
-
ref_def: Optional[Union[int,str]] # for medal creator
|
|
171
|
-
|
|
172
|
-
class Config:
|
|
173
|
-
use_enum_values = True # <--
|
|
174
|
-
|
|
175
|
-
# to be updated while processing because final expression will be possible to build$
|
|
176
|
-
# #only the last time the script will go through the node (all prev node expression would be created
|
|
177
|
-
|
|
178
|
-
def get_name(self):
|
|
179
|
-
if self.label is not None:
|
|
180
|
-
name = next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label
|
|
181
|
-
if len(name) < 50:
|
|
182
|
-
return name
|
|
183
|
-
else:
|
|
184
|
-
return name[:50]
|
|
185
|
-
elif self.name is not None:
|
|
186
|
-
return self.name
|
|
187
|
-
else:
|
|
188
|
-
# TODO call parent.get_name instead
|
|
189
|
-
return self.id
|
|
190
|
-
|
|
191
|
-
def make_instance(self, instance_nb, activity=None):
|
|
192
|
-
instance = super().make_instance(instance_nb)
|
|
193
|
-
instance.group = activity
|
|
194
|
-
if hasattr(self, 'activity') and activity is not None:
|
|
195
|
-
instance.activity = activity
|
|
196
|
-
next_nodes = []
|
|
197
|
-
instance.next_nodes = next_nodes
|
|
198
|
-
prev_nodes = []
|
|
199
|
-
instance.prev_nodes = prev_nodes
|
|
200
|
-
expression_inputs = []
|
|
201
|
-
instance.expression_inputs = expression_inputs
|
|
202
|
-
|
|
203
|
-
return instance
|
|
204
|
-
|
|
205
|
-
def gen_name(self):
|
|
206
|
-
if self.name is None:
|
|
207
|
-
self.name = ''.join(random.choices(string.ascii_lowercase, k=8))
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
class TriccNodeActivity(TriccNodeBaseModel):
|
|
211
|
-
tricc_type: TriccNodeType = TriccNodeType.activity
|
|
212
|
-
# starting point of the activity
|
|
213
|
-
root: TriccNodeBaseModel
|
|
214
|
-
# edge list
|
|
215
|
-
edges: List[TriccEdge] = []
|
|
216
|
-
# copy of the edge for later restauration
|
|
217
|
-
unused_edges: List[TriccEdge] = []
|
|
218
|
-
# nodes part of that actvity
|
|
219
|
-
nodes: Dict[str, TriccNodeBaseModel] = {}
|
|
220
|
-
# groups
|
|
221
|
-
groups: Dict[str, TriccGroup] = {}
|
|
222
|
-
# save the instance on the base activity
|
|
223
|
-
instances: Dict[int, TriccNodeBaseModel] = {}
|
|
224
|
-
relevance: Optional[Expression]
|
|
225
|
-
#caclulate that are not part of the any skip logic:
|
|
226
|
-
# - inputs
|
|
227
|
-
# - dandling calculate
|
|
228
|
-
# - case definition
|
|
229
|
-
calculates: List[TriccNodeCalculateBase] = []
|
|
230
|
-
|
|
231
|
-
# redefine
|
|
232
|
-
def make_instance(self, instance_nb, **kwargs):
|
|
233
|
-
# shallow copy
|
|
234
|
-
if instance_nb in self.instances:
|
|
235
|
-
return self.instances[instance_nb]
|
|
236
|
-
else:
|
|
237
|
-
instance = super().make_instance(instance_nb, activity=None)
|
|
238
|
-
self.instances[instance_nb] = instance
|
|
239
|
-
# instance.base_instance = self
|
|
240
|
-
# we duplicate all the related nodes (not the calculate, duplication is manage in calculate version code)
|
|
241
|
-
nodes = {}
|
|
242
|
-
instance.nodes = nodes
|
|
243
|
-
edges = []
|
|
244
|
-
instance.edges = edges
|
|
245
|
-
unused_edges = []
|
|
246
|
-
instance.edges = unused_edges
|
|
247
|
-
calculates = []
|
|
248
|
-
instance.calculates = calculates
|
|
249
|
-
relevance = None
|
|
250
|
-
instance.relevance = relevance
|
|
251
|
-
groups = {}
|
|
252
|
-
instance.groups = groups
|
|
253
|
-
instance.group = instance
|
|
254
|
-
instance.activity = instance
|
|
255
|
-
for edge in self.edges:
|
|
256
|
-
instance.edges.append(edge.make_instance(instance_nb, activity=instance))
|
|
257
|
-
instance.update_nodes(self.root)
|
|
258
|
-
# we walk throught the nodes and replace them when ready
|
|
259
|
-
for node in list(filter(lambda p_node: isinstance(p_node, (TriccNodeDisplayBridge,TriccNodeBridge)),list(self.nodes.values()) )):
|
|
260
|
-
instance.update_nodes(node)
|
|
261
|
-
for node in list(filter(lambda p_node: p_node != self.root and not isinstance(p_node, (TriccNodeDisplayBridge,TriccNodeBridge)),list(self.nodes.values()) )):
|
|
262
|
-
instance_node = instance.update_nodes(node)
|
|
263
|
-
if node in self.calculates and instance_node:
|
|
264
|
-
instance.calulates.append(instance_node)
|
|
265
|
-
|
|
266
|
-
for group in self.groups:
|
|
267
|
-
instance.update_groups(group)
|
|
268
|
-
# update parent group
|
|
269
|
-
for group in self.groups:
|
|
270
|
-
instance.update_groups_group(group)
|
|
271
|
-
|
|
272
|
-
return instance
|
|
273
|
-
|
|
274
|
-
def update_groups_group(self, group):
|
|
275
|
-
for instance_group in self.groups:
|
|
276
|
-
if instance_group.group == group:
|
|
277
|
-
instance_group.group == instance_group
|
|
278
|
-
elif instance_group.group == self.base_instance:
|
|
279
|
-
instance_group.group == self
|
|
280
|
-
|
|
281
|
-
def update_groups(self, group):
|
|
282
|
-
# create new group
|
|
283
|
-
instance_group = group.make_instance(self.instance, activity=self)
|
|
284
|
-
# update the group in all activity
|
|
285
|
-
for node in list(self.nodes.values()):
|
|
286
|
-
if node.group == group:
|
|
287
|
-
node.group == instance_group
|
|
288
|
-
self.groups[instance_group.id] = instance_group
|
|
289
|
-
|
|
290
|
-
def update_nodes(self, node_origin):
|
|
291
|
-
updated_edges = 0
|
|
292
|
-
node_instance = None
|
|
293
|
-
if not isinstance(node_origin, TriccNodeSelectOption):
|
|
294
|
-
# do not perpetuate the instance number in the underlying activities
|
|
295
|
-
if isinstance(node_origin, TriccNodeActivity):
|
|
296
|
-
node_instance = node_origin.make_instance(node_origin.instance if node_origin.instance<100 else 0 , activity=self)
|
|
297
|
-
else:
|
|
298
|
-
node_instance = node_origin.make_instance(self.instance, activity=self)
|
|
299
|
-
self.nodes[node_instance.id] = node_instance
|
|
300
|
-
if isinstance(node_instance, (TriccNodeActivityEnd, TriccNodeEnd)):
|
|
301
|
-
node_instance.set_name()
|
|
302
|
-
# update root
|
|
303
|
-
if isinstance(node_origin, TriccNodeActivityStart) and node_origin == node_origin.activity.root:
|
|
304
|
-
self.root = node_instance
|
|
305
|
-
if issubclass(node_instance.__class__, TriccRhombusMixIn):
|
|
306
|
-
old_path = node_origin.path
|
|
307
|
-
if old_path is not None:
|
|
308
|
-
for n in node_instance.activity.nodes.values():
|
|
309
|
-
if n.base_instance.id == old_path.id:
|
|
310
|
-
node_instance.path = n
|
|
311
|
-
if node_instance.path is None:
|
|
312
|
-
logger.error("new path not found")
|
|
313
|
-
elif not (len(node_instance.reference)== 1 and issubclass(node_instance.reference[0].__class__, TriccNodeInputModel)):
|
|
314
|
-
logger.warning("Rhombus without a path")
|
|
315
|
-
|
|
316
|
-
# generate options
|
|
317
|
-
elif issubclass(node_instance.__class__, TriccNodeSelect):
|
|
318
|
-
for key, option_instance in node_instance.options.items():
|
|
319
|
-
updated_edges += self.update_edges(node_origin.options[key], option_instance)
|
|
320
|
-
updated_edges += self.update_edges(node_origin, node_instance)
|
|
321
|
-
if updated_edges == 0:
|
|
322
|
-
node_edge = list(filter(lambda x: (x.source == node_instance.id or x.source == node_instance) , node_instance.activity.edges))
|
|
323
|
-
node_edge_origin = list(filter(lambda x: (x.source == node_origin.id or x.source == node_origin) , node_origin.activity.edges))
|
|
324
|
-
if len(node_edge) == 0:
|
|
325
|
-
logger.error("no edge was updated for node {}::{}::{}::{}".format(node_instance.activity.get_name(),
|
|
326
|
-
node_instance.__class__,
|
|
327
|
-
node_instance.get_name(),
|
|
328
|
-
node_instance.instance))
|
|
329
|
-
return node_instance
|
|
330
|
-
|
|
331
|
-
def update_edges(self, node_origin, node_instance):
|
|
332
|
-
updates = 0
|
|
333
|
-
|
|
334
|
-
for edge in self.edges:
|
|
335
|
-
if edge.source == node_origin.id or edge.source == node_origin:
|
|
336
|
-
edge.source = node_instance.id
|
|
337
|
-
updates += 1
|
|
338
|
-
if edge.target == node_origin.id or edge.target == node_origin:
|
|
339
|
-
edge.target = node_instance.id
|
|
340
|
-
updates += 1
|
|
341
|
-
return updates
|
|
342
|
-
|
|
343
|
-
def get_end_nodes(self):
|
|
344
|
-
return list(filter(lambda x: issubclass(x.__class__, (TriccNodeEnd,TriccNodeActivityEnd)), self.nodes.values()))
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
class TriccNodeDisplayModel(TriccNodeBaseModel):
|
|
348
|
-
name: str
|
|
349
|
-
image: Optional[b64]
|
|
350
|
-
hint: Optional[Union[str, Dict[str,str]]]
|
|
351
|
-
help: Optional[Union[str, Dict[str,str]]]
|
|
352
|
-
group: Optional[Union[TriccGroup, TriccNodeActivity]]
|
|
353
|
-
relevance: Optional[Expression]
|
|
354
|
-
|
|
355
|
-
def make_instance(self, instance_nb, activity=None):
|
|
356
|
-
instance = super().make_instance(instance_nb, activity=activity)
|
|
357
|
-
instance.relevance = None
|
|
358
|
-
return instance
|
|
359
|
-
|
|
360
|
-
# to use the enum value of the TriccNodeType
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
class TriccNodeNote(TriccNodeDisplayModel):
|
|
364
|
-
tricc_type: TriccNodeType = TriccNodeType.note
|
|
365
|
-
|
|
366
|
-
class TriccNodeInputModel(TriccNodeDisplayModel):
|
|
367
|
-
required: Optional[Expression]
|
|
368
|
-
constraint_message: Optional[Union[str, Dict[str,str]]]
|
|
369
|
-
constraint: Optional[Expression]
|
|
370
|
-
save: Optional[str] # contribute to another calculate
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
class TriccNodeDate(TriccNodeInputModel):
|
|
374
|
-
tricc_type: TriccNodeType = TriccNodeType.date
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
class TriccNodeMainStart(TriccNodeBaseModel):
|
|
378
|
-
tricc_type: TriccNodeType = TriccNodeType.start
|
|
379
|
-
form_id: Optional[str]
|
|
380
|
-
process: Optional[str]
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
class TriccNodeLinkIn(TriccNodeBaseModel):
|
|
384
|
-
tricc_type: TriccNodeType = TriccNodeType.link_in
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
class TriccNodeLinkOut(TriccNodeBaseModel):
|
|
388
|
-
tricc_type: TriccNodeType = TriccNodeType.link_out
|
|
389
|
-
reference: Optional[Union[TriccNodeLinkIn, triccId]]
|
|
390
|
-
# no need to copy
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
class TriccNodeGoTo(TriccNodeBaseModel):
|
|
394
|
-
tricc_type: TriccNodeType = TriccNodeType.goto
|
|
395
|
-
link: Union[TriccNodeActivity, triccId]
|
|
396
|
-
|
|
397
|
-
# no need ot copy
|
|
398
|
-
def make_instance(self, instance_nb, activity, **kwargs):
|
|
399
|
-
# shallow copy
|
|
400
|
-
instance = super().make_instance(instance_nb, activity=activity)
|
|
401
|
-
# do not use activity instance for goto
|
|
402
|
-
instance.instance = self.instance
|
|
403
|
-
return instance
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
class TriccNodeSelectOption(TriccNodeDisplayModel):
|
|
407
|
-
tricc_type: TriccNodeType = TriccNodeType.select_option
|
|
408
|
-
label: Union[str, Dict[str,str]]
|
|
409
|
-
save: Optional[str]
|
|
410
|
-
select: TriccNodeInputModel
|
|
411
|
-
list_name: str
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
def make_instance(self, instance_nb, activity, select, **kwargs):
|
|
415
|
-
# shallow copy
|
|
416
|
-
instance = super().make_instance(instance_nb, activity=activity)
|
|
417
|
-
instance.select = select
|
|
418
|
-
return instance
|
|
419
|
-
|
|
420
|
-
def get_name(self):
|
|
421
|
-
name = super().get_name()
|
|
422
|
-
select_name = self.select.get_name()
|
|
423
|
-
return select_name + "::" + name
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
class TriccNodeSelect(TriccNodeInputModel):
|
|
427
|
-
filter: Optional[str]
|
|
428
|
-
options: Dict[int, TriccNodeSelectOption] = {}
|
|
429
|
-
list_name: str
|
|
430
|
-
|
|
431
|
-
def make_instance(self, instance_nb, activity, **kwargs):
|
|
432
|
-
# shallow copy, no copy of filter and list_name
|
|
433
|
-
instance = super().make_instance(instance_nb, activity=activity)
|
|
434
|
-
instance.options = {}
|
|
435
|
-
for key, option in self.options.items():
|
|
436
|
-
instance.options[key] = option.make_instance(instance_nb, activity=activity, select=instance)
|
|
437
|
-
return instance
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
class TriccNodeSelectOne(TriccNodeSelect):
|
|
441
|
-
tricc_type: TriccNodeType = TriccNodeType.select_one
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
class TriccNodeSelectYesNo(TriccNodeSelectOne):
|
|
445
|
-
pass
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
# options: List[TriccNodeSelectOption] = [TriccNodeSelectOption(label='Yes', name='yes'),
|
|
449
|
-
# TriccNodeSelectOption(label='No', name='no')]
|
|
450
|
-
class TriccNodeSelectNotAvailable(TriccNodeSelectOne):
|
|
451
|
-
pass
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
class TriccNodeSelectMultiple(TriccNodeSelect):
|
|
455
|
-
tricc_type: TriccNodeType = TriccNodeType.select_multiple
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
class TriccNodeNumber(TriccNodeInputModel):
|
|
459
|
-
min: Optional[float]
|
|
460
|
-
max: Optional[float]
|
|
461
|
-
# no need to copy min max in make isntance
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
class TriccNodeDecimal(TriccNodeNumber):
|
|
465
|
-
tricc_type: TriccNodeType = TriccNodeType.decimal
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
class TriccNodeInteger(TriccNodeNumber):
|
|
469
|
-
tricc_type: TriccNodeType = TriccNodeType.integer
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
class TriccNodeText(TriccNodeInputModel):
|
|
473
|
-
tricc_type: TriccNodeType = TriccNodeType.text
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
class TriccNodeCalculateBase(TriccNodeBaseModel):
|
|
477
|
-
input: Dict[TriccOperation, TriccNodeBaseModel] = {}
|
|
478
|
-
reference: Optional[Union[List[TriccNodeBaseModel], Expression]]
|
|
479
|
-
expression_reference: Optional[str]
|
|
480
|
-
version: int = 1
|
|
481
|
-
last: bool = True
|
|
482
|
-
|
|
483
|
-
# to use the enum value of the TriccNodeType
|
|
484
|
-
class Config:
|
|
485
|
-
use_enum_values = True # <--
|
|
486
|
-
|
|
487
|
-
def make_instance(self, instance_nb, activity, **kwargs):
|
|
488
|
-
# shallow copy
|
|
489
|
-
instance = super().make_instance(instance_nb, activity=activity)
|
|
490
|
-
input = {}
|
|
491
|
-
instance.input = input
|
|
492
|
-
expression = self.expression.copy() if self.expression is not None else None
|
|
493
|
-
instance.expression = expression
|
|
494
|
-
version = 1
|
|
495
|
-
instance.version = version
|
|
496
|
-
return instance
|
|
497
|
-
|
|
498
|
-
def __init__(self, **data):
|
|
499
|
-
super().__init__(**data)
|
|
500
|
-
self.gen_name()
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
class TriccNodeDisplayCalculateBase(TriccNodeCalculateBase):
|
|
504
|
-
save: Optional[str] # contribute to another calculate
|
|
505
|
-
hint: Optional[str] # for diagnostic display
|
|
506
|
-
help: Optional[str] # for diagnostic display
|
|
507
|
-
# no need to copy save
|
|
508
|
-
def to_fake(self):
|
|
509
|
-
data = vars(self)
|
|
510
|
-
del data['hint']
|
|
511
|
-
del data['help']
|
|
512
|
-
del data['save']
|
|
513
|
-
fake = TriccNodeFakeCalculateBase(**data)
|
|
514
|
-
replace_node(self,fake)
|
|
515
|
-
return fake
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
# qualculate that saves quantity, or we may merge integer/decimals
|
|
519
|
-
class TriccNodeQuantity(TriccNodeDisplayCalculateBase):
|
|
520
|
-
tricc_type: TriccNodeType = TriccNodeType.quantity
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
class TriccNodeCalculate(TriccNodeDisplayCalculateBase):
|
|
524
|
-
tricc_type: TriccNodeType = TriccNodeType.calculate
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
class TriccNodeAdd(TriccNodeDisplayCalculateBase):
|
|
528
|
-
tricc_type: TriccNodeType = TriccNodeType.add
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
class TriccNodeCount(TriccNodeDisplayCalculateBase):
|
|
532
|
-
tricc_type: TriccNodeType = TriccNodeType.count
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
class TriccNodeFakeCalculateBase(TriccNodeCalculateBase):
|
|
536
|
-
pass
|
|
537
|
-
|
|
538
|
-
class TriccNodeDisplayBridge(TriccNodeDisplayCalculateBase):
|
|
539
|
-
tricc_type: TriccNodeType = TriccNodeType.bridge
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
class TriccNodeBridge(TriccNodeFakeCalculateBase):
|
|
543
|
-
tricc_type: TriccNodeType = TriccNodeType.bridge
|
|
544
|
-
|
|
545
|
-
class TriccRhombusMixIn():
|
|
546
|
-
|
|
547
|
-
def make_mixin_instance(self, instance, instance_nb, activity, **kwargs):
|
|
548
|
-
# shallow copy
|
|
549
|
-
reference = []
|
|
550
|
-
instance.path = None
|
|
551
|
-
if isinstance(self.reference, str):
|
|
552
|
-
reference = self.reference
|
|
553
|
-
elif isinstance(self.reference, list):
|
|
554
|
-
for ref in self.reference:
|
|
555
|
-
if issubclass(ref.__class__, TriccBaseModel):
|
|
556
|
-
pass
|
|
557
|
-
# get the reference
|
|
558
|
-
if self.activity == ref.activity:
|
|
559
|
-
for sub_node in activity.nodes.values():
|
|
560
|
-
if sub_node.base_instance == ref:
|
|
561
|
-
reference.append(sub_node)
|
|
562
|
-
else: # ref from outside
|
|
563
|
-
# FIXME find the latest version
|
|
564
|
-
reference.append(ref)
|
|
565
|
-
elif isinstance(ref, str):
|
|
566
|
-
logger.debug("passing raw reference {} on node {}".format(ref, self.get_name()))
|
|
567
|
-
reference.append(ref)
|
|
568
|
-
else:
|
|
569
|
-
logger.error("unexpected reference in node node {}".format(ref, self.get_name()))
|
|
570
|
-
exit()
|
|
571
|
-
instance.reference = reference
|
|
572
|
-
instance.name = get_rand_name(8)
|
|
573
|
-
return instance
|
|
574
|
-
|
|
575
|
-
class TriccNodeRhombus(TriccNodeCalculateBase,TriccRhombusMixIn):
|
|
576
|
-
tricc_type: TriccNodeType = TriccNodeType.rhombus
|
|
577
|
-
path: Optional[TriccNodeBaseModel] = None
|
|
578
|
-
reference: Union[List[TriccNodeBaseModel], Expression]
|
|
579
|
-
|
|
580
|
-
def make_instance(self, instance_nb, activity, **kwargs):
|
|
581
|
-
instance = super(TriccNodeRhombus, self).make_instance(instance_nb, activity, **kwargs)
|
|
582
|
-
instance = self.make_mixin_instance(instance, instance_nb, activity, **kwargs)
|
|
583
|
-
return instance
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
def __init__(self, **data):
|
|
587
|
-
super().__init__(**data)
|
|
588
|
-
# rename rhombus
|
|
589
|
-
self.name = get_rand_name(8)
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
def get_rand_name(k):
|
|
593
|
-
return "r_" + ''.join(random.choices(string.ascii_lowercase, k=k))
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
class TriccNodeExclusive(TriccNodeFakeCalculateBase):
|
|
597
|
-
tricc_type: TriccNodeType = TriccNodeType.exclusive
|
|
598
|
-
|
|
599
|
-
def get_node_from_id(activity, node, edge_only):
|
|
600
|
-
node_id = getattr(node,'id',node)
|
|
601
|
-
if not isinstance(node_id, str):
|
|
602
|
-
logger.error("can set prev_next only with string or node")
|
|
603
|
-
exit()
|
|
604
|
-
if issubclass(node.__class__, TriccBaseModel):
|
|
605
|
-
return node_id, node
|
|
606
|
-
elif node_id in activity.nodes:
|
|
607
|
-
node = activity.nodes[node_id]
|
|
608
|
-
elif not edge_only:
|
|
609
|
-
logger.error(f"cannot find {node_id} in {activiy.get_name()}")
|
|
610
|
-
exit()
|
|
611
|
-
return node_id, node
|
|
612
|
-
|
|
613
|
-
# Set the source next node to target and clean next nodes of replace node
|
|
614
|
-
def set_prev_next_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
|
|
615
|
-
activity = activity or source_node.activity
|
|
616
|
-
source_id, source_node = get_node_from_id(activity, source_node, edge_only)
|
|
617
|
-
target_id, target_node = get_node_from_id(activity, target_node, edge_only)
|
|
618
|
-
# if it is end node, attached it to the activity/page
|
|
619
|
-
if not edge_only:
|
|
620
|
-
set_prev_node(source_node, target_node, replaced_node, edge_only)
|
|
621
|
-
set_next_node(source_node, target_node, replaced_node,edge_only)
|
|
622
|
-
|
|
623
|
-
if not any([(e.source == source_id) and ( e.target == target_id) for e in activity.edges]):
|
|
624
|
-
activity.edges.append(TriccEdge(id = generate_id(), source = source_id, target = target_id))
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
def set_next_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
|
|
630
|
-
activity = activity or source_node.activity
|
|
631
|
-
if not edge_only:
|
|
632
|
-
if replaced_node is not None and hasattr(source_node, 'path') and replaced_node == source_node.path:
|
|
633
|
-
source_node.path = target_node
|
|
634
|
-
if replaced_node is not None and hasattr(source_node, 'next_nodes') and replaced_node in source_node.next_nodes:
|
|
635
|
-
source_node.next_nodes.remove(replaced_node)
|
|
636
|
-
if replaced_node is not None and hasattr(target_node, 'next_nodes') and replaced_node in target_node.next_nodes:
|
|
637
|
-
target_node.next_nodes.remove(replaced_node)
|
|
638
|
-
if target_node not in source_node.next_nodes:
|
|
639
|
-
source_node.next_nodes.append(target_node)
|
|
640
|
-
# if rhombus in next_node of prev node and next node as ref
|
|
641
|
-
if replaced_node is not None:
|
|
642
|
-
rhombus_list = list(filter(lambda x: issubclass(x.__class__, TriccRhombusMixIn), source_node.next_nodes))
|
|
643
|
-
for rhm in rhombus_list:
|
|
644
|
-
if isinstance(rhm.reference, list):
|
|
645
|
-
if replaced_node in rhm.reference:
|
|
646
|
-
rhm.reference.remove(replaced_node)
|
|
647
|
-
rhm.reference.append(target_node)
|
|
648
|
-
next_edges = [ e for e in activity.edges if replaced_node and (e.target == replaced_node.id or e.target == replaced_node)]
|
|
649
|
-
if len(next_edges)==0:
|
|
650
|
-
for e in next_edges:
|
|
651
|
-
e.target = target_node.id
|
|
652
|
-
|
|
653
|
-
# Set the target_node prev node to source and clean prev nodes of replace_node
|
|
654
|
-
def set_prev_node(source_node, target_node, replaced_node=None, edge_only = False, activity=None):
|
|
655
|
-
activity = activity or source_node.activity
|
|
656
|
-
# update the prev node of the target not if not an end node
|
|
657
|
-
# update directly the prev node of the target
|
|
658
|
-
if replaced_node is not None and hasattr(target_node, 'path') and replaced_node == target_node.path:
|
|
659
|
-
target_node.path = source_node
|
|
660
|
-
if replaced_node is not None and hasattr(target_node, 'prev_nodes') and replaced_node in target_node.prev_nodes:
|
|
661
|
-
target_node.prev_nodes.remove(replaced_node)
|
|
662
|
-
if replaced_node is not None and hasattr(source_node, 'prev_nodes') and replaced_node in source_node.prev_nodes:
|
|
663
|
-
source_node.prev_nodes.remove(replaced_node)
|
|
664
|
-
if source_node not in target_node.prev_nodes:
|
|
665
|
-
target_node.prev_nodes.append(source_node)
|
|
666
|
-
|
|
667
|
-
prev_edges = [ e for e in activity.edges if replaced_node and (e.source == replaced_node.id or e.source == replaced_node)]
|
|
668
|
-
if len(prev_edges)==0:
|
|
669
|
-
for e in prev_edges:
|
|
670
|
-
e.source = source_node.id
|
|
671
|
-
|
|
672
|
-
def replace_node(old, new, page = None):
|
|
673
|
-
if page is None:
|
|
674
|
-
page = old.activity
|
|
675
|
-
logger.debug("replacing node {} with node {} from page {}".format(old.get_name(), new.get_name(), page.get_name()))
|
|
676
|
-
# list_node used to avoid updating a list in the loop
|
|
677
|
-
list_nodes = []
|
|
678
|
-
for prev_node in old.prev_nodes:
|
|
679
|
-
list_nodes.append(prev_node)
|
|
680
|
-
for prev_node in list_nodes:
|
|
681
|
-
set_prev_next_node(prev_node, new, old)
|
|
682
|
-
old.prev_nodes = []
|
|
683
|
-
list_nodes = []
|
|
684
|
-
for next_node in old.next_nodes:
|
|
685
|
-
list_nodes.append(next_node)
|
|
686
|
-
for next_node in list_nodes:
|
|
687
|
-
set_prev_next_node(new, next_node, old)
|
|
688
|
-
old.next_nodes = []
|
|
689
|
-
if old in page.nodes:
|
|
690
|
-
del page.nodes[old.id]
|
|
691
|
-
page.nodes[new.id] = new
|
|
692
|
-
|
|
693
|
-
for edge in page.edges:
|
|
694
|
-
if edge.source == old.id:
|
|
695
|
-
edge.source = new.id
|
|
696
|
-
if edge.target == old.id:
|
|
697
|
-
edge.target = new.id
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
def reorder_node_list(list_node, group):
|
|
701
|
-
if len(list_node)>1:
|
|
702
|
-
list_out = []
|
|
703
|
-
list_out_group = []
|
|
704
|
-
list_out_other = []
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
for l_node in list_node:
|
|
708
|
-
group_id = l_node.group.id if hasattr(l_node, 'group') and l_node.group is not None else None
|
|
709
|
-
if group is not None and group.id == group_id:
|
|
710
|
-
list_out.append(l_node)
|
|
711
|
-
elif hasattr(group, 'group') and group.group is not None and group.group.id == group_id:
|
|
712
|
-
list_out_group.append(l_node)
|
|
713
|
-
else:
|
|
714
|
-
list_out_other.append(l_node)
|
|
715
|
-
|
|
716
|
-
list_node = []
|
|
717
|
-
if len(list_out)>0:
|
|
718
|
-
list_node.extend(list_out)
|
|
719
|
-
if len(list_out_group)>0:
|
|
720
|
-
list_node.extend(list_out_group)
|
|
721
|
-
if len(list_out_other)>0:
|
|
722
|
-
list_node.extend(list_out_other)
|
|
723
|
-
|
|
724
|
-
logger.debug("reorder list init len: {}, group : {} group.group: {} other: {}".format(len(list_node), len(list_out), len(list_out_group), len(list_out_other)))
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
# walkthough all node in an iterative way, the same node might be parsed 2 times
|
|
730
|
-
# therefore to avoid double processing the nodes variable saves the node already processed
|
|
731
|
-
# there 2 strategies : process it the first time or the last time (wait that all the previuous node are processed)
|
|
732
|
-
|
|
733
|
-
def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, stashed_nodes, path_len, recursive=True, warn = False,
|
|
734
|
-
**kwargs):
|
|
735
|
-
# logger.debug("walkthrough::{}::{}".format(callback.__name__, node.get_name()))
|
|
736
|
-
if hasattr(node, 'prev_nodes') and len(node.prev_nodes) > 0:
|
|
737
|
-
path_len = max(path_len, *[n.path_len + 1 for n in node.prev_nodes], len(processed_nodes)+1)
|
|
738
|
-
node.path_len = max(node.path_len, path_len)
|
|
739
|
-
if (callback(node, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, warn = warn,**kwargs)):
|
|
740
|
-
# node processing succeed
|
|
741
|
-
if node not in processed_nodes:
|
|
742
|
-
processed_nodes.append(node)
|
|
743
|
-
logger.debug("{}::{}: processed ({})".format(callback.__name__, node.get_name(), len(processed_nodes)))
|
|
744
|
-
if node in stashed_nodes:
|
|
745
|
-
stashed_nodes.remove(node)
|
|
746
|
-
# logger.debug("{}::{}: unstashed ({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
|
|
747
|
-
# put the stached node from that group first
|
|
748
|
-
# if has next, walkthrough them (support options)
|
|
749
|
-
# if len(stashed_nodes)>1:
|
|
750
|
-
if not recursive:
|
|
751
|
-
reorder_node_list(stashed_nodes, node.group)
|
|
752
|
-
# logger.debug("{}::{}: reorder stashed ({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
|
|
753
|
-
if isinstance(node, TriccNodeActivity):
|
|
754
|
-
if node.root is not None:
|
|
755
|
-
node.root.path_len = max(path_len, node.root.path_len)
|
|
756
|
-
if recursive:
|
|
757
|
-
walktrhough_tricc_node_processed_stached(node.root, callback, processed_nodes, stashed_nodes, path_len,
|
|
758
|
-
recursive, warn = warn,**kwargs)
|
|
759
|
-
for gp in node.groups:
|
|
760
|
-
walktrhough_tricc_node_processed_stached(gp, callback, processed_nodes, stashed_nodes, path_len,
|
|
761
|
-
recursive, warn = warn,**kwargs)
|
|
762
|
-
if node.calculates:
|
|
763
|
-
for c in node.calculates:
|
|
764
|
-
walktrhough_tricc_node_processed_stached(c, callback, processed_nodes, stashed_nodes, path_len,
|
|
765
|
-
recursive, warn = warn,**kwargs)
|
|
766
|
-
elif node.root not in stashed_nodes:
|
|
767
|
-
#stashed_nodes.insert(0,node.root)
|
|
768
|
-
stashed_nodes.append(node.root)
|
|
769
|
-
if node.calculates:
|
|
770
|
-
stashed_nodes += node.calculates
|
|
771
|
-
for gp in node.groups:
|
|
772
|
-
stashed_nodes.append(gp)
|
|
773
|
-
# stashed_nodes.insert(0,gp)
|
|
774
|
-
return
|
|
775
|
-
elif isinstance(node, TriccNodeActivityEnd):
|
|
776
|
-
for next_node in node.activity.next_nodes:
|
|
777
|
-
if next_node not in stashed_nodes:
|
|
778
|
-
#stashed_nodes.insert(0,next_node)
|
|
779
|
-
stashed_nodes.append(next_node)
|
|
780
|
-
elif issubclass(node.__class__, TriccNodeSelect):
|
|
781
|
-
for option in node.options.values():
|
|
782
|
-
option.path_len = max(path_len, option.path_len)
|
|
783
|
-
callback(option, processed_nodes=processed_nodes, stashed_nodes=stashed_nodes, **kwargs)
|
|
784
|
-
if option not in processed_nodes:
|
|
785
|
-
processed_nodes.append(option)
|
|
786
|
-
logger.debug(
|
|
787
|
-
"{}::{}: processed ({})".format(callback.__name__, option.get_name(), len(processed_nodes)))
|
|
788
|
-
walkthrough_tricc_option(node, callback, processed_nodes, stashed_nodes, path_len + 1, recursive,
|
|
789
|
-
warn = warn, **kwargs)
|
|
790
|
-
if hasattr(node, 'next_nodes') and len(node.next_nodes) > 0:
|
|
791
|
-
walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes, path_len + 1, recursive,
|
|
792
|
-
warn = warn,**kwargs)
|
|
793
|
-
else:
|
|
794
|
-
if node not in processed_nodes and node not in stashed_nodes:
|
|
795
|
-
if node not in stashed_nodes:
|
|
796
|
-
stashed_nodes.insert(0,node)
|
|
797
|
-
logger.debug("{}::{}: stashed({})".format(callback.__name__, node.get_name(), len(stashed_nodes)))
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
def walkthrough_tricc_next_nodes(node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn = False, **kwargs):
|
|
801
|
-
if not recursive:
|
|
802
|
-
for next_node in node.next_nodes:
|
|
803
|
-
if next_node not in stashed_nodes:
|
|
804
|
-
stashed_nodes.append(next_node)
|
|
805
|
-
else:
|
|
806
|
-
list_next = []
|
|
807
|
-
while not all(elem in list_next for elem in node.next_nodes):
|
|
808
|
-
for next_node in node.next_nodes:
|
|
809
|
-
if next_node not in list_next:
|
|
810
|
-
list_next.append(next_node)
|
|
811
|
-
if not isinstance(node, (TriccNodeActivityEnd, TriccNodeEnd)):
|
|
812
|
-
walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes, stashed_nodes,
|
|
813
|
-
path_len + 1,recursive, warn = warn, **kwargs)
|
|
814
|
-
else:
|
|
815
|
-
logger.error(
|
|
816
|
-
"{}::end node of {} has a next node".format(callback.__name__.node.activity.get_name()))
|
|
817
|
-
exit()
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
def walkthrough_tricc_option(node, callback, processed_nodes, stashed_nodes, path_len, recursive, warn = False, **kwargs):
|
|
821
|
-
if not recursive:
|
|
822
|
-
for option in node.options.values():
|
|
823
|
-
if hasattr(option, 'next_nodes') and len(option.next_nodes) > 0:
|
|
824
|
-
for next_node in option.next_nodes:
|
|
825
|
-
if next_node not in stashed_nodes:
|
|
826
|
-
stashed_nodes.append(next_node)
|
|
827
|
-
#stashed_nodes.insert(0,next_node)
|
|
828
|
-
else:
|
|
829
|
-
list_option = []
|
|
830
|
-
while not all(elem in list_option for elem in list(node.options.values())):
|
|
831
|
-
for option in node.options.values():
|
|
832
|
-
if option not in list_option:
|
|
833
|
-
list_option.append(option)
|
|
834
|
-
# then walk the options
|
|
835
|
-
if hasattr(option, 'next_nodes') and len(option.next_nodes) > 0:
|
|
836
|
-
list_next = []
|
|
837
|
-
while not all(elem in list_next for elem in option.next_nodes):
|
|
838
|
-
for next_node in option.next_nodes:
|
|
839
|
-
if next_node not in list_next:
|
|
840
|
-
list_next.append(next_node)
|
|
841
|
-
walktrhough_tricc_node_processed_stached(next_node, callback, processed_nodes,
|
|
842
|
-
stashed_nodes, path_len + 1, recursive,
|
|
843
|
-
warn = warn,**kwargs)
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
def get_data_for_log(node):
|
|
847
|
-
return "{}:{}|{} {}:{}".format(
|
|
848
|
-
node.group.get_name() if node.group is not None else node.activity.get_name(),
|
|
849
|
-
node.group.instance if node.group is not None else node.activityinstance ,
|
|
850
|
-
node.__class__,
|
|
851
|
-
node.get_name(),
|
|
852
|
-
node.instance)
|
|
853
|
-
|
|
854
|
-
def stashed_node_func(node, callback, recusive=False, **kwargs):
|
|
855
|
-
processed_nodes = kwargs.get('processed_nodes', [])
|
|
856
|
-
stashed_nodes = kwargs.get('stashed_nodes', [])
|
|
857
|
-
path_len = 0
|
|
858
|
-
walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, stashed_nodes, path_len, recusive,
|
|
859
|
-
**kwargs)
|
|
860
|
-
# callback( node, **kwargs)
|
|
861
|
-
## MANAGE STASHED NODES
|
|
862
|
-
prev_stashed_nodes = stashed_nodes.copy()
|
|
863
|
-
loop_count = 0
|
|
864
|
-
len_prev_processed_nodes = 0
|
|
865
|
-
while len(stashed_nodes) > 0:
|
|
866
|
-
loop_count = check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes,
|
|
867
|
-
loop_count)
|
|
868
|
-
prev_stashed_nodes = stashed_nodes.copy()
|
|
869
|
-
len_prev_processed_nodes = len(processed_nodes)
|
|
870
|
-
if len(stashed_nodes) > 0:
|
|
871
|
-
s_node = stashed_nodes.pop()
|
|
872
|
-
# remove duplicates
|
|
873
|
-
if s_node in stashed_nodes:
|
|
874
|
-
stashed_nodes.remove(s_node)
|
|
875
|
-
logger.debug("{}:: {}: unstashed for processing ({})".format(callback.__name__, s_node.__class__,
|
|
876
|
-
get_data_for_log(s_node),
|
|
877
|
-
len(stashed_nodes)))
|
|
878
|
-
warn = loop_count == (10 * len(stashed_nodes )-1)
|
|
879
|
-
walktrhough_tricc_node_processed_stached(s_node, callback, processed_nodes, stashed_nodes, path_len,
|
|
880
|
-
recusive, warn= warn, **kwargs)
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
# check if the all the prev nodes are processed
|
|
884
|
-
def is_ready_to_process(in_node, processed_nodes, strict=True, local = False):
|
|
885
|
-
if isinstance(in_node, TriccNodeSelectOption):
|
|
886
|
-
node = in_node.select
|
|
887
|
-
elif isinstance(in_node, TriccNodeActivityStart):
|
|
888
|
-
if local:
|
|
889
|
-
# an activitiy start iss always processable locally
|
|
890
|
-
return True
|
|
891
|
-
node = in_node.activity
|
|
892
|
-
else:
|
|
893
|
-
node = in_node
|
|
894
|
-
if hasattr(node, 'prev_nodes'):
|
|
895
|
-
# ensure the previous node of the select are processed, not the option prev nodes
|
|
896
|
-
for prev_node in node.prev_nodes:
|
|
897
|
-
if isinstance(prev_node, TriccNodeActivity):
|
|
898
|
-
if not local:
|
|
899
|
-
# other activity dont affect local evaluation
|
|
900
|
-
activity_end_nodes = prev_node.get_end_nodes()
|
|
901
|
-
if len(activity_end_nodes) == 0:
|
|
902
|
-
|
|
903
|
-
logger.error("is_ready_to_process:failed: endless activity {0} before {0}".format(prev_node.get_name(),
|
|
904
|
-
node.get_name()))
|
|
905
|
-
return False
|
|
906
|
-
for end_node in activity_end_nodes:
|
|
907
|
-
if end_node not in processed_nodes:
|
|
908
|
-
logger.debug("is_ready_to_process:failed:via_end: {} - {} > {} {}:{}".format(
|
|
909
|
-
get_data_for_log(prev_node),
|
|
910
|
-
end_node.get_name(),
|
|
911
|
-
node.__class__, node.get_name(), node.instance))
|
|
912
|
-
return False
|
|
913
|
-
elif prev_node not in processed_nodes and (not local or prev_node.activity == node.activity):
|
|
914
|
-
if isinstance(prev_node, TriccNodeExclusive):
|
|
915
|
-
logger.debug("is_ready_to_process:failed:via_excl: {} - {} > {} {}:{}".format(
|
|
916
|
-
get_data_for_log(prev_node.prev_nodes[0]),
|
|
917
|
-
prev_node.get_name(),
|
|
918
|
-
node.__class__, node.get_name(), node.instance))
|
|
919
|
-
|
|
920
|
-
else:
|
|
921
|
-
logger.debug("is_ready_to_process:failed: {} -> {} {}:{}".format(
|
|
922
|
-
get_data_for_log(prev_node),
|
|
923
|
-
node.__class__, node.get_name(), node.instance))
|
|
924
|
-
|
|
925
|
-
logger.debug("prev node node {}:{} for node {} not in processed".format(prev_node.__class__,
|
|
926
|
-
prev_node.get_name(),
|
|
927
|
-
node.get_name()))
|
|
928
|
-
return False
|
|
929
|
-
if strict:
|
|
930
|
-
return is_rhombus_ready_to_process(node, processed_nodes, local)
|
|
931
|
-
else:
|
|
932
|
-
return True
|
|
933
|
-
else:
|
|
934
|
-
return True
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
def print_trace(node, prev_node, processed_nodes, stashed_nodes, history = []):
|
|
938
|
-
|
|
939
|
-
if node != prev_node:
|
|
940
|
-
if node in processed_nodes:
|
|
941
|
-
logger.warning("print trace :: node {} was the last not processed ({})".format(
|
|
942
|
-
get_data_for_log(prev_node), node.id, ">".join(history)))
|
|
943
|
-
processed_nodes.append(prev_node)
|
|
944
|
-
return False
|
|
945
|
-
elif node in history:
|
|
946
|
-
logger.error("print trace :: CYCLE node {} found in history ({})".format(
|
|
947
|
-
get_data_for_log(prev_node), ">".join(history)))
|
|
948
|
-
exit()
|
|
949
|
-
elif node in stashed_nodes:
|
|
950
|
-
# logger.debug("print trace :: node {}::{} in stashed".format(node.__class__,node.get_name()))
|
|
951
|
-
return False
|
|
952
|
-
# else:
|
|
953
|
-
# logger.debug("print trace :: node {} not processed/stashed".format(node.get_name()))
|
|
954
|
-
return True
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
def reverse_walkthrough(in_node, next_node, callback, processed_nodes, stashed_nodes, history = []):
|
|
958
|
-
# transform dead-end nodes
|
|
959
|
-
if next_node == in_node and next_node not in stashed_nodes:
|
|
960
|
-
# workaround fir loop
|
|
961
|
-
return False
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
if isinstance(in_node, TriccNodeSelectOption):
|
|
965
|
-
node = in_node.select
|
|
966
|
-
elif isinstance(in_node, TriccNodeActivityStart):
|
|
967
|
-
node = in_node.activity
|
|
968
|
-
else:
|
|
969
|
-
node = in_node
|
|
970
|
-
if callback(node, next_node, processed_nodes, stashed_nodes):
|
|
971
|
-
history.append(node)
|
|
972
|
-
if isinstance(in_node, TriccNodeActivity):
|
|
973
|
-
prev_nodes = in_node.get_end_nodes()
|
|
974
|
-
for prev in prev_nodes:
|
|
975
|
-
reverse_walkthrough(prev, next_node, callback, processed_nodes, stashed_nodes, history)
|
|
976
|
-
if hasattr(node, 'prev_nodes'):
|
|
977
|
-
if node.prev_nodes:
|
|
978
|
-
for prev in node.prev_nodes:
|
|
979
|
-
reverse_walkthrough(prev, node, callback, processed_nodes, stashed_nodes, history)
|
|
980
|
-
elif node in node.activity.calculates:
|
|
981
|
-
reverse_walkthrough(prev, node.activity.root, callback, processed_nodes, stashed_nodes, history)
|
|
982
|
-
|
|
983
|
-
if issubclass(node.__class__, TriccRhombusMixIn):
|
|
984
|
-
if isinstance(node.reference, list):
|
|
985
|
-
for ref in node.reference:
|
|
986
|
-
reverse_walkthrough(ref, node, callback, processed_nodes, stashed_nodes, history)
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
def is_rhombus_ready_to_process(node, processed_nodes, local = False):
|
|
990
|
-
if issubclass(node.__class__, TriccRhombusMixIn):
|
|
991
|
-
if isinstance(node.reference, str):
|
|
992
|
-
return False # calculate not yet processed
|
|
993
|
-
elif isinstance(node.reference, list):
|
|
994
|
-
for ref in node.reference:
|
|
995
|
-
if issubclass(ref.__class__, TriccNodeBaseModel) and ref not in processed_nodes and (not local or ref.activity == node.activity):
|
|
996
|
-
return False
|
|
997
|
-
elif issubclass(ref.__class__, str):
|
|
998
|
-
logger.debug("Node {1} as still a reference to string")
|
|
999
|
-
return True
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
def get_prev_node_by_name(processed_nodes, name, node):
|
|
1003
|
-
filtered = list(filter(lambda p_node: hasattr(p_node,'name') and p_node.name == name and p_node.instance == node.instance and p_node.path_len <= node.path_len, processed_nodes))
|
|
1004
|
-
if len(filtered) == 0:
|
|
1005
|
-
filtered = list(filter(lambda p_node: hasattr(p_node, 'name') and p_node.name == name, processed_nodes))
|
|
1006
|
-
if len(filtered) > 0:
|
|
1007
|
-
return sorted(filtered, key=lambda x: x.path_len, reverse=False)[0]
|
|
1008
|
-
|
|
1009
|
-
MIN_LOOP_COUNT = 10
|
|
1010
|
-
|
|
1011
|
-
def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_prev_processed_nodes, loop_count):
|
|
1012
|
-
loop_out = {}
|
|
1013
|
-
|
|
1014
|
-
if len(stashed_nodes) == len(prev_stashed_nodes):
|
|
1015
|
-
# to avoid checking the details
|
|
1016
|
-
if loop_count<=0:
|
|
1017
|
-
if loop_count < -MIN_LOOP_COUNT:
|
|
1018
|
-
loop_count = MIN_LOOP_COUNT+1
|
|
1019
|
-
else:
|
|
1020
|
-
loop_count -= 1
|
|
1021
|
-
if loop_count>MIN_LOOP_COUNT:
|
|
1022
|
-
# copy to sort
|
|
1023
|
-
cur_stashed_nodes = sorted(stashed_nodes, key=lambda x: x.id, reverse=True)
|
|
1024
|
-
|
|
1025
|
-
prev_stashed_nodes = sorted(prev_stashed_nodes, key=lambda x: x.id, reverse=True)
|
|
1026
|
-
|
|
1027
|
-
if cur_stashed_nodes == prev_stashed_nodes and len(processed_nodes) == len_prev_processed_nodes:
|
|
1028
|
-
loop_count += 1
|
|
1029
|
-
if loop_count > max(MIN_LOOP_COUNT, 10 * len(prev_stashed_nodes) + 1):
|
|
1030
|
-
logger.error("Stashed node list was unchanged: loop likely or a cyclic redundancy")
|
|
1031
|
-
for es_node in cur_stashed_nodes:
|
|
1032
|
-
logger.error("Stashed node {}:{}|{} {}:{}".format(
|
|
1033
|
-
es_node.group.get_name() if es_node.group is not None else es_node.activity.get_name() ,
|
|
1034
|
-
es_node.group.instance if es_node.group is not None else es_node.activityinstance ,
|
|
1035
|
-
es_node.__class__,
|
|
1036
|
-
es_node.get_name(), es_node.instance))
|
|
1037
|
-
#reverse_walkthrough(es_node, es_node, print_trace, processed_nodes, stashed_nodes)
|
|
1038
|
-
if len(stashed_nodes) == len(prev_stashed_nodes):
|
|
1039
|
-
exit()
|
|
1040
|
-
#else:
|
|
1041
|
-
# loop_count += 1
|
|
1042
|
-
else:
|
|
1043
|
-
loop_count = 0
|
|
1044
|
-
return loop_count
|
|
1045
|
-
|
|
1046
|
-
class TriccNodeWait(TriccNodeFakeCalculateBase, TriccRhombusMixIn):
|
|
1047
|
-
tricc_type: TriccNodeType = TriccNodeType.wait
|
|
1048
|
-
path: Optional[TriccNodeBaseModel] = None
|
|
1049
|
-
reference: Union[List[TriccNodeBaseModel], Expression]
|
|
1050
|
-
|
|
1051
|
-
def make_instance(self, instance_nb, activity, **kwargs):
|
|
1052
|
-
instance = super(TriccNodeWait, self).make_instance(instance_nb, activity, **kwargs)
|
|
1053
|
-
instance = self.make_mixin_instance(instance, instance_nb, activity, **kwargs)
|
|
1054
|
-
return instance
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
class TriccNodeActivityEnd(TriccNodeFakeCalculateBase):
|
|
1058
|
-
tricc_type: TriccNodeType = TriccNodeType.activity_end
|
|
1059
|
-
|
|
1060
|
-
def __init__(self, **data):
|
|
1061
|
-
super().__init__(**data)
|
|
1062
|
-
# FOR END
|
|
1063
|
-
self.set_name()
|
|
1064
|
-
|
|
1065
|
-
def set_name(self):
|
|
1066
|
-
self.name = ACTIVITY_END_NODE_FORMAT.format(self.activity.id)
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
class TriccNodeEnd(TriccNodeCalculate):
|
|
1070
|
-
tricc_type: TriccNodeType = TriccNodeType.end
|
|
1071
|
-
|
|
1072
|
-
def __init__(self, **data):
|
|
1073
|
-
super().__init__(**data)
|
|
1074
|
-
# FOR END
|
|
1075
|
-
self.set_name()
|
|
1076
|
-
|
|
1077
|
-
def set_name(self):
|
|
1078
|
-
self.name = END_NODE_FORMAT.format(self.activity.id)
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
class TriccNodeActivityStart(TriccNodeFakeCalculateBase):
|
|
1082
|
-
tricc_type: TriccNodeType = TriccNodeType.activity_start
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
def get_node_from_list(in_nodes, node_id):
|
|
1086
|
-
nodes = list(filter(lambda x: x.id == node_id, in_nodes))
|
|
1087
|
-
if len(nodes) > 0:
|
|
1088
|
-
return nodes[0]
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
# update FF
|
|
1092
|
-
TriccEdge.update_forward_refs()
|
|
1093
|
-
TriccNodeBridge.update_forward_refs()
|