|
17 | 17 | )
|
18 | 18 | from execution_engine.omop.vocabulary import standard_vocabulary
|
19 | 19 | from execution_engine.util.value import Value, ValueConcept, ValueNumber
|
| 20 | +from execution_engine.util.value.value import ValueScalar |
20 | 21 |
|
21 | 22 |
|
22 | 23 | def select_value(
|
@@ -45,12 +46,12 @@ def parse_code_value(
|
45 | 46 | return parse_code(code), parse_value(value_parent, value_prefix)
|
46 | 47 |
|
47 | 48 |
|
48 |
| -def parse_code(code: CodeableConcept) -> Concept: |
| 49 | +def parse_code(code: CodeableConcept, standard: bool = True) -> Concept: |
49 | 50 | """
|
50 | 51 | Parses a FHIR code into a standard OMOP concept.
|
51 | 52 | """
|
52 | 53 | cc = get_coding(code)
|
53 |
| - return standard_vocabulary.get_standard_concept(cc.system, cc.code) |
| 54 | + return standard_vocabulary.get_concept(cc.system, cc.code, standard=standard) |
54 | 55 |
|
55 | 56 |
|
56 | 57 | def code_display(code: CodeableConcept) -> str:
|
@@ -85,28 +86,44 @@ def parse_value(
|
85 | 86 | )
|
86 | 87 | value_obj = ValueConcept(value=value_omop_concept)
|
87 | 88 | elif isinstance(value, Quantity):
|
88 |
| - value_obj = ValueNumber( |
89 |
| - value=value.value, |
90 |
| - unit=standard_vocabulary.get_standard_unit_concept(value.code), |
91 |
| - ) |
| 89 | + # Check if there's a code to determine if we use ValueNumber or ValueScalar |
| 90 | + if value.code is None: |
| 91 | + value_obj = ValueScalar(value=value.value) |
| 92 | + else: |
| 93 | + value_obj = ValueNumber( |
| 94 | + value=value.value, |
| 95 | + unit=standard_vocabulary.get_standard_unit_concept(value.code), |
| 96 | + ) |
92 | 97 | elif isinstance(value, Range):
|
93 |
| - if value.low is not None and value.high is not None: |
94 |
| - assert ( |
95 |
| - value.low.code == value.high.code |
96 |
| - ), "Range low and high unit must be the same" |
97 |
| - |
98 |
| - unit_code = value.low.code if value.low is not None else value.high.code |
99 |
| - |
| 98 | + # Handle the case where range has low/high with or without code |
100 | 99 | def value_or_none(x: Quantity | None) -> float | None:
|
101 | 100 | if x is None:
|
102 | 101 | return None
|
103 | 102 | return x.value
|
104 | 103 |
|
105 |
| - value_obj = ValueNumber( |
106 |
| - unit=standard_vocabulary.get_standard_unit_concept(unit_code), |
107 |
| - value_min=value_or_none(value.low), |
108 |
| - value_max=value_or_none(value.high), |
109 |
| - ) |
| 104 | + low_code = value.low.code if value.low is not None else None |
| 105 | + high_code = value.high.code if value.high is not None else None |
| 106 | + |
| 107 | + # If both low/high exist, ensure they share the same unit |
| 108 | + if value.low is not None and value.high is not None: |
| 109 | + assert low_code == high_code, "Range low and high unit must be the same" |
| 110 | + |
| 111 | + # Decide if we have a code at all |
| 112 | + unit_code = low_code if low_code is not None else high_code |
| 113 | + |
| 114 | + if unit_code is None: |
| 115 | + # No code => ValueScalar |
| 116 | + value_obj = ValueScalar( |
| 117 | + value_min=value_or_none(value.low), |
| 118 | + value_max=value_or_none(value.high), |
| 119 | + ) |
| 120 | + else: |
| 121 | + value_obj = ValueNumber( |
| 122 | + unit=standard_vocabulary.get_standard_unit_concept(unit_code), |
| 123 | + value_min=value_or_none(value.low), |
| 124 | + value_max=value_or_none(value.high), |
| 125 | + ) |
| 126 | + |
110 | 127 | else:
|
111 | 128 | raise NotImplementedError(f"Value type {type(value)} not implemented")
|
112 | 129 |
|
@@ -178,7 +195,8 @@ def get(self, fhir: Element) -> CriterionConverter:
|
178 | 195 | return converter.from_fhir(fhir)
|
179 | 196 |
|
180 | 197 | message = f"Cannot find a converter for the given FHIR element: {fhir.__class__.__name__}"
|
181 |
| - if fhir.id is not None: |
| 198 | + |
| 199 | + if getattr(fhir, "id", None) is not None: |
182 | 200 | message += f' (id="{fhir.id}")'
|
183 | 201 |
|
184 | 202 | message += f"\nFHIR element details: {json.dumps(fhir.model_dump(), indent=2)}"
|
|
0 commit comments