tricc-oo 1.5.13__py3-none-any.whl → 1.6.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tests/build.py +20 -28
- tests/test_build.py +260 -0
- tests/test_cql.py +48 -109
- tests/to_ocl.py +15 -17
- tricc_oo/__init__.py +0 -6
- tricc_oo/converters/codesystem_to_ocl.py +51 -40
- tricc_oo/converters/cql/cqlLexer.py +1 -0
- tricc_oo/converters/cql/cqlListener.py +1 -0
- tricc_oo/converters/cql/cqlParser.py +1 -0
- tricc_oo/converters/cql/cqlVisitor.py +1 -0
- tricc_oo/converters/cql_to_operation.py +129 -123
- tricc_oo/converters/datadictionnary.py +45 -54
- tricc_oo/converters/drawio_type_map.py +146 -65
- tricc_oo/converters/tricc_to_xls_form.py +58 -28
- tricc_oo/converters/utils.py +4 -4
- tricc_oo/converters/xml_to_tricc.py +296 -235
- tricc_oo/models/__init__.py +2 -1
- tricc_oo/models/base.py +333 -305
- tricc_oo/models/calculate.py +66 -51
- tricc_oo/models/lang.py +26 -27
- tricc_oo/models/ocl.py +146 -161
- tricc_oo/models/ordered_set.py +15 -19
- tricc_oo/models/tricc.py +149 -89
- tricc_oo/parsers/xml.py +15 -30
- tricc_oo/serializers/planuml.py +4 -6
- tricc_oo/serializers/xls_form.py +110 -153
- tricc_oo/strategies/input/base_input_strategy.py +28 -32
- tricc_oo/strategies/input/drawio.py +59 -71
- tricc_oo/strategies/output/base_output_strategy.py +151 -65
- tricc_oo/strategies/output/dhis2_form.py +908 -0
- tricc_oo/strategies/output/fhir_form.py +377 -0
- tricc_oo/strategies/output/html_form.py +224 -0
- tricc_oo/strategies/output/openmrs_form.py +694 -0
- tricc_oo/strategies/output/spice.py +106 -127
- tricc_oo/strategies/output/xls_form.py +322 -244
- tricc_oo/strategies/output/xlsform_cdss.py +627 -142
- tricc_oo/strategies/output/xlsform_cht.py +252 -125
- tricc_oo/strategies/output/xlsform_cht_hf.py +13 -24
- tricc_oo/visitors/tricc.py +1424 -1033
- tricc_oo/visitors/utils.py +16 -16
- tricc_oo/visitors/xform_pd.py +91 -89
- {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/METADATA +128 -84
- tricc_oo-1.6.8.dist-info/RECORD +52 -0
- tricc_oo-1.6.8.dist-info/licenses/LICENSE +373 -0
- {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/top_level.txt +0 -0
- tricc_oo-1.5.13.dist-info/RECORD +0 -46
- {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/WHEEL +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
|
|
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
|
-
]
|
|
14
|
+
Expression = Annotated[str, StringConstraints(pattern=r".+")]
|
|
26
15
|
|
|
27
|
-
|
|
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
|
-
]
|
|
16
|
+
triccId = Annotated[str, StringConstraints(pattern=r"^[^\\/\: ]+$")]
|
|
36
17
|
|
|
18
|
+
triccName = Annotated[str, StringConstraints(pattern=r"^[^\s]+( [^\s]+)*$")]
|
|
37
19
|
|
|
38
|
-
|
|
39
|
-
# data:page/id,UkO_xCL5ZjyshJO9Bexg
|
|
20
|
+
b64 = Annotated[str, StringConstraints(pattern=r"^[^-A-Za-z0-9+/=]|=[^=]|={3,}$")]
|
|
40
21
|
|
|
41
|
-
|
|
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 =
|
|
49
|
-
calculate =
|
|
50
|
-
output =
|
|
51
|
-
select_multiple =
|
|
52
|
-
select_one =
|
|
53
|
-
select_yesno =
|
|
54
|
-
select_option =
|
|
55
|
-
decimal =
|
|
56
|
-
integer =
|
|
57
|
-
text =
|
|
58
|
-
date =
|
|
59
|
-
rhombus =
|
|
60
|
-
goto =
|
|
61
|
-
start =
|
|
62
|
-
activity_start =
|
|
63
|
-
link_in =
|
|
64
|
-
link_out =
|
|
65
|
-
count =
|
|
66
|
-
add =
|
|
67
|
-
container_hint_media =
|
|
68
|
-
activity =
|
|
69
|
-
help =
|
|
70
|
-
hint =
|
|
71
|
-
exclusive =
|
|
72
|
-
end =
|
|
73
|
-
activity_end =
|
|
74
|
-
edge =
|
|
75
|
-
page =
|
|
76
|
-
not_available =
|
|
77
|
-
quantity =
|
|
78
|
-
bridge =
|
|
79
|
-
wait =
|
|
80
|
-
operation =
|
|
81
|
-
context =
|
|
82
|
-
diagnosis =
|
|
83
|
-
proposed_diagnosis =
|
|
84
|
-
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,
|
|
97
|
+
if getattr(self, "instances", None):
|
|
118
98
|
return max(100, *[n.instance for n in self.instances.values()]) + 1
|
|
119
|
-
if getattr(self,
|
|
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
|
|
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(
|
|
112
|
+
if not attr.startswith("_") and value is not None:
|
|
133
113
|
try:
|
|
134
|
-
if hasattr(value,
|
|
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,
|
|
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
|
|
178
|
-
data[
|
|
156
|
+
if "id" not in data:
|
|
157
|
+
data["id"] = generate_id(str(data))
|
|
179
158
|
super().__init__(**data)
|
|
180
159
|
|
|
181
160
|
|
|
@@ -185,41 +164,45 @@ 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]
|
|
189
|
-
|
|
167
|
+
value: Optional[str] = None
|
|
190
168
|
|
|
191
169
|
|
|
192
170
|
class TriccGroup(TriccBaseModel):
|
|
193
171
|
tricc_type: TriccNodeType = TriccNodeType.page
|
|
194
172
|
group: Optional[TriccBaseModel] = None
|
|
173
|
+
activity: TriccBaseModel
|
|
195
174
|
name: Optional[str] = None
|
|
196
|
-
export_name:Optional[str] = None
|
|
197
|
-
label: Optional[Union[str, Dict[str,str]]] = None
|
|
175
|
+
export_name: Optional[str] = None
|
|
176
|
+
label: Optional[Union[str, Dict[str, str]]] = None
|
|
198
177
|
relevance: Optional[Union[Expression, TriccOperation]] = None
|
|
199
178
|
path_len: int = 0
|
|
200
179
|
prev_nodes: OrderedSet[TriccBaseModel] = OrderedSet()
|
|
180
|
+
|
|
201
181
|
def __init__(self, **data):
|
|
202
182
|
super().__init__(**data)
|
|
203
183
|
if self.name is None:
|
|
204
184
|
self.name = generate_id(str(data))
|
|
205
185
|
|
|
186
|
+
def gen_name(self):
|
|
187
|
+
if self.name is None:
|
|
188
|
+
self.name = get_rand_name(self.id)
|
|
189
|
+
|
|
206
190
|
def get_name(self):
|
|
207
191
|
result = str(super().get_name())
|
|
208
|
-
name =
|
|
209
|
-
label =
|
|
210
|
-
|
|
192
|
+
name = getattr(self, "name", None)
|
|
193
|
+
label = getattr(self, "label", None)
|
|
194
|
+
|
|
211
195
|
if name:
|
|
212
196
|
result = result + "::" + name
|
|
213
197
|
if label:
|
|
214
|
-
result = result + "::" + (
|
|
215
|
-
next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label
|
|
216
|
-
)
|
|
198
|
+
result = result + "::" + (next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label)
|
|
217
199
|
if len(name) < 50:
|
|
218
200
|
return result
|
|
219
201
|
else:
|
|
220
|
-
return result[:50]
|
|
202
|
+
return result[:50]
|
|
221
203
|
|
|
222
|
-
|
|
204
|
+
|
|
205
|
+
FwTriccNodeBaseModel = ForwardRef("TriccNodeBaseModel")
|
|
223
206
|
|
|
224
207
|
|
|
225
208
|
class TriccNodeBaseModel(TriccBaseModel):
|
|
@@ -227,44 +210,40 @@ class TriccNodeBaseModel(TriccBaseModel):
|
|
|
227
210
|
group: Optional[Union[TriccGroup, FwTriccNodeBaseModel]] = None
|
|
228
211
|
name: Optional[str] = None
|
|
229
212
|
export_name: Optional[str] = None
|
|
230
|
-
label: Optional[Union[str, Dict[str,str]]] = None
|
|
213
|
+
label: Optional[Union[str, Dict[str, str]]] = None
|
|
231
214
|
next_nodes: OrderedSet[TriccNodeBaseModel] = OrderedSet()
|
|
232
215
|
prev_nodes: OrderedSet[TriccNodeBaseModel] = OrderedSet()
|
|
233
216
|
expression: Optional[Union[Expression, TriccOperation, TriccStatic]] = None # will be generated based on the input
|
|
234
217
|
expression_inputs: List[Expression] = []
|
|
235
218
|
activity: Optional[FwTriccNodeBaseModel] = None
|
|
236
|
-
ref_def: Optional[Union[int,str]]
|
|
219
|
+
ref_def: Optional[Union[int, str]] = None # for medal creator
|
|
237
220
|
|
|
238
221
|
class Config:
|
|
239
222
|
use_enum_values = True # <--
|
|
240
|
-
|
|
223
|
+
|
|
241
224
|
def __hash__(self):
|
|
242
|
-
return hash(self.id
|
|
225
|
+
return hash(self.id)
|
|
243
226
|
|
|
244
227
|
# to be updated while processing because final expression will be possible to build$
|
|
245
|
-
# #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
|
|
246
229
|
def get_name(self):
|
|
247
|
-
result = self.__class__.__name__[9:]# str(super().get_name())
|
|
248
|
-
name =
|
|
249
|
-
label =
|
|
250
|
-
|
|
230
|
+
result = self.__class__.__name__[9:] # str(super().get_name())
|
|
231
|
+
name = getattr(self, "name", None)
|
|
232
|
+
label = getattr(self, "label", None)
|
|
233
|
+
|
|
251
234
|
if name:
|
|
252
235
|
result += name
|
|
253
236
|
if label:
|
|
254
|
-
result += "::" + (
|
|
255
|
-
next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label
|
|
256
|
-
)
|
|
237
|
+
result += "::" + (next(iter(self.label.values())) if isinstance(self.label, Dict) else self.label)
|
|
257
238
|
if len(result) < 80:
|
|
258
239
|
return result
|
|
259
240
|
else:
|
|
260
|
-
return result[:80]
|
|
261
|
-
|
|
262
|
-
|
|
241
|
+
return result[:80]
|
|
263
242
|
|
|
264
243
|
def make_instance(self, instance_nb=None, activity=None):
|
|
265
244
|
instance = super().make_instance(instance_nb)
|
|
266
245
|
instance.group = activity
|
|
267
|
-
if hasattr(self,
|
|
246
|
+
if hasattr(self, "activity") and activity is not None:
|
|
268
247
|
instance.activity = activity
|
|
269
248
|
next_nodes = OrderedSet()
|
|
270
249
|
instance.next_nodes = next_nodes
|
|
@@ -272,31 +251,42 @@ class TriccNodeBaseModel(TriccBaseModel):
|
|
|
272
251
|
instance.prev_nodes = prev_nodes
|
|
273
252
|
expression_inputs = []
|
|
274
253
|
instance.expression_inputs = expression_inputs
|
|
275
|
-
|
|
276
|
-
for attr in [
|
|
254
|
+
|
|
255
|
+
for attr in [
|
|
256
|
+
"expression",
|
|
257
|
+
"relevance",
|
|
258
|
+
"default",
|
|
259
|
+
"reference",
|
|
260
|
+
"remote_reference",
|
|
261
|
+
"expression_reference",
|
|
262
|
+
]:
|
|
277
263
|
if getattr(self, attr, None):
|
|
278
|
-
setattr(instance, attr,
|
|
279
|
-
|
|
264
|
+
setattr(instance, attr, getattr(self, attr))
|
|
265
|
+
|
|
280
266
|
return instance
|
|
281
267
|
|
|
282
268
|
def gen_name(self):
|
|
283
269
|
if self.name is None:
|
|
284
270
|
self.name = get_rand_name(self.id)
|
|
271
|
+
|
|
285
272
|
def get_references(self):
|
|
286
273
|
return OrderedSet()
|
|
287
274
|
|
|
275
|
+
|
|
288
276
|
class TriccStatic(BaseModel):
|
|
289
|
-
value: Union[str, float, int, bool]
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
277
|
+
value: Union[str, float, int, bool, TriccNodeBaseModel]
|
|
278
|
+
|
|
279
|
+
def __init__(self, value):
|
|
280
|
+
super().__init__(value=value)
|
|
281
|
+
|
|
293
282
|
def get_datatype(self):
|
|
294
|
-
if str(type(self.value)) ==
|
|
295
|
-
return
|
|
296
|
-
elif
|
|
297
|
-
return
|
|
283
|
+
if str(type(self.value)) == "bool":
|
|
284
|
+
return "boolean"
|
|
285
|
+
elif str(self.value).isnumeric():
|
|
286
|
+
return "number"
|
|
298
287
|
else:
|
|
299
288
|
return str(type(self.value))
|
|
289
|
+
|
|
300
290
|
def __eq__(self, other):
|
|
301
291
|
if isinstance(other, self.__class__):
|
|
302
292
|
return self.value == other.value
|
|
@@ -309,20 +299,23 @@ class TriccStatic(BaseModel):
|
|
|
309
299
|
def __hash__(self):
|
|
310
300
|
hash_value = hash(self.value)
|
|
311
301
|
return hash_value
|
|
302
|
+
|
|
312
303
|
def get_name(self):
|
|
313
304
|
return self.value
|
|
314
|
-
|
|
305
|
+
|
|
315
306
|
def __str__(self):
|
|
316
307
|
return str(self.value)
|
|
317
|
-
|
|
308
|
+
|
|
318
309
|
def __repr__(self):
|
|
319
|
-
return self.__class__.__name__+":"+str(type(self.value))+
|
|
310
|
+
return self.__class__.__name__ + ":" + str(type(self.value)) + ":" + str(self.value)
|
|
320
311
|
|
|
321
312
|
def get_references(self):
|
|
322
313
|
return OrderedSet()
|
|
323
314
|
|
|
315
|
+
|
|
324
316
|
class TriccReference(TriccStatic):
|
|
325
317
|
value: str
|
|
318
|
+
|
|
326
319
|
def __copy__(self):
|
|
327
320
|
return type(self)(self.value)
|
|
328
321
|
|
|
@@ -333,57 +326,60 @@ class TriccReference(TriccStatic):
|
|
|
333
326
|
return OrderedSet([self])
|
|
334
327
|
|
|
335
328
|
|
|
336
|
-
class TriccOperator(StrEnum):
|
|
337
|
-
AND =
|
|
338
|
-
ADD_OR =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
+
|
|
364
359
|
# CDSS Specific
|
|
365
|
-
HAS_QUALIFIER =
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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 = [
|
|
387
383
|
TriccOperator.ADD_OR,
|
|
388
384
|
TriccOperator.AND,
|
|
389
385
|
TriccOperator.OR,
|
|
@@ -404,7 +400,7 @@ RETURNS_BOOLEAN =[
|
|
|
404
400
|
TriccOperator.LESS_OR_EQUAL,
|
|
405
401
|
TriccOperator.EQUAL,
|
|
406
402
|
TriccOperator.MORE,
|
|
407
|
-
TriccOperator.LESS
|
|
403
|
+
TriccOperator.LESS,
|
|
408
404
|
]
|
|
409
405
|
|
|
410
406
|
RETURNS_NUMBER = [
|
|
@@ -413,6 +409,8 @@ RETURNS_NUMBER = [
|
|
|
413
409
|
TriccOperator.AGE_YEAR,
|
|
414
410
|
TriccOperator.ZSCORE,
|
|
415
411
|
TriccOperator.IZSCORE,
|
|
412
|
+
TriccOperator.ROUND,
|
|
413
|
+
TriccOperator.DATETIME_TO_DECIMAL,
|
|
416
414
|
TriccOperator.PLUS,
|
|
417
415
|
TriccOperator.MINUS,
|
|
418
416
|
TriccOperator.DIVIDED,
|
|
@@ -420,86 +418,100 @@ RETURNS_NUMBER = [
|
|
|
420
418
|
TriccOperator.COUNT,
|
|
421
419
|
TriccOperator.MODULO,
|
|
422
420
|
TriccOperator.CAST_NUMBER,
|
|
423
|
-
TriccOperator.CAST_INTEGER
|
|
421
|
+
TriccOperator.CAST_INTEGER,
|
|
424
422
|
]
|
|
425
423
|
|
|
426
|
-
RETURNS_DATE =[
|
|
427
|
-
TriccOperator.CAST_DATE
|
|
428
|
-
]
|
|
424
|
+
RETURNS_DATE = [TriccOperator.CAST_DATE]
|
|
429
425
|
|
|
430
426
|
OPERATION_LIST = {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
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
|
+
|
|
439
436
|
|
|
440
437
|
class TriccOperation(BaseModel):
|
|
441
438
|
tricc_type: TriccNodeType = TriccNodeType.operation
|
|
442
439
|
operator: TriccOperator = TriccOperator.NATIVE
|
|
443
440
|
reference: OrderedSet[
|
|
444
441
|
Union[
|
|
445
|
-
TriccStatic,
|
|
446
|
-
|
|
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
|
+
],
|
|
447
456
|
]
|
|
448
457
|
] = []
|
|
449
|
-
|
|
458
|
+
|
|
450
459
|
def __str__(self):
|
|
451
460
|
str_ref = map(str, self.reference)
|
|
452
461
|
return f"{self.operator}({', '.join(map(str, str_ref))})"
|
|
453
|
-
|
|
462
|
+
|
|
454
463
|
def __hash__(self):
|
|
455
464
|
return hash(self.__repr__())
|
|
456
465
|
|
|
457
466
|
def __repr__(self):
|
|
458
|
-
|
|
459
|
-
|
|
467
|
+
str_ref = map(repr, self.reference)
|
|
468
|
+
return f"TriccOperation:{self.operator}({', '.join(map(str, str_ref))})"
|
|
469
|
+
|
|
460
470
|
def __eq__(self, other):
|
|
461
471
|
return self.__str__() == str(other)
|
|
462
|
-
|
|
472
|
+
|
|
463
473
|
def __init__(self, operator, reference=[]):
|
|
464
474
|
super().__init__(operator=operator, reference=reference)
|
|
465
|
-
|
|
475
|
+
|
|
466
476
|
def get_datatype(self):
|
|
467
477
|
if self.operator in RETURNS_BOOLEAN:
|
|
468
|
-
return
|
|
478
|
+
return "boolean"
|
|
469
479
|
elif self.operator in RETURNS_NUMBER:
|
|
470
|
-
return
|
|
480
|
+
return "number"
|
|
471
481
|
elif self.operator in RETURNS_DATE:
|
|
472
|
-
return
|
|
482
|
+
return "date"
|
|
473
483
|
elif self.operator == TriccOperator.CONCATENATE:
|
|
474
|
-
return
|
|
484
|
+
return "string"
|
|
475
485
|
elif self.operator == TriccOperator.PARENTHESIS:
|
|
476
486
|
return self.get_reference_datatype(self.reference)
|
|
477
487
|
elif self.operator == TriccOperator.IF:
|
|
478
488
|
return self.get_reference_datatype(self.reference[1:])
|
|
479
|
-
elif self.operator in (
|
|
489
|
+
elif self.operator in (TriccOperator.IFS, TriccOperator.CASE):
|
|
480
490
|
rtype = set()
|
|
481
491
|
for rl in self.reference:
|
|
482
492
|
rtype.add(self.get_reference_datatype(self.reference[-2:]))
|
|
483
|
-
if len(rtype)>1:
|
|
484
|
-
return
|
|
493
|
+
if len(rtype) > 1:
|
|
494
|
+
return "mixed"
|
|
485
495
|
else:
|
|
486
|
-
return rtype.pop()
|
|
487
|
-
|
|
496
|
+
return rtype.pop()
|
|
497
|
+
else:
|
|
498
|
+
return self.get_reference_datatype(self.reference)
|
|
499
|
+
|
|
488
500
|
def get_reference_datatype(self, references):
|
|
489
501
|
rtype = set()
|
|
490
502
|
for r in references:
|
|
491
|
-
if hasattr(r,
|
|
503
|
+
if hasattr(r, "get_datatype"):
|
|
492
504
|
rtype.add(r.get_datatype())
|
|
493
|
-
elif hasattr(r,
|
|
505
|
+
elif hasattr(r, "value"):
|
|
494
506
|
return str(type(r.value))
|
|
495
507
|
else:
|
|
496
508
|
return str(type(r))
|
|
497
|
-
|
|
498
|
-
if len(rtype)>1:
|
|
499
|
-
return
|
|
509
|
+
|
|
510
|
+
if len(rtype) > 1:
|
|
511
|
+
return "mixed"
|
|
500
512
|
else:
|
|
501
|
-
return rtype.pop()
|
|
502
|
-
|
|
513
|
+
return rtype.pop()
|
|
514
|
+
|
|
503
515
|
def get_references(self):
|
|
504
516
|
predecessor = OrderedSet()
|
|
505
517
|
if isinstance(self.reference, list):
|
|
@@ -508,7 +520,7 @@ class TriccOperation(BaseModel):
|
|
|
508
520
|
else:
|
|
509
521
|
raise NotImplementedError("cannot find predecessor of a str")
|
|
510
522
|
return predecessor
|
|
511
|
-
|
|
523
|
+
|
|
512
524
|
def _process_reference(self, reference, predecessor):
|
|
513
525
|
if isinstance(reference, list):
|
|
514
526
|
for e in reference:
|
|
@@ -522,10 +534,11 @@ class TriccOperation(BaseModel):
|
|
|
522
534
|
|
|
523
535
|
def append(self, value):
|
|
524
536
|
self.reference.append(value)
|
|
525
|
-
|
|
537
|
+
|
|
538
|
+
def replace_node(self, old_node, new_node):
|
|
526
539
|
if isinstance(self.reference, list):
|
|
527
540
|
for key in [i for i, x in enumerate(self.reference)]:
|
|
528
|
-
self.reference[key] = self._replace_reference(self.reference[key], new_node, old_node)
|
|
541
|
+
self.reference[key] = self._replace_reference(self.reference[key], new_node, old_node)
|
|
529
542
|
elif self.reference is not None:
|
|
530
543
|
raise NotImplementedError(f"cannot manage {self.reference.__class__}")
|
|
531
544
|
|
|
@@ -534,55 +547,64 @@ class TriccOperation(BaseModel):
|
|
|
534
547
|
for key in [i for i, x in enumerate(reference)]:
|
|
535
548
|
reference[key] = self._replace_reference(reference[key], new_node, old_node)
|
|
536
549
|
if isinstance(reference, TriccOperation):
|
|
537
|
-
reference.replace_node(old_node
|
|
550
|
+
reference.replace_node(old_node, new_node)
|
|
538
551
|
elif issubclass(reference.__class__, (TriccNodeBaseModel, TriccReference)) and reference == old_node:
|
|
539
552
|
reference = new_node
|
|
540
553
|
# to cover the options
|
|
541
|
-
if
|
|
542
|
-
|
|
554
|
+
if (
|
|
555
|
+
hasattr(reference, "select")
|
|
556
|
+
and hasattr(new_node, "select")
|
|
557
|
+
and issubclass(reference.select.__class__, TriccNodeBaseModel)
|
|
558
|
+
):
|
|
559
|
+
self.replace_node(reference.select, new_node.select)
|
|
543
560
|
return reference
|
|
544
|
-
|
|
561
|
+
|
|
545
562
|
def __copy__(self, keep_node=False):
|
|
546
563
|
# Create a new instance
|
|
547
564
|
if keep_node:
|
|
548
565
|
reference = [e for e in self.reference]
|
|
549
566
|
else:
|
|
550
|
-
reference = [
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
567
|
+
reference = [
|
|
568
|
+
(
|
|
569
|
+
e.copy()
|
|
570
|
+
if isinstance(e, (TriccReference, TriccOperation))
|
|
571
|
+
else (TriccReference(e.name) if hasattr(e, "name") else e)
|
|
572
|
+
)
|
|
573
|
+
for e in self.reference
|
|
574
|
+
]
|
|
575
|
+
|
|
576
|
+
new_instance = type(self)(self.operator, reference)
|
|
557
577
|
# Copy attributes (shallow copy for mutable attributes)
|
|
558
|
-
|
|
578
|
+
|
|
559
579
|
return new_instance
|
|
560
|
-
|
|
580
|
+
|
|
561
581
|
def copy(self, keep_node=False):
|
|
562
582
|
return self.__copy__(keep_node)
|
|
563
583
|
|
|
584
|
+
|
|
564
585
|
# function that make multipat and
|
|
565
586
|
# @param argv list of expression to join with and
|
|
566
587
|
def clean_and_list(argv):
|
|
567
588
|
for a in list(argv):
|
|
568
589
|
if isinstance(a, TriccOperation) and a.operator == TriccOperator.AND:
|
|
569
590
|
argv.remove(a)
|
|
570
|
-
return clean_and_list([*argv
|
|
571
|
-
elif a == TriccStatic(True) or a
|
|
591
|
+
return clean_and_list([*argv, *a.reference])
|
|
592
|
+
elif a == TriccStatic(True) or a is True:
|
|
572
593
|
argv.remove(a)
|
|
573
|
-
elif a == TriccStatic(False)
|
|
594
|
+
elif a == TriccStatic(False):
|
|
574
595
|
return [TriccStatic(False)]
|
|
575
|
-
|
|
596
|
+
|
|
576
597
|
internal = list(set(argv))
|
|
577
598
|
for a in internal:
|
|
578
|
-
for b in internal[internal.index(a)+1:]:
|
|
599
|
+
for b in internal[internal.index(a) + 1:]:
|
|
579
600
|
if not_clean(b) == a:
|
|
580
601
|
return [TriccStatic(False)]
|
|
581
602
|
return sorted(list(set(argv)), key=str)
|
|
582
|
-
|
|
603
|
+
|
|
604
|
+
|
|
583
605
|
def not_clean(a):
|
|
584
606
|
new_operator = None
|
|
585
|
-
if a is None or isinstance(a, str) and a ==
|
|
607
|
+
if a is None or isinstance(a, str) and a == "":
|
|
586
608
|
return TriccStatic(False)
|
|
587
609
|
elif isinstance(a, TriccStatic) and a == TriccStatic(False):
|
|
588
610
|
return TriccStatic(True)
|
|
@@ -610,26 +632,16 @@ def not_clean(a):
|
|
|
610
632
|
new_operator = TriccOperator.LESS
|
|
611
633
|
elif isinstance(a, TriccOperation) and a.operator == TriccOperator.NOT:
|
|
612
634
|
return a.reference[0]
|
|
613
|
-
|
|
635
|
+
|
|
614
636
|
if new_operator:
|
|
615
|
-
return TriccOperation(
|
|
616
|
-
|
|
617
|
-
a.reference
|
|
618
|
-
)
|
|
619
|
-
|
|
637
|
+
return TriccOperation(new_operator, a.reference)
|
|
638
|
+
|
|
620
639
|
elif not isinstance(a, TriccOperation) and issubclass(a.__class__, object):
|
|
621
|
-
return TriccOperation(
|
|
622
|
-
operator=TriccOperator.NOTEXISTS,
|
|
623
|
-
reference=[a]
|
|
624
|
-
)
|
|
640
|
+
return TriccOperation(operator=TriccOperator.NOTEXISTS, reference=[a])
|
|
625
641
|
else:
|
|
626
|
-
return TriccOperation(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
)
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
642
|
+
return TriccOperation(TriccOperator.NOT, [a])
|
|
643
|
+
|
|
644
|
+
|
|
633
645
|
# function that generate remove unsure condition
|
|
634
646
|
# @param list_or
|
|
635
647
|
# @param and elm use upstream
|
|
@@ -641,44 +653,67 @@ def clean_or_list(list_or, elm_and=None):
|
|
|
641
653
|
for a in list(list_or):
|
|
642
654
|
if isinstance(a, TriccOperation) and a.operator == TriccOperator.OR:
|
|
643
655
|
list_or.remove(a)
|
|
644
|
-
return clean_or_list([*list_or
|
|
645
|
-
elif a == TriccStatic(False) or a
|
|
656
|
+
return clean_or_list([*list_or, *a.reference])
|
|
657
|
+
elif a == TriccStatic(False) or a is False or a == 0:
|
|
646
658
|
list_or.remove(a)
|
|
647
|
-
elif a == TriccStatic(True) or a
|
|
659
|
+
elif a == TriccStatic(True) or a is True or a == 1 or (elm_and is not None and not_clean(a) in list_or):
|
|
648
660
|
return [TriccStatic(True)]
|
|
649
661
|
# if there is x and not(X) in an OR list them the list is always true
|
|
650
|
-
elif elm_and is not None and
|
|
662
|
+
elif elm_and is not None and (not_clean(a) == elm_and or a == elm_and):
|
|
651
663
|
list_or.remove(a)
|
|
652
|
-
internal = list(list_or)
|
|
664
|
+
internal = list(list_or)
|
|
653
665
|
for a in internal:
|
|
654
|
-
for b in internal[internal.index(a)+1:]:
|
|
666
|
+
for b in internal[internal.index(a) + 1:]:
|
|
655
667
|
if not_clean(b) == a:
|
|
656
668
|
return [TriccStatic(True)]
|
|
657
669
|
if len(list_or) == 0:
|
|
658
670
|
return []
|
|
659
671
|
|
|
660
|
-
return sorted(list(set(list_or)), key=
|
|
672
|
+
return sorted(list(set(list_or)), key=repr)
|
|
673
|
+
|
|
661
674
|
|
|
662
675
|
def and_join(argv):
|
|
663
|
-
argv=clean_and_list(argv)
|
|
676
|
+
argv = clean_and_list(argv)
|
|
664
677
|
if len(argv) == 0:
|
|
665
|
-
return
|
|
678
|
+
return ""
|
|
666
679
|
elif len(argv) == 1:
|
|
667
680
|
return argv[0]
|
|
668
681
|
else:
|
|
669
|
-
return
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
682
|
+
return TriccOperation(TriccOperator.AND, argv)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def string_join(left: Union[str, TriccOperation], right: Union[str, TriccOperation]) -> TriccOperation:
|
|
686
|
+
"""
|
|
687
|
+
Concatenates two arguments (strings or TriccOperation) into a TriccOperation with CONCATENATE operator.
|
|
688
|
+
If either argument is a TriccOperation with CONCATENATE operator, its operands are merged into the result.
|
|
689
|
+
"""
|
|
690
|
+
# Initialize operands list for the new TriccOperation
|
|
691
|
+
operands: List[Union[str, TriccOperation]] = []
|
|
692
|
+
|
|
693
|
+
# Check if left is a TriccOperation with CONCATENATE
|
|
694
|
+
if isinstance(left, TriccOperation) and left.operator == TriccOperator.CONCATENATE:
|
|
695
|
+
operands.extend(left.reference) # Merge left's operands
|
|
696
|
+
else:
|
|
697
|
+
operands.append(left) # Add left as-is
|
|
698
|
+
|
|
699
|
+
# Check if right is a TriccOperation with CONCATENATE
|
|
700
|
+
if isinstance(right, TriccOperation) and right.operator == TriccOperator.CONCATENATE:
|
|
701
|
+
operands.extend(right.reference) # Merge right's operands
|
|
702
|
+
else:
|
|
703
|
+
operands.append(right) # Add right as-is
|
|
704
|
+
|
|
705
|
+
# Return a new TriccOperation with the merged operands
|
|
706
|
+
return TriccOperation(operator=TriccOperator.CONCATENATE, reference=operands)
|
|
707
|
+
|
|
673
708
|
|
|
674
709
|
# function that make a 2 part and
|
|
675
710
|
# @param left part
|
|
676
711
|
# @param right part
|
|
677
712
|
def simple_and_join(left, right):
|
|
678
|
-
|
|
713
|
+
pass
|
|
679
714
|
# no term is considered as True
|
|
680
|
-
left_issue = left is None or left ==
|
|
681
|
-
right_issue = right is None or right ==
|
|
715
|
+
left_issue = left is None or left == ""
|
|
716
|
+
right_issue = right is None or right == ""
|
|
682
717
|
left_neg = not_clean(left)
|
|
683
718
|
right_neg = not_clean(right)
|
|
684
719
|
if left_issue and right_issue:
|
|
@@ -686,61 +721,54 @@ def simple_and_join(left, right):
|
|
|
686
721
|
elif left_neg == right or right_neg == left:
|
|
687
722
|
return TriccStatic(False)
|
|
688
723
|
elif left_issue:
|
|
689
|
-
logger.debug(
|
|
690
|
-
return
|
|
691
|
-
elif left ==
|
|
692
|
-
return
|
|
724
|
+
logger.debug("and with empty left term")
|
|
725
|
+
return right
|
|
726
|
+
elif left == "1" or left == 1 or left == TriccStatic(True) or left is True:
|
|
727
|
+
return right
|
|
693
728
|
elif right_issue:
|
|
694
|
-
logger.debug(
|
|
695
|
-
return
|
|
696
|
-
elif right ==
|
|
697
|
-
return
|
|
729
|
+
logger.debug("and with empty right term")
|
|
730
|
+
return left
|
|
731
|
+
elif right == "1" or right == 1 or right == TriccStatic(True) or right is True:
|
|
732
|
+
return left
|
|
698
733
|
else:
|
|
699
|
-
return
|
|
700
|
-
|
|
701
|
-
[left, right]
|
|
702
|
-
)
|
|
734
|
+
return TriccOperation(TriccOperator.AND, [left, right])
|
|
735
|
+
|
|
703
736
|
|
|
704
737
|
def or_join(list_or, elm_and=None):
|
|
705
|
-
cleaned_list
|
|
738
|
+
cleaned_list = clean_or_list(set(list_or), elm_and)
|
|
706
739
|
if len(cleaned_list) == 1:
|
|
707
740
|
return cleaned_list[0]
|
|
708
|
-
elif len(cleaned_list)>1:
|
|
709
|
-
return TriccOperation(
|
|
710
|
-
TriccOperator.OR,
|
|
711
|
-
cleaned_list
|
|
712
|
-
)
|
|
741
|
+
elif len(cleaned_list) > 1:
|
|
742
|
+
return TriccOperation(TriccOperator.OR, cleaned_list)
|
|
713
743
|
else:
|
|
714
744
|
logger.error("empty or list")
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
745
|
+
|
|
746
|
+
|
|
718
747
|
# function that make a 2 part NAND
|
|
719
748
|
# @param left part
|
|
720
749
|
# @param right part
|
|
721
750
|
def nand_join(left, right):
|
|
722
751
|
# no term is considered as True
|
|
723
|
-
left_issue = left is None or left ==
|
|
724
|
-
right_issue = right is None or right ==
|
|
725
|
-
left_neg = left
|
|
726
|
-
right_neg = right
|
|
752
|
+
left_issue = left is None or left == ""
|
|
753
|
+
right_issue = right is None or right == ""
|
|
754
|
+
left_neg = left is False or left == 0 or left == "0" or left == TriccStatic(False)
|
|
755
|
+
right_neg = right is False or right == 0 or right == "0" or right == TriccStatic(False)
|
|
727
756
|
if left_issue and right_issue:
|
|
728
757
|
logger.critical("and with both terms empty")
|
|
729
758
|
elif left_issue:
|
|
730
|
-
logger.debug(
|
|
731
|
-
return
|
|
732
|
-
elif left ==
|
|
733
|
-
return
|
|
734
|
-
elif right_issue
|
|
735
|
-
logger.debug(
|
|
736
|
-
return
|
|
737
|
-
elif right ==
|
|
738
|
-
return
|
|
759
|
+
logger.debug("and with empty left term")
|
|
760
|
+
return not_clean(right)
|
|
761
|
+
elif left == "1" or left == 1 or left == TriccStatic(True):
|
|
762
|
+
return not_clean(right)
|
|
763
|
+
elif right_issue:
|
|
764
|
+
logger.debug("and with empty right term")
|
|
765
|
+
return TriccStatic(False)
|
|
766
|
+
elif right == "1" or right == 1 or left_neg or right == TriccStatic(True):
|
|
767
|
+
return TriccStatic(False)
|
|
739
768
|
elif right_neg:
|
|
740
769
|
return left
|
|
741
770
|
else:
|
|
742
|
-
return
|
|
771
|
+
return and_join([left, not_clean(right)])
|
|
743
772
|
|
|
744
773
|
|
|
745
774
|
TriccGroup.update_forward_refs()
|
|
746
|
-
TriccEdge.update_forward_refs()
|