Skip to content

Commit 5b365e4

Browse files
xisparamonski
authored andcommitted
Points in QC Charts are not displayed in accordance with results capture date (senaite#653)
* Unexpected date format of results capture date for QC Charts * Added senaite#653 entry in CHANGES.rst * Added is_date and get_date functions in the api * Use api.is_date to check if getResultCaptureDate is a valid date * api.get_date() -> api.to_date() with default value as fallback * Added tests for api.to_date in API.rst * Redux isinstance for DateTime + datetime in api.is_date
1 parent 6926d3f commit 5b365e4

File tree

5 files changed

+172
-4
lines changed

5 files changed

+172
-4
lines changed

CHANGES.rst

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Changelog
2525

2626
**Fixed**
2727

28+
- #653 Points in QC Charts are not displayed in accordance with capture date
2829
- #662 Viewing Cancelled AR's fails
2930
- #550 Wrong Listings of Analyses called from Dashboard
3031
- #666 "Rejected" filter is displayed in AR lists regardless of Setup setting

bika/lims/api.py

+39
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from Acquisition import aq_base
99
from AccessControl.PermissionRole import rolesForPermissionOn
1010

11+
from datetime import datetime
12+
from DateTime import DateTime
13+
1114
from Products.CMFPlone.utils import base_hasattr
1215
from Products.CMFCore.interfaces import ISiteRoot
1316
from Products.CMFCore.interfaces import IFolderish
@@ -1143,6 +1146,7 @@ def normalize_filename(string):
11431146
normalizer = getUtility(IFileNameNormalizer).normalize
11441147
return normalizer(string)
11451148

1149+
11461150
def is_uid(uid, validate=False):
11471151
"""Checks if the passed in uid is a valid UID
11481152
@@ -1166,3 +1170,38 @@ def is_uid(uid, validate=False):
11661170
if brains:
11671171
assert (len(brains) == 1)
11681172
return len(brains) > 0
1173+
1174+
1175+
def is_date(date):
1176+
"""Checks if the passed in value is a valid Zope's DateTime
1177+
1178+
:param date: The date to check
1179+
:type date: DateTime
1180+
:return: True if a valid date
1181+
:rtype: bool
1182+
"""
1183+
if not date:
1184+
return False
1185+
return isinstance(date, (DateTime, datetime))
1186+
1187+
1188+
def to_date(value, default=None):
1189+
"""Tries to convert the passed in value to Zope's DateTime
1190+
1191+
:param value: The value to be converted to a valid DateTime
1192+
:type value: str, DateTime or datetime
1193+
:return: The DateTime representation of the value passed in or default
1194+
"""
1195+
if isinstance(value, DateTime):
1196+
return value
1197+
if not value:
1198+
if default is None:
1199+
return None
1200+
return to_date(default)
1201+
try:
1202+
if isinstance(value, str) and '.' in value:
1203+
# https://docs.plone.org/develop/plone/misc/datetime.html#datetime-problems-and-pitfalls
1204+
return DateTime(value, datefmt='international')
1205+
return DateTime(value)
1206+
except:
1207+
return to_date(default)

bika/lims/browser/instrument.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from Products.CMFCore.utils import getToolByName
1313
from Products.CMFPlone.utils import safe_unicode
1414
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
15-
from bika.lims import bikaMessageFactory as _
15+
from bika.lims import bikaMessageFactory as _, api
1616
from bika.lims.browser import BrowserView
1717
from bika.lims.browser.analyses import AnalysesView
1818
from bika.lims.browser.bika_listing import BikaListingView
@@ -532,9 +532,11 @@ def folderitems(self):
532532
error_amount = ((target / 100) * error) if target > 0 else 0
533533
upper = smax + error_amount
534534
lower = smin - error_amount
535-
535+
cap_date = obj.getResultCaptureDate()
536+
cap_date = api.is_date(cap_date) and \
537+
cap_date.strftime('%Y-%m-%d %I:%M %p') or ''
536538
anrow = {
537-
'date': item['CaptureDate'],
539+
'date': cap_date,
538540
'min': smin,
539541
'max': smax,
540542
'target': target,

bika/lims/browser/referencesample.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ def addToJSON(self, analysis, service_uid, item):
198198
trows = self.anjson.get(serviceref, {})
199199
anrows = trows.get(qcid, [])
200200
rr = parent.getResultsRangeDict()
201-
cap_date = item.get('CaptureDate', None)
201+
cap_date = item['obj'].getResultCaptureDate
202+
cap_date = api.is_date(cap_date) and \
203+
cap_date.strftime('%Y-%m-%d %I:%M %p') or ''
202204
if service_uid in rr:
203205
specs = rr.get(service_uid, None)
204206
try:

bika/lims/tests/doctests/API.rst

+124
Original file line numberDiff line numberDiff line change
@@ -1177,3 +1177,127 @@ Checks if an UID is a valid 23 alphanumeric uid and with a brain:
11771177
>>> uid = serv.UID()
11781178
>>> api.is_uid(uid, validate=True)
11791179
True
1180+
1181+
Check if a Date is valid
1182+
------------------------
1183+
1184+
Do some imports first:
1185+
1186+
>>> from datetime import datetime
1187+
>>> from DateTime import DateTime
1188+
1189+
Checks if a DateTime is valid:
1190+
1191+
>>> now = DateTime()
1192+
>>> api.is_date(now)
1193+
True
1194+
1195+
>>> now = datetime.now()
1196+
>>> api.is_date(now)
1197+
True
1198+
1199+
>>> now = DateTime(now)
1200+
>>> api.is_date(now)
1201+
True
1202+
1203+
>>> api.is_date(None)
1204+
False
1205+
1206+
>>> api.is_date('2018-04-23')
1207+
False
1208+
1209+
Try conversions to Date
1210+
-----------------------
1211+
1212+
Try to convert to DateTime:
1213+
1214+
>>> now = DateTime()
1215+
>>> zpdt = api.to_date(now)
1216+
>>> zpdt.ISO8601() == now.ISO8601()
1217+
True
1218+
1219+
>>> now = datetime.now()
1220+
>>> zpdt = api.to_date(now)
1221+
>>> pydt = zpdt.asdatetime()
1222+
1223+
Note that here, for the comparison between dates, we convert DateTime to python
1224+
datetime, cause DateTime.strftime() is broken for timezones (always looks at
1225+
system time zone, ignores the timezone and offset of the DateTime instance
1226+
itself):
1227+
1228+
>>> pydt.strftime('%Y-%m-%dT%H:%M:%S') == now.strftime('%Y-%m-%dT%H:%M:%S')
1229+
True
1230+
1231+
Try the same, but with utcnow() instead:
1232+
1233+
>>> now = datetime.utcnow()
1234+
>>> zpdt = api.to_date(now)
1235+
>>> pydt = zpdt.asdatetime()
1236+
>>> pydt.strftime('%Y-%m-%dT%H:%M:%S') == now.strftime('%Y-%m-%dT%H:%M:%S')
1237+
True
1238+
1239+
Now we convert just a string formatted date:
1240+
1241+
>>> strd = "2018-12-01 17:50:34"
1242+
>>> zpdt = api.to_date(strd)
1243+
>>> zpdt.ISO8601()
1244+
'2018-12-01T17:50:34'
1245+
1246+
Now we convert just a string formatted date, but with timezone:
1247+
1248+
>>> strd = "2018-12-01 17:50:34 GMT+1"
1249+
>>> zpdt = api.to_date(strd)
1250+
>>> zpdt.ISO8601()
1251+
'2018-12-01T17:50:34+01:00'
1252+
1253+
We also check a bad date here (note the month is 13):
1254+
1255+
>>> strd = "2018-13-01 17:50:34"
1256+
>>> zpdt = api.to_date(strd)
1257+
>>> api.is_date(zpdt)
1258+
False
1259+
1260+
And with European format:
1261+
1262+
>>> strd = "01.12.2018 17:50:34"
1263+
>>> zpdt = api.to_date(strd)
1264+
>>> zpdt.ISO8601()
1265+
'2018-12-01T17:50:34'
1266+
1267+
>>> zpdt = api.to_date(None)
1268+
>>> zpdt is None
1269+
True
1270+
1271+
Use a string formatted date as fallback:
1272+
1273+
>>> strd = "2018-13-01 17:50:34"
1274+
>>> default_date = "2018-01-01 19:30:30"
1275+
>>> zpdt = api.to_date(strd, default_date)
1276+
>>> zpdt.ISO8601()
1277+
'2018-01-01T19:30:30'
1278+
1279+
Use a DateTime object as fallback:
1280+
1281+
>>> strd = "2018-13-01 17:50:34"
1282+
>>> default_date = "2018-01-01 19:30:30"
1283+
>>> default_date = api.to_date(default_date)
1284+
>>> zpdt = api.to_date(strd, default_date)
1285+
>>> zpdt.ISO8601() == default_date.ISO8601()
1286+
True
1287+
1288+
Use a datetime object as fallback:
1289+
1290+
>>> strd = "2018-13-01 17:50:34"
1291+
>>> default_date = datetime.now()
1292+
>>> zpdt = api.to_date(strd, default_date)
1293+
>>> dzpdt = api.to_date(default_date)
1294+
>>> zpdt.ISO8601() == dzpdt.ISO8601()
1295+
True
1296+
1297+
Use a non-conversionable value as fallback:
1298+
1299+
>>> strd = "2018-13-01 17:50:34"
1300+
>>> default_date = "something wrong here"
1301+
>>> zpdt = api.to_date(strd, default_date)
1302+
>>> zpdt is None
1303+
True

0 commit comments

Comments
 (0)