"""
========================
Template aggreate tags
-----------------------
tags:
- agg_init - init variable
- agg_sum - sumator
- agg_get - retrive variable
Tags operate with Decimal values.
Description:
---------------
At the top of template use {% agg_init 'key' %}
then in loop you can use
{% agg_sum value_to_sum 'key' %}
when need print total value just output it with
{{ key }}
For some reason, key should be composite:
{% agg_init 'key' 'subkey' model.type %}
{% agg_sum value_to_sum 'key' 'subkey' model.type %}
Number of key part items not limited.
In this case key will be composite by call str() for each key item.
Retrive composite key value possible with agg_get:
{% agg_get 'key' 'subkey' model.id model.type %}
Typically, composite key using in cross-grid reports where you don't know
how many columns will be generated, but require to have total line with value for each column
===============================
Complete example:
-------------------------------
{% load aggregate %}
{% agg_init 'total_payments' %}
| Date |
Sum |
{% for payment in all_payments %}
| {{ payment.date }} |
{{ payment.sum }} |
{% agg_sum payment.sum 'total_payments' %}
{% endfor %}
| Total sum |
{{ total_payments }} |
{% comment %}
Or get value with agg_get:
{% endcomment %}
| Total sum |
{% agg_get 'total_payments' %} |
"""
from decimal import Decimal
from django import template
from django.template import Variable
register = template.Library()
def get_or_set_marker(context):
for d in context.dicts:
if 'agg_top' in d:
return d
context.dicts[-1]['agg_top'] = True
return context.dicts[-1]
class AggInitNode(template.Node):
def __init__(self, keys):
self.keys = [ Variable(key) for key in keys]
def render(self, context):
key = "".join([ item.resolve(context) for item in self.keys ])
dicts = get_or_set_marker(context)
dicts[key] = Decimal('0.0')
return ''
@register.tag()
def agg_init(parset, token):
items = token.split_contents()
if len(items) < 2:
raise template.TemplateSyntaxError, "%r tag requires at least 1 arguments" % items[0]
return AggInitNode(items[1:])
class AggGetNode(template.Node):
def __init__(self, keys):
self.keys = [ Variable(key) for key in keys]
def render(self, context):
key = "".join([ item.resolve(context) for item in self.keys ])
return context.get(key)
@register.tag()
def agg_get(parset, token):
items = token.split_contents()
if len(items) < 2:
raise template.TemplateSyntaxError, "%r tag requires at least 1 arguments" % items[0]
return AggGetNode(items[1:])
class AggSumNode(template.Node):
def __init__(self, value, keys):
self.keys = [ Variable(key) for key in keys]
self.value = Variable(value)
def render(self, context):
key = "".join([ item.resolve(context) for item in self.keys ])
value = context.get(key) or Decimal('0.0')
value += self.value.resolve(context) or Decimal('0.0')
dicts = get_or_set_marker(context)
dicts[key] = value
return ''
@register.tag()
def agg_sum(parset, token):
items = token.split_contents()
if len(items) < 3:
raise template.TemplateSyntaxError, "%r tag requires at least 2 arguments" % items[0]
return AggSumNode(items[1], items[2:])