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:
parent
720d23cd1c
commit
785af76bc0
3 changed files with 99 additions and 73 deletions
|
|
@ -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"]'
|
||||
|
|
|
|||
84
sat/tools/common/date_utils.py
Normal file
84
sat/tools/common/date_utils.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue