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