tools (common): moved date_fmt function from template filters to new date_utils module, so it can be used everywhere.

This commit is contained in:
Goffi 2018-06-01 12:04:06 +02:00
commit 785af76bc0
3 changed files with 99 additions and 73 deletions

View file

@ -368,6 +368,10 @@ class Const(object):
KEY_PROGRESS_ID = u'progress_id'
#internationalisation
DEFAULT_LOCALE = u'en_GB'
## Misc ##
SAVEFILE_DATABASE = APP_NAME_FILE + ".db"
IQ_SET = '/iq[@type="set"]'

View file

@ -0,0 +1,84 @@
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# SAT: a jabber client
# Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""tools to help manipulating time and dates"""
from sat.core.constants import Const as C
import datetime
from babel import dates
import time
def date_fmt(timestamp, fmt='short', date_only=False, auto_limit=7, auto_old_fmt='short', auto_new_fmt='relative', locale_str=C.DEFAULT_LOCALE):
"""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: use auto_old_fmt if date is older than auto_limit
else use auto_new_fmt
- auto_day: shorcut to set auto format with change on day
old format will be short, and new format will be time only
or a free value which is passed to babel.dates.format_datetime
@param date_only(bool): if True, only display date (not datetime)
@param auto_limit (int): limit in days before using auto_old_fmt
use 0 to have a limit at last midnight (day change)
@param auto_old_fmt(unicode): format to use when date is older than limit
@param auto_new_fmt(unicode): format to use when date is equal to or more recent
than limit
"""
if fmt == 'auto_day':
fmt, auto_limit, auto_old_fmt, auto_new_fmt = 'auto', 0, 'short', 'HH:mm'
if fmt == 'auto':
if auto_limit == 0:
today = time.mktime(datetime.date.today().timetuple())
if int(timestamp) < today:
fmt = auto_old_fmt
else:
fmt = auto_new_fmt
else:
days_delta = (time.time() - int(timestamp)) / 3600
if days_delta > (auto_limit or 7):
fmt = auto_old_fmt
else:
fmt = auto_new_fmt
if fmt == 'relative':
delta = int(timestamp) - time.time()
return dates.format_timedelta(delta, granularity="minute", add_direction=True, locale=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=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=locale_str)

View file

@ -22,17 +22,16 @@
from sat.core.constants import Const as C
from sat.core.i18n import _
from sat.core import exceptions
from sat.tools.common import date_utils
from sat.core.log import getLogger
log = getLogger(__name__)
import os.path
from xml.sax.saxutils import quoteattr
import datetime
import time
import re
from babel import support
from babel import Locale
from babel.core import UnknownLocaleError
from babel import dates
import pygments
from pygments import lexers
from pygments import formatters
@ -53,7 +52,6 @@ from jinja2 import is_undefined
from lxml import etree
HTML_EXT = ('html', 'xhtml')
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
@ -205,7 +203,7 @@ class Renderer(object):
lstrip_blocks=True,
extensions=['jinja2.ext.i18n'],
)
self._locale_str = DEFAULT_LOCALE
self._locale_str = C.DEFAULT_LOCALE
self._locale = Locale.parse(self._locale_str)
self.installTranslations()
# we want to have access to SàT constants in templates
@ -258,22 +256,22 @@ class Renderer(object):
locale = Locale.parse(locale_str)
except ValueError as e:
log.warning(_(u"invalid locale value: {msg}").format(msg=e))
locale_str = self._locale_str = DEFAULT_LOCALE
locale_str = self._locale_str = C.DEFAULT_LOCALE
locale = Locale.parse(locale_str)
locale_str = unicode(locale)
if locale_str != DEFAULT_LOCALE:
if locale_str != C.DEFAULT_LOCALE:
try:
translations = self.translations[locale]
except KeyError:
log.warning(_(u"Can't find locale {locale}".format(locale=locale)))
locale_str = DEFAULT_LOCALE
locale_str = C.DEFAULT_LOCALE
locale = Locale.parse(self._locale_str)
else:
self.env.install_gettext_translations(translations, True)
log.debug(_(u'Switched to {lang}').format(lang=locale.english_name))
if locale_str == DEFAULT_LOCALE:
if locale_str == C.DEFAULT_LOCALE:
self.env.install_null_translations(True)
self._locale = locale
@ -366,74 +364,14 @@ class Renderer(object):
return value if not current else u"{}_{}".format(value, current)
def _date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=None, auto_old_fmt=None):
try:
return self.date_fmt(timestamp, fmt, date_only, auto_limit, auto_old_fmt)
except Exception as e:
log.warning(_(u"Can't parse date: {msg}").format(msg=e))
return timestamp
def date_fmt(self, timestamp, fmt='short', date_only=False, auto_limit=7, auto_old_fmt='short', auto_new_fmt='relative'):
"""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: use auto_old_fmt if date is older than auto_limit
else use auto_new_fmt
- auto_day: shorcut to set auto format with change on day
old format will be short, and new format will be time only
or a free value which is passed to babel.dates.format_datetime
@param date_only(bool): if True, only display date (not datetime)
@param auto_limit (int): limit in days before using auto_old_fmt
use 0 to have a limit at last midnight (day change)
@param auto_old_fmt(unicode): format to use when date is older than limit
@param auto_new_fmt(unicode): format to use when date is equal to or more recent
than limit
"""
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_day':
fmt, auto_limit, auto_old_fmt, auto_new_fmt = 'auto', 0, 'short', 'HH:mm'
if fmt == 'auto':
if auto_limit == 0:
today = time.mktime(datetime.date.today().timetuple())
if int(timestamp) < today:
fmt = auto_old_fmt
else:
fmt = auto_new_fmt
else:
days_delta = (time.time() - int(timestamp)) / 3600
if days_delta > (auto_limit or 7):
fmt = auto_old_fmt
else:
fmt = auto_new_fmt
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)
try:
return date_utils.date_fmt(timestamp, fmt, date_only, auto_limit, auto_old_fmt)
except Exception as e:
log.warning(_(u"Can't parse date: {msg}").format(msg=e))
return timestamp
def attr_escape(self, text):
"""escape a text to a value usable as an attribute
@ -595,7 +533,7 @@ class Renderer(object):
name=name,
cls=(' ' + cls) if cls else ''))
def render(self, template, theme=None, locale=DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs):
def render(self, template, theme=None, locale=C.DEFAULT_LOCALE, root_path=u'', media_path=u'', css_files=None, css_inline=False, **kwargs):
"""render a template
.
@param template(unicode): template to render (e.g. blog/articles.html)