From e4e3a12fa9221e43c472a8c7388b32b2e9e7a185 Mon Sep 17 00:00:00 2001 From: atuonufure Date: Wed, 10 Jan 2024 02:51:00 +0100 Subject: [PATCH] Update math - plus and minus --- fhirpathpy/engine/invocations/math.py | 19 +++++++---- fhirpathpy/engine/nodes.py | 46 +++++++++++++++++++++++---- fhirpathpy/engine/util.py | 6 ++++ 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/fhirpathpy/engine/invocations/math.py b/fhirpathpy/engine/invocations/math.py index aaf2d83..e9adb4a 100644 --- a/fhirpathpy/engine/invocations/math.py +++ b/fhirpathpy/engine/invocations/math.py @@ -1,4 +1,5 @@ from decimal import Decimal +from fhirpathpy.engine.invocations.equality import remove_duplicate_extension import fhirpathpy.engine.util as util import fhirpathpy.engine.nodes as nodes @@ -42,10 +43,13 @@ def amp(ctx, x="", y=""): return x + y -def minus(ctx, xs, ys): +def minus(ctx, xs_, ys_): + xs = remove_duplicate_extension(xs_) + ys = remove_duplicate_extension(ys_) + if len(xs) == 1 and len(ys) == 1: - x = util.get_data(xs[0]) - y = util.get_data(ys[0]) + x = util.get_data(util.val_data_converted(xs[0])) + y = util.get_data(util.val_data_converted(ys[0])) if util.is_number(x) and util.is_number(y): return x - y @@ -81,12 +85,15 @@ def mod(ctx, x, y): # HACK: for only polymorphic function # Actually, "minus" is now also polymorphic -def plus(ctx, xs, ys): +def plus(ctx, xs_, ys_): + xs = remove_duplicate_extension(xs_) + ys = remove_duplicate_extension(ys_) + if len(xs) != 1 or len(ys) != 1: raise Exception("Cannot " + str(xs) + " + " + str(ys)) - x = util.get_data(xs[0]) - y = util.get_data(ys[0]) + x = util.get_data(util.val_data_converted(xs[0])) + y = util.get_data(util.val_data_converted(ys[0])) """ In the future, this and other functions might need to return ResourceNode diff --git a/fhirpathpy/engine/nodes.py b/fhirpathpy/engine/nodes.py index ebabf22..8882eac 100644 --- a/fhirpathpy/engine/nodes.py +++ b/fhirpathpy/engine/nodes.py @@ -211,9 +211,14 @@ def deep_equal(self, other): else: if self.unit != other.unit: converted = FP_Quantity.conv_unit_to(self.unit, self.value, other.unit) - reverse_converted = FP_Quantity.conv_unit_to(converted.unit, converted.value, self.unit) + reverse_converted = FP_Quantity.conv_unit_to( + converted.unit, converted.value, self.unit + ) if converted is not None: - return self.value == reverse_converted.value and self.unit == reverse_converted.unit + return ( + self.value == reverse_converted.value + and self.unit == reverse_converted.unit + ) return self.__eq__(other) else: return super().__eq__(other) @@ -257,7 +262,9 @@ def conv_unit_to(fromUnit, value, toUnit): to_g_mg_magnitude = FP_Quantity._g_mg_conversion_factor.get(toUnit) if from_g_mg_magnitude and to_g_mg_magnitude: value = Decimal(value) * Decimal(FP_Quantity._g_mg_conversion_factor[fromUnit]) - result = (value / Decimal(FP_Quantity._g_mg_conversion_factor[toUnit])).quantize(Decimal('1.'), rounding=ROUND_HALF_UP) + result = (value / Decimal(FP_Quantity._g_mg_conversion_factor[toUnit])).quantize( + Decimal("1."), rounding=ROUND_HALF_UP + ) return FP_Quantity(result, toUnit) return None @@ -528,6 +535,11 @@ def plus(self, time_quantity): self._extractTimeByPrecision(result, precision if precision < 3 else 4) + dt_list[4] ) + @staticmethod + def check_string(cls, str_val): + val = cls(str_val) + return val + class FP_Time(FP_TimeBase): matchGroupsIndices = [ @@ -667,7 +679,7 @@ def __init__(self, dateStr): self._precision = self._calculatePrecision(self._dateTimeAsList) def __str__(self): - if re.match(r'^\d{4}-\d{2}-\d{2}$', self.asStr): + if re.match(r"^\d{4}-\d{2}-\d{2}$", self.asStr): return self.asStr if self.asStr and len(self.asStr) <= 4: return self.asStr @@ -787,7 +799,7 @@ def get_type_info(self): if not TypeInfo.model: return TypeInfo.create_by_value_in_namespace(namespace=namespace, value=self.data) - return TypeInfo(namespace=namespace, name='BackboneElement') + return TypeInfo(namespace=namespace, name="BackboneElement") def toJSON(self): return json.dumps(self.data) @@ -798,12 +810,25 @@ def create_node(data, path=None, _data=None): return data return ResourceNode(data, path, _data) + def convert_data(self): + data = self.data + cls = TypeInfo.type_to_class_with_check_string.get(self.path) + if cls: + data = FP_TimeBase.check_string(cls, data) or data + return data + class TypeInfo: model = None System = "System" FHIR = "FHIR" + type_to_class_with_check_string = { + "date": FP_DateTime, + "dateTime": FP_DateTime, + "time": FP_Time, + } + def __init__(self, name, namespace): self.name = name self.namespace = namespace @@ -813,8 +838,15 @@ def is_type(type_name, super_type): while type_name: if type_name == super_type: return True - # TODO: Double check it - type_name = TypeInfo.model.get("type2Parent").get(type_name) or TypeInfo.model.get("path2Type").get(type_name) + + if TypeInfo.model is not None: + parent_type = TypeInfo.model.get("type2Parent", {}).get(type_name) + if parent_type is None: + parent_type = TypeInfo.model.get("path2Type", {}).get(type_name) + type_name = parent_type + else: + return False + return False def is_(self, other): diff --git a/fhirpathpy/engine/util.py b/fhirpathpy/engine/util.py index c6d3aea..fafabbf 100644 --- a/fhirpathpy/engine/util.py +++ b/fhirpathpy/engine/util.py @@ -91,3 +91,9 @@ def uniq(arr): key = str(x) ordered_dict[key] = x return list(ordered_dict.values()) + +def val_data_converted(val): + if isinstance(val, ResourceNode): + val = val.convert_data() + + return val