Today, I'm very proud to announce the birth of our new project : relatorio, a
reporting module able to generate a whole lot of different document types from
templates. relatorio is a Portuguese word that means report, I choose
Portuguese because the Spanish/French words did not sound good enough.
Technically, we are using genshi and trml2pdf (the homepage does not work
anymore) to create PDF and OpenOffice documents.
In this post I'll show you how easy it is to create OpenOffice documents
using relatorio. First we need some objects to work on. Let's create a fake
invoice object:
class Invoice(dict):
@property
def total(self):
return reduce(operator.add, (l['amount'] for l in self['lines']), 0)
@property
def vat(self):
return self.total * 0.21
inv = Invoice(customer={'name': 'John Bonham',
'address': {'street': 'Smirnov street',
'zip': 1000,
'city': 'Montreux'}},
lines=[{'item': {'name': 'Vodka 70cl',
'reference': 'VDKA-001',
'price': 10.34},
'quantity': 7,
'amount': 7*10.34},
{'item': {'name': 'Cognac 70cl',
'reference': 'CGNC-067',
'price': 13.46},
'quantity': 12,
'amount': 12*13.46},
{'item': {'name': 'Sparkling water 25cl',
'reference': 'WATR-007',
'price': 0.4},
'quantity': 1,
'amount': 0.4},
{'item': {'name': 'Good customer rebate',
'reference': 'BONM-001',
'price': -20},
'quantity': 1,
'amount': -20},
],
id='MZY-20080703',
status='late')
So we created an invoice for the famous Led Zeppelin's drummer and his favorite
addiction.
The next thing to do is to create a template for invoices. We
will use the one displayed below. To create the genshi directives, you
just need to create a text-type placeholder field, and fill it
with the expression you want to use.

You can now start to use relatorio to create John Bonham's invoice.
from relatorio.templates.odt import Template
basic = Template(source=None, filepath='basic.odt')
file('bonham_basic.odt', 'w').write(basic.generate(o=inv).getvalue())
On the first line we import the odt Template engine. This class has the same
signature as the one from genshi but uses only the filepath arguments.
It returns a StringIO object that can be used to write the report on the disc.

And so here is our invoice with all the fields completed
according to the Invoice object we created earlier. Notice how the style we set in the template are also applied in the resulting invoice.
We made use of the py:for directive. But it is not the only genshi directive supported by the relatorio odt plugin, it also support py:if, py:choose/py:when/py:otherwise and py:with.

We also included a simple report repository that allows you to link your reports to a class and retrieve
them by the name you gave them or by the mimetype they output.
>>> import relatorio
>>> repos = relatorio.ReportRepository()
>>> repos.add_report(Invoice, 'application/vnd.oasis.opendocument.text',
... 'basic.odt', report_name='basic')
>>> repos.add_report(Invoice, 'application/vnd.oasis.opendocument.text',
... 'invoice.odt', report_name='complicated')
>>> repos.reports[Invoice]['basic']
('application/vnd.oasis.opendocument.text',
)
>>> repos.reports[Invoice]['application/vnd.oasis.opendocument.text']
[('basic',
),
('complicated',
)]
Moreover, the report repository works with a TemplateLoader object that automatically reloads a template when the file used to create it has been modified.
When using the report repository, you're also working with the relatorio reports, those are proxies over a Template that enables you to pre-process data before rendering it. The default behavior is to bind your object to the template variable o and all other arguments to the variable args. Here is an example showing you how you could use it in cherrypy to make the request object available to all your reports.
class OHFactory(relatorio.DefaultFactory):
def __call__(self, obj, **kwargs):
data = {}
data['o'] = obj
data['args'] = kwargs
data['request'] = cherrypy.request
return data
I hope you will find this module usefull and abuse it in every way.