TabbycatDebate/tabbycat

View on GitHub
tabbycat/utils/templatetags/debate_tags.py

Summary

Maintainability
A
1 hr
Test Coverage
D
69%
import os
import re
from random import randint

from django import template
from django.conf import settings
from django.template.base import kwarg_re, TemplateSyntaxError, Variable
from django.template.defaulttags import URLNode

from tournaments.utils import get_side_name

register = template.Library()
STATIC_PATH = settings.MEDIA_ROOT
version_cache = {}

rx = re.compile(r'^(.*)\.(.*?)$')


@register.simple_tag
def version(path_string, base_url=settings.MEDIA_URL):

    if not hasattr(
            settings,
            'ENABLE_MEDIA_VERSIONING') or not settings.ENABLE_MEDIA_VERSIONING:
        return base_url + path_string

    try:
        if path_string in version_cache:
            mtime = version_cache[path_string]
        else:
            mtime = os.path.getmtime(os.path.join(settings.MEDIA_ROOT,
                                                  path_string))
            version_cache[path_string] = mtime

        return base_url + rx.sub(r'\1.%d.\2' % mtime, path_string)
    except Exception:
        return base_url + path_string


@register.simple_tag(takes_context=True)
def tournament_side_names(context, name_type):
    side_names = [get_side_name(context['tournament'], 'aff', name_type),
                  get_side_name(context['tournament'], 'neg', name_type)]
    return side_names


@register.simple_tag(takes_context=True)
def debate_team_side_name(context, debate_team):
    # If returned directly from the object it will have to lookup tournament
    return debate_team.get_side_name(context['tournament'])


class TournamentURLNode(URLNode):

    def __init__(self, view_name, args, kwargs, asvar):
        self._args = args      # static copy, as we modify self.args in render()
        self._kwargs = kwargs
        super().__init__(view_name, args, kwargs, asvar)

    def render(self, context):
        """Add the tournament in the context to the arguments, then render as
        usual."""

        # In order to take advantage of the superclass's method, the easiest
        # thing to do is to grab the string we want to insert, and turn it into
        # a (literal) Variable for the superclass. This is a little roundabout,
        # since we're creating a variable only for it to be turned back into
        # the string again, but it's better than copy-pasting the superclass's
        # method itself.
        try:
            tournament_slug = context['tournament'].slug
        except KeyError:
            raise TemplateSyntaxError("tournamenturl can only be used in contexts with a tournament rtf ")
        tournament_slug = Variable("'" + tournament_slug + "'")
        self.args = list(self._args)      # make a copy in case render() gets called multiple times
        self.kwargs = dict(self._kwargs)

        if self.kwargs:
            self.kwargs['tournament_slug'] = tournament_slug
        else:
            self.args.insert(0, tournament_slug)

        return super().render(context)


class TournamentAbsoluteURLNode(TournamentURLNode):
    def render(self, context):
        path = super(TournamentAbsoluteURLNode, self).render(context)
        if self.asvar:
            context[self.asvar] = context['request'].build_absolute_uri(context[self.asvar])
            return path
        else:
            return context['request'].build_absolute_uri(path)


class RoundURLNode(URLNode):

    def __init__(self, view_name, args, kwargs, asvar):
        # Pull round out of the arguments, then let URLNode do the rest.
        if "round" in kwargs:
            self.round = kwargs.pop("round")
        elif len(args) > 0:
            self.round = args.pop(0)
        else:
            self.round = None  # None means take from context
        self._args = args      # static copy, as we modify self.args in render()
        self._kwargs = kwargs

        super().__init__(view_name, args, kwargs, asvar)

    def render(self, context):
        """Add the round to the arguments, then render as usual."""

        # Similar comment as for TournamentURLNode.render()
        round = self.round.resolve(context) if self.round else context['round']
        tournament_slug = Variable("'" + round.tournament.slug + "'")
        round_seq = Variable("'%d'" % round.seq)
        self.args = list(self._args)      # make a copy in case render() gets called multiple times
        self.kwargs = dict(self._kwargs)

        if self.kwargs:
            self.kwargs['tournament_slug'] = tournament_slug
            self.kwargs['round_seq'] = round_seq
        else:
            self.args = [tournament_slug, round_seq] + self.args

        return super().render(context)


def get_url_args(parser, token):
    """Helper function, returns the arguments necessary to construct a URL node.
    This code is copied directly from `url` in django/template/defaulttags.py,
    except that instead of constructing a URL node with the identified
    parameters, it simply returns the parameters in a tuple.
    """

    bits = token.split_contents()
    if len(bits) < 2:
        raise TemplateSyntaxError("'%s' takes at least one argument, the name of a url()." % bits[0])
    viewname = parser.compile_filter(bits[1])
    args = []
    kwargs = {}
    asvar = None
    bits = bits[2:]
    if len(bits) >= 2 and bits[-2] == 'as':
        asvar = bits[-1]
        bits = bits[:-2]

    if len(bits):
        for bit in bits:
            match = kwarg_re.match(bit)
            if not match:
                raise TemplateSyntaxError("Malformed arguments to url tag")
            name, value = match.groups()
            if name:
                kwargs[name] = parser.compile_filter(value)
            else:
                args.append(parser.compile_filter(value))

    return viewname, args, kwargs, asvar


@register.tag
def tournamenturl(parser, token):
    """Returns an absolute URL given the matching view for the given tournament,
    or the tournament in the context if no tournament is given."""
    args = get_url_args(parser, token)
    return TournamentURLNode(*args)


@register.tag
def tournament_absurl(parser, token):
    args = get_url_args(parser, token)
    return TournamentAbsoluteURLNode(*args)


@register.tag
def roundurl(parser, token):
    """Returns an absolute URL given the matching view for the given tournament
    and round, or the tournament and round in the context if no tournament is
    given."""
    args = get_url_args(parser, token)
    return RoundURLNode(*args)


@register.simple_tag(takes_context=True)
def person_display_name(context, person):
    # If returned directly from the object it will have to lookup tournament
    return person.get_public_name(context['tournament'])


@register.filter
def next_value(value, arg):
    try:
        return value[int(arg) + 1]
    except Exception:
        return None


@register.filter
def prev_value(value, arg):
    try:
        return value[int(arg) - 1]
    except Exception:
        return None


@register.filter(name='times')
def times(number):
    return list(range(number))


@register.simple_tag
def divide_to_int(number_a, number_b):
    try:
        return int(int(number_a) / int(number_b))
    except (ValueError, ZeroDivisionError):
        return None


@register.simple_tag
def get_unit(type):
    return "promotions/" + type + "-unit-" + str(randint(0, 1)) + ".jpg"


@register.simple_tag
def percentage(number_a, number_b):
    if number_b > 0:
        return number_a / number_b * 100 # Used for progress bars
    else:
        return 0


@register.filter
def subtract(value, arg):
    return value - arg # Used in BP Motion Stats


@register.filter(name='abbreviatename')
def abbreviatename(name):
    """Takes a two-part name and returns an abbreviation like 'E.Lučić'."""
    parts = name.split(" ")
    return "%s.%s" % (parts[0][:5], parts[-1][:5]) # Used for barcodes