Ticket #919: random_tag_current.diff

File random_tag_current.diff, 4.0 KB (added by Tom Tobin <korpios@…>, 18 years ago)

Implements random template tag; current version (will overwrite with future updates).

  • core/template/defaulttags.py

     
    179179                return self.nodelist_true.render(context)
    180180        return self.nodelist_false.render(context)
    181181
     182class RandomNode(Node):
     183    def __init__(self, nodelist_options, weights=None):
     184        self.nodelist_options = nodelist_options
     185        self.weights = weights
     186        if weights:
     187            self.weight_total = 0
     188            self.weight_items = {}
     189            for i, option in enumerate(nodelist_options):
     190                self.weight_total += weights[i]
     191                self.weight_items[self.weight_total] = option
     192            self.weight_boundaries = self.weight_items.keys()
     193            self.weight_boundaries.sort()
     194
     195    def render(self, context):
     196        if self.weights:
     197            from bisect import bisect_right
     198            from random import randint
     199            return self.weight_items[
     200                        self.weight_boundaries[
     201                            bisect_right(
     202                                self.weight_boundaries,
     203                                randint(0, self.weight_total - 1)
     204                                )
     205                            ]
     206                        ].render(context)
     207        else:
     208            from random import choice
     209            return choice(self.nodelist_options).render(context)
     210
    182211class RegroupNode(Node):
    183212    def __init__(self, target, expression, var_name):
    184213        self.target, self.expression = target, expression
     
    598627ifchanged = register.tag(ifchanged)
    599628
    600629#@register.tag
     630def random(parser, token):
     631    """
     632    Output the contents of a random block, with optional weighting.
     633
     634    The `random` block tag must contain one or more `or` tags, which separate
     635    possible choices; a choice in this context is everything between a
     636    `random` and `or` tag, between two `or` tags, or between an `or` and an
     637    `endrandom` tag.
     638
     639    Sample usage::
     640
     641        {% random %}
     642        You will see me half the time.
     643        {% or %}
     644        You will see me the other half.
     645        {% endrandom %}
     646
     647    To specify optional weights for the items, include comma-separated integers
     648    in the `random` tag, one for each item in order, like so::
     649
     650        {% random 4,1 %}
     651        You will see me four times more often than the other option.
     652        {% or %}
     653        You will see me only one-fifth of the time.
     654        {% endrandom %}
     655
     656    The chance of a weighted item being shown is its weight value divided by
     657    the total of all weight values.
     658    """
     659    args = token.contents.split()
     660    if len(args) == 1:
     661        # {% random %}
     662        weights = None
     663    elif len(args) == 2 and ',' in args[1]:
     664        # {% random 4,2,1 %}
     665        try:
     666            weights = [int(w) for w in args[1].split(',') if w]
     667        except ValueError:
     668            raise TemplateSyntaxError("Weight arguments for 'random' must be integers")
     669        for w in weights:
     670            if w < 1:
     671                raise TemplateSyntaxError("Weight arguments must be positive integers")
     672    else:
     673        raise TemplateSyntaxError("Invalid arguments to 'random': %s" % args)
     674    options = NodeList()
     675    while True:
     676        option = parser.parse(('or', 'endrandom'))
     677        token = parser.next_token()
     678        options.append(option)
     679        if token.contents == 'or':
     680            continue
     681        parser.delete_first_token()
     682        break
     683    if len(options) < 2:
     684        raise TemplateSyntaxError, "'random' tag must have at least two possibilities"
     685    elif weights and len(options) != len(weights):
     686        raise TemplateSyntaxError, "The number of specified weights (%s)" \
     687                " must equal the number of possible options (%s)" % (len(options), len(weights))
     688    return RandomNode(options, weights=weights)
     689do_random = register.tag("random", random)
     690
     691#@register.tag
    601692def ssi(parser, token):
    602693    """
    603694    Output the contents of a given file into the page.
Back to Top