diff --git a/docs/changelog.rst b/docs/changelog.rst index 3062f5b..6a9a274 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog 1.1.0 (unreleased) ------------------ +- #29 Avoid UnknownTimeZoneError when creating patient from Sample - #28 Remember age/dob selection on context - #27 Fix cannot create samples with empty Patient ID diff --git a/src/senaite/patient/browser/patientfolder.py b/src/senaite/patient/browser/patientfolder.py index 7f367f7..0973ebc 100644 --- a/src/senaite/patient/browser/patientfolder.py +++ b/src/senaite/patient/browser/patientfolder.py @@ -79,7 +79,8 @@ def __init__(self, context, request): ("gender", { "title": _("Gender"), }), ("birthdate", { - "title": _("Birthdate"), }), + "title": _("Birthdate"), + "index": "patient_birthdate"}), )) self.review_states = [ diff --git a/src/senaite/patient/catalog/configure.zcml b/src/senaite/patient/catalog/configure.zcml index bd576f0..03ce71d 100644 --- a/src/senaite/patient/catalog/configure.zcml +++ b/src/senaite/patient/catalog/configure.zcml @@ -9,6 +9,7 @@ + diff --git a/src/senaite/patient/catalog/indexers.py b/src/senaite/patient/catalog/indexers.py index 384aca7..8bd81dd 100644 --- a/src/senaite/patient/catalog/indexers.py +++ b/src/senaite/patient/catalog/indexers.py @@ -46,7 +46,7 @@ def patient_mrn(instance): @indexer(IPatient) def patient_fullname(instance): - """Index client fullname + """Index fullname """ fullname = instance.get_fullname() return fullname @@ -54,12 +54,20 @@ def patient_fullname(instance): @indexer(IPatient) def patient_email(instance): - """Index client email + """Index email """ email = instance.get_email() return email +@indexer(IPatient) +def patient_birthdate(instance): + """Index birthdate + """ + birthdate = instance.get_birthdate() + return birthdate + + @indexer(IPatient) def patient_searchable_text(instance): """Index for searchable text queries diff --git a/src/senaite/patient/catalog/patient_catalog.py b/src/senaite/patient/catalog/patient_catalog.py index ae530e6..a4067c7 100644 --- a/src/senaite/patient/catalog/patient_catalog.py +++ b/src/senaite/patient/catalog/patient_catalog.py @@ -34,6 +34,7 @@ ("patient_mrn", "", "FieldIndex"), ("patient_email", "", "FieldIndex"), ("patient_fullname", "", "FieldIndex"), + ("patient_birthdate", "", "DateIndex"), ("patient_searchable_text", "", "ZCTextIndex"), ] diff --git a/src/senaite/patient/content/patient.py b/src/senaite/patient/content/patient.py index 6ab0821..6d9d9c0 100644 --- a/src/senaite/patient/content/patient.py +++ b/src/senaite/patient/content/patient.py @@ -20,13 +20,15 @@ from six import string_types +from AccessControl import ClassSecurityInfo from bika.lims import api from bika.lims.api.mail import is_valid_email_address -from DateTime import DateTime from plone.autoform import directives from plone.dexterity.content import Container from plone.supermodel import model from plone.supermodel.directives import fieldset +from Products.CMFCore import permissions +from senaite.core.api import dtime from senaite.core.schema import DatetimeField from senaite.core.z3cform.widgets.datetimewidget import DatetimeWidget from senaite.patient import api as patient_api @@ -219,6 +221,26 @@ class Patient(Container): """ _catalogs = [PATIENT_CATALOG] + security = ClassSecurityInfo() + + @security.private + def accessor(self, fieldname): + """Return the field accessor for the fieldname + """ + schema = api.get_schema(self) + if fieldname not in schema: + return None + return schema[fieldname].get + + @security.private + def mutator(self, fieldname): + """Return the field mutator for the fieldname + """ + schema = api.get_schema(self) + if fieldname not in schema: + return None + return schema[fieldname].set + def Title(self): fullname = self.get_fullname() return fullname.encode("utf8") @@ -297,22 +319,16 @@ def set_gender(self, value): value = k self.gender = value + @security.protected(permissions.View) def get_birthdate(self): - if not self.birthdate: - return None - return self.birthdate + """Returns the birthday with the field accessor + """ + accessor = self.accessor("birthdate") + return accessor(self) + @security.protected(permissions.ModifyPortalContent) def set_birthdate(self, value): - """Set birthdate field - - Tries to convert value to datetime before set. + """Set birthdate by the field accessor """ - if isinstance(value, DateTime): - # convert DateTime -> datetime - value = value.asdatetime() - elif isinstance(value, string_types): - # convert string to datetime - dt = api.to_date(value, None) - if dt is not None: - value = dt.asdatetime() - self.birthdate = value + mutator = self.mutator("birthdate") + return mutator(self, value) diff --git a/src/senaite/patient/upgrade/v01_01_000.py b/src/senaite/patient/upgrade/v01_01_000.py index 506d74c..0c24bea 100644 --- a/src/senaite/patient/upgrade/v01_01_000.py +++ b/src/senaite/patient/upgrade/v01_01_000.py @@ -18,10 +18,14 @@ # Copyright 2020-2022 by it's authors. # Some rights reserved, see README and LICENSE. +from bika.lims import api +from senaite.core.api import dtime from senaite.core.upgrade import upgradestep from senaite.core.upgrade.utils import UpgradeUtils from senaite.patient import logger +from senaite.patient.config import PATIENT_CATALOG from senaite.patient.config import PRODUCT_NAME +from senaite.patient.setuphandlers import setup_catalogs version = "1.1.0" @@ -43,5 +47,33 @@ def upgrade(tool): # -------- ADD YOUR STUFF BELOW -------- + # add dateindex for birthdates + setup_catalogs(portal) + + # migrate birthdates w/o time but with valid timezone + migrate_birthdates(portal) + logger.info("{0} upgraded to version {1}".format(PRODUCT_NAME, version)) return True + + +def migrate_birthdates(portal): + """Migrate all birthdates from patients to be timezone aware + """ + logger.info("Migrate patient birthdate timezones ...") + catalog = api.get_tool(PATIENT_CATALOG) + results = catalog({"portal_type": "Patient"}) + timezone = dtime.get_os_timezone() + for brain in results: + patient = api.get_object(brain) + birthdate = patient.birthdate + if birthdate: + # clean existing time and timezone + date = birthdate.strftime("%Y-%m-%d") + # append current OS timezone if possible + if timezone: + date = date + " %s" % timezone + patient.set_birthdate(date) + patient.reindexObject() + + logger.info("Migrate patient birthdate timezones [DONE]")