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.
- tests/build.py +13 -23
- tests/test_cql.py +37 -108
- 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 +125 -123
- tricc_oo/converters/datadictionnary.py +39 -53
- tricc_oo/converters/drawio_type_map.py +143 -61
- tricc_oo/converters/tricc_to_xls_form.py +14 -24
- tricc_oo/converters/utils.py +3 -3
- tricc_oo/converters/xml_to_tricc.py +286 -231
- tricc_oo/models/__init__.py +2 -1
- tricc_oo/models/base.py +300 -308
- tricc_oo/models/calculate.py +63 -49
- 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 +144 -88
- tricc_oo/parsers/xml.py +15 -30
- tricc_oo/serializers/planuml.py +4 -6
- tricc_oo/serializers/xls_form.py +81 -135
- tricc_oo/strategies/input/base_input_strategy.py +28 -32
- tricc_oo/strategies/input/drawio.py +57 -69
- tricc_oo/strategies/output/base_output_strategy.py +108 -67
- tricc_oo/strategies/output/spice.py +106 -127
- tricc_oo/strategies/output/xls_form.py +275 -200
- tricc_oo/strategies/output/xlsform_cdss.py +623 -142
- tricc_oo/strategies/output/xlsform_cht.py +114 -120
- tricc_oo/strategies/output/xlsform_cht_hf.py +13 -24
- tricc_oo/visitors/tricc.py +1191 -1021
- tricc_oo/visitors/utils.py +16 -16
- tricc_oo/visitors/xform_pd.py +91 -89
- {tricc_oo-1.5.21.dist-info → tricc_oo-1.5.23.dist-info}/METADATA +3 -1
- tricc_oo-1.5.23.dist-info/RECORD +47 -0
- tricc_oo-1.5.23.dist-info/licenses/LICENSE +373 -0
- tricc_oo-1.5.21.dist-info/RECORD +0 -46
- {tricc_oo-1.5.21.dist-info → tricc_oo-1.5.23.dist-info}/WHEEL +0 -0
- {tricc_oo-1.5.21.dist-info → tricc_oo-1.5.23.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
from antlr4 import
|
|
1
|
+
from antlr4.error.ErrorListener import ErrorListener
|
|
2
|
+
from antlr4 import CommonTokenStream, InputStream
|
|
2
3
|
from tricc_oo.converters.cql.cqlLexer import cqlLexer
|
|
3
4
|
from tricc_oo.converters.cql.cqlParser import cqlParser
|
|
4
5
|
from tricc_oo.converters.cql.cqlVisitor import cqlVisitor
|
|
5
6
|
from tricc_oo.converters.utils import clean_name
|
|
6
|
-
from tricc_oo.models.base import
|
|
7
|
+
from tricc_oo.models.base import (
|
|
8
|
+
TriccOperator,
|
|
9
|
+
TriccOperation,
|
|
10
|
+
TriccStatic,
|
|
11
|
+
TriccReference,
|
|
12
|
+
not_clean,
|
|
13
|
+
or_join,
|
|
14
|
+
and_join,
|
|
15
|
+
string_join,
|
|
16
|
+
)
|
|
7
17
|
import logging
|
|
8
18
|
|
|
9
19
|
logger = logging.getLogger("default")
|
|
@@ -14,17 +24,19 @@ NUMBER = 2
|
|
|
14
24
|
ANY = 3
|
|
15
25
|
|
|
16
26
|
FUNCTION_MAP = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
"AgeInYears": TriccOperator.AGE_YEAR,
|
|
28
|
+
"AgeInMonths": TriccOperator.AGE_MONTH,
|
|
29
|
+
"AgeInDays": TriccOperator.AGE_DAY,
|
|
30
|
+
"Coalesce": TriccOperator.COALESCE,
|
|
31
|
+
"Concatenate": TriccOperator.CONCATENATE,
|
|
32
|
+
"Izscore": TriccOperator.IZSCORE,
|
|
33
|
+
"Zscore": TriccOperator.ZSCORE,
|
|
34
|
+
"Round": TriccOperator.ROUND,
|
|
35
|
+
"Integer": TriccOperator.CAST_INTEGER,
|
|
36
|
+
"DrugDosage": TriccOperator.DRUG_DOSAGE,
|
|
37
|
+
"HasQualifier": TriccOperator.HAS_QUALIFIER,
|
|
38
|
+
"DateTimeToDecimal": TriccOperator.DATETIME_TO_DECIMAL,
|
|
39
|
+
"Count": TriccOperator.COUNT,
|
|
28
40
|
}
|
|
29
41
|
# TODO
|
|
30
42
|
# Min
|
|
@@ -32,41 +44,44 @@ FUNCTION_MAP = {
|
|
|
32
44
|
# Round
|
|
33
45
|
# this need to be done by contribution to DMN
|
|
34
46
|
|
|
47
|
+
|
|
35
48
|
class cqlToXlsFormVisitor(cqlVisitor):
|
|
36
49
|
def __init__(self):
|
|
37
50
|
self.xlsform_rows = []
|
|
38
|
-
self.errors= []
|
|
39
|
-
|
|
51
|
+
self.errors = []
|
|
52
|
+
|
|
40
53
|
def resolve_scv(self, arg):
|
|
41
|
-
|
|
42
|
-
# TODO
|
|
54
|
+
|
|
55
|
+
# TODO
|
|
43
56
|
# look for the system, if not found fallback on default system
|
|
44
57
|
# look for the code in the system
|
|
45
58
|
# if no code or not found return None
|
|
46
59
|
if arg.startswith('"') and arg.endswith('"'):
|
|
47
60
|
return TriccReference(arg[1:-1])
|
|
48
|
-
elif arg.lower() in [
|
|
49
|
-
return TriccStatic(arg.lower() ==
|
|
50
|
-
elif arg !=
|
|
61
|
+
elif arg.lower() in ["true", "false"]:
|
|
62
|
+
return TriccStatic(arg.lower() == "true")
|
|
63
|
+
elif arg != "runner":
|
|
51
64
|
self.errors.append(f"'{arg}' will be poccessed as reference ")
|
|
52
65
|
return TriccReference(arg)
|
|
53
|
-
|
|
66
|
+
|
|
54
67
|
else:
|
|
55
|
-
return
|
|
56
|
-
|
|
68
|
+
return "runner"
|
|
69
|
+
|
|
57
70
|
def translate(self, arg, type=ANY):
|
|
58
71
|
return self.resolve_scv(arg) or str(arg)
|
|
59
|
-
|
|
72
|
+
|
|
60
73
|
def visitExpressionDefinition(self, ctx):
|
|
61
74
|
identifier = ctx.identifier().getText()
|
|
62
75
|
expression = self.visit(ctx.expression())
|
|
63
|
-
self.xlsform_rows.append(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
self.xlsform_rows.append(
|
|
77
|
+
{
|
|
78
|
+
"type": "calculate",
|
|
79
|
+
"name": clean_name(identifier[1:-1].lower()),
|
|
80
|
+
"calculation": expression,
|
|
81
|
+
}
|
|
82
|
+
)
|
|
68
83
|
return expression
|
|
69
|
-
|
|
84
|
+
|
|
70
85
|
def visitIdentifier(self, arg):
|
|
71
86
|
return self.translate(arg.getText(), 1)
|
|
72
87
|
|
|
@@ -79,27 +94,25 @@ class cqlToXlsFormVisitor(cqlVisitor):
|
|
|
79
94
|
return aggregate
|
|
80
95
|
else:
|
|
81
96
|
aggregate = aggregate if isinstance(aggregate, list) else [aggregate]
|
|
82
|
-
return [
|
|
83
|
-
*aggregate,
|
|
84
|
-
nextResult
|
|
85
|
-
]
|
|
97
|
+
return [*aggregate, nextResult]
|
|
86
98
|
else:
|
|
87
99
|
return nextResult
|
|
88
100
|
|
|
89
101
|
def visitExpression(self, ctx):
|
|
90
102
|
return self.visitChildren(ctx)
|
|
103
|
+
|
|
91
104
|
def visitThisInvocation(self, ctx):
|
|
92
|
-
return
|
|
93
|
-
|
|
105
|
+
return "$this"
|
|
106
|
+
|
|
94
107
|
def visitBooleanLiteral(self, ctx):
|
|
95
|
-
literal =
|
|
96
|
-
if literal ==
|
|
108
|
+
literal = ctx.getChild(0).getText()
|
|
109
|
+
if literal == "true":
|
|
97
110
|
return TriccStatic(True)
|
|
98
|
-
elif literal ==
|
|
111
|
+
elif literal == "false":
|
|
99
112
|
return TriccStatic(False)
|
|
100
113
|
else:
|
|
101
114
|
return None
|
|
102
|
-
|
|
115
|
+
|
|
103
116
|
def visitFunctionInvocation(self, ctx, operator=TriccOperator.NATIVE):
|
|
104
117
|
if ctx.getChildCount() == 1:
|
|
105
118
|
return self.visitFunctionInvocation(ctx.getChild(0))
|
|
@@ -115,54 +128,45 @@ class cqlToXlsFormVisitor(cqlVisitor):
|
|
|
115
128
|
args = ctx.paramList()
|
|
116
129
|
if args:
|
|
117
130
|
op.reference += [self.visit(arg) for arg in args.expression() if arg]
|
|
118
|
-
|
|
119
131
|
|
|
120
132
|
return op
|
|
121
|
-
|
|
133
|
+
|
|
122
134
|
def __std_function(self, ctx, operator=TriccOperator.NATIVE):
|
|
123
135
|
args = ctx.expressions
|
|
124
136
|
if args:
|
|
125
137
|
args = [self.visit(arg) for arg in ctx.expression() if arg]
|
|
126
138
|
op = TriccOperation(operator)
|
|
127
|
-
op.reference = [
|
|
128
|
-
*args
|
|
129
|
-
]
|
|
139
|
+
op.reference = [*args]
|
|
130
140
|
|
|
131
141
|
def visitParenthesizedTerm(self, ctx):
|
|
132
142
|
return TriccOperation(TriccOperator.PARENTHESIS, [self.visitChildren(ctx)])
|
|
133
|
-
|
|
143
|
+
|
|
134
144
|
def visitMemberInvocation(self, ctx):
|
|
135
145
|
return self.visitChildren(ctx)
|
|
136
146
|
|
|
137
147
|
def visitMembershipExpression(self, ctx):
|
|
138
148
|
function_name = ctx.getChild(1).getText()
|
|
139
149
|
return self._get_membership_expression(ctx, function_name)
|
|
140
|
-
|
|
150
|
+
|
|
141
151
|
def _get_membership_expression(self, ctx, function_name):
|
|
142
152
|
left = self.visit(ctx.expression(0))
|
|
143
153
|
right = self.visit(ctx.expression(1))
|
|
144
|
-
if function_name ==
|
|
154
|
+
if function_name == "in":
|
|
145
155
|
op = TriccOperation(TriccOperator.SELECTED)
|
|
146
|
-
op.reference = [
|
|
147
|
-
|
|
148
|
-
left
|
|
149
|
-
]
|
|
150
|
-
elif function_name == 'contains':
|
|
156
|
+
op.reference = [right, left]
|
|
157
|
+
elif function_name == "contains":
|
|
151
158
|
op = TriccOperation(TriccOperator.CONTAINS)
|
|
152
|
-
op.reference = [
|
|
153
|
-
left,
|
|
154
|
-
right
|
|
155
|
-
]
|
|
159
|
+
op.reference = [left, right]
|
|
156
160
|
return op
|
|
157
|
-
|
|
161
|
+
|
|
158
162
|
def visitNegateMembershipExpression(self, ctx):
|
|
159
163
|
function_name = ctx.getChild(2).getText()
|
|
160
164
|
return not_clean(self._get_membership_expression(ctx, function_name))
|
|
161
|
-
|
|
165
|
+
|
|
162
166
|
def visitInvocationExpressionTerm(self, ctx):
|
|
163
167
|
result = super().visitInvocationExpressionTerm(ctx)
|
|
164
168
|
if isinstance(result, list) and all(isinstance(x, TriccStatic) for x in result):
|
|
165
|
-
value =
|
|
169
|
+
value = ".".join([x.value for x in result])
|
|
166
170
|
logger.warning(f"guessed reference for '{value}'")
|
|
167
171
|
return TriccReference(value)
|
|
168
172
|
return result
|
|
@@ -179,19 +183,19 @@ class cqlToXlsFormVisitor(cqlVisitor):
|
|
|
179
183
|
expr = self.visit(ctx.expression())
|
|
180
184
|
params = [c.getText() for c in list(ctx.getChildren())[2:]]
|
|
181
185
|
op = TriccOperation(
|
|
182
|
-
operator
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
operator={
|
|
187
|
+
"true": TriccOperator.ISTRUE,
|
|
188
|
+
"false": TriccOperator.ISFALSE,
|
|
189
|
+
"null": TriccOperator.ISNULL,
|
|
186
190
|
}[params[-1]],
|
|
187
|
-
reference
|
|
191
|
+
reference=[expr],
|
|
188
192
|
)
|
|
189
|
-
|
|
190
|
-
if params[0] ==
|
|
193
|
+
|
|
194
|
+
if params[0] == "not":
|
|
191
195
|
if isinstance(op, TriccStatic) and isinstance(op.value, str):
|
|
192
196
|
logger.warning(f"not operator on a string {op.value}")
|
|
193
197
|
op = not_clean(op)
|
|
194
|
-
|
|
198
|
+
|
|
195
199
|
return op
|
|
196
200
|
|
|
197
201
|
def visitExistenceExpression(self, ctx):
|
|
@@ -205,17 +209,17 @@ class cqlToXlsFormVisitor(cqlVisitor):
|
|
|
205
209
|
|
|
206
210
|
def visitOrExpression(self, ctx):
|
|
207
211
|
return self.__std_operator(TriccOperator.OR, ctx)
|
|
208
|
-
|
|
212
|
+
|
|
209
213
|
def __std_operator(self, operator, ctx):
|
|
210
|
-
if hasattr(ctx,
|
|
214
|
+
if hasattr(ctx, "expression"):
|
|
211
215
|
left = self.visit(ctx.expression(0))
|
|
212
216
|
right = self.visit(ctx.expression(1))
|
|
213
|
-
elif hasattr(ctx,
|
|
217
|
+
elif hasattr(ctx, "expressionTerm"):
|
|
214
218
|
left = self.visit(ctx.expressionTerm(0))
|
|
215
219
|
right = self.visit(ctx.expressionTerm(1))
|
|
216
|
-
if
|
|
220
|
+
if operator == TriccOperator.AND:
|
|
217
221
|
return and_join([left, right])
|
|
218
|
-
elif
|
|
222
|
+
elif operator == TriccOperator.OR:
|
|
219
223
|
return or_join([left, right])
|
|
220
224
|
elif operator == TriccOperator.CONCATENATE:
|
|
221
225
|
left = "" if left is None else left
|
|
@@ -250,94 +254,91 @@ class cqlToXlsFormVisitor(cqlVisitor):
|
|
|
250
254
|
right = self.visit(ctx.expression(1))
|
|
251
255
|
op_text = ctx.getChild(1).getText()
|
|
252
256
|
op_map = {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
"<": TriccOperator.LESS,
|
|
258
|
+
"<=": TriccOperator.LESS_OR_EQUAL,
|
|
259
|
+
">": TriccOperator.MORE,
|
|
260
|
+
">=": TriccOperator.MORE_OR_EQUAL,
|
|
261
|
+
"=": TriccOperator.EQUAL,
|
|
262
|
+
"!=": TriccOperator.NOTEQUAL,
|
|
259
263
|
}
|
|
260
264
|
op = TriccOperation(op_map[op_text])
|
|
261
265
|
op.reference = [left, right]
|
|
262
266
|
return op
|
|
263
267
|
|
|
264
268
|
def visitInvocationExpression(self, ctx):
|
|
265
|
-
raise NotImplementedError(
|
|
266
|
-
|
|
269
|
+
raise NotImplementedError("Invocation not supported")
|
|
270
|
+
|
|
267
271
|
def visitIndexerExpression(self, ctx):
|
|
268
|
-
raise NotImplementedError(
|
|
269
|
-
|
|
272
|
+
raise NotImplementedError("Indexer not supported")
|
|
273
|
+
|
|
270
274
|
def visitCastExpression(self, ctx):
|
|
271
275
|
# TODO
|
|
272
|
-
raise NotImplementedError(
|
|
273
|
-
|
|
276
|
+
raise NotImplementedError("Cast not supported")
|
|
277
|
+
|
|
274
278
|
def visitPolarityExpressionTerm(self, ctx):
|
|
275
|
-
if ctx.getChild(0).getText() ==
|
|
279
|
+
if ctx.getChild(0).getText() == "-":
|
|
276
280
|
return TriccOperation(TriccOperator.MINUS, [self.visit(ctx.getChild(1))])
|
|
277
|
-
|
|
281
|
+
|
|
278
282
|
def visitMultiplicationExpressionTerm(self, ctx):
|
|
279
283
|
op_text = ctx.getChild(1).getText()
|
|
280
284
|
op_map = {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
"*": TriccOperator.MULTIPLIED,
|
|
286
|
+
"div": TriccOperator.DIVIDED,
|
|
287
|
+
"/": TriccOperator.DIVIDED,
|
|
288
|
+
"%": TriccOperator.MODULO,
|
|
289
|
+
"mod": TriccOperator.MODULO,
|
|
286
290
|
}
|
|
287
291
|
return self.__std_operator(op_map.get(op_text), ctx)
|
|
288
|
-
|
|
292
|
+
|
|
289
293
|
def visitAdditionExpressionTerm(self, ctx):
|
|
290
294
|
op_text = ctx.getChild(1).getText()
|
|
291
295
|
op_map = {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
296
|
+
"+": TriccOperator.PLUS,
|
|
297
|
+
"-": TriccOperator.MINUS,
|
|
298
|
+
"&": TriccOperator.CONCATENATE,
|
|
295
299
|
}
|
|
296
300
|
return self.__std_operator(op_map.get(op_text), ctx)
|
|
297
|
-
|
|
298
|
-
|
|
301
|
+
|
|
299
302
|
def visitTypeExpression(self, ctx):
|
|
300
303
|
to_type = ctx.getChild(2).getText()
|
|
301
304
|
expression = self.visit(ctx.getChild(0))
|
|
302
|
-
if to_type ==
|
|
305
|
+
if to_type == "int" or to_type == "integer":
|
|
303
306
|
return TriccOperation(TriccOperator.CAST_INTEGER, [expression])
|
|
304
|
-
elif to_type ==
|
|
307
|
+
elif to_type == "float" or to_type == "number":
|
|
305
308
|
return TriccOperation(TriccOperator.CAST_NUMBER, [expression])
|
|
306
|
-
elif to_type ==
|
|
309
|
+
elif to_type == "date":
|
|
307
310
|
return TriccOperation(TriccOperator.CAST_DATE, [expression])
|
|
308
311
|
else:
|
|
309
|
-
raise NotImplementedError(f
|
|
310
|
-
|
|
311
|
-
def visitUnionExpression(self, ctx):
|
|
312
|
-
raise NotImplementedError('union not supported')
|
|
312
|
+
raise NotImplementedError(f"cast {to_type} not supported")
|
|
313
313
|
|
|
314
|
-
|
|
314
|
+
def visitUnionExpression(self, ctx):
|
|
315
|
+
raise NotImplementedError("union not supported")
|
|
315
316
|
|
|
316
317
|
def visitQuantity(self, ctx):
|
|
317
318
|
# TODO
|
|
318
|
-
raise NotImplementedError(
|
|
319
|
+
raise NotImplementedError("Indexer not supported")
|
|
319
320
|
|
|
320
321
|
def visitUnit(self, ctx):
|
|
321
|
-
raise NotImplementedError(
|
|
322
|
+
raise NotImplementedError("Indexer not supported")
|
|
322
323
|
|
|
323
324
|
def visistDateTimePrecision(self, ctx):
|
|
324
325
|
# TODO
|
|
325
|
-
raise NotImplementedError(
|
|
326
|
+
raise NotImplementedError("Indexer not supported")
|
|
326
327
|
|
|
327
328
|
def visitPluralDateTimePrecision(self, ctx):
|
|
328
329
|
# TODO
|
|
329
|
-
raise NotImplementedError(
|
|
330
|
+
raise NotImplementedError("Indexer not supported")
|
|
330
331
|
|
|
331
|
-
#def visitQualifiedIdentifier(self, ctx):
|
|
332
|
+
# def visitQualifiedIdentifier(self, ctx):
|
|
332
333
|
# raise NotImplementedError('qualifiedIdentifier not supported')
|
|
333
334
|
|
|
334
335
|
def visitTypeSpecifier(self, ctx):
|
|
335
|
-
raise NotImplementedError(
|
|
336
|
+
raise NotImplementedError("typeSpecifier not supported")
|
|
336
337
|
|
|
337
338
|
def visitRetrieve(self, ctx):
|
|
338
339
|
# TODO
|
|
339
|
-
raise NotImplementedError(
|
|
340
|
-
|
|
340
|
+
raise NotImplementedError("retrieve not supported")
|
|
341
|
+
|
|
341
342
|
def visitEqualityExpression(self, ctx):
|
|
342
343
|
return self.visitExpressionComparison(ctx)
|
|
343
344
|
|
|
@@ -362,30 +363,30 @@ class cqlToXlsFormVisitor(cqlVisitor):
|
|
|
362
363
|
op = TriccOperation(TriccOperator.IF)
|
|
363
364
|
op.reference = [condition, true_value, false_value]
|
|
364
365
|
return op
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
from antlr4.error.ErrorListener import ErrorListener
|
|
366
|
+
|
|
368
367
|
|
|
369
368
|
class CQLErrorListener(ErrorListener):
|
|
370
369
|
context = None
|
|
370
|
+
|
|
371
371
|
def __init__(self, context=None):
|
|
372
372
|
super(CQLErrorListener, self).__init__()
|
|
373
373
|
self.errors = []
|
|
374
374
|
self.context = context
|
|
375
375
|
|
|
376
376
|
def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
|
|
377
|
-
error = f"{self.context} \n" if self.context else
|
|
377
|
+
error = f"{self.context} \n" if self.context else ""
|
|
378
378
|
error += f"Line {line}:{column} - {msg}"
|
|
379
379
|
self.errors.append(error)
|
|
380
380
|
|
|
381
|
+
|
|
381
382
|
def transform_cql_to_operation(cql_input, context=None):
|
|
382
383
|
lib_input = f"""
|
|
383
384
|
library runner
|
|
384
|
-
|
|
385
|
+
|
|
385
386
|
define "calc":
|
|
386
387
|
{cql_input.replace('−', '-')}
|
|
387
388
|
"""
|
|
388
|
-
input_stream = InputStream(chr(10).join(lib_input.split(
|
|
389
|
+
input_stream = InputStream(chr(10).join(lib_input.split("\n")))
|
|
389
390
|
lexer = cqlLexer(input_stream)
|
|
390
391
|
stream = CommonTokenStream(lexer)
|
|
391
392
|
parser = cqlParser(stream)
|
|
@@ -406,14 +407,15 @@ def transform_cql_to_operation(cql_input, context=None):
|
|
|
406
407
|
|
|
407
408
|
# If no errors, proceed with visitor
|
|
408
409
|
visitor = cqlToXlsFormVisitor()
|
|
409
|
-
|
|
410
|
+
|
|
410
411
|
visitor.visit(tree)
|
|
411
412
|
if visitor.errors:
|
|
412
413
|
logger.warning(f"while visiting cql: \n{cql_input}")
|
|
413
414
|
for e in visitor.errors:
|
|
414
415
|
logger.warning(e)
|
|
415
|
-
|
|
416
|
-
return visitor.xlsform_rows[0][
|
|
416
|
+
|
|
417
|
+
return visitor.xlsform_rows[0]["calculation"]
|
|
418
|
+
|
|
417
419
|
|
|
418
420
|
def transform_cql_lib_to_operations(cql_input):
|
|
419
421
|
input_stream = InputStream(cql_input)
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
from fhir.resources.codesystem import (
|
|
2
2
|
CodeSystem,
|
|
3
3
|
CodeSystemConcept,
|
|
4
|
-
|
|
5
|
-
CodeSystemConceptProperty
|
|
4
|
+
CodeSystemConceptProperty,
|
|
6
5
|
)
|
|
7
|
-
from fhir.resources.codeableconcept import CodeableConcept
|
|
8
|
-
from fhir.resources.range import Range
|
|
9
|
-
from fhir.resources.quantity import Quantity
|
|
10
|
-
from fhir.resources.coding import Coding
|
|
11
6
|
|
|
12
|
-
from fhir.resources.valueset import ValueSet
|
|
7
|
+
from fhir.resources.valueset import ValueSet
|
|
13
8
|
import logging
|
|
14
9
|
|
|
15
10
|
logger = logging.getLogger("default")
|
|
16
11
|
|
|
17
12
|
|
|
18
13
|
def lookup_codesystems_code(codesystems, ref):
|
|
19
|
-
if ref.startswith(
|
|
14
|
+
if ref.startswith("final."):
|
|
20
15
|
concept = lookup_codesystems_code(codesystems, ref[6:])
|
|
21
16
|
if concept:
|
|
22
17
|
return concept
|
|
@@ -30,41 +25,39 @@ def add_concept(codesystems, system, code, display, attributes):
|
|
|
30
25
|
if system and system not in codesystems:
|
|
31
26
|
logger.info(f"New codesystem {system} added to project")
|
|
32
27
|
codesystems[system] = init_codesystem(system, system)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
|
|
36
29
|
return check_and_add_concept(codesystems[system], code, display, attributes)
|
|
37
|
-
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
|
|
41
32
|
def init_codesystem(code, name):
|
|
42
33
|
return CodeSystem(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
34
|
+
id=code.replace("_", "-"),
|
|
35
|
+
url=f"http://example.com/fhir/CodeSystem/{code}",
|
|
36
|
+
version="1.0.0",
|
|
37
|
+
name=name,
|
|
38
|
+
title=name,
|
|
39
|
+
status="draft",
|
|
40
|
+
description=f"Code system for {name}",
|
|
41
|
+
content="complete",
|
|
42
|
+
concept=[],
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
54
46
|
def init_valueset(code, name):
|
|
55
47
|
return ValueSet(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
id=code,
|
|
49
|
+
url=f"http://example.com/fhir/ValueSet/{code}",
|
|
50
|
+
version="1.0.0",
|
|
51
|
+
name=name,
|
|
52
|
+
title=name,
|
|
53
|
+
status="draft",
|
|
54
|
+
description=f"Valueset for {name}",
|
|
55
|
+
content="complete",
|
|
56
|
+
conatains=[],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def check_and_add_concept(code_system: CodeSystem, code: str, display: str, attributes: dict = {}):
|
|
68
61
|
"""
|
|
69
62
|
Checks if a concept with the given code already exists in the CodeSystem.
|
|
70
63
|
If it exists with a different display, raises an error. Otherwise, adds the concept.
|
|
@@ -81,10 +74,11 @@ def check_and_add_concept(code_system: CodeSystem, code: str, display: str, attr
|
|
|
81
74
|
# Check if the concept already exists
|
|
82
75
|
for concept in code_system.concept or []:
|
|
83
76
|
if concept.code == code:
|
|
84
|
-
|
|
77
|
+
|
|
85
78
|
if concept.display.lower() != display.lower():
|
|
86
79
|
logger.warning(
|
|
87
|
-
f"Code {code} already exists with a different display
|
|
80
|
+
f"""Code {code} already exists with a different display:
|
|
81
|
+
Concept:{concept.display}\n Current:{display}"""
|
|
88
82
|
)
|
|
89
83
|
new_concept = concept
|
|
90
84
|
if not new_concept:
|
|
@@ -93,27 +87,19 @@ def check_and_add_concept(code_system: CodeSystem, code: str, display: str, attr
|
|
|
93
87
|
if not hasattr(code_system, "concept"):
|
|
94
88
|
code_system.concept = []
|
|
95
89
|
code_system.concept.append(new_concept)
|
|
96
|
-
|
|
90
|
+
|
|
97
91
|
if attributes and not new_concept.property:
|
|
98
92
|
new_concept.property = []
|
|
99
|
-
|
|
100
|
-
for k,v in attributes.items():
|
|
93
|
+
|
|
94
|
+
for k, v in attributes.items():
|
|
101
95
|
existing_attributes = False
|
|
102
96
|
for p in new_concept.property:
|
|
103
97
|
if p.code == k:
|
|
104
|
-
#TODO support other type of Codesystem Concept Property Value
|
|
98
|
+
# TODO support other type of Codesystem Concept Property Value
|
|
105
99
|
existing_attributes
|
|
106
100
|
if p.valueString != v:
|
|
107
101
|
logger.warning(f"conflicting value for property {k}: {p.valueString} != {v}")
|
|
108
102
|
if not existing_attributes:
|
|
109
|
-
new_concept.property.append(
|
|
110
|
-
CodeSystemConceptProperty(
|
|
111
|
-
code=k,
|
|
112
|
-
valueString=v
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
return new_concept
|
|
117
|
-
|
|
103
|
+
new_concept.property.append(CodeSystemConceptProperty(code=k, valueString=v))
|
|
118
104
|
|
|
119
|
-
return
|
|
105
|
+
return new_concept
|