template: improved date formatter:
the date filter is now named "date_fmt" and can be use to specify several useful formats (see docstring), using the current locale. The "relative" format return the whole human readable relative date. The new in_the_past test allow to check if a date is passed.
This commit is contained in:
parent
dde17b2b06
commit
ecb3ba897e
1 changed files with 75 additions and 4 deletions
|
|
@ -31,6 +31,7 @@ import re
|
|||
from babel import support
|
||||
from babel import Locale
|
||||
from babel.core import UnknownLocaleError
|
||||
from babel import dates
|
||||
try:
|
||||
import sat_templates
|
||||
except ImportError:
|
||||
|
|
@ -44,9 +45,10 @@ except:
|
|||
raise exceptions.MissingModule(u'Missing module jinja2, please install it from http://jinja.pocoo.org or with pip install jinja2')
|
||||
|
||||
from jinja2 import Markup as safe
|
||||
from jinja2 import is_undefined
|
||||
|
||||
HTML_EXT = ('html', 'xhtml')
|
||||
DEFAULT_LOCALE = u'en'
|
||||
DEFAULT_LOCALE = u'en_GB'
|
||||
RE_ATTR_ESCAPE = re.compile(r'[^a-z_-]')
|
||||
# TODO: handle external path (an additional search path for templates should be settable by user
|
||||
# TODO: handle absolute URL (should be used for trusted use cases) only (e.g. jp) for security reason
|
||||
|
|
@ -200,11 +202,13 @@ class Renderer(object):
|
|||
# custom filters
|
||||
self.env.filters['next_gidx'] = self._next_gidx
|
||||
self.env.filters['cur_gidx'] = self._cur_gidx
|
||||
self.env.filters['date_days'] = self._date_days
|
||||
self.env.filters['date_fmt'] = self._date_fmt
|
||||
self.env.filters['xmlui_class'] = self._xmlui_class
|
||||
self.env.filters['attr_escape'] = self.attr_escape
|
||||
self.env.filters['item_filter'] = self._item_filter
|
||||
self.env.filters['adv_format'] = self._adv_format
|
||||
# custom tests
|
||||
self.env.tests['in_the_past'] = self._in_the_past
|
||||
|
||||
def installTranslations(self):
|
||||
i18n_dir = os.path.join(self.base_dir, 'i18n')
|
||||
|
|
@ -226,8 +230,16 @@ class Renderer(object):
|
|||
self.env.install_null_translations(True)
|
||||
|
||||
def setLocale(self, locale_str):
|
||||
"""set current locale
|
||||
|
||||
change current translation locale and self self._locale and self._locale_str
|
||||
"""
|
||||
if locale_str == self._locale_str:
|
||||
return
|
||||
if locale_str == 'en':
|
||||
# we default to GB English when it's not specified
|
||||
# one of the main reason is to avoid the nonsense U.S. short date format
|
||||
locale_str = 'en_GB'
|
||||
try:
|
||||
locale = Locale.parse(locale_str)
|
||||
except ValueError as e:
|
||||
|
|
@ -324,6 +336,9 @@ class Renderer(object):
|
|||
|
||||
return css_files
|
||||
|
||||
|
||||
## custom filters ##
|
||||
|
||||
@jinja2.contextfilter
|
||||
def _next_gidx(self, ctx, value):
|
||||
"""Use next current global index as suffix"""
|
||||
|
|
@ -336,8 +351,54 @@ class Renderer(object):
|
|||
current = ctx['gidx'].current(value)
|
||||
return value if not current else u"{}_{}".format(value, current)
|
||||
|
||||
def _date_days(self, timestamp):
|
||||
return int(time.time() - int(timestamp))/(3600*24)
|
||||
def _date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=None, auto_old_fmt=None):
|
||||
"""format date according to locale
|
||||
|
||||
@param timestamp(basestring, int): unix time
|
||||
@param fmt(str): one of:
|
||||
- short: e.g. u'31/12/17'
|
||||
- medium: e.g. u'Apr 1, 2007'
|
||||
- long: e.g. u'April 1, 2007'
|
||||
- full: e.g. u'Sunday, April 1, 2007'
|
||||
- relative: format in relative time
|
||||
e.g.: 3 hours
|
||||
note that this format is not precise
|
||||
- iso: ISO 8601 format
|
||||
e.g.: u'2007-04-01T19:53:23Z'
|
||||
- auto_limit (int, None): limit in days before using auto_old_fmt
|
||||
None: use default(7 days)
|
||||
- auto_old_fmt(unicode, None): format to use when date is olded than limit
|
||||
None: use default(short)
|
||||
or a free value which is passed to babel.dates.format_datetime
|
||||
@param date_only(bool): if True, only display date (not datetime)
|
||||
|
||||
"""
|
||||
if is_undefined(fmt):
|
||||
fmt = u'short'
|
||||
|
||||
if (auto_limit is not None or auto_old_fmt is not None) and fmt != 'auto':
|
||||
raise ValueError(u'auto argument can only be used with auto fmt')
|
||||
if fmt == 'auto':
|
||||
days_delta = (time.time() - int(timestamp)) / 3600
|
||||
if days_delta > (auto_limit or 7):
|
||||
fmt = auto_old_fmt or 'short'
|
||||
else:
|
||||
fmt = 'relative'
|
||||
|
||||
if fmt == 'relative':
|
||||
delta = int(timestamp) - time.time()
|
||||
return dates.format_timedelta(delta, granularity="minute", add_direction=True, locale=self._locale_str)
|
||||
elif fmt in ('short', 'long'):
|
||||
formatter = dates.format_date if date_only else dates.format_datetime
|
||||
return formatter(int(timestamp), format=fmt, locale=self._locale_str)
|
||||
elif fmt == 'iso':
|
||||
if date_only:
|
||||
fmt = 'yyyy-MM-dd'
|
||||
else:
|
||||
fmt = "yyyy-MM-ddTHH:mm:ss'Z'"
|
||||
return dates.format_datetime(int(timestamp), format=fmt)
|
||||
else:
|
||||
return dates.format_datetime(int(timestamp), format=fmt, locale=self._locale_str)
|
||||
|
||||
def attr_escape(self, text):
|
||||
"""escape a text to a value usable as an attribute
|
||||
|
|
@ -409,6 +470,16 @@ class Renderer(object):
|
|||
# jinja use string when no special char is used, so we have to convert to unicode
|
||||
return unicode(template).format(value=value, **kwargs)
|
||||
|
||||
## custom tests ##
|
||||
|
||||
def _in_the_past(self, timestamp):
|
||||
"""check if a date is in the past
|
||||
|
||||
@param timestamp(unicode, int): unix time
|
||||
@return (bool): True if date is in the past
|
||||
"""
|
||||
return time.time() > int(timestamp)
|
||||
|
||||
def render(self, template, theme=None, locale=DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs):
|
||||
"""render a template
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue