瀏覽代碼

fix: Handle parsing and formatting timedeltas

* Added utils parse_timedelta, format_timedelta
* Added to json_handler for de-serializing timedelta objects
version-14
Gavin D'souza 3 年之前
父節點
當前提交
8037866dc1
共有 4 個文件被更改,包括 51 次插入11 次删除
  1. +1
    -1
      frappe/utils/__init__.py
  2. +35
    -2
      frappe/utils/data.py
  3. +7
    -2
      frappe/utils/formatters.py
  4. +8
    -6
      frappe/utils/response.py

+ 1
- 1
frappe/utils/__init__.py 查看文件

@@ -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


+ 35
- 2
frappe/utils/data.py 查看文件

@@ -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()})

+ 7
- 2
frappe/utils/formatters.py 查看文件

@@ -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


+ 8
- 6
frappe/utils/response.py 查看文件

@@ -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…
取消
儲存