Source code for abilian.web.filters

"""
Add a few specific filters to Jinja2.
"""

import re
from functools import wraps
import datetime
from pytz import utc
from calendar import timegm
from babel.dates import DateTimePattern, format_timedelta, parse_pattern
import bleach

from werkzeug.routing import BuildError
from jinja2 import Markup, escape, evalcontextfilter
from flask import Flask
from flask.ext import babel

from ..core.util import local_dt, utc_dt, slugify
from .util import url_for


[docs]def autoescape(filter_func): """ Decorator to autoescape result from filters. """ @evalcontextfilter @wraps(filter_func) def _autoescape(eval_ctx, *args, **kwargs): result = filter_func(*args, **kwargs) if eval_ctx.autoescape: result = Markup(result) return result return _autoescape
@autoescape
[docs]def nl2br(value): """ Replace newlines with <br />. """ result = escape(value).replace(u'\n', Markup(u'<br />\n')) return result
_PARAGRAPH_RE = re.compile(r'(?:\r\n|\r|\n){2,}') @autoescape
[docs]def paragraphs(value): """ Blank lines delimitates paragraphs. """ result = u'\n\n'.join( (u'<p>{}</p>'.format(p.strip().replace('\n', Markup('<br />\n'))) for p in _PARAGRAPH_RE.split(escape(value)))) return result
[docs]def labelize(s): return u" ".join([ w.capitalize() for w in s.split(u"_") ])
[docs]def filesize(d): if not isinstance(d, int): d = int(d) if d < 1000: s = "%d&nbsp;B" % d elif d < 1e4: s = "%.1f&nbsp;kB" % (d / 1e3) elif d < 1e6: s = "%.0f&nbsp;kB" % (d / 1e3) elif d < 1e7: s = "%.1f&nbsp;MB" % (d / 1e6) elif d < 1e9: s = "%.0f&nbsp;MB" % (d / 1e6) elif d < 1e10: s = "%.1f&nbsp;GB" % (d / 1e9) else: s = "%.0f&nbsp;GB" % (d / 1e9) return Markup(s)
[docs]def roughsize(size, above=20, mod=10): """ 6 -> '6' 15 -> '15' 134 -> '130+' """ if size < above: return unicode(size) return u'{:d}+'.format(size - size % mod)
[docs]def age(dt, now=None): # Fail silently for now XXX if not dt: return "" if not now: now = datetime.datetime.utcnow() dt = utc_dt(dt) now = utc_dt(now) # don't use (flask.ext.)babel.format_timedelta: as of Flask-Babel 0.9 it # doesn't support "threshold" arg. return format_timedelta((dt - now), locale=babel.get_locale(), granularity='minute', threshold=0.9, add_direction=True)
[docs]def date_age(dt, now=None): # Fail silently for now XXX if not dt: return "" formatted_date = babel.format_datetime(dt, format='yyyy-MM-dd HH:mm') return u"{} ({})".format(formatted_date, age(dt, now))
[docs]def date(value, format="EE, d MMMM y"): if isinstance(value, datetime.date): return babel.format_date(value, format) else: return babel.format_date(local_dt(value), format)
[docs]def babel2datepicker(pattern): """ Converts date format from babel (http://babel.pocoo.org/docs/dates/#date-fields)) to a format understood by bootstrap-datepicker. """ if not isinstance(pattern, DateTimePattern): pattern = parse_pattern(pattern) map_fmt = { # days 'd': 'dd', 'dd': 'dd', 'EEE': 'D', 'EEEE': 'DD', 'EEEEE': 'D', # narrow name => short name # months 'M': 'mm', 'MM': 'mm', 'MMM': 'M', 'MMMM': 'MM', # years 'y': 'yyyy', 'yy': 'yyyy', 'yyy': 'yyyy', 'yyyy': 'yyyy', # time picker format # hours 'h': '%I', 'hh': '%I', 'H': '%H', 'HH': '%H', # minutes, 'm': '%M', 'mm': '%M', # seconds 's': '%S', 'ss': '%S', # am/pm 'a': '%p', } return pattern.format % map_fmt # Doesn't work yet. TZ issues.
[docs]def to_timestamp(dt): utc_datetime = dt.astimezone(utc) return timegm(utc_datetime.timetuple()) + utc_datetime.microsecond / 1e6
[docs]def abbrev(s, max_size): if len(s) <= max_size: return s else: h = max_size / 2 - 1 return s[0:h] + "..." + s[-h:]
@autoescape
[docs]def linkify(s): return Markup(bleach.linkify(s))
[docs]def obj_to_url(obj): """ Find url for obj using :func:`url_for`, return empty string is not found. :func:`url_for` is also provided in jinja context, the filtering version is forgiving when `obj` has no default view set. """ try: return url_for(obj) except BuildError: return u''
[docs]def init_filters(env): if isinstance(env, Flask): # old api for init_filters: we used to pass flask application env = env.jinja_env env.filters['nl2br'] = nl2br env.filters['paragraphs'] = paragraphs env.filters['date_age'] = date_age env.filters['age'] = age env.filters['date'] = date env.filters['babel2datepicker'] = babel2datepicker env.filters['to_timestamp'] = to_timestamp env.filters['url_for'] = obj_to_url env.filters['abbrev'] = abbrev env.filters['filesize'] = filesize env.filters['roughsize'] = roughsize env.filters['labelize'] = labelize env.filters['linkify'] = linkify env.filters['toslug'] = slugify