From d079c112c8071c2a2d4fcf1db047bba8d23d99f0 Mon Sep 17 00:00:00 2001 From: Md Nazrul Islam Date: Mon, 16 Nov 2020 21:43:43 +0100 Subject: [PATCH] :sparkles: More helper functions (``get_local_timezone``, ``timestamp_utc``, ``timestamp_utc``) are created. * ``initial_bundle_data`` method is now available in Base Elasticsearch engine class, * fixes: Default bundle initial data was constructed ``meta.lastUpdated`` value with utc now time but without timezone offset --- HISTORY.rst | 11 ++++++- setup.py | 2 +- src/fhirpath/engine/es/__init__.py | 9 +++++- src/fhirpath/utils.py | 47 ++++++++++++++++++++++++++---- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index ab37f09..a2c7673 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,8 +5,17 @@ History 0.10.3 (unreleased) ------------------- -- Nothing changed yet. +Improvements + +- More helper functions (``get_local_timezone``, ``timestamp_utc``, ``timestamp_utc``) are created. + +- ``initial_bundle_data`` method is now available in Base Elasticsearch engine class, + meaning that it is possible construct Bundle initial data into the derived class, so more flexibility. + +Bugfixes +- Default bundle initial data was constructed ``meta.lastUpdated`` value with utc now time but without timezone offset, so + during json serialization, timezone info was missed as a result reverse construct of Bundle complains validation error. 0.10.2 (2020-11-06) ------------------- diff --git a/setup.py b/setup.py index 72494b4..601a39b 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,7 @@ def run(self): "zope.interface>=5.1.2", "multidict", "fhirspec>=0.2.5", - "fhir.resources>=6.0.0b9,<7.0", + "fhir.resources>=6.0.0b10,<7.0", "yarl", "isodate", ] diff --git a/src/fhirpath/engine/es/__init__.py b/src/fhirpath/engine/es/__init__.py index 8861032..2f14c5a 100644 --- a/src/fhirpath/engine/es/__init__.py +++ b/src/fhirpath/engine/es/__init__.py @@ -47,6 +47,10 @@ def navigate_indexed_path(source, path_): class ElasticsearchEngineBase(Engine): + def initial_bundle_data(self): + """Can be overridden in sub class""" + return BundleWrapper.init_data() + def extract_hits(self, source_filters, hits, container, doc_type="_doc"): """ """ for res in hits: @@ -92,7 +96,10 @@ def wrapped_with_bundle(self, result, includes=None, as_json=False): url = self.current_url() if includes is None: includes = list() - wrapper = BundleWrapper(self, result, includes, url, "searchset") + init_data = self.initial_bundle_data() + wrapper = BundleWrapper( + self, result, includes, url, "searchset", init_data=init_data + ) return wrapper(as_json=as_json) def generate_mappings( diff --git a/src/fhirpath/utils.py b/src/fhirpath/utils.py index 454b107..75a5144 100644 --- a/src/fhirpath/utils.py +++ b/src/fhirpath/utils.py @@ -6,6 +6,7 @@ import pkgutil import re import sys +import time import uuid from importlib import import_module from inspect import signature @@ -50,6 +51,8 @@ __author__ = "Md Nazrul Islam " +LOCAL_TIMEZONE: Optional[datetime.timezone] = None + def _reraise(tp, value, tb=None): if value is None: @@ -529,15 +532,21 @@ class BundleWrapper: """ """ def __init__( - self, engine, result, includes: List, url: URL, bundle_type="searchset" + self, + engine, + result, + includes: List, + url: URL, + bundle_type="searchset", + init_data: Dict[str, Any] = None, ): """ """ self.fhir_version = engine.fhir_release self.bundle_model = lookup_fhir_class("Bundle", fhir_release=self.fhir_version) - self.data: Dict[str, Any] = dict() - self.data["id"] = str(uuid.uuid4()) - - self.data["meta"] = {"lastUpdated": datetime.datetime.utcnow()} + if init_data: + self.data = init_data + else: + self.data = BundleWrapper.init_data() self.data["type"] = bundle_type # our pagination is based main query result. @@ -553,6 +562,12 @@ def __init__( self.attach_links(url, len(result.body)) + @staticmethod + def init_data() -> Dict[str, Any]: + """Initialized Bundle data""" + data = {"id": str(uuid.uuid4()), "meta": {"lastUpdated": timestamp_utc()}} + return data + def attach_entry(self, result, mode="match"): """ """ if "entry" not in self.data: @@ -653,3 +668,25 @@ def __call__(self, as_json=False): def json(self): """ """ return self.__call__().json() + + +def get_local_timezone() -> datetime.timezone: + if LOCAL_TIMEZONE is not None: + return LOCAL_TIMEZONE + + is_dst = time.daylight and time.localtime().tm_isdst > 0 + seconds = -(time.altzone if is_dst else time.timezone) + tz = datetime.timezone(datetime.timedelta(seconds=seconds)) + return tz + + +def timestamp_utc() -> datetime.datetime: + """UTC datetime with timezone offset""" + dt_now = datetime.datetime.utcnow() + dt_now.replace(tzinfo=datetime.timezone.utc) + return dt_now + + +def timestamp_local() -> datetime.datetime: + """Timezone aware datetime with local timezone offset""" + return datetime.datetime.now(tz=get_local_timezone())