tricc-oo 1.5.22__py3-none-any.whl → 1.5.24__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 (45) hide show
  1. tests/build.py +17 -23
  2. tests/test_cql.py +37 -108
  3. tests/to_ocl.py +15 -17
  4. tricc_oo/__init__.py +0 -6
  5. tricc_oo/converters/codesystem_to_ocl.py +51 -40
  6. tricc_oo/converters/cql/cqlLexer.py +1 -0
  7. tricc_oo/converters/cql/cqlListener.py +1 -0
  8. tricc_oo/converters/cql/cqlParser.py +1 -0
  9. tricc_oo/converters/cql/cqlVisitor.py +1 -0
  10. tricc_oo/converters/cql_to_operation.py +125 -123
  11. tricc_oo/converters/datadictionnary.py +45 -54
  12. tricc_oo/converters/drawio_type_map.py +143 -61
  13. tricc_oo/converters/tricc_to_xls_form.py +14 -24
  14. tricc_oo/converters/utils.py +3 -3
  15. tricc_oo/converters/xml_to_tricc.py +286 -231
  16. tricc_oo/models/__init__.py +2 -1
  17. tricc_oo/models/base.py +300 -308
  18. tricc_oo/models/calculate.py +63 -49
  19. tricc_oo/models/lang.py +26 -27
  20. tricc_oo/models/ocl.py +146 -161
  21. tricc_oo/models/ordered_set.py +15 -19
  22. tricc_oo/models/tricc.py +145 -89
  23. tricc_oo/parsers/xml.py +15 -30
  24. tricc_oo/serializers/planuml.py +4 -6
  25. tricc_oo/serializers/xls_form.py +81 -135
  26. tricc_oo/strategies/input/base_input_strategy.py +28 -32
  27. tricc_oo/strategies/input/drawio.py +59 -71
  28. tricc_oo/strategies/output/base_output_strategy.py +142 -67
  29. tricc_oo/strategies/output/fhir_form.py +377 -0
  30. tricc_oo/strategies/output/html_form.py +224 -0
  31. tricc_oo/strategies/output/openmrs_form.py +647 -0
  32. tricc_oo/strategies/output/spice.py +106 -127
  33. tricc_oo/strategies/output/xls_form.py +263 -222
  34. tricc_oo/strategies/output/xlsform_cdss.py +623 -142
  35. tricc_oo/strategies/output/xlsform_cht.py +108 -115
  36. tricc_oo/strategies/output/xlsform_cht_hf.py +13 -24
  37. tricc_oo/visitors/tricc.py +1297 -1016
  38. tricc_oo/visitors/utils.py +16 -16
  39. tricc_oo/visitors/xform_pd.py +91 -89
  40. {tricc_oo-1.5.22.dist-info → tricc_oo-1.5.24.dist-info}/METADATA +127 -84
  41. tricc_oo-1.5.24.dist-info/RECORD +50 -0
  42. tricc_oo-1.5.24.dist-info/licenses/LICENSE +373 -0
  43. tricc_oo-1.5.22.dist-info/RECORD +0 -46
  44. {tricc_oo-1.5.22.dist-info → tricc_oo-1.5.24.dist-info}/WHEEL +0 -0
  45. {tricc_oo-1.5.22.dist-info → tricc_oo-1.5.24.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 TriccOperator, TriccOperation, TriccStatic, TriccReference, not_clean, or_join, and_join, string_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,17 +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
- 'Round': TriccOperator.ROUND,
25
- 'DrugDosage': TriccOperator.DRUG_DOSAGE,
26
- 'HasQualifier': TriccOperator.HAS_QUALIFIER,
27
- 'DateTimeToDecimal' : TriccOperator.DATETIME_TO_DECIMAL
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 ['true', 'false']:
49
- return TriccStatic(arg.lower() == 'true')
50
- elif arg != 'runner':
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 'runner'
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
- 'type': 'calculate',
65
- 'name': clean_name(identifier[1:-1].lower()),
66
- 'calculation': expression
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 '$this'
93
-
105
+ return "$this"
106
+
94
107
  def visitBooleanLiteral(self, ctx):
95
- literal = ctx.getChild(0).getText()
96
- if literal == 'true':
108
+ literal = ctx.getChild(0).getText()
109
+ if literal == "true":
97
110
  return TriccStatic(True)
98
- elif literal == 'false':
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 == 'in':
154
+ if function_name == "in":
145
155
  op = TriccOperation(TriccOperator.SELECTED)
146
- op.reference = [
147
- right,
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 = '.'.join([x.value for x in result])
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
- 'true': TriccOperator.ISTRUE,
184
- 'false': TriccOperator.ISFALSE,
185
- 'null': TriccOperator.ISNULL
186
+ operator={
187
+ "true": TriccOperator.ISTRUE,
188
+ "false": TriccOperator.ISFALSE,
189
+ "null": TriccOperator.ISNULL,
186
190
  }[params[-1]],
187
- reference = [expr]
191
+ reference=[expr],
188
192
  )
189
-
190
- if params[0] == 'not':
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, 'expression'):
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, 'expressionTerm'):
217
+ elif hasattr(ctx, "expressionTerm"):
214
218
  left = self.visit(ctx.expressionTerm(0))
