Ticket #919: random_tag_weighted_newstyle_fixed.patch

File random_tag_weighted_newstyle_fixed.patch, 3.9 KB (added by Tom Tobin <korpios@…>, 19 years ago)

Implements random template tag, fixed for bisect / randint bug.

  • 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
     
    594623ifchanged = register.tag(ifchanged)
    595624
    596625#@register.tag
     626def random(parser, token):
     627    """
     628    Output the contents of a random block, with optional weighting.
     629
     630    The `random` block tag must contain one or more `or` tags, which separate
     631    possible choices; a choice in this context is everything between a
     632    `random` and `or` tag, between two `or` tags, or between an `or` and an
     633    `endrandom` tag.
     634
     635    Sample usage::
     636
     637        {% random %}
     638        You will see me half the time.
     639        {% or %}
     640        You will see me the other half.
     641        {% endrandom %}
     642
     643    To specify optional weights for the items, include comma-separated integers
     644    in the `random` tag, one for each item in order, like so::
     645
     646        {% random 4,1 %}
     647        You will see me four times more often than the other option.
     648        {% or %}
     649        You will see me only one-fifth of the time.
     650        {% endrandom %}
     651
     652    The chance of a weighted item being shown is its weight value divided by
     653    the total of all weight values.
     654    """
     655    args = token.contents.split()
     656    if len(args) == 1:
     657        # {% random %}
     658        weights = None
     659    elif len(args) == 2 and ',' in args[1]:
     660        # {% random 4,2,1 %}
     661        try:
     662            weights = [int(w) for w in args[1].split(',') if w]
     663        except ValueError:
     664            raise TemplateSyntaxError("Weight arguments for 'random' must be integers")
     665    else:
     666        raise TemplateSyntaxError("Invalid arguments to 'random': %s" % args)
     667    options = NodeList()
     668    while True:
     669        option = parser.parse(('or', 'endrandom'))
     670        token = parser.next_token()
     671        options.append(option)
     672        if token.contents == 'or':
     673            continue
     674        parser.delete_first_token()
     675        break
     676    if len(options) < 2:
     677        raise TemplateSyntaxError, "'random' tag must have at least two possibilities"
     678    elif weights and len(options) != len(weights):
     679        raise TemplateSyntaxError, "The number of specified weights (%s)" \
     680                " must equal the number of possible options (%s)" % (len(options), len(weights))
     681    return RandomNode(options, weights=weights)
     682do_random = register.tag("random", random)
     683
     684#@register.tag
    597685def ssi(parser, token):
    598686    """
    599687    Output the contents of a given file into the page.
Back to Top