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