<commit_before>import datetime
import json as jsonlib
import logging
import re
import urlparse

from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static as django_static
from django.core.urlresolvers import reverse as django_reverse
from django.http import QueryDict
from django.template.loader import render_to_string
from django.utils.encoding import smart_str, smart_text
from django.utils.http import urlencode
from django.utils.translation import ugettext_lazy as _lazy, ugettext as _, ungettext
from django.utils.tzinfo import LocalTimezone

import bleach
import jinja2
from babel import localedata
from babel.dates import format_date, format_time, format_datetime
from babel.numbers import format_decimal
from django_jinja import library
from jinja2.utils import Markup
from pytz import timezone

from kitsune.sumo import parser
from kitsune.sumo.urlresolvers import reverse
from kitsune.users.models import Profile
from kitsune.wiki.showfor import showfor_data as _showfor_data


log = logging.getLogger('k.helpers')


class DateTimeFormatError(Exception):
    """Called by the datetimeformat function when receiving invalid format."""
    pass


@library.filter
def paginator(pager):
    """Render list of pages."""
    return Paginator(pager).render()


@library.filter
def simple_paginator(pager):
    return jinja2.Markup(render_to_string('includes/simple_paginator.html', {'pager': pager}))


@library.filter
def quick_paginator(pager):
    return jinja2.Markup(render_to_string('includes/quick_paginator.html', {'pager': pager}))


@library.filter
def mobile_paginator(pager):
    return jinja2.Markup(render_to_string('includes/mobile/paginator.html', {'pager': pager}))


@library.global_function
def url(viewname, *args, **kwargs):
    """Helper for Django's ``reverse`` in templates.

    Uses sumo's locale-aware reverse."""
    locale = kwargs.pop('locale', None)
    return reverse(viewname, locale=locale, args=args, kwargs=kwargs)


@library.global_function
def unlocalized_url(viewname, *args, **kwargs):
    """Helper for Django's ``reverse`` in templates.

    Uses django's default reverse."""
    return django_reverse(viewname, args=args, kwargs=kwargs)


@library.filter
def urlparams(url_, hash=None, query_dict=None, **query):
    """
    Add a fragment and/or query parameters to a URL.

    New query params will be appended to exising parameters, except duplicate
    names, which will be replaced.
    """
    url_ = urlparse.urlparse(url_)
    fragment = hash if hash is not None else url_.fragment

    q = url_.query
    new_query_dict = (QueryDict(smart_str(q), mutable=True) if
                      q else QueryDict('', mutable=True))
    if query_dict:
        for k, l in query_dict.lists():
            new_query_dict[k] = None  # Replace, don't append.
            for v in l:
                new_query_dict.appendlist(k, v)

    for k, v in query.items():
        new_query_dict[k] = v  # Replace, don't append.

    query_string = urlencode([(k, v) for k, l in new_query_dict.lists() for
                              v in l if v is not None])
    new = urlparse.ParseResult(url_.scheme, url_.netloc, url_.path,
                               url_.params, query_string, fragment)
    return new.geturl()


@library.filter
def wiki_to_html(wiki_markup, locale=settings.WIKI_DEFAULT_LANGUAGE,
                 nofollow=True):
    """Wiki Markup -> HTML jinja2.Markup object"""
    return jinja2.Markup(parser.wiki_to_html(wiki_markup, locale=locale,
                                             nofollow=nofollow))


@library.filter
def truncate_question(text, length, longtext=None):
    if len(text) > length:
        if longtext is None:
            longtext = text
        stripped_text = bleach.clean(text, tags=[], strip=True)

        f = ('<p class="short-text">%s&hellip; ' +
             '<span class="show-more-link">(' + _('read more') + ')</span>' +
             '</p><div class="long-text">%s</div>')
        return f % (stripped_text[:length], longtext)

    return text


