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]")