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.
Files changed (66) hide show
  1. tests/build.py +213 -0
  2. tests/test_cql.py +197 -0
  3. tests/to_ocl.py +51 -0
  4. {tricc → tricc_oo}/__init__.py +3 -1
  5. tricc_oo/converters/codesystem_to_ocl.py +169 -0
  6. tricc_oo/converters/cql/cqlLexer.py +822 -0
  7. tricc_oo/converters/cql/cqlListener.py +1632 -0
  8. tricc_oo/converters/cql/cqlParser.py +11204 -0
  9. tricc_oo/converters/cql/cqlVisitor.py +913 -0
  10. tricc_oo/converters/cql_to_operation.py +402 -0
  11. tricc_oo/converters/datadictionnary.py +115 -0
  12. tricc_oo/converters/drawio_type_map.py +222 -0
  13. tricc_oo/converters/tricc_to_xls_form.py +61 -0
  14. tricc_oo/converters/utils.py +65 -0
  15. tricc_oo/converters/xml_to_tricc.py +1003 -0
  16. tricc_oo/models/__init__.py +4 -0
  17. tricc_oo/models/base.py +732 -0
  18. tricc_oo/models/calculate.py +216 -0
  19. tricc_oo/models/ocl.py +281 -0
  20. tricc_oo/models/ordered_set.py +125 -0
  21. tricc_oo/models/tricc.py +418 -0
  22. tricc_oo/parsers/xml.py +138 -0
  23. tricc_oo/serializers/__init__.py +0 -0
  24. tricc_oo/serializers/xls_form.py +745 -0
  25. tricc_oo/strategies/__init__.py +0 -0
  26. tricc_oo/strategies/input/__init__.py +0 -0
  27. tricc_oo/strategies/input/base_input_strategy.py +111 -0
  28. tricc_oo/strategies/input/drawio.py +317 -0
  29. tricc_oo/strategies/output/base_output_strategy.py +148 -0
  30. tricc_oo/strategies/output/spice.py +365 -0
  31. tricc_oo/strategies/output/xls_form.py +697 -0
  32. tricc_oo/strategies/output/xlsform_cdss.py +189 -0
  33. tricc_oo/strategies/output/xlsform_cht.py +200 -0
  34. tricc_oo/strategies/output/xlsform_cht_hf.py +334 -0
  35. tricc_oo/visitors/__init__.py +0 -0
  36. tricc_oo/visitors/tricc.py +2198 -0
  37. tricc_oo/visitors/utils.py +17 -0
  38. tricc_oo/visitors/xform_pd.py +264 -0
  39. tricc_oo-1.4.15.dist-info/METADATA +219 -0
  40. tricc_oo-1.4.15.dist-info/RECORD +46 -0
  41. {tricc_oo-1.0.1.dist-info → tricc_oo-1.4.15.dist-info}/WHEEL +1 -1
  42. tricc_oo-1.4.15.dist-info/top_level.txt +2 -0
  43. tricc/converters/mc_to_tricc.py +0 -542
  44. tricc/converters/tricc_to_xls_form.py +0 -553
  45. tricc/converters/utils.py +0 -44
  46. tricc/converters/xml_to_tricc.py +0 -740
  47. tricc/models/tricc.py +0 -1093
  48. tricc/parsers/xml.py +0 -81
  49. tricc/serializers/xls_form.py +0 -364
  50. tricc/strategies/input/base_input_strategy.py +0 -80
  51. tricc/strategies/input/drawio.py +0 -246
  52. tricc/strategies/input/medalcreator.py +0 -168
  53. tricc/strategies/output/base_output_strategy.py +0 -92
  54. tricc/strategies/output/xls_form.py +0 -194
  55. tricc/strategies/output/xlsform_cdss.py +0 -46
  56. tricc/strategies/output/xlsform_cht.py +0 -106
  57. tricc/visitors/tricc.py +0 -375
  58. tricc_oo-1.0.1.dist-info/LICENSE +0 -78
  59. tricc_oo-1.0.1.dist-info/METADATA +0 -229
  60. tricc_oo-1.0.1.dist-info/RECORD +0 -26
  61. tricc_oo-1.0.1.dist-info/top_level.txt +0 -2
  62. venv/bin/vba_extract.py +0 -78
  63. {tricc → tricc_oo}/converters/__init__.py +0 -0
  64. {tricc → tricc_oo}/models/lang.py +0 -0
  65. {tricc/serializers → tricc_oo/parsers}/__init__.py +0 -0
  66. {tricc → tricc_oo}/serializers/planuml.py +0 -0
