fix: Handle parsing and formatting timedeltas
* Added utils parse_timedelta, format_timedelta * Added to json_handler for de-serializing timedelta objects
This commit is contained in:
parent
9091b2a037
commit
8037866dc1
4 changed files with 51 additions and 11 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import functools
|
||||
|
|
|
|||
|
|
@ -104,11 +104,17 @@ def get_timedelta(time: Optional[str] = None) -> Optional[datetime.timedelta]:
|
|||
datetime.timedelta: Timedelta object equivalent of the passed `time` string
|
||||
"""
|
||||
from dateutil import parser
|
||||
from dateutil.parser import ParserError
|
||||
|
||||
time = time or "0:0:0"
|
||||
|
||||
try:
|
||||
t = parser.parse(time)
|
||||
try:
|
||||
t = parser.parse(time)
|
||||
except ParserError as e:
|
||||
if "day" in e.args[1]:
|
||||
from frappe.utils import parse_timedelta
|
||||
return parse_timedelta(time)
|
||||
return datetime.timedelta(
|
||||
hours=t.hour, minutes=t.minute, seconds=t.second, microseconds=t.microsecond
|
||||
)
|
||||
|
|
@ -332,7 +338,7 @@ def get_time(time_str):
|
|||
return time_str
|
||||
else:
|
||||
if isinstance(time_str, datetime.timedelta):
|
||||
time_str = str(time_str)
|
||||
return format_timedelta(time_str)
|
||||
return parser.parse(time_str).time()
|
||||
|
||||
def get_datetime_str(datetime_obj):
|
||||
|
|
@ -1678,3 +1684,30 @@ class UnicodeWithAttrs(str):
|
|||
def __init__(self, text):
|
||||
self.toc_html = text.toc_html
|
||||
self.metadata = text.metadata
|
||||
|
||||
|
||||
def format_timedelta(o: datetime.timedelta) -> str:
|
||||
# mariadb allows a wide diff range - https://mariadb.com/kb/en/time/
|
||||
# but frappe doesnt - i think via babel : only allows 0..23 range for hour
|
||||
total_seconds = o.total_seconds()
|
||||
hours, remainder = divmod(total_seconds, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
rounded_seconds = round(seconds, 6)
|
||||
int_seconds = int(seconds)
|
||||
|
||||
if rounded_seconds == int_seconds:
|
||||
seconds = int_seconds
|
||||
else:
|
||||
seconds = rounded_seconds
|
||||
|
||||
return "{:01}:{:02}:{:02}".format(int(hours), int(minutes), seconds)
|
||||
|
||||
|
||||
def parse_timedelta(s: str) -> datetime.timedelta:
|
||||
# ref: https://stackoverflow.com/a/21074460/10309266
|
||||
if 'day' in s:
|
||||
m = re.match(r"(?P<days>[-\d]+) day[s]*, (?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d[\.\d+]*)", s)
|
||||
else:
|
||||
m = re.match(r"(?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d[\.\d+]*)", s)
|
||||
|
||||
return datetime.timedelta(**{key: float(val) for key, val in m.groupdict().items()})
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
import frappe
|
||||
import datetime
|
||||
from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime, format_time, format_duration
|
||||
from frappe.utils import formatdate, fmt_money, flt, cstr, cint, format_datetime, format_time, format_duration, format_timedelta
|
||||
from frappe.model.meta import get_field_currency, get_field_precision
|
||||
import re
|
||||
from dateutil.parser import ParserError
|
||||
|
||||
|
||||
def format_value(value, df=None, doc=None, currency=None, translated=False, format=None):
|
||||
'''Format value based on given fieldtype, document reference, currency reference.
|
||||
|
|
@ -47,7 +49,10 @@ def format_value(value, df=None, doc=None, currency=None, translated=False, form
|
|||
return format_datetime(value)
|
||||
|
||||
elif df.get("fieldtype")=="Time":
|
||||
return format_time(value)
|
||||
try:
|
||||
return format_time(value)
|
||||
except ParserError:
|
||||
return format_timedelta(value)
|
||||
|
||||
elif value==0 and df.get("fieldtype") in ("Int", "Float", "Currency", "Percent") and df.get("print_hide_if_no_value"):
|
||||
# this is required to show 0 as blank in table columns
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import json
|
||||
|
|
@ -16,7 +16,7 @@ from werkzeug.local import LocalProxy
|
|||
from werkzeug.wsgi import wrap_file
|
||||
from werkzeug.wrappers import Response
|
||||
from werkzeug.exceptions import NotFound, Forbidden
|
||||
from frappe.utils import cint
|
||||
from frappe.utils import cint, format_timedelta
|
||||
from urllib.parse import quote
|
||||
from frappe.core.doctype.access_log.access_log import make_access_log
|
||||
|
||||
|
|
@ -122,12 +122,14 @@ def make_logs(response = None):
|
|||
|
||||
def json_handler(obj):
|
||||
"""serialize non-serializable data for json"""
|
||||
# serialize date
|
||||
import collections.abc
|
||||
from collections.abc import Iterable
|
||||
|
||||
if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime, datetime.time)):
|
||||
if isinstance(obj, (datetime.date, datetime.datetime, datetime.time)):
|
||||
return str(obj)
|
||||
|
||||
elif isinstance(obj, datetime.timedelta):
|
||||
return format_timedelta(obj)
|
||||
|
||||
elif isinstance(obj, decimal.Decimal):
|
||||
return float(obj)
|
||||
|
||||
|
|
@ -138,7 +140,7 @@ def json_handler(obj):
|
|||
doc = obj.as_dict(no_nulls=True)
|
||||
return doc
|
||||
|
||||
elif isinstance(obj, collections.abc.Iterable):
|
||||
elif isinstance(obj, Iterable):
|
||||
return list(obj)
|
||||
|
||||
elif type(obj)==type or isinstance(obj, Exception):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue