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.