Edgewall Software
Version 3 (modified by cmlenz, 8 years ago)

--

Helper functions for use in Markup templates

Often you need non-trivial presentation logic in templates, but Markup does not (yet) let you drop into straight Python. In Markup, such presentation logic must be either performed in the controller (i.e. the Python code feeding the template with date), or in helper functions that are called from within template expressions.

This page serves as a place where generalized functions that solve common tasks in presentation logic can be collected. At some point, Markup might include a library of such functions.

Python Standard Library

Many of the Python  builtin functions (such as reversed or sorted), as well as those in the  itertools package (such as groupby), can be quite useful in templates. The builtin functions are available by default, whereas other functions need to be put in the template context data explicitly.

group()

The following was written by  Christopher Lenz for use in the  Trac project:

def group(iterable, num, predicate=None):
    """Combines the elements produced by the given iterable so that every `n`
    items are returned as a tuple.
    
    >>> items = [1, 2, 3, 4]
    >>> for item in group(items, 2):
    ...     print item
    (1, 2)
    (3, 4)
    
    The last tuple is padded with `None` values if its' length is smaller than
    `num`.
    
    >>> items = [1, 2, 3, 4, 5]
    >>> for item in group(items, 2):
    ...     print item
    (1, 2)
    (3, 4)
    (5, None)
    
    The optional `predicate` parameter can be used to flag elements that should
    not be packed together with other items. Only those elements where the
    predicate function returns True are grouped with other elements, otherwise
    they are returned as a tuple of length 1:
    
    >>> items = [1, 2, 3, 4]
    >>> for item in group(items, 2, lambda x: x != 3):
    ...     print item
    (1, 2)
    (3,)
    (4, None)
    """
    buf = []
    for item in iterable:
        flush = predicate and not predicate(item)
        if buf and flush:
            buf += [None] * (num - len(buf))
            yield tuple(buf)
            del buf[:]
        buf.append(item)
        if flush or len(buf) == num:
            yield tuple(buf)
            del buf[:]
    if buf:
        buf += [None] * (num - len(buf))
        yield tuple(buf)

If the predicate functionality is not needed, a vastly simpler implementation of that function would be:

def group(iterable, num):
    """Group an iterable into an n-tuples iterable. Incomplete tuples
    are discarded e.g.
    
    >>> list(group(range(10), 3))
    [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
    """
    return map(None, *[iter(iterable)] * num)

See also the Python Cookbook recipe “ Group a list into sequential n-tuples”.

Example usage

<table py:with="fields = ['a', 'b', 'c', 'd', 'e']">
  <tr py:for="row in group(fields, 2)">
    <td py:for="cell in row">${cell}</td>
  </tr>
</table>

That should result in the following output:

<table>
  <tr>
    <td>a</td><td>b</td>
  </tr><tr>
    <td>c</td><td>d</td>
  </tr><tr>
    <td>e</td><td></td>
  </tr>
</table>

countoccurrences()

Written by Arnar Birgisson and shared on the IrcChannel.

def countoccurrences(numbers, minlength=0):
    """Takes a list of integers numbers, in the range of 0..n and returns
    a list of integers where i-th item is the number of times i appears in types.
    
    If minlength is specified and n+1 < minlength, the returned list is
    right-padded with zeroes to make it contain minlength items.
    
    Examples:
    >>> countoccurrences([0,3,1,2,1,1,3,5])
    [1,3,1,2,0,1]
    >>> countoccurrences([0,3,1,2,1,1,3,5], 10)
    [1,3,1,2,0,1,0,0,0,0]
    """
    # TODO come up with a better name
    counts = [0] * max(max(numbers)+1, minlength)
    for x in numbers:
        counts[x] += 1
    return counts

Example usage

<py:with vars="counts = countoccurrences([p.status.value for p in subdir.job.pages], 3)">
    <td><span style="color: #e18f01;">${counts[0]}</span></td>
    <td><span style="color: #249f0b;">${counts[1]}</span></td>
    <td><span style="color: #ae0a0a;">${counts[2]}</span></td>
</py:with>

See also: MarkupGuide?, MarkupRecipes?