class Paginator(object):

    def __init__(self, pager):
        self.pager = pager

        self.max = 10
        self.span = (self.max - 1) / 2

        self.page = pager.number
        self.num_pages = pager.paginator.num_pages
        self.count = pager.paginator.count

        pager.page_range = self.range()
        pager.dotted_upper = self.num_pages not in pager.page_range
        pager.dotted_lower = 1 not in pager.page_range

    def range(self):
        """Return a list of page numbers to show in the paginator."""
        page, total, span = self.page, self.num_pages, self.span
        if total < self.max:
            lower, upper = 0, total
        elif page < span + 1:
            lower, upper = 0, span * 2
        elif page > total - span:
            lower, upper = total - span * 2, total
        else:
            lower, upper = page - span, page + span - 1
        return range(max(lower + 1, 1), min(total, upper) + 1)

    def render(self):
        c = {'pager': self.pager, 'num_pages': self.num_pages,
             'count': self.count}
        return jinja2.Markup(render_to_string('layout/paginator.html', c))


@jinja2.contextfunction
@library.global_function
def breadcrumbs(context, items=list(), add_default=True, id=None):
    """
    Show a list of breadcrumbs. If url is None, it won't be a link.
    Accepts: [(url, label)]
    """
    if add_default:
        first_crumb = u'Home'

        crumbs = [(reverse('home'), _lazy(first_crumb))]
    else:
        crumbs = []

    # add user-defined breadcrumbs
    if items:
        try:
            crumbs += items
        except TypeError:
            crumbs.append(items)

    c = {'breadcrumbs': crumbs, 'id': id}

    return jinja2.Markup(render_to_string('layout/breadcrumbs.html', c))


def _babel_locale(locale):
    """Return the Babel locale code, given a normal one."""
    # Babel uses underscore as separator.
    return locale.replace('-', '_')


def _contextual_locale(context):
    """Return locale from the context, falling back to a default if invalid."""
    request = context.get('request')
    locale = request.LANGUAGE_CODE
    if not localedata.exists(locale):
        locale = settings.LANGUAGE_CODE
    return locale


