From 9806c7a0a2cb60d56a0128f1ac67e3adf38c520e Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 18 Nov 2014 19:37:52 -0600 Subject: [PATCH] Add `DateTimeFieldRenderer` to show human-friendly timestamps. This leverages the `humanize` package to do so. Unfortunately that doesn't seem to handle tz-aware times though, so we may need to revisit that at some point...? --- setup.py | 1 + tailbone/forms/renderers/__init__.py | 14 +++--- tailbone/forms/renderers/common.py | 64 +++++++++++++++++++++++++--- 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index c5ea99af..b2d7ceb5 100644 --- a/setup.py +++ b/setup.py @@ -75,6 +75,7 @@ requires = [ # deprecated 'paster create' (and friends). 'pyramid>=1.3a1', # 1.3b2 1.4.5 + 'humanize', # 0.5.1 'Mako', # 0.6.2 'pyramid_beaker>=0.6', # 0.6.1 'pyramid_debugtoolbar', # 1.0 diff --git a/tailbone/forms/renderers/__init__.py b/tailbone/forms/renderers/__init__.py index 3b1d0c58..146fdaf0 100644 --- a/tailbone/forms/renderers/__init__.py +++ b/tailbone/forms/renderers/__init__.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2012 Lance Edgar +# Copyright © 2010-2014 Lance Edgar # # This file is part of Rattail. # @@ -21,11 +20,16 @@ # along with Rattail. If not, see . # ################################################################################ - """ FormAlchemy Field Renderers """ -from .common import * +from tailbone.forms.renderers.common import ( + AutocompleteFieldRenderer, + DateTimeFieldRenderer, + EnumFieldRenderer, + YesNoFieldRenderer, + ) + from .people import * from .products import * diff --git a/tailbone/forms/renderers/common.py b/tailbone/forms/renderers/common.py index b6f315b3..346db6a2 100644 --- a/tailbone/forms/renderers/common.py +++ b/tailbone/forms/renderers/common.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2012 Lance Edgar +# Copyright © 2010-2014 Lance Edgar # # This file is part of Rattail. # @@ -21,16 +20,21 @@ # along with Rattail. If not, see . # ################################################################################ - """ Common Field Renderers """ +from __future__ import unicode_literals + +import pytz +import humanize + +import formalchemy from formalchemy.fields import FieldRenderer, SelectFieldRenderer, CheckBoxFieldRenderer from pyramid.renderers import render +from webhelpers.html import HTML - -__all__ = ['AutocompleteFieldRenderer', 'EnumFieldRenderer', 'YesNoFieldRenderer'] +from rattail.time import timezone class AutocompleteFieldRenderer(FieldRenderer): @@ -76,6 +80,54 @@ class AutocompleteFieldRenderer(FieldRenderer): return unicode(value) +def pretty_datetime(config, value): + """ + Formats a datetime as a "pretty" human-readable string, with a tooltip + showing the ISO string value. + + :param config: Reference to a config object. + + :param value: A ``datetime.datetime`` instance. Note that if this instance + is not timezone-aware, its timezone is assumed to be UTC. + """ + if not value: + return '' + + # Make sure we're dealing with a tz-aware value. If we're given a naive + # value, we assume it to be local to the UTC timezone. + if not value.tzinfo: + value = pytz.utc.localize(value) + + # Convert value to local timezone, and make a naive copy. + local = timezone(config) + value = local.normalize(value.astimezone(local)) + naive_value = value.replace(tzinfo=None) + + return HTML.tag('span', + title=value.strftime('%Y-%m-%d %H:%M:%S %Z%z'), + c=humanize.naturaltime(naive_value)) + + +class DateTimeFieldRenderer(formalchemy.DateTimeFieldRenderer): + """ + Custom date/time field renderer, which displays a "pretty" value in + read-only mode, leveraging config to show the correct timezone. + """ + + def __init__(self, config): + self.config = config + + def __call__(self, field): + super(DateTimeFieldRenderer, self).__init__(field) + return self + + def render_readonly(self, **kwargs): + value = self.raw_value + if not value: + return '' + return pretty_datetime(self.config, value) + + class EnumFieldRenderer(SelectFieldRenderer): """ Renderer for simple enumeration fields.