tricc-oo 1.5.26__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 +1 -0
- tests/test_build.py +260 -0
- tricc_oo/converters/tricc_to_xls_form.py +15 -6
- tricc_oo/converters/xml_to_tricc.py +9 -8
- tricc_oo/models/base.py +4 -2
- tricc_oo/serializers/xls_form.py +34 -18
- tricc_oo/strategies/output/base_output_strategy.py +7 -0
- tricc_oo/strategies/output/dhis2_form.py +908 -0
- tricc_oo/strategies/output/openmrs_form.py +52 -5
- tricc_oo/strategies/output/xls_form.py +62 -32
- tricc_oo/strategies/output/xlsform_cht.py +107 -0
- tricc_oo/visitors/tricc.py +145 -71
- {tricc_oo-1.5.26.dist-info → tricc_oo-1.6.8.dist-info}/METADATA +2 -1
- {tricc_oo-1.5.26.dist-info → tricc_oo-1.6.8.dist-info}/RECORD +17 -15
- {tricc_oo-1.5.26.dist-info → tricc_oo-1.6.8.dist-info}/WHEEL +0 -0
- {tricc_oo-1.5.26.dist-info → tricc_oo-1.6.8.dist-info}/licenses/LICENSE +0 -0
- {tricc_oo-1.5.26.dist-info → tricc_oo-1.6.8.dist-info}/top_level.txt +0 -0
tests/build.py
CHANGED
|
@@ -6,6 +6,7 @@ from tricc_oo.strategies.output.xls_form import XLSFormStrategy # noqa: F401
|
|
|
6
6
|
from tricc_oo.strategies.output.openmrs_form import OpenMRSStrategy # noqa: F401
|
|
7
7
|
from tricc_oo.strategies.output.fhir_form import FHIRStrategy # noqa: F401
|
|
8
8
|
from tricc_oo.strategies.output.html_form import HTMLStrategy # noqa: F401
|
|
9
|
+
from tricc_oo.strategies.output.dhis2_form import DHIS2Strategy # noqa: F401
|
|
9
10
|
from tricc_oo.strategies.input.drawio import DrawioStrategy # noqa: F401
|
|
10
11
|
import getopt
|
|
11
12
|
import logging
|
tests/test_build.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from pyxform import create_survey_from_xls
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestBuildScript(unittest.TestCase):
|
|
13
|
+
"""Test cases for the build.py script with different argument combinations."""
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
"""Set up test fixtures."""
|
|
17
|
+
self.test_data_dir = Path(__file__).parent / "data"
|
|
18
|
+
self.test_output_dir = Path(__file__).parent / "output"
|
|
19
|
+
self.demo_file = self.test_data_dir / "demo.drawio"
|
|
20
|
+
|
|
21
|
+
# Ensure test data exists
|
|
22
|
+
self.assertTrue(self.demo_file.exists(), f"Test data file {self.demo_file} does not exist")
|
|
23
|
+
|
|
24
|
+
def run_build_script(self, args):
|
|
25
|
+
"""Helper method to run build.py with given arguments."""
|
|
26
|
+
cmd = [sys.executable, str(Path(__file__).parent / "build.py")] + args
|
|
27
|
+
result = subprocess.run(cmd, capture_output=True, text=True, cwd=Path(__file__).parent.parent)
|
|
28
|
+
return result
|
|
29
|
+
|
|
30
|
+
def test_basic_build_with_demo_file(self):
|
|
31
|
+
"""Test basic build with demo.drawio file."""
|
|
32
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
33
|
+
args = [
|
|
34
|
+
"-i", str(self.demo_file),
|
|
35
|
+
"-o", temp_dir,
|
|
36
|
+
"-l", "i"
|
|
37
|
+
]
|
|
38
|
+
result = self.run_build_script(args)
|
|
39
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
40
|
+
|
|
41
|
+
def test_build_with_directory_input(self):
|
|
42
|
+
"""Test build with directory containing drawio files."""
|
|
43
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
44
|
+
args = [
|
|
45
|
+
"-i", str(self.test_data_dir),
|
|
46
|
+
"-o", temp_dir,
|
|
47
|
+
"-l", "i"
|
|
48
|
+
]
|
|
49
|
+
result = self.run_build_script(args)
|
|
50
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
51
|
+
|
|
52
|
+
def test_build_with_xlsform_strategy(self):
|
|
53
|
+
"""Test build with XLSFormStrategy (default)."""
|
|
54
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
55
|
+
args = [
|
|
56
|
+
"-i", str(self.demo_file),
|
|
57
|
+
"-o", temp_dir,
|
|
58
|
+
"-O", "XLSFormStrategy",
|
|
59
|
+
"-l", "i"
|
|
60
|
+
]
|
|
61
|
+
result = self.run_build_script(args)
|
|
62
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
63
|
+
|
|
64
|
+
def test_build_with_html_strategy(self):
|
|
65
|
+
"""Test build with HTMLStrategy."""
|
|
66
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
67
|
+
args = [
|
|
68
|
+
"-i", str(self.demo_file),
|
|
69
|
+
"-o", temp_dir,
|
|
70
|
+
"-O", "HTMLStrategy",
|
|
71
|
+
"-l", "i"
|
|
72
|
+
]
|
|
73
|
+
result = self.run_build_script(args)
|
|
74
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
75
|
+
|
|
76
|
+
def test_build_with_fhir_strategy(self):
|
|
77
|
+
"""Test build with FHIRStrategy."""
|
|
78
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
79
|
+
args = [
|
|
80
|
+
"-i", str(self.demo_file),
|
|
81
|
+
"-o", temp_dir,
|
|
82
|
+
"-O", "FHIRStrategy",
|
|
83
|
+
"-l", "i"
|
|
84
|
+
]
|
|
85
|
+
result = self.run_build_script(args)
|
|
86
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
87
|
+
|
|
88
|
+
def test_build_with_dhis2_strategy(self):
|
|
89
|
+
"""Test build with DHIS2Strategy."""
|
|
90
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
91
|
+
args = [
|
|
92
|
+
"-i", str(self.demo_file),
|
|
93
|
+
"-o", temp_dir,
|
|
94
|
+
"-O", "DHIS2Strategy",
|
|
95
|
+
"-l", "i"
|
|
96
|
+
]
|
|
97
|
+
result = self.run_build_script(args)
|
|
98
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
99
|
+
|
|
100
|
+
def test_build_with_openmrs_strategy(self):
|
|
101
|
+
"""Test build with OpenMRSStrategy."""
|
|
102
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
103
|
+
args = [
|
|
104
|
+
"-i", str(self.demo_file),
|
|
105
|
+
"-o", temp_dir,
|
|
106
|
+
"-O", "OpenMRSStrategy",
|
|
107
|
+
"-l", "i"
|
|
108
|
+
]
|
|
109
|
+
result = self.run_build_script(args)
|
|
110
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
111
|
+
|
|
112
|
+
def test_build_with_cht_strategy(self):
|
|
113
|
+
"""Test build with XLSFormCHTStrategy."""
|
|
114
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
115
|
+
args = [
|
|
116
|
+
"-i", str(self.demo_file),
|
|
117
|
+
"-o", temp_dir,
|
|
118
|
+
"-O", "XLSFormCHTStrategy",
|
|
119
|
+
"-l", "i"
|
|
120
|
+
]
|
|
121
|
+
result = self.run_build_script(args)
|
|
122
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
123
|
+
|
|
124
|
+
def test_build_with_cht_hf_strategy(self):
|
|
125
|
+
"""Test build with XLSFormCHTHFStrategy."""
|
|
126
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
127
|
+
args = [
|
|
128
|
+
"-i", str(self.demo_file),
|
|
129
|
+
"-o", temp_dir,
|
|
130
|
+
"-O", "XLSFormCHTHFStrategy",
|
|
131
|
+
"-l", "i"
|
|
132
|
+
]
|
|
133
|
+
result = self.run_build_script(args)
|
|
134
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
135
|
+
|
|
136
|
+
def test_build_with_cdss_strategy(self):
|
|
137
|
+
"""Test build with XLSFormCDSSStrategy."""
|
|
138
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
139
|
+
args = [
|
|
140
|
+
"-i", str(self.demo_file),
|
|
141
|
+
"-o", temp_dir,
|
|
142
|
+
"-O", "XLSFormCDSSStrategy",
|
|
143
|
+
"-l", "i"
|
|
144
|
+
]
|
|
145
|
+
result = self.run_build_script(args)
|
|
146
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
147
|
+
|
|
148
|
+
# def test_build_with_spice_strategy(self):
|
|
149
|
+
# """Test build with SpiceStrategy."""
|
|
150
|
+
# with tempfile.TemporaryDirectory() as temp_dir:
|
|
151
|
+
# args = [
|
|
152
|
+
# "-i", str(self.demo_file),
|
|
153
|
+
# "-o", temp_dir,
|
|
154
|
+
# "-O", "SpiceStrategy",
|
|
155
|
+
# "-l", "i"
|
|
156
|
+
# ]
|
|
157
|
+
# result = self.run_build_script(args)
|
|
158
|
+
# self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
159
|
+
|
|
160
|
+
def test_build_with_debug_level(self):
|
|
161
|
+
"""Test build with debug logging level."""
|
|
162
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
163
|
+
args = [
|
|
164
|
+
"-i", str(self.demo_file),
|
|
165
|
+
"-o", temp_dir,
|
|
166
|
+
"-l", "d"
|
|
167
|
+
]
|
|
168
|
+
result = self.run_build_script(args)
|
|
169
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
170
|
+
|
|
171
|
+
def test_build_with_trad_option(self):
|
|
172
|
+
"""Test build with translation option."""
|
|
173
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
174
|
+
args = [
|
|
175
|
+
"-i", str(self.demo_file),
|
|
176
|
+
"-o", temp_dir,
|
|
177
|
+
"-t",
|
|
178
|
+
"-l", "i"
|
|
179
|
+
]
|
|
180
|
+
result = self.run_build_script(args)
|
|
181
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
182
|
+
|
|
183
|
+
def test_build_with_form_id(self):
|
|
184
|
+
"""Test build with custom form ID."""
|
|
185
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
186
|
+
args = [
|
|
187
|
+
"-i", str(self.demo_file),
|
|
188
|
+
"-o", temp_dir,
|
|
189
|
+
"-d", "test_form_123",
|
|
190
|
+
"-l", "i"
|
|
191
|
+
]
|
|
192
|
+
result = self.run_build_script(args)
|
|
193
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
194
|
+
|
|
195
|
+
def test_build_missing_input(self):
|
|
196
|
+
"""Test build with missing input file."""
|
|
197
|
+
args = ["-o", "/tmp/test_output"]
|
|
198
|
+
result = self.run_build_script(args)
|
|
199
|
+
self.assertNotEqual(result.returncode, 0, "Build should fail with missing input")
|
|
200
|
+
|
|
201
|
+
def test_build_invalid_input_file(self):
|
|
202
|
+
"""Test build with invalid input file."""
|
|
203
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
204
|
+
args = [
|
|
205
|
+
"-i", "/nonexistent/file.drawio",
|
|
206
|
+
"-o", temp_dir,
|
|
207
|
+
"-l", "i"
|
|
208
|
+
]
|
|
209
|
+
result = self.run_build_script(args)
|
|
210
|
+
self.assertNotEqual(result.returncode, 0, "Build should fail with invalid input file")
|
|
211
|
+
|
|
212
|
+
def validate_xls_form(self, xls_path):
|
|
213
|
+
"""Helper method to validate XLS form using ODK libraries."""
|
|
214
|
+
try:
|
|
215
|
+
# Convert XLS to XML using pyxform
|
|
216
|
+
survey = create_survey_from_xls(xls_path)
|
|
217
|
+
xml_output = survey.to_xml()
|
|
218
|
+
|
|
219
|
+
# Basic validation - check if XML was generated successfully
|
|
220
|
+
# In a real scenario, you might want to use odk_validate command line tool
|
|
221
|
+
if xml_output and len(xml_output.strip()) > 0:
|
|
222
|
+
return True, "Validation successful"
|
|
223
|
+
else:
|
|
224
|
+
return False, "Empty XML output"
|
|
225
|
+
except Exception as e:
|
|
226
|
+
return False, str(e)
|
|
227
|
+
|
|
228
|
+
def test_xlsform_strategy_validation(self):
|
|
229
|
+
"""Test XLSFormStrategy output validation with ODK libraries."""
|
|
230
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
231
|
+
xls_output = Path(temp_dir) / "demo_tricc.xlsx"
|
|
232
|
+
args = [
|
|
233
|
+
"-i", str(self.demo_file),
|
|
234
|
+
"-o", temp_dir,
|
|
235
|
+
"-O", "XLSFormStrategy",
|
|
236
|
+
"-l", "i"
|
|
237
|
+
]
|
|
238
|
+
result = self.run_build_script(args)
|
|
239
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
240
|
+
|
|
241
|
+
# Check if XLS file was created
|
|
242
|
+
self.assertTrue(xls_output.exists(), f"XLS file {xls_output} was not created")
|
|
243
|
+
|
|
244
|
+
def test_xlsform_cdss_strategy_validation(self):
|
|
245
|
+
"""Test XLSFormCDSSStrategy output validation with ODK libraries."""
|
|
246
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
247
|
+
xls_output = Path(temp_dir) / "demo_tricc.xlsx"
|
|
248
|
+
args = [
|
|
249
|
+
"-i", str(self.demo_file),
|
|
250
|
+
"-o", temp_dir,
|
|
251
|
+
"-O", "XLSFormCDSSStrategy",
|
|
252
|
+
"-l", "i"
|
|
253
|
+
]
|
|
254
|
+
result = self.run_build_script(args)
|
|
255
|
+
self.assertEqual(result.returncode, 0, f"Build failed: {result.stderr}")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
if __name__ == "__main__":
|
|
260
|
+
unittest.main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from tricc_oo.converters.utils import clean_name
|
|
3
|
-
from tricc_oo.models.tricc import TriccNodeSelectOption, TRICC_TRUE_VALUE, TRICC_FALSE_VALUE
|
|
2
|
+
from tricc_oo.converters.utils import clean_name
|
|
3
|
+
from tricc_oo.models.tricc import TriccNodeSelectOption, TRICC_TRUE_VALUE, TRICC_FALSE_VALUE, TriccNodeActivity
|
|
4
4
|
from tricc_oo.models.calculate import TriccNodeInput
|
|
5
5
|
from tricc_oo.models.base import TriccNodeBaseModel, TriccStatic, TriccReference
|
|
6
6
|
|
|
@@ -43,6 +43,8 @@ def get_export_name(node, replace_dots=True):
|
|
|
43
43
|
value = node.name
|
|
44
44
|
elif isinstance(node, TriccStatic):
|
|
45
45
|
value = node.value
|
|
46
|
+
if isinstance(value, TriccNodeSelectOption):
|
|
47
|
+
value = value.name
|
|
46
48
|
else:
|
|
47
49
|
value = node
|
|
48
50
|
if isinstance(value, bool): # or r.value in ('true', 'false')
|
|
@@ -51,10 +53,12 @@ def get_export_name(node, replace_dots=True):
|
|
|
51
53
|
export_name = BOOLEAN_MAP[str(TRICC_TRUE_VALUE)]
|
|
52
54
|
elif value == TRICC_FALSE_VALUE:
|
|
53
55
|
export_name = BOOLEAN_MAP[str(TRICC_FALSE_VALUE)]
|
|
54
|
-
elif
|
|
55
|
-
export_name =
|
|
56
|
+
elif value == '$this':
|
|
57
|
+
export_name = '.'
|
|
58
|
+
elif isinstance(value, str) and not isinstance(node, str):
|
|
59
|
+
export_name = f"'{value}'"
|
|
56
60
|
else:
|
|
57
|
-
export_name =
|
|
61
|
+
export_name = value
|
|
58
62
|
if hasattr(node, 'export_name'):
|
|
59
63
|
node.export_name = export_name
|
|
60
64
|
return export_name
|
|
@@ -62,7 +66,12 @@ def get_export_name(node, replace_dots=True):
|
|
|
62
66
|
return node
|
|
63
67
|
else:
|
|
64
68
|
node.gen_name()
|
|
65
|
-
if isinstance(node,
|
|
69
|
+
if isinstance(node, TriccNodeActivity) and getattr(node, 'instance', 1) > 1:
|
|
70
|
+
node.export_name = clean_name(
|
|
71
|
+
node.name + INSTANCE_SEPARATOR + str(node.instance),
|
|
72
|
+
replace_dots=replace_dots,
|
|
73
|
+
)
|
|
74
|
+
elif isinstance(node, TriccNodeSelectOption):
|
|
66
75
|
node.export_name = node.name
|
|
67
76
|
elif node.last is False:
|
|
68
77
|
node.export_name = clean_name(
|
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
|
|
8
8
|
from tricc_oo.converters.utils import remove_html, clean_str
|
|
9
9
|
from tricc_oo.converters.cql_to_operation import transform_cql_to_operation
|
|
10
|
-
from tricc_oo.converters.utils import generate_id
|
|
10
|
+
from tricc_oo.converters.utils import generate_id
|
|
11
11
|
from tricc_oo.models.base import (
|
|
12
12
|
TriccOperator, TriccOperation,
|
|
13
13
|
TriccStatic, TriccReference, TriccNodeType, TriccEdge, OPERATION_LIST
|
|
@@ -101,16 +101,16 @@ def create_activity(diagram, media_path, project):
|
|
|
101
101
|
|
|
102
102
|
external_id = diagram.attrib.get("id")
|
|
103
103
|
id = get_id(external_id, diagram.attrib.get("id"))
|
|
104
|
-
root = create_root_node(diagram)
|
|
105
|
-
|
|
104
|
+
root, name = create_root_node(diagram)
|
|
105
|
+
label = diagram.attrib.get("name")
|
|
106
106
|
form_id = diagram.attrib.get("name", None)
|
|
107
107
|
if root is not None:
|
|
108
108
|
activity = TriccNodeActivity(
|
|
109
109
|
root=root,
|
|
110
|
-
name=
|
|
110
|
+
name=name, # start node 'name' is saved in label
|
|
111
111
|
id=id,
|
|
112
112
|
external_id=external_id,
|
|
113
|
-
label=
|
|
113
|
+
label=label,
|
|
114
114
|
form_id=form_id,
|
|
115
115
|
)
|
|
116
116
|
if root.relevance is not None:
|
|
@@ -130,7 +130,7 @@ def create_activity(diagram, media_path, project):
|
|
|
130
130
|
if (
|
|
131
131
|
issubclass(n.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase))
|
|
132
132
|
and not isinstance(n, (TriccRhombusMixIn, TriccNodeRhombus, TriccNodeDisplayBridge))
|
|
133
|
-
and not n.name.startswith("label_")
|
|
133
|
+
and not n.name.startswith("label_") # FIXME
|
|
134
134
|
):
|
|
135
135
|
system = n.name.split(".")[0] if "." in n.name else "tricc"
|
|
136
136
|
if isinstance(n, TriccNodeSelectOption) and isinstance(n.select, TriccNodeSelectNotAvailable):
|
|
@@ -444,17 +444,18 @@ def create_root_node(diagram):
|
|
|
444
444
|
if elm is not None:
|
|
445
445
|
external_id = elm.attrib.get("id")
|
|
446
446
|
id = get_id(external_id, diagram.attrib.get("id"))
|
|
447
|
+
name = generate_id("start"+external_id)
|
|
447
448
|
node = TriccNodeActivityStart(
|
|
448
449
|
id=id,
|
|
449
450
|
external_id=external_id,
|
|
450
451
|
# parent=elm.attrib.get("parent"),
|
|
451
|
-
name=
|
|
452
|
+
name=name,
|
|
452
453
|
label=diagram.attrib.get("name"),
|
|
453
454
|
relevance=elm.attrib.get("relevance"),
|
|
454
455
|
instance=int(elm.attrib.get("instance") if elm.attrib.get("instance") is not None else 1),
|
|
455
456
|
)
|
|
456
457
|
load_expressions(node)
|
|
457
|
-
return node
|
|
458
|
+
return node, _get_name(elm.attrib.get("name", "act_"), external_id, diagram.attrib.get("id")) if node else None
|
|
458
459
|
|
|
459
460
|
|
|
460
461
|
# converter XML item to object
|
tricc_oo/models/base.py
CHANGED
|
@@ -11,7 +11,7 @@ from tricc_oo.models.ordered_set import OrderedSet
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger("default")
|
|
13
13
|
|
|
14
|
-
Expression = Annotated[str, StringConstraints(pattern=r"
|
|
14
|
+
Expression = Annotated[str, StringConstraints(pattern=r".+")]
|
|
15
15
|
|
|
16
16
|
triccId = Annotated[str, StringConstraints(pattern=r"^[^\\/\: ]+$")]
|
|
17
17
|
|
|
@@ -274,7 +274,7 @@ class TriccNodeBaseModel(TriccBaseModel):
|
|
|
274
274
|
|
|
275
275
|
|
|
276
276
|
class TriccStatic(BaseModel):
|
|
277
|
-
value: Union[str, float, int, bool]
|
|
277
|
+
value: Union[str, float, int, bool, TriccNodeBaseModel]
|
|
278
278
|
|
|
279
279
|
def __init__(self, value):
|
|
280
280
|
super().__init__(value=value)
|
|
@@ -494,6 +494,8 @@ class TriccOperation(BaseModel):
|
|
|
494
494
|
return "mixed"
|
|
495
495
|
else:
|
|
496
496
|
return rtype.pop()
|
|
497
|
+
else:
|
|
498
|
+
return self.get_reference_datatype(self.reference)
|
|
497
499
|
|
|
498
500
|
def get_reference_datatype(self, references):
|
|
499
501
|
rtype = set()
|
tricc_oo/serializers/xls_form.py
CHANGED
|
@@ -6,16 +6,17 @@ from tricc_oo.converters.tricc_to_xls_form import (
|
|
|
6
6
|
get_export_name, BOOLEAN_MAP
|
|
7
7
|
)
|
|
8
8
|
from tricc_oo.models.lang import SingletonLangClass
|
|
9
|
-
from tricc_oo.converters.utils import clean_name, remove_html
|
|
9
|
+
from tricc_oo.converters.utils import clean_name, remove_html, generate_id
|
|
10
10
|
from tricc_oo.models.base import (
|
|
11
11
|
TriccOperator,
|
|
12
12
|
TriccOperation, TriccStatic, TriccReference, and_join, TriccNodeType
|
|
13
13
|
)
|
|
14
14
|
from tricc_oo.models.calculate import (
|
|
15
15
|
TriccNodeDisplayCalculateBase,
|
|
16
|
+
TriccNodeCalculate
|
|
16
17
|
)
|
|
17
18
|
from tricc_oo.models.tricc import (
|
|
18
|
-
|
|
19
|
+
TriccNodeBaseModel, TriccNodeSelectMultiple, TriccNodeSelectOption,
|
|
19
20
|
TriccNodeSelectOne,
|
|
20
21
|
TriccNodeSelect,
|
|
21
22
|
TriccNodeMoreInfo,
|
|
@@ -39,6 +40,9 @@ langs = SingletonLangClass()
|
|
|
39
40
|
TRICC_CALC_EXPRESSION = "${{{0}}}>0"
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
def get_export_group_name(in_node): return f"gcalc_{get_export_name(in_node)}"
|
|
44
|
+
|
|
45
|
+
|
|
42
46
|
def start_group(
|
|
43
47
|
strategy,
|
|
44
48
|
cur_group,
|
|
@@ -58,10 +62,27 @@ def start_group(
|
|
|
58
62
|
|
|
59
63
|
else:
|
|
60
64
|
groups[name] = 0
|
|
61
|
-
is_activity = isinstance(cur_group, TriccNodeActivity)
|
|
62
65
|
relevance = relevance and cur_group.relevance is not None and cur_group.relevance != ""
|
|
63
|
-
|
|
64
|
-
group_calc_required =
|
|
66
|
+
past_instances = len(getattr(cur_group.base_instance, "instances", []))
|
|
67
|
+
group_calc_required = relevance is not None and (len(str(relevance)) > 100 or past_instances > 1)
|
|
68
|
+
calc = None
|
|
69
|
+
if group_calc_required and getattr(cur_group.relevance, 'operator', None) != TriccOperator.ISTRUE:
|
|
70
|
+
|
|
71
|
+
calc = TriccNodeCalculate(
|
|
72
|
+
id=generate_id(get_export_group_name(name)),
|
|
73
|
+
group=cur_group,
|
|
74
|
+
activity=cur_group.activity,
|
|
75
|
+
name=get_export_group_name(name),
|
|
76
|
+
expression=cur_group.relevance
|
|
77
|
+
)
|
|
78
|
+
if calc not in cur_group.calculates:
|
|
79
|
+
cur_group.calculates.append(calc)
|
|
80
|
+
processed_nodes.add(calc)
|
|
81
|
+
|
|
82
|
+
cur_group.relevance = TriccOperation(
|
|
83
|
+
TriccOperator.ISTRUE,
|
|
84
|
+
[calc]
|
|
85
|
+
)
|
|
65
86
|
|
|
66
87
|
relevance_expression = cur_group.relevance
|
|
67
88
|
relevance_expression = get_applicability_expression(cur_group, processed_nodes, process, relevance_expression)
|
|
@@ -69,14 +90,9 @@ def start_group(
|
|
|
69
90
|
relevance_expression = get_process_skip_expression(cur_group, processed_nodes, process, relevance_expression)
|
|
70
91
|
|
|
71
92
|
if not relevance:
|
|
72
|
-
|
|
93
|
+
relevance_expression_str = ""
|
|
73
94
|
elif isinstance(relevance_expression, (TriccOperation, TriccStatic)):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# elif is_activity:
|
|
77
|
-
# relevance_expression = TRICC_CALC_EXPRESSION.format(get_export_name(cur_group.root))
|
|
78
|
-
elif group_calc_required:
|
|
79
|
-
relevance_expression = TRICC_CALC_EXPRESSION.format("gcalc_" + name)
|
|
95
|
+
relevance_expression_str = strategy.get_tricc_operation_expression(relevance_expression)
|
|
80
96
|
|
|
81
97
|
# group
|
|
82
98
|
values = []
|
|
@@ -91,23 +107,23 @@ def start_group(
|
|
|
91
107
|
if relevance_expression is True:
|
|
92
108
|
values.append("")
|
|
93
109
|
else:
|
|
94
|
-
values.append(
|
|
110
|
+
values.append(relevance_expression_str)
|
|
95
111
|
|
|
96
112
|
else:
|
|
97
113
|
values.append(get_xfrom_trad(strategy, cur_group, column, SURVEY_MAP))
|
|
98
114
|
df_survey.loc[len(df_survey)] = values
|
|
99
115
|
|
|
100
116
|
# calc
|
|
101
|
-
if
|
|
117
|
+
if calc and len(df_calculate[df_calculate["name"] == get_export_group_name(name)]) == 0:
|
|
102
118
|
calc_values = []
|
|
103
119
|
for column in SURVEY_MAP:
|
|
104
120
|
if column == "type":
|
|
105
121
|
calc_values.append("calculate")
|
|
106
122
|
elif column == "name":
|
|
107
|
-
value =
|
|
123
|
+
value = get_export_name(calc)
|
|
108
124
|
calc_values.append(value)
|
|
109
125
|
elif column == "calculation":
|
|
110
|
-
calc_values.append(
|
|
126
|
+
calc_values.append(f"number({strategy.get_tricc_operation_expression(calc.expression)}")
|
|
111
127
|
elif column == "relevance":
|
|
112
128
|
calc_values.append("")
|
|
113
129
|
else:
|
|
@@ -376,7 +392,7 @@ def get_attr_if_exists(strategy, node, column, map_array):
|
|
|
376
392
|
|
|
377
393
|
elif isinstance(value, (TriccOperation, TriccStatic, TriccReference)):
|
|
378
394
|
expression = strategy.get_tricc_operation_expression(value)
|
|
379
|
-
return expression
|
|
395
|
+
return expression
|
|
380
396
|
elif value is not None:
|
|
381
397
|
return str(value) if not isinstance(value, dict) else value
|
|
382
398
|
else:
|
|
@@ -399,7 +415,7 @@ def get_more_info_select(strategy, node):
|
|
|
399
415
|
if column == "type":
|
|
400
416
|
values.append("select_one more_info")
|
|
401
417
|
elif column == "label":
|
|
402
|
-
values.append(
|
|
418
|
+
values.append(strategy.get_empty_label())
|
|
403
419
|
elif column == "name":
|
|
404
420
|
values.append(get_export_name(node) + "_optin")
|
|
405
421
|
elif column == "hint":
|
|
@@ -43,6 +43,9 @@ class BaseOutPutStrategy:
|
|
|
43
43
|
|
|
44
44
|
self.export(self.project.start_pages, version=version)
|
|
45
45
|
|
|
46
|
+
logger.info("validate the output")
|
|
47
|
+
self.validate()
|
|
48
|
+
|
|
46
49
|
# walking function
|
|
47
50
|
def process_base(self, start_pages, **kwargs):
|
|
48
51
|
# for each node, check if condition is required issubclass(TriccNodeDisplayModel)
|
|
@@ -106,6 +109,10 @@ class BaseOutPutStrategy:
|
|
|
106
109
|
def export(self, **kwargs):
|
|
107
110
|
pass
|
|
108
111
|
|
|
112
|
+
@abc.abstractmethod
|
|
113
|
+
def validate(self):
|
|
114
|
+
pass
|
|
115
|
+
|
|
109
116
|
def tricc_operation_equal(self, ref_expressions):
|
|
110
117
|
# r[0] = r[1]
|
|
111
118
|
raise NotImplementedError("This type of opreration is not supported in this strategy")
|