@jinja2.contextfunction
@library.global_function
def datetimeformat(context, value, format='shortdatetime'):
    """
    Returns a formatted date/time using Babel's locale settings. Uses the
    timezone from settings.py, if the user has not been authenticated.
    """
    if not isinstance(value, datetime.datetime):
        # Expecting date value
        raise ValueError(
            'Unexpected value {value} passed to datetimeformat'.format(
                value=value))

    request = context.get('request')

    default_tzinfo = convert_tzinfo = timezone(settings.TIME_ZONE)
    if value.tzinfo is None:
        value = default_tzinfo.localize(value)
        new_value = value.astimezone(default_tzinfo)
    else:
        new_value = value

    if 'timezone' not in request.session:
        if request.user.is_authenticated():
            try:
                convert_tzinfo = (Profile.objects.get(user=request.user).timezone or
                                  default_tzinfo)
            except (Profile.DoesNotExist, AttributeError):
                pass
        request.session['timezone'] = convert_tzinfo
    else:
        convert_tzinfo = request.session['timezone']

    convert_value = new_value.astimezone(convert_tzinfo)
    locale = _babel_locale(_contextual_locale(context))

    # If within a day, 24 * 60 * 60 = 86400s
    if format == 'shortdatetime':
        # Check if the date is today
        today = datetime.datetime.now(tz=convert_tzinfo).toordinal()
        if convert_value.toordinal() == today:
            formatted = _lazy(u'Today at %s') % format_time(
                convert_value, format='short', tzinfo=convert_tzinfo,
                locale=locale)
        else:
            formatted = format_datetime(convert_value,
                                        format='short',
                                        tzinfo=convert_tzinfo,
                                        locale=locale)
    elif format == 'longdatetime':
        formatted = format_datetime(convert_value, format='long',
                                    tzinfo=convert_tzinfo, locale=locale)
    elif format == 'date':
        formatted = format_date(convert_value, locale=locale)
    elif format == 'time':
        formatted = format_time(convert_value, tzinfo=convert_tzinfo,<commit_msg>[Bug 1259342] Fix datetimeformat jinja helpers<commit_after>import datetime
import json as jsonlib
import logging
import re
import urlparse

from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static as django_static
from django.core.urlresolvers import reverse as django_reverse
from django.http import QueryDict
from django.template.loader import render_to_string
from django.utils.encoding import smart_str, smart_text
from django.utils.http import urlencode
from django.utils.translation import ugettext_lazy as _lazy, ugettext as _, ungettext
from django.utils.tzinfo import LocalTimezone

import bleach
import jinja2
from babel import localedata
from babel.dates import format_date, format_time, format_datetime
from babel.numbers import format_decimal
from django_jinja import library
from jinja2.utils import Markup
from pytz import timezone

from kitsune.sumo import parser
from kitsune.sumo.urlresolvers import reverse
from kitsune.users.models import Profile
from kitsune.wiki.showfor import showfor_data as _showfor_data


log = logging.getLogger('k.helpers')


class DateTimeFormatError(Exception):
    """Called by the datetimeformat function when receiving invalid format."""
    pass


@library.filter
def paginator(pager):
    """Render list of pages."""
    return Paginator(pager).render()


@library.filter
def simple_paginator(pager):
    return jinja2.Markup(render_to_string('includes/simple_paginator.html', {'pager': pager}))


@library.filter
def quick_paginator(pager):
    return jinja2.Markup(render_to_string('includes/quick_paginator.html', {'pager': pager}))


@library.filter
def mobile_paginator(pager):
    return jinja2.Markup(render_to_string('includes/mobile/paginator.html', {'pager': pager}))


@library.global_function
def url(viewname, *args, **kwargs):
    """Helper for Django's ``reverse`` in templates.

    Uses sumo's locale-aware reverse."""
    locale = kwargs.pop('locale', None)
    return reverse(viewname, locale=locale, args=args, kwargs=kwargs)


@library.global_function
def unlocalized_url(viewname, *args, **kwargs):
    """Helper for Django's ``reverse`` in templates.

    Uses django's default reverse."""
    return django_reverse(viewname, args=args, kwargs=kwargs)


@library.filter
def urlparams(url_, hash=None, query_dict=None, **query):
    """
    Add a fragment and/or query parameters to a URL.

    New query params will be appended to exising parameters, except duplicate
    names, which will be replaced.
    """
    url_ = urlparse.urlparse(url_)
    fragment = hash if hash is not None else url_.fragment

    q = url_.query
    new_query_dict = (QueryDict(smart_str(q), mutable=True) if
                      q else QueryDict('', mutable=True))
    if query_dict:
        for k, l in query_dict.lists():
            new_query_dict[k] = None  # Replace, don't append.
            for v in l:
                new_query_dict.appendlist(k, v)

    for k, v in query.items():
        new_query_dict[k] = v  # Replace, don't append.

    query_string = urlencode([(k, v) for k, l in new_query_dict.lists() for
                              v in l if v is not None])
    new = urlparse.ParseResult(url_.scheme, url_.netloc, url_.path,
                               url_.params, query_string, fragment)
    return new.geturl()


@library.filter
def wiki_to_html(wiki_markup, locale=settings.WIKI_DEFAULT_LANGUAGE,
                 nofollow=True):
    """Wiki Markup -> HTML jinja2.Markup object"""
    return jinja2.Markup(parser.wiki_to_html(wiki_markup, locale=locale,
                                             nofollow=nofollow))


@library.filter
def truncate_question(text, length, longtext=None):
    if len(text) > length:
        if longtext is None:
            longtext = text
        stripped_text = bleach.clean(text, tags=[], strip=True)

        f = ('<p class="short-text">%s&hellip; ' +
             '<span class="show-more-link">(' + _('read more') + ')</span>' +
             '</p><div class="long-text">%s</div>')
        return f % (stripped_text[:length], longtext)

    return text


class Paginator(object):

    def __init__(self, pager):
        self.pager = pager

        self.max = 10
        self.span = (self.max - 1) / 2

        self.page = pager.number
        self.num_pages = pager.paginator.num_pages
        self.count = pager.paginator.count

        pager.page_range = self.range()
        pager.dotted_upper = self.num_pages not in pager.page_range
        pager.dotted_lower = 1 not in pager.page_range

    def range(self):
        """Return a list of page numbers to show in the paginator."""
        page, total, span = self.page, self.num_pages, self.span
        if total < self.max:
            lower, upper = 0, total
        elif page < span + 1:
            lower, upper = 0, span * 2
        elif page > total - span:
            lower, upper = total - span * 2, total
        else:
            lower, upper = page - span, page + span - 1
        return range(max(lower + 1, 1), min(total, upper) + 1)

    def render(self):
        c = {'pager': self.pager, 'num_pages': self.num_pages,
             'count': self.count}
        return jinja2.Markup(render_to_string('layout/paginator.html', c))


@jinja2.contextfunction
@library.global_function
def breadcrumbs(context, items=list(), add_default=True, id=None):
    """
    Show a list of breadcrumbs. If url is None, it won't be a link.
    Accepts: [(url, label)]
    """
    if add_default:
        first_crumb = u'Home'

        crumbs = [(reverse('home'), _lazy(first_crumb))]
    else:
        crumbs = []

    # add user-defined breadcrumbs
    if items:
        try:
            crumbs += items
        except TypeError:
            crumbs.append(items)

    c = {'breadcrumbs': crumbs, 'id': id}

    return jinja2.Markup(render_to_string('layout/breadcrumbs.html', c))


def _babel_locale(locale):
    """Return the Babel locale code, given a normal one."""
    # Babel uses underscore as separator.
    return locale.replace('-', '_')


def _contextual_locale(context):
    """Return locale from the context, falling back to a default if invalid."""
    request = context.get('request')
    locale = request.LANGUAGE_CODE
    if not localedata.exists(locale):
        locale = settings.LANGUAGE_CODE
    return locale


@jinja2.contextfunction
@library.global_function
def datetimeformat(context, value, format='shortdatetime'):
    """
    Returns a formatted date/time using Babel's locale settings. Uses the
    timezone from settings.py, if the user has not been authenticated.
    """

    def get_user_timezone(user):
        try:
            timezone = Profile.objects.get(user=user).timezone
            return timezone
        except (Profile.DoesNotExist, AttributeError):
            return None

    if not isinstance(value, datetime.datetime):
        # Expecting date value
        raise ValueError(
            'Unexpected value {value} passed to datetimeformat'.format(
                value=value))

    request = context.get('request')

    default_tzinfo = convert_tzinfo = timezone(settings.TIME_ZONE)
    if value.tzinfo is None:
        value = default_tzinfo.localize(value)
        new_value = value.astimezone(default_tzinfo)
    else:
        new_value = value

    try:
        if 'timezone' not in request.session:
            if request.user.is_authenticated():
                user_timezone = get_user_timezone(request.user)
                if user_timezone is not None:
                    convert_tzinfo = user_timezone
            request.session['timezone'] = convert_tzinfo
        else:
            convert_tzinfo = request.session['timezone']

    except AttributeError:
        pass

    convert_value = new_value.astimezone(convert_tzinfo)
    locale = _babel_locale(_contextual_locale(context))

    # If within a day, 24 * 60 * 60 = 86400s
    if format == 'shortdatetime':
        # Check if the date is today
        today = datetime.datetime.now(tz=convert_tzinfo).toordinal()
        if convert_value.toordinal() == today:
            formatted = _lazy(u'Today at %s') % format_time(
                convert_value, format='short', tzinfo=convert_tzinfo,
                locale=locale)
        else:
            formatted = format_datetime(convert_value,
                                        format='short',
                                        tzinfo=convert_tzinfo,
                                        locale=locale)
    elif format == 'longdatetime':
        formatted = format_datetime(convert_value, format='long',
                                    tzinfo=convert_tzinfo, locale=locale)
    elif format == 'date':
        formatted = format_date(convert_value, locale=locale)
    elif format == 'time':
        formatted = format_time(convert_value, tzinfo=convert_tzinfo,
{'fraction_non_alphanumeric': 0.1006519501315338, 'fraction_numerical': 0.004517900034313165, 'mean_word_length': 2.740534759358289, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 15, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3858635', 'n_tokens_mistral': 5276, 'n_tokens_neox': 5062, 'n_words': 1388}