I found it good to put report classes in a separate file in models. So create a reports.py in models with this:
class Report(FPDF, HTMLMixin):
def header(self):
self.set_font('Arial','B',15)
self.cell(65) # padding
self.cell(60,10,response.title,1,0,'C')
self.ln(20)
def footer(self):
self.set_y(-15)
self.set_font('Arial','I',8)
txt = 'Page %s of %s' % (self.page_no(), self.alias_nb_pages())
self.cell(0,10,txt,0,0,'C')
Note: this was taken and adpate from here
You will need a controller to make the form, do the query and render the PDF:
def report():
#form for the dates input
form = SQLFORM.factory(Field('start_date', 'date',
label='Start Date',
requires=IS_DATE(),
Field('end_date', 'date',
label='End Date',
requires=IS_DATE()))
#form with invisible fields for PDF requesting
pdf_link = FORM(INPUT(_type='submit', _value='PDF'),
hidden=dict(start_date=request.vars.start_date,
end_date=request.vars.end_date),
_action='report.pd')
#to do the query only one time
#ask about extension for pdf or valid form data
if request.extension == 'pd' or form.accepts(request.vars, session):
#get date objects
start_date = request.now.strptime(request.vars.data_ini, '%Y-%m-%d')
end_date = request.now.strptime(request.vars.data_fim, '%Y-%m-%d')
#the query
rep = db((db.table.data >= data_ini) &
(db.table.data <= data_fim)).select()
#html table with the rows and labels from the models
t_rep = SQLTABLE(rep, headers=dict([('table.' + f, db.table[f].label) for
f in db.table.fields]))
#if a pdf was requested
if request.extension == 'pd':
pdf = Report()
pdf.add_page()
#PYFPDF needs width attributs
#as SQLTABLE don´t do like this, we need a hack
ths = t_rep.elements('th')
ths[0]['_width'] = '12%' # column widths
ths[1]['_width'] = '60%'
......
.....
#send html to the pdf with required encoding
pdf.write_html(t_rep.xml().decode('UTF-8').encode('cp1252'))
response.headers['Content-Type'] = 'application/pdf'
#send the PDF as a string
return pdf.output(dest='S')
else:
#displays form for input, the report and a PDF button
return dict(form=form, rep=rep, pdf_link=pdf_link)
else:
#display only the form and a message for input
return dict(form=form, rep='Pick a date range.', pdf_link=None)
Note: Maybe there´s a better logic to achieve this.
And sure we need a view:
{{extend "layout.html"}}
{{=rep}}
<hr>
{{if pdf_link:}}
{{=pdf_link}}
{{pass}}
{{=rep}}
That´s it! Maybe the oddest thing here is I coudn´t use a .pdf extension. This problem seems to appera when you have a non-generic view for the action.
Comments (1)
0
adesanto-asman-10337 12 years ago
I don't know if somebody had the same experienced, that the header() not handled
the linefeed properly at the next page.
I leaved a comment here, http://code.google.com/p/pyfpdf/wiki/Web2Py, hope it helps.