@@ -0,0 +1,732 @@
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, Set, Annotated
8
+
9
+ from pydantic import BaseModel, StringConstraints
10
+ from strenum import StrEnum
11
+
12
+ from tricc_oo.converters.utils import generate_id, get_rand_name
13
+ from tricc_oo.models.ordered_set import OrderedSet
14
+
15
+ logger = logging.getLogger("default")
16
+
17
+ Expression = Annotated[
18
+ str,
19
+ StringConstraints(pattern=r'^[^\\/\:]+$')
20
+ ]
21
+
22
+ triccId = Annotated[
23
+ str,
24
+ StringConstraints(pattern=r'^[^\\/\: ]+$')
25
+ ]
26
+
27
+ triccName = Annotated[
28
+ str,
29
+ StringConstraints(pattern=r'^[^\s]+( [^\s]+)*$')
30
+ ]
31
+
32
+ b64 = Annotated[
33
+ str,
34
+ StringConstraints(pattern=r'^[^-A-Za-z0-9+/=]|=[^=]|={3,}$')
35
+ ]
36
+
37
+
38
+ TriccEdge = ForwardRef('TriccEdge')
39
+ # data:page/id,UkO_xCL5ZjyshJO9Bexg
40
+
41
+
42
+ ACTIVITY_END_NODE_FORMAT = "aend_{}"
43
+ END_NODE_FORMAT = "end_{}"
44
+
45
+
46
+ class TriccNodeType(StrEnum):
47
+ #replace with auto ?
48
+ note = 'note'
49
+ calculate = 'calculate',
50
+ output = 'output',
51
+ select_multiple = 'select_multiple'
52
+ select_one = 'select_one'
53
+ select_yesno = 'select_one yesno'
54
+ select_option = 'select_option'
55
+ decimal = 'decimal'
56
+ integer = 'integer'
57
+ text = 'text'
58
+ date = 'date'
59
+ rhombus = 'rhombus' # fetch data
60
+ goto = 'goto' #: start the linked activity within the target activity
61
+ start = 'start' #: main start of the algo
62
+ activity_start = 'activity_start' #: start of an activity (link in)
63
+ link_in = 'link_in'
64
+ link_out = 'link_out'
65
+ count = 'count' #: count the number of valid input
66
+ add = 'add' # add counts
67
+ container_hint_media = 'container_hint_media' # DEPRECATED
68
+ activity = 'activity'
69
+ help = 'help-message'
70
+ hint = 'hint-message'
71
+ exclusive = 'not'
72
+ end = 'end'
73
+ activity_end = 'activity_end'
74
+ edge = 'edge'
75
+ page = 'container_page'
76
+ not_available = 'not_available'
77
+ quantity = 'quantity'
78
+ bridge = 'bridge'
79
+ wait = 'wait'
80
+ operation = 'operation'
81
+ context = 'context'
82
+ diagnosis = 'diagnosis'
83
+ proposed_diagnosis = 'proposed_diagnosis'
84
+ input = 'input'
85
+
86
+ def __iter__(self):
87
+ return iter(self.__members__.values())
88
+
89
+ def __next__(self):
90
+ return next(iter(self))
91
+
92
+
93
+
94
+ media_nodes = [
95
+ TriccNodeType.note,
96
+ TriccNodeType.select_multiple,
97
+ TriccNodeType.select_one,
98
+ TriccNodeType.decimal,
99
+ TriccNodeType.integer,
100
+ TriccNodeType.text,
101
+ ]
102
+
103
+
104
+ class TriccBaseModel(BaseModel):
105
+ id: triccId
106
+ external_id: triccId = None
107
+ tricc_type: TriccNodeType
108
+ datatype: str = None
109
+ instance: int = 1
110
+ base_instance: Optional[TriccBaseModel] = None
111
+ last: bool = None
112
+ version: int = 1
113
+ def get_datatype(self):
114
+ return self.datatype or self.tricc_type
115
+
116
+ def to_dict(self):
117
+ return {key: value for key, value in vars(self).items() if not key.startswith('_')}
118
+
119
+ def make_instance(self, nb_instance, **kwargs):
120
+ instance = self.copy()
121
+ # change the id to avoid collision of name
122
+ instance.id = generate_id(f"{self.id}{nb_instance}")
123
+ instance.instance = int(nb_instance)
124
+ instance.base_instance = self
125
+ attr_dict = self.to_dict()
126
+ for attr, value in attr_dict.items():
127
+ if not attr.startswith('_') and value is not None:
128
+ try:
129
+ if hasattr(value, 'copy'):
130
+ setattr(instance, attr, value.copy())
131
+ else:
132
+ setattr(instance, attr, value)
133
+ except Exception as e:
134
+ logger.warning(f"Warning: Could not copy attribute {attr}: {e}")
135
+ # assign the defualt group
136
+ # if activity is not None and self.group == activity.base_instance:
137
+ # instance.group = instance
138
+ return instance
139
+
140
+ def __eq__(self, other):
141
+ if isinstance(other, self.__class__):
142
+ return self.id == other.id
143
+ else:
144
+ return False
145
+
146
+ def __ne__(self, other):
147
+ return not self.__eq__(other)
148
+
149
+ def __hash__(self):
150
+ hash_value = hash(self.id)
151
+ return hash_value
152
+
153
+ def get_name(self):
154
+ return self.id
155
+
156
+ def __str__(self):
157
+ return self.get_name()
158
+
159
+ def __repr__(self):
160
+ return f"{self.tricc_type}:{self.get_name()}({self.id})"
161
+
162
+ def __init__(self, **data):
163
+ if 'id' not in data:
164
+ data['id'] = generate_id(str(data))
165
+ super().__init__(**data)
166
+
167
+
168
+ class TriccEdge(TriccBaseModel):
169
+ tricc_type: TriccNodeType = TriccNodeType.edge
170
+ source: Union[triccId, TriccNodeBaseModel]
171
+ source_external_id: triccId = None
172
+ target: Union[triccId, TriccNodeBaseModel]
173
+ target_external_id: triccId = None
174
+ value: Optional[str] = None
175
+
176
+
177
+
178
+ class TriccGroup(TriccBaseModel):
179
+ tricc_type: TriccNodeType = TriccNodeType.page
180
+ group: Optional[TriccBaseModel] = None
181
+ name: Optional[str] = None
182
+ export_name:Optional[str] = None
183
+ label: Optional[Union[str, Dict[str,str]]] = None
184
+ relevance: Optional[Union[Expression, TriccOperation]] = None
185
+ path_len: int = 0
186
+ prev_nodes: OrderedSet[TriccBaseModel] = OrderedSet()
187
+ def __init__(self, **data):
188
+ super().__init__(**data)
189
+ if self.name is None:
190
+ self.name = generate_id(str(data))
191
+
192
+ def get_name(self):
193
+ result = str(super().get_name())
194
+ name = getattr(self, 'name', None)
195
+ label = getattr(self, 'label', None)
196
+
197
+ if name:
198
+ result = result + "::" + name
199
+ if label:
200
+ result = result + "::" + (
201
+ next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label
202
+ )
203
+ if len(name) < 50:
204
+ return result
205
+ else:
206
+ return result[:50]
207
+
208
+ FwTriccNodeBaseModel = ForwardRef('TriccNodeBaseModel')
209
+
210
+
211
+ class TriccNodeBaseModel(TriccBaseModel):
212
+ path_len: int = 0
213
+ group: Optional[Union[TriccGroup, FwTriccNodeBaseModel]] = None
214
+ name: Optional[str] = None
215
+ export_name: Optional[str] = None
216
+ label: Optional[Union[str, Dict[str,str]]] = None
217
+ next_nodes: OrderedSet[TriccNodeBaseModel] = OrderedSet()
218
+ prev_nodes: OrderedSet[TriccNodeBaseModel] = OrderedSet()
219
+ expression: Optional[Union[Expression, TriccOperation]] = None # will be generated based on the input
220
+ expression_inputs: List[Expression] = []
221
+ activity: Optional[FwTriccNodeBaseModel] = None
222
+ ref_def: Optional[Union[int,str]] = None# for medal creator
223
+
224
+ class Config:
225
+ use_enum_values = True # <--
226
+
227
+ def __hash__(self):
228
+ return hash(self.id )
229
+
230
+ # to be updated while processing because final expression will be possible to build$
231
+ # #only the last time the script will go through the node (all prev node expression would be created
232
+ def get_name(self):
233
+ result = str(super().get_name())
234
+ name = getattr(self, 'name', None)
235
+ label = getattr(self, 'label', None)
236
+
237
+ if name:
238
+ result = result + "::" + name
239
+ if label:
240
+ result = result + "::" + (
241
+ next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label
242
+ )
243
+ if len(result) < 50:
244
+ return result
245
+ else:
246
+ return result[:50]
247
+
248
+
249
+
250
+ def make_instance(self, instance_nb, activity=None):
251
+ instance = super().make_instance(instance_nb)
252
+ instance.group = activity
253
+ if hasattr(self, 'activity') and activity is not None:
254
+ instance.activity = activity
255
+ next_nodes = OrderedSet()
256
+ instance.next_nodes = next_nodes
257
+ prev_nodes = OrderedSet()
258
+ instance.prev_nodes = prev_nodes
259
+ expression_inputs = []
260
+ instance.expression_inputs = expression_inputs
261
+
262
+ for attr in ['expression', 'relevance', 'default', 'reference', 'expression_reference']:
263
+ if getattr(self, attr, None):
264
+ setattr(instance, attr, getattr(self, attr).copy())
265
+
266
+ return instance
267
+
268
+ def gen_name(self):
269
+ if self.name is None:
270
+ self.name = get_rand_name(self.id)
271
+ def get_references(self):
272
+ return OrderedSet([self])
273
+
274
+ class TriccStatic(BaseModel):
275
+ value: Union[str, float, int, bool]
276
+ def __init__(self,value):
277
+ super().__init__(value = value)
278
+
279
+ def get_datatype(self):
280
+ if str(type(self.value)) == 'bool':
281
+ return 'boolean'
282
+ elif str(self.value).isnumeric():
283
+ return 'number'
284
+ else:
285
+ return str(type(self.value))
286
+ def __eq__(self, other):
287
+ if isinstance(other, self.__class__):
288
+ return self.value == other.value
289
+ else:
290
+ return False
291
+
292
+ def __ne__(self, other):
293
+ return not self.__eq__(other)
294
+
295
+ def __hash__(self):
296
+ hash_value = hash(self.value)
297
+ return hash_value
298
+ def get_name(self):
299
+ return self.value
300
+
301
+ def __str__(self):
302
+ return str(self.value)
303
+
304
+ def __repr__(self):
305
+ return "TriccStatic:"+str(type(self.value))+':' +str(self.value)
306
+
307
+ def get_references(self):
308
+ return OrderedSet()
309
+
310
+ class TriccReference(TriccStatic):
311
+ value: str
312
+ def __copy__(self):
313
+ return type(self)(self.value)
314
+
315
+ def copy(self):
316
+ return self.__copy__()
317
+
318
+ def get_references(self):
319
+ return OrderedSet([self])
320
+
321
+
322
+ class TriccOperator(StrEnum):
323
+ AND = 'and' # and between left and rights
324
+ ADD_OR = 'and_or' # left and one of the righs
325
+ OR = 'or' # or between left and rights
326
+ NATIVE = 'native' #default left is native expression
327
+ ISTRUE = 'istrue' # left is right
328
+ ISNOTTRUE = 'isnottrue'
329
+ ISFALSE = 'isfalse' # left is false
330
+ ISNOTFALSE = 'isnotfalse' # left is false
331
+ SELECTED = 'selected' # right must be la select and one or several options
332
+ MORE_OR_EQUAL = 'more_or_equal'
333
+ LESS_OR_EQUAL = 'less_or_equal'
334
+ EQUAL = 'equal'
335
+ MORE = 'more'
336
+ NOTEQUAL = 'not_equal'
337
+ BETWEEN = 'between'
338
+ LESS = 'less'
339
+ CONTAINS = 'contains' # ref, txt Does CONTAINS make sense, like Select with wildcard
340
+ NOTEXISTS = 'notexists'
341
+ EXISTS = 'exists'
342
+ NOT = 'not'
343
+ ISNULL = 'isnull'
344
+ ISNOTNULL= 'isnotnull'
345
+
346
+ CASE = 'case' # ref (equal value, res), (equal value,res)
347
+ IFS = 'ifs' #(cond, res), (cond,res)
348
+ IF = 'if' # cond val_true, val_false
349
+
350
+ # CDSS Specific
351
+ HAS_QUALIFIER = 'has_qualifier'
352
+
353
+ ZSCORE = 'zscore' # left table_name, right Y, gender give Z
354
+ IZSCORE = 'izscore' #left table_name, right Z, gender give Y
355
+ AGE_DAY = 'age_day' # age from dob
356
+ AGE_MONTH = 'age_month' # age from dob
357
+ AGE_YEAR = 'age_year' # age from dob
358
+ DIVIDED = 'divided'
359
+ MULTIPLIED = 'multiplied'
360
+ PLUS = 'plus'
361
+ MINUS = 'minus'
362
+ MODULO = 'modulo'
363
+ COUNT = 'count'
364
+ CAST_NUMBER = 'cast_number'
365
+ CAST_INTEGER = 'cast_integer'
366
+ DRUG_DOSAGE = 'drug_dosage' # drug name, *param1 (ex: weight, age)
367
+ COALESCE = 'coalesce'
368
+ CAST_DATE = 'cast_date'
369
+ PARENTHESIS = 'parenthesis'
370
+ CONCATENATE = 'concatenate'
371
+
372
+ RETURNS_BOOLEAN =[
373
+ TriccOperator.ADD_OR,
374
+ TriccOperator.AND,
375
+ TriccOperator.OR,
376
+ TriccOperator.BETWEEN,
377
+ TriccOperator.CONTAINS,
378
+ TriccOperator.EXISTS,
379
+ TriccOperator.NOTEXISTS,
380
+ TriccOperator.ISFALSE,
381
+ TriccOperator.ISNOTFALSE,
382
+ TriccOperator.ISNOTNULL,
383
+ TriccOperator.ISTRUE,
384
+ TriccOperator.ISNOTTRUE,
385
+ TriccOperator.SELECTED,
386
+ TriccOperator.HAS_QUALIFIER,
387
+ TriccOperator.NOT,
388
+ TriccOperator.NOTEQUAL,
389
+ TriccOperator.MORE_OR_EQUAL,
390
+ TriccOperator.LESS_OR_EQUAL,
391
+ TriccOperator.EQUAL,
392
+ TriccOperator.MORE,
393
+ TriccOperator.LESS
394
+ ]
395
+
396
+ RETURNS_NUMBER = [
397
+ TriccOperator.AGE_DAY,
398
+ TriccOperator.AGE_MONTH,
399
+ TriccOperator.AGE_YEAR,
400
+ TriccOperator.ZSCORE,
401
+ TriccOperator.IZSCORE,
402
+ TriccOperator.PLUS,
403
+ TriccOperator.MINUS,
404
+ TriccOperator.DIVIDED,
405
+ TriccOperator.MULTIPLIED,
406
+ TriccOperator.COUNT,
407
+ TriccOperator.MODULO,
408
+ TriccOperator.CAST_NUMBER,
409
+ TriccOperator.CAST_INTEGER
410
+ ]
411
+
412
+ RETURNS_DATE =[
413
+ TriccOperator.CAST_DATE
414
+ ]
415
+
416
+ OPERATION_LIST = {
417
+ '>=': TriccOperator.MORE_OR_EQUAL,
418
+ '<=': TriccOperator.LESS_OR_EQUAL,
419
+ '==': TriccOperator.EQUAL,
420
+ '!=': TriccOperator.NOTEQUAL,
421
+ '=': TriccOperator.EQUAL,
422
+ '>': TriccOperator.MORE,
423
+ '<': TriccOperator.LESS
424
+ }
425
+
426
+ class TriccOperation(BaseModel):
427
+ tricc_type: TriccNodeType = TriccNodeType.operation
428
+ operator: TriccOperator = TriccOperator.NATIVE
429
+ reference: OrderedSet[
430
+ Union[
431
+ TriccStatic, TriccNodeBaseModel, TriccOperation, TriccReference, Expression,
432
+ List[Union[TriccStatic, TriccNodeBaseModel, TriccOperation, TriccReference, Expression]]
433
+ ]
434
+ ] = []
435
+
436
+ def __str__(self):
437
+ str_ref = map(str, self.reference)
438
+ return f"{self.operator}({', '.join(map(str, str_ref))})"
439
+
440
+ def __hash__(self):
441
+ return hash(self.__repr__())
442
+
443
+ def __repr__(self):
444
+ return "TriccOperation:"+self.__str__()
445
+
446
+ def __eq__(self, other):
447
+ return self.__str__() == str(other)
448
+
449
+ def __init__(self, operator, reference=[]):
450
+ super().__init__(operator=operator, reference=reference)
451
+
452
+ def get_datatype(self):
453
+ if self.operator in RETURNS_BOOLEAN:
454
+ return 'boolean'
455
+ elif self.operator in RETURNS_NUMBER:
456
+ return 'number'
457
+ elif self.operator in RETURNS_DATE:
458
+ return 'date'
459
+ elif self.operator == TriccOperator.CONCATENATE:
460
+ return 'string'
461
+ elif self.operator == TriccOperator.PARENTHESIS:
462
+ return self.get_reference_datatype(self.reference)
463
+ elif self.operator == TriccOperator.IF:
464
+ return self.get_reference_datatype(self.reference[1:])
465
+ elif self.operator in ( TriccOperator.IFS, TriccOperator.CASE):
466
+ rtype = set()
467
+ for rl in self.reference:
468
+ rtype.add(self.get_reference_datatype(self.reference[-2:]))
469
+ if len(rtype)>1:
470
+ return 'mixed'
471
+ else:
472
+ return rtype.pop()
473
+
474
+ def get_reference_datatype(self, references):
475
+ rtype = set()
476
+ for r in references:
477
+ if hasattr(r, 'get_datatype'):
478
+ rtype.add(r.get_datatype())
479
+ elif hasattr(r, 'value'):
480
+ return str(type(r.value))
481
+ else:
482
+ return str(type(r))
483
+
484
+ if len(rtype)>1:
485
+ return 'mixed'
486
+ else:
487
+ return rtype.pop()
488
+
489
+ def get_references(self):
490
+ predecessor = OrderedSet()
491
+ if isinstance(self.reference, list):
492
+ for reference in self.reference:
493
+ self._process_reference(reference, predecessor)
494
+ else:
495
+ raise NotImplementedError("cannot find predecessor of a str")
496
+ return predecessor
497
+
498
+ def _process_reference(self, reference, predecessor):
499
+ if isinstance(reference, list):
500
+ for e in reference:
501
+ self._process_reference(e, predecessor)
502
+ elif isinstance(reference, TriccOperation):
503
+ subs = reference.get_references()
504
+ for s in subs:
505
+ predecessor.add(s)
506
+ elif issubclass(reference.__class__, (TriccNodeBaseModel, TriccReference)):
507
+ predecessor.add(reference)
508
+
509
+ def append(self, value):
510
+ self.reference.append(value)
511
+ def replace_node(self, old_node ,new_node):
512
+ if isinstance(self.reference, list):
513
+ for key in [i for i, x in enumerate(self.reference)]:
514
+ self.reference[key] = self._replace_reference(self.reference[key], new_node, old_node)
515
+ elif self.reference is not None:
516
+ raise NotImplementedError(f"cannot manage {self.reference.__class__}")
517
+
518
+ def _replace_reference(self, reference, new_node, old_node):
519
+ if isinstance(reference, list):
520
+ for key in [i for i, x in enumerate(reference)]:
521
+ reference[key] = self._replace_reference(reference[key], new_node, old_node)
522
+ if isinstance(reference, TriccOperation):
523
+ reference.replace_node(old_node ,new_node)
524
+ elif issubclass(reference.__class__, (TriccNodeBaseModel, TriccReference)) and reference == old_node:
525
+ reference = new_node
526
+ # to cover the options
527
+ if hasattr(reference, 'select') and hasattr(new_node, 'select') and issubclass(reference.select.__class__, TriccNodeBaseModel ) :
528
+ self.replace_node(reference.select ,new_node.select)
529
+ return reference
530
+
531
+ def __copy__(self, keep_node=False):
532
+ # Create a new instance
533
+ if keep_node:
534
+ reference = [e for e in self.reference]
535
+ else:
536
+ reference = [e.copy() if isinstance(e, (TriccReference, TriccOperation)) else (TriccReference(e.name) if hasattr(e, 'name') else e) for e in self.reference]
537
+
538
+
539
+ new_instance = type(self)(
540
+ self.operator,
541
+ reference
542
+ )
543
+ # Copy attributes (shallow copy for mutable attributes)
544
+
545
+ return new_instance
546
+
547
+ def copy(self, keep_node=False):
548
+ return self.__copy__(keep_node)
549
+
550
+ # function that make multipat and
551
+ # @param argv list of expression to join with and
552
+ def clean_and_list(argv):
553
+ for a in list(argv):
554
+ if isinstance(a, TriccOperation) and a.operator == TriccOperator.AND:
555
+ argv.remove(a)
556
+ return clean_and_list([*argv,*a.reference])
557
+ elif a == TriccStatic(True) or a == True:
558
+ argv.remove(a)
559
+ elif a == TriccStatic(False) :
560
+ return [TriccStatic(False)]
561
+
562
+ internal = list(set(argv))
563
+ for a in internal:
564
+ for b in internal[internal.index(a)+1:]:
565
+ if not_clean(b) == a:
566
+ return [TriccStatic(False)]
567
+ return sorted(list(set(argv)), key=str)
568
+
569
+ def not_clean(a):
570
+ new_operator = None
571
+ if a is None or isinstance(a, str) and a == '':
572
+ return TriccStatic(False)
573
+ elif isinstance(a, TriccStatic) and a == TriccStatic(False):
574
+ return TriccStatic(True)
575
+ elif isinstance(a, TriccStatic) and a == TriccStatic(True):
576
+ return TriccStatic(False)
577
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.ISTRUE:
578
+ new_operator = TriccOperator.ISNOTTRUE
579
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.ISNOTTRUE:
580
+ new_operator = TriccOperator.ISTRUE
581
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.ISFALSE:
582
+ new_operator = TriccOperator.ISNOTFALSE
583
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.ISNOTFALSE:
584
+ new_operator = TriccOperator.ISFALSE
585
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.ISNULL:
586
+ new_operator = TriccOperator.ISNOTNULL
587
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.ISNOTNULL:
588
+ new_operator = TriccOperator.ISNULL
589
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.LESS:
590
+ new_operator = TriccOperator.MORE_OR_EQUAL
591
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.MORE:
592
+ new_operator = TriccOperator.LESS_OR_EQUAL
593
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.LESS_OR_EQUAL:
594
+ new_operator = TriccOperator.MORE
595
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.MORE_OR_EQUAL:
596
+ new_operator = TriccOperator.LESS
597
+ elif isinstance(a, TriccOperation) and a.operator == TriccOperator.NOT:
598
+ return a.reference[0]
599
+
600
+ if new_operator:
601
+ return TriccOperation(
602
+ new_operator,
603
+ a.reference
604
+ )
605
+
606
+ elif not isinstance(a, TriccOperation) and issubclass(a.__class__, object):
607
+ return TriccOperation(
608
+ operator=TriccOperator.NOTEXISTS,
609
+ reference=[a]
610
+ )
611
+ else:
612
+ return TriccOperation(
613
+ TriccOperator.NOT,
614
+ [a]
615
+ )
616
+
617
+
618
+
619
+ # function that generate remove unsure condition
620
+ # @param list_or
621
+ # @param and elm use upstream
622
+ def clean_or_list(list_or, elm_and=None):
623
+ for a in list(list_or):
624
+ if isinstance(a, TriccOperation) and a.operator == TriccOperator.OR:
625
+ list_or.remove(a)
626
+ return clean_or_list([*list_or,*a.reference])
627
+ elif a == TriccStatic(False) or a == False or a == 0:
628
+ list_or.remove(a)
629
+ elif a == TriccStatic(True) or a == True or a == 1 or (elm_and is not None and not_clean(a) in list_or):
630
+ return [TriccStatic(True)]
631
+ # if there is x and not(X) in an OR list them the list is always true
632
+ elif elm_and is not None and (not_clean(a) == elm_and or a == elm_and ):
633
+ list_or.remove(a)
634
+ internal = list(list_or)
635
+ for a in internal:
636
+ for b in internal[internal.index(a)+1:]:
637
+ if not_clean(b) == a:
638
+ return [TriccStatic(True)]
639
+ if len(list_or) == 0:
640
+ return []
641
+
642
+ return sorted(list(set(list_or)), key=str)
643
+
644
+ def and_join(argv):
645
+ argv=clean_and_list(argv)
646
+ if len(argv) == 0:
647
+ return ''
648
+ elif len(argv) == 1:
649
+ return argv[0]
650
+ else:
651
+ return TriccOperation(
652
+ TriccOperator.AND,
653
+ argv
654
+ )
655
+
656
+ # function that make a 2 part and
657
+ # @param left part
658
+ # @param right part
659
+ def simple_and_join(left, right):
660
+ expression = None
661
+ # no term is considered as True
662
+ left_issue = left is None or left == ''
663
+ right_issue = right is None or right == ''
664
+ left_neg = not_clean(left)
665
+ right_neg = not_clean(right)
666
+ if left_issue and right_issue:
667
+ logger.critical("and with both terms empty")
668
+ elif left_neg == right or right_neg == left:
669
+ return TriccStatic(False)
670
+ elif left_issue:
671
+ logger.debug('and with empty left term')
672
+ return right
673
+ elif left == '1' or left == 1 or left == TriccStatic(True) or left is True:
674
+ return right
675
+ elif right_issue:
676
+ logger.debug('and with empty right term')
677
+ return left
678
+ elif right == '1' or right == 1 or right == TriccStatic(True) or right is True:
679
+ return left
680
+ else:
681
+ return TriccOperation(
682
+ TriccOperator.AND,
683
+ [left, right]
684
+ )
685
+
686
+ def or_join(list_or, elm_and=None):
687
+ cleaned_list = clean_or_list(list_or, elm_and)
688
+ if len(cleaned_list) == 1:
689
+ return cleaned_list[0]
690
+ elif len(cleaned_list)>1:
691
+ return TriccOperation(
692
+ TriccOperator.OR,
693
+ cleaned_list
694
+ )
695
+ else:
696
+ logger.error("empty or list")
697
+
698
+
699
+
700
+ # function that make a 2 part NAND
701
+ # @param left part
702
+ # @param right part
703
+ def nand_join(left, right):
704
+ # no term is considered as True
705
+ left_issue = left is None or left == ''
706
+ right_issue = right is None or right == ''
707
+ left_neg = left == False or left == 0 or left == '0' or left == TriccStatic(False)
708
+ right_neg = right == False or right == 0 or right == '0' or right == TriccStatic(False)
709
+ if issubclass(left.__class__, TriccNodeBaseModel):
710
+ left = get_export_name(left)
711
+ if issubclass(right.__class__, TriccNodeBaseModel):
712
+ right = get_export_name(right)
713
+ if left_issue and right_issue:
714
+ logger.critical("and with both terms empty")
715
+ elif left_issue:
716
+ logger.debug('and with empty left term')
717
+ return negate_term(right)
718
+ elif left == '1' or left == 1 or left == TriccStatic(True):
719
+ return negate_term(right)
720
+ elif right_issue :
721
+ logger.debug('and with empty right term')
722
+ return TriccStatic(False)
723
+ elif right == '1' or right == 1 or left_neg or right == TriccStatic(True):
724
+ return TriccStatic(False)
725
+ elif right_neg:
726
+ return left
727
+ else:
728
+ return and_join([left, negate_term(right)])
729
+
730
+
731
+ TriccGroup.update_forward_refs()
732
+ TriccEdge.update_forward_refs()