215
219
  right = self.visit(ctx.expressionTerm(1))
216
- if operator == TriccOperator.AND:
220
+ if operator == TriccOperator.AND:
217
221
  return and_join([left, right])
218
- elif operator == TriccOperator.OR:
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
- '<': TriccOperator.LESS,
254
- '<=': TriccOperator.LESS_OR_EQUAL,
255
- '>': TriccOperator.MORE,
256
- '>=': TriccOperator.MORE_OR_EQUAL,
257
- '=': TriccOperator.EQUAL,
258
- '!=': TriccOperator.NOTEQUAL
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('Invocation not supported')
266
-
269
+ raise NotImplementedError("Invocation not supported")
270
+
267
271
  def visitIndexerExpression(self, ctx):
268
- raise NotImplementedError('Indexer not supported')
269
-
272
+ raise NotImplementedError("Indexer not supported")
273
+
270
274
  def visitCastExpression(self, ctx):
271
275
  # TODO
272
- raise NotImplementedError('Cast not supported')
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
- '*': TriccOperator.MULTIPLIED,
282
- 'div': TriccOperator.DIVIDED,
283
- '/': TriccOperator.DIVIDED,
284
- '%': TriccOperator.MODULO,
285
- 'mod': TriccOperator.MODULO,
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
- '+': TriccOperator.PLUS,
293
- '-': TriccOperator.MINUS,
294
- '&': TriccOperator.CONCATENATE
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 == 'int' or to_type == 'integer':
305
+ if to_type == "int" or to_type == "integer":
303
306
  return TriccOperation(TriccOperator.CAST_INTEGER, [expression])
304
- elif to_type == 'float' or to_type == 'number':
307
+ elif to_type == "float" or to_type == "number":
305
308
  return TriccOperation(TriccOperator.CAST_NUMBER, [expression])
306
- elif to_type == 'date':
309
+ elif to_type == "date":
307
310
  return TriccOperation(TriccOperator.CAST_DATE, [expression])
308
311
  else:
309
- raise NotImplementedError(f'cast {to_type} not supported')
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('Indexer not supported')
319
+ raise NotImplementedError("Indexer not supported")
319
320
 
320
321
  def visitUnit(self, ctx):
321
- raise NotImplementedError('Indexer not supported')
322
+ raise NotImplementedError("Indexer not supported")
322
323
 
323
324
  def visistDateTimePrecision(self, ctx):
324
325
  # TODO
325
- raise NotImplementedError('Indexer not supported')
326
+ raise NotImplementedError("Indexer not supported")
326
327
 
327
328
  def visitPluralDateTimePrecision(self, ctx):
328
329
  # TODO
329
- raise NotImplementedError('Indexer not supported')
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('typeSpecifier not supported')
336
+ raise NotImplementedError("typeSpecifier not supported")
336
337
 
337
338
  def visitRetrieve(self, ctx):
338
339
  # TODO
339
- raise NotImplementedError('retrieve not supported')
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('\n')))
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]['calculation']
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,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