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
tests/build.py
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
from tricc_oo.strategies.output.spice import SpiceStrategy # noqa: F401
|
|
2
|
+
from tricc_oo.strategies.output.xlsform_cht_hf import XLSFormCHTHFStrategy # noqa: F401
|
|
3
|
+
from tricc_oo.strategies.output.xlsform_cht import XLSFormCHTStrategy # noqa: F401
|
|
4
|
+
from tricc_oo.strategies.output.xlsform_cdss import XLSFormCDSSStrategy # noqa: F401
|
|
5
|
+
from tricc_oo.strategies.output.xls_form import XLSFormStrategy # noqa: F401
|
|
6
|
+
from tricc_oo.strategies.output.openmrs_form import OpenMRSStrategy # noqa: F401
|
|
7
|
+
from tricc_oo.strategies.output.fhir_form import FHIRStrategy # noqa: F401
|
|
8
|
+
from tricc_oo.strategies.output.html_form import HTMLStrategy # noqa: F401
|
|
9
|
+
from tricc_oo.strategies.output.dhis2_form import DHIS2Strategy # noqa: F401
|
|
10
|
+
from tricc_oo.strategies.input.drawio import DrawioStrategy # noqa: F401
|
|
1
11
|
import getopt
|
|
2
|
-
import gettext
|
|
3
12
|
import logging
|
|
4
13
|
import os
|
|
5
14
|
import sys
|
|
6
15
|
import gc
|
|
7
|
-
import shutil
|
|
8
16
|
from pathlib import Path
|
|
9
17
|
|
|
10
18
|
# set up logging to file
|
|
@@ -23,15 +31,9 @@ langs = SingletonLangClass()
|
|
|
23
31
|
# langs.add_trad('fr', fr)
|
|
24
32
|
# langs.add_trad('en', en)
|
|
25
33
|
|
|
26
|
-
from tricc_oo.strategies.input.drawio import DrawioStrategy
|
|
27
34
|
|
|
28
35
|
# from tricc_oo.serializers.medalcreator import execute
|
|
29
36
|
|
|
30
|
-
from tricc_oo.strategies.output.xls_form import XLSFormStrategy
|
|
31
|
-
from tricc_oo.strategies.output.xlsform_cdss import XLSFormCDSSStrategy
|
|
32
|
-
from tricc_oo.strategies.output.xlsform_cht import XLSFormCHTStrategy
|
|
33
|
-
from tricc_oo.strategies.output.xlsform_cht_hf import XLSFormCHTHFStrategy
|
|
34
|
-
from tricc_oo.strategies.output.spice import SpiceStrategy
|
|
35
37
|
|
|
36
38
|
def setup_logger(
|
|
37
39
|
logger_name,
|
|
@@ -39,15 +41,15 @@ def setup_logger(
|
|
|
39
41
|
level=logging.INFO,
|
|
40
42
|
formatting="[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s",
|
|
41
43
|
):
|
|
42
|
-
|
|
44
|
+
logger = logging.getLogger(logger_name)
|
|
43
45
|
formatter = logging.Formatter(formatting)
|
|
44
46
|
file_handler = logging.FileHandler(log_file, mode="w+")
|
|
45
47
|
file_handler.setFormatter(formatter)
|
|
46
48
|
stream_handler = logging.StreamHandler()
|
|
47
49
|
stream_handler.setFormatter(formatter)
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
logger.setLevel(level)
|
|
52
|
+
logger.addHandler(file_handler)
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
class ColorFormatter(logging.Formatter):
|
|
@@ -94,9 +96,7 @@ LEVELS = {
|
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def print_help():
|
|
97
|
-
print(
|
|
98
|
-
"-i / --input draw.io filepath (MANDATORY) or directory containing drawio files"
|
|
99
|
-
)
|
|
99
|
+
print("-i / --input draw.io filepath (MANDATORY) or directory containing drawio files")
|
|
100
100
|
print("-o / --output xls file ")
|
|
101
101
|
print("-d form_id ")
|
|
102
102
|
print("-s L4 system/strategy (odk, cht, cc)")
|
|
@@ -116,9 +116,7 @@ if __name__ == "__main__":
|
|
|
116
116
|
input_strategy = "DrawioStrategy"
|
|
117
117
|
output_strategy = "XLSFormStrategy"
|
|
118
118
|
try:
|
|
119
|
-
opts, args = getopt.getopt(
|
|
120
|
-
sys.argv[1:], "hti:o:s:I:O:l:d:D:", ["input=", "output=", "help", "trads"]
|
|
121
|
-
)
|
|
119
|
+
opts, args = getopt.getopt(sys.argv[1:], "hti:o:s:I:O:l:d:D:", ["input=", "output=", "help", "trads"])
|
|
122
120
|
except getopt.GetoptError:
|
|
123
121
|
print_help()
|
|
124
122
|
sys.exit(1)
|
|
@@ -145,7 +143,7 @@ if __name__ == "__main__":
|
|
|
145
143
|
if in_filepath is None:
|
|
146
144
|
print_help()
|
|
147
145
|
sys.exit(2)
|
|
148
|
-
|
|
146
|
+
|
|
149
147
|
if not download_dir:
|
|
150
148
|
download_dir = out_path
|
|
151
149
|
debug_path = os.fspath(out_path + "/debug.log")
|
|
@@ -165,7 +163,7 @@ if __name__ == "__main__":
|
|
|
165
163
|
setup_logger("default", debug_file_path, logging.INFO)
|
|
166
164
|
file_content = []
|
|
167
165
|
files = []
|
|
168
|
-
in_filepath_list = in_filepath.split(
|
|
166
|
+
in_filepath_list = in_filepath.split(",")
|
|
169
167
|
for in_filepath in in_filepath_list:
|
|
170
168
|
pre, ext = os.path.splitext(in_filepath)
|
|
171
169
|
|
|
@@ -173,18 +171,13 @@ if __name__ == "__main__":
|
|
|
173
171
|
# if output file path not specified, just chagne the extension
|
|
174
172
|
out_path = os.path.dirname(pre)
|
|
175
173
|
|
|
176
|
-
|
|
177
174
|
if os.path.isdir(in_filepath):
|
|
178
|
-
files = [
|
|
179
|
-
os.path.join(in_filepath, f)
|
|
180
|
-
for f in os.listdir(in_filepath)
|
|
181
|
-
if f.endswith(".drawio")
|
|
182
|
-
]
|
|
175
|
+
files = [os.path.join(in_filepath, f) for f in os.listdir(in_filepath) if f.endswith(".drawio")]
|
|
183
176
|
elif os.path.isfile(in_filepath) and in_filepath.endswith(".drawio"):
|
|
184
177
|
files = [in_filepath]
|
|
185
|
-
|
|
178
|
+
|
|
186
179
|
for f in files:
|
|
187
|
-
with open(f,
|
|
180
|
+
with open(f, "r") as s:
|
|
188
181
|
content = s.read()
|
|
189
182
|
# present issue with some drawio file that miss the XML header
|
|
190
183
|
|
|
@@ -193,7 +186,6 @@ if __name__ == "__main__":
|
|
|
193
186
|
logger.critical(f"{in_filepath} is neither a drawio file nor a directory containing drawio files")
|
|
194
187
|
exit(1)
|
|
195
188
|
|
|
196
|
-
|
|
197
189
|
strategy = globals()[input_strategy](files)
|
|
198
190
|
logger.info(f"build the graph from strategy {input_strategy}")
|
|
199
191
|
media_path = os.path.join(out_path, "media-tmp")
|
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()
|
tests/test_cql.py
CHANGED
|
@@ -1,51 +1,40 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
|
|
3
|
-
from tricc_oo.converters.cql_to_operation import
|
|
4
|
-
from tricc_oo.models.base import
|
|
3
|
+
from tricc_oo.converters.cql_to_operation import transform_cql_to_operation
|
|
4
|
+
from tricc_oo.models.base import TriccOperator, TriccOperation, TriccStatic, TriccReference
|
|
5
|
+
|
|
5
6
|
|
|
6
7
|
class TestCql(unittest.TestCase):
|
|
7
8
|
def test_and(self):
|
|
8
|
-
if_cql = "
|
|
9
|
+
if_cql = '"p_weight" is not null and "p_age" > 2'
|
|
9
10
|
dg_operation = transform_cql_to_operation(if_cql)
|
|
10
11
|
dg_expected = TriccOperation(
|
|
11
12
|
operator=TriccOperator.AND,
|
|
12
13
|
reference=[
|
|
13
|
-
TriccOperation(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
TriccOperation(
|
|
17
|
-
operator=TriccOperator.ISNULL,
|
|
18
|
-
reference=[TriccReference("p_weight")]
|
|
19
|
-
)
|
|
20
|
-
]
|
|
21
|
-
),
|
|
22
|
-
TriccOperation(
|
|
23
|
-
operator=TriccOperator.MORE,
|
|
24
|
-
reference=[
|
|
25
|
-
TriccReference("p_age"),
|
|
26
|
-
TriccStatic(
|
|
27
|
-
value=2
|
|
28
|
-
)
|
|
29
|
-
]
|
|
30
|
-
)
|
|
31
|
-
]
|
|
14
|
+
TriccOperation(operator=TriccOperator.ISNOTNULL, reference=[TriccReference("p_weight")]),
|
|
15
|
+
TriccOperation(operator=TriccOperator.MORE, reference=[TriccReference("p_age"), TriccStatic(value=2)]),
|
|
16
|
+
],
|
|
32
17
|
)
|
|
33
18
|
self.assertEqual(str(dg_operation), str(dg_expected))
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
|
|
36
20
|
def test_durg_doage(self):
|
|
37
|
-
if_cql =
|
|
21
|
+
if_cql = 'DrugDosage(\'paracetamol\', "p_weight", "p_age")'
|
|
38
22
|
dg_operation = transform_cql_to_operation(if_cql)
|
|
39
23
|
dg_expected = TriccOperation(
|
|
40
24
|
operator=TriccOperator.DRUG_DOSAGE,
|
|
41
|
-
reference=[
|
|
42
|
-
TriccStatic(value='paracetamol'),
|
|
43
|
-
TriccReference("p_weight"),
|
|
44
|
-
TriccReference("p_age")
|
|
45
|
-
]
|
|
25
|
+
reference=[TriccStatic(value="paracetamol"), TriccReference("p_weight"), TriccReference("p_age")],
|
|
46
26
|
)
|
|
47
27
|
self.assertEqual(str(dg_operation), str(dg_expected))
|
|
48
|
-
|
|
28
|
+
|
|
29
|
+
def test_implied_concat(self):
|
|
30
|
+
if_cql = "'A' & \"B\" & 'C'"
|
|
31
|
+
cc_operation = transform_cql_to_operation(if_cql)
|
|
32
|
+
cc_expected = TriccOperation(
|
|
33
|
+
operator=TriccOperator.CONCATENATE,
|
|
34
|
+
reference=[TriccStatic(value="A"), TriccReference("B"), TriccStatic(value="C")],
|
|
35
|
+
)
|
|
36
|
+
self.assertEqual(str(cc_operation), str(cc_expected))
|
|
37
|
+
|
|
49
38
|
def test_if(self):
|
|
50
39
|
if_cql = "if AgeInDays() < 60 then 'newborn' else 'child'"
|
|
51
40
|
if_operation = transform_cql_to_operation(if_cql)
|
|
@@ -54,61 +43,36 @@ class TestCql(unittest.TestCase):
|
|
|
54
43
|
reference=[
|
|
55
44
|
TriccOperation(
|
|
56
45
|
operator=TriccOperator.LESS,
|
|
57
|
-
reference=[
|
|
58
|
-
TriccOperation(
|
|
59
|
-
operator=TriccOperator.AGE_DAY,
|
|
60
|
-
reference=[]
|
|
61
|
-
),
|
|
62
|
-
TriccStatic(
|
|
63
|
-
value=60
|
|
64
|
-
)
|
|
65
|
-
]
|
|
46
|
+
reference=[TriccOperation(operator=TriccOperator.AGE_DAY, reference=[]), TriccStatic(value=60)],
|
|
66
47
|
),
|
|
67
|
-
TriccStatic(value=
|
|
68
|
-
TriccStatic(value=
|
|
69
|
-
]
|
|
48
|
+
TriccStatic(value="newborn"),
|
|
49
|
+
TriccStatic(value="child"),
|
|
50
|
+
],
|
|
70
51
|
)
|
|
71
52
|
self.assertEqual(str(if_operation), str(if_expected))
|
|
72
53
|
|
|
73
54
|
def test_case(self):
|
|
74
55
|
case_cql = """
|
|
75
|
-
case AgeInMonths()
|
|
76
|
-
when 0 then 'newborn'
|
|
77
|
-
when 1 then 'newborn'
|
|
56
|
+
case AgeInMonths()
|
|
57
|
+
when 0 then 'newborn'
|
|
58
|
+
when 1 then 'newborn'
|
|
78
59
|
else 'child' end
|
|
79
60
|
"""
|
|
80
61
|
case_operation = transform_cql_to_operation(case_cql)
|
|
81
62
|
case_expected = TriccOperation(
|
|
82
63
|
operator=TriccOperator.CASE,
|
|
83
64
|
reference=[
|
|
84
|
-
TriccOperation(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
),
|
|
88
|
-
|
|
89
|
-
TriccStatic(
|
|
90
|
-
value=0
|
|
91
|
-
),
|
|
92
|
-
TriccStatic(
|
|
93
|
-
value="newborn"
|
|
94
|
-
)
|
|
95
|
-
],
|
|
96
|
-
[
|
|
97
|
-
TriccStatic(
|
|
98
|
-
value=1
|
|
99
|
-
),
|
|
100
|
-
TriccStatic(
|
|
101
|
-
value="newborn"
|
|
102
|
-
)
|
|
103
|
-
],
|
|
104
|
-
TriccStatic(value='child'),
|
|
105
|
-
]
|
|
65
|
+
TriccOperation(operator=TriccOperator.AGE_MONTH, reference=[]),
|
|
66
|
+
[TriccStatic(value=0), TriccStatic(value="newborn")],
|
|
67
|
+
[TriccStatic(value=1), TriccStatic(value="newborn")],
|
|
68
|
+
TriccStatic(value="child"),
|
|
69
|
+
],
|
|
106
70
|
)
|
|
107
71
|
self.assertEqual(str(case_operation), str(case_expected))
|
|
108
72
|
|
|
109
73
|
def test_ifs(self):
|
|
110
74
|
case_cql = """
|
|
111
|
-
case
|
|
75
|
+
case
|
|
112
76
|
when AgeInMonths() <= 2 then true
|
|
113
77
|
when AgeInYears() > 5 then true
|
|
114
78
|
else false end
|
|
@@ -121,57 +85,32 @@ class TestCql(unittest.TestCase):
|
|
|
121
85
|
TriccOperation(
|
|
122
86
|
operator=TriccOperator.LESS_OR_EQUAL,
|
|
123
87
|
reference=[
|
|
124
|
-
TriccOperation(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
),
|
|
128
|
-
TriccStatic(
|
|
129
|
-
value=2
|
|
130
|
-
)
|
|
131
|
-
]
|
|
88
|
+
TriccOperation(operator=TriccOperator.AGE_MONTH, reference=[]),
|
|
89
|
+
TriccStatic(value=2),
|
|
90
|
+
],
|
|
132
91
|
),
|
|
133
|
-
TriccStatic(
|
|
134
|
-
value=True
|
|
135
|
-
)
|
|
92
|
+
TriccStatic(value=True),
|
|
136
93
|
],
|
|
137
94
|
[
|
|
138
95
|
TriccOperation(
|
|
139
96
|
operator=TriccOperator.MORE,
|
|
140
|
-
reference=[
|
|
141
|
-
TriccOperation(
|
|
142
|
-
operator=TriccOperator.AGE_YEAR,
|
|
143
|
-
reference=[]
|
|
144
|
-
),
|
|
145
|
-
TriccStatic(
|
|
146
|
-
value=5
|
|
147
|
-
)
|
|
148
|
-
]
|
|
97
|
+
reference=[TriccOperation(operator=TriccOperator.AGE_YEAR, reference=[]), TriccStatic(value=5)],
|
|
149
98
|
),
|
|
150
|
-
TriccStatic(
|
|
151
|
-
value=True
|
|
152
|
-
)
|
|
99
|
+
TriccStatic(value=True),
|
|
153
100
|
],
|
|
154
101
|
TriccStatic(value=False),
|
|
155
|
-
]
|
|
102
|
+
],
|
|
156
103
|
)
|
|
157
104
|
self.assertEqual(str(case_operation), str(case_expected))
|
|
158
105
|
|
|
159
|
-
|
|
160
106
|
def test_minus(self):
|
|
161
|
-
minus_cql =""" "WFL" >= -3"""
|
|
107
|
+
minus_cql = """ "WFL" >= -3"""
|
|
162
108
|
# minus_cql = """
|
|
163
109
|
# ("age_in_months" < 6 and "WFL" >= -3 and "WFL" < -2) is true
|
|
164
110
|
# """
|
|
165
111
|
minus_operation = transform_cql_to_operation(minus_cql)
|
|
166
112
|
minus_expected = TriccOperation(
|
|
167
|
-
TriccOperator.MORE_OR_EQUAL,
|
|
168
|
-
[
|
|
169
|
-
TriccReference("WFL"),
|
|
170
|
-
TriccOperation(
|
|
171
|
-
TriccOperator.MINUS,
|
|
172
|
-
[TriccStatic(3)]
|
|
173
|
-
)
|
|
174
|
-
]
|
|
113
|
+
TriccOperator.MORE_OR_EQUAL, [TriccReference("WFL"), TriccOperation(TriccOperator.MINUS, [TriccStatic(3)])]
|
|
175
114
|
)
|
|
176
115
|
self.assertEqual(str(minus_operation), str(minus_expected))
|
|
177
116
|
|
|
@@ -185,13 +124,13 @@ class TestCql(unittest.TestCase):
|
|
|
185
124
|
operator=TriccOperator.SELECTED,
|
|
186
125
|
reference=[
|
|
187
126
|
TriccReference("identifier"),
|
|
188
|
-
TriccStatic(value=
|
|
189
|
-
]
|
|
127
|
+
TriccStatic(value="code"),
|
|
128
|
+
],
|
|
190
129
|
)
|
|
191
|
-
]
|
|
130
|
+
],
|
|
192
131
|
)
|
|
193
132
|
self.assertEqual(str(dg_operation), str(dg_expected))
|
|
194
|
-
|
|
195
133
|
|
|
196
|
-
|
|
197
|
-
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
unittest.main()
|
tests/to_ocl.py
CHANGED
|
@@ -1,51 +1,49 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import json
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
from tricc_oo.converters.codesystem_to_ocl import transform_fhir_to_ocl
|
|
5
4
|
|
|
5
|
+
|
|
6
6
|
def find_and_process_codesystems(directory_path):
|
|
7
7
|
# Convert string path to Path object if not already
|
|
8
8
|
dir_path = Path(directory_path)
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
# Find all JSON files in the directory
|
|
11
11
|
for json_file in dir_path.glob("*.json"):
|
|
12
12
|
try:
|
|
13
13
|
# Read the JSON file
|
|
14
|
-
with open(json_file,
|
|
14
|
+
with open(json_file, "r", encoding="utf-8") as f:
|
|
15
15
|
data = json.load(f)
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
# Check if resource_type is CodeSystem
|
|
18
18
|
if data.get("resourceType") == "CodeSystem":
|
|
19
19
|
# Get the filename without extension for output naming
|
|
20
20
|
file_key = json_file.stem
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
# Write the original CodeSystem JSON
|
|
23
23
|
output_cs_path = dir_path / f"{file_key}_codesystem.json"
|
|
24
|
-
with open(output_cs_path, "w", encoding=
|
|
24
|
+
with open(output_cs_path, "w", encoding="utf-8") as file:
|
|
25
25
|
file.write(json.dumps(data, indent=4))
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
# Transform to OCL payload
|
|
28
28
|
ocl_payload = transform_fhir_to_ocl(
|
|
29
|
-
data,
|
|
30
|
-
source_name="ALM",
|
|
31
|
-
source_owner="pdelcroix",
|
|
32
|
-
source_owner_type="User"
|
|
29
|
+
data, source_name="ALM", source_owner="pdelcroix", source_owner_type="User"
|
|
33
30
|
)
|
|
34
|
-
|
|
31
|
+
|
|
35
32
|
# Save the transformed OCL payload
|
|
36
33
|
output_ocl_path = dir_path / f"{file_key}_ocl_bulk_upload.json"
|
|
37
|
-
with open(output_ocl_path, "w", encoding=
|
|
34
|
+
with open(output_ocl_path, "w", encoding="utf-8") as f:
|
|
38
35
|
for item in ocl_payload:
|
|
39
36
|
json_line = json.dumps(item.dict(exclude_none=True))
|
|
40
|
-
f.write(json_line +
|
|
41
|
-
|
|
37
|
+
f.write(json_line + "\n")
|
|
38
|
+
|
|
42
39
|
print(f"OCL bulk upload payload generated successfully for {file_key}!")
|
|
43
|
-
|
|
40
|
+
|
|
44
41
|
except json.JSONDecodeError:
|
|
45
42
|
print(f"Error: Invalid JSON in file {json_file}")
|
|
46
43
|
except Exception as e:
|
|
47
44
|
print(f"Error processing {json_file}: {str(e)}")
|
|
48
45
|
|
|
46
|
+
|
|
49
47
|
# Example usage
|
|
50
48
|
media_path = "path/to/your/directory" # Replace with your directory path
|
|
51
|
-
find_and_process_codesystems(media_path)
|
|
49
|
+
find_and_process_codesystems(media_path)
|