Tutorial using Web2Py
=====================

Once you have Geraldo installed and its dependencies resolved (read the 
Installation document to know about the dependencies on ReportLab and Python
Imaging Library), you can start using it from **Web2Py** without any need to adjust settings.


Step 1. Preparing your project
------------------------------

Let's create a new URL to load a PDF report online.
 
We are going to work with a common example for this kind of solution: a
purchasing application.

Let's say you have the following model:

::

    db.define_table(product,
                Field('name', length=100))

    db.define_table(customer,
                Field('name', length=100))
    
    db.define_table(purchase,
                Field('customer', db.customer),
                Field('delivery_address', length=70),
                Field('date_creation', 'datetime', default=request.now),
                Field('date_bill', 'date'),
                Field('date_delivery', 'date'),
                Field('taxes', 'float', default=0.00),
                Field('sub_total_price', 'float', default=0.00),
                Field('total_price', 'float', default=0.00)
                )

    db.define_table(purchase_item,
                Field('purchase', db.purchase),
                Field('product', db.product),
                Field('unit_price', 'float')
                Field('quantity', 'float')
                Field('total_price', 'float', default=0.00)
                )

Add a new function to your Controller, for example:

::

    def purchase_report():
        from reports import ReportPurchase
        from geraldo.generators import PDFGenerator
        import gluon.contenttype
        import StringIO

        resp = StringIO.StringIO()
        
        purchases = db(db.purchase.id > 0).select(orderby=db.purchase.customer|db.purchase.id)
        report = ReportPurchase(queryset=purchases)
        report.generate_by(PDFGenerator, filename=resp)

        resp.seek(0)
        response.headers['Content-Type'] = gluon.contenttype.contenttype('.pdf')
        filename = "%s_Purchases.pdf" % (request.env.server_name)
        response.headers['Content-disposition'] = "attachment; filename=\"%s\"" % filename
        return resp.read()

You can see you first imported two important things:

::

    from reports import ReportPurchase
    from geraldo.generators import PDFGenerator

Your report class (we will create it in the next step) and the generator class
**PDFGenerator** you will need to generate a PDF file.

The next thing to note is that you instantiate a StringIO object to use to return the PDF:

::

        import StringIO
        resp = StringIO.StringIO()
    

Now you are going to work your report instance:

::

        purchases = db(db.purchase.id > 0).select(orderby=db.purchase.customer|db.purchase.id)
        report = ReportPurchase(queryset=purchases)
        report.generate_by(PDFGenerator, filename=resp)

Finally you prepare the response:

::

        resp.seek(0)
        response.headers['Content-Type'] = gluon.contenttype.contenttype('.pdf')
        filename = "%s_Purchases.pdf" % (request.env.server_name)
        response.headers['Content-disposition'] = "attachment; filename=\"%s\"" % filename
        return resp.read()
       
        
- The first thing you did is to get all purchases, ordered by 'customer' and 'id'
  fields;
- Afterwards, you got a **report** instance from your report class
  **ReportPurchase**, using purchases queryset as report driver queryset;
- Next, you called the 'generate_by' method of the generator class.
- And last, you prepared the StringIO object to be delivered to the browser.

Now you have a prepared Web2Py application to use a report you are going to
create at the next step!

Declaring the report class
--------------------------

The report class must be an inheritance from **geraldo.Report** class. You can
create a new file 'reports.py' to declare your report classes inside.

::

    from geraldo import Report

    class ReportPurchase(Report):
        title = 'Purchases list'
        author = 'John Smith Corporation'

To see something more complex, you can set the page size, margins and
other report attributes, like below:

::

    from geraldo import Report, landscape
    from reportlab.lib.pagesizes import A5
    from reportlab.lib.units import cm

    class ReportPurchase(Report):
        title = 'Purchases list'
        author = 'John Smith Corporation'
        
        page_size = landscape(A5)
        margin_left = 2*cm
        margin_top = 0.5*cm
        margin_right = 0.5*cm
        margin_bottom = 0.5*cm

As you can see, we use what we can from the ReportLab libraries, their units, page
sizes, stylizing, etc.

A report is driven by **bands**. If you are used to working with other report
engines you know what a band is.

A band is a row with elements in the report canvas. Bands in essence are the
same but their use varies depending whether you are using a band as the Page Header or
Report Summary, for example.

A report can have a band for each one of following attributes:

Detail band
-----------

The detail band is the most important band in a report. This is because the
detail band is the reason of the existence of every report.

The detail band is used most of the time to show object field values. This is the
same for a detailed info page or just a simple list or grid.

The detail band is rendered one time for each object in the queryset attribute.
So, if you have 10 objects in the queryset, you will have the detail band rendered
10 times.

Let's change our report to have a detail band, see below:

::

    from geraldo import Report, landscape, ReportBand, ObjectValue
    from reportlab.lib.pagesizes import A5
    from reportlab.lib.units import cm

    class ReportPurchase(Report):
        title = 'Purchases list'
        author = 'John Smith Corporation'
        
        page_size = landscape(A5)
        margin_left = 2*cm
        margin_top = 0.5*cm
        margin_right = 0.5*cm
        margin_bottom = 0.5*cm
    
        class band_detail(ReportBand):
            height = 0.5*cm
            elements=(
                ObjectValue(attribute_name='id', left=0.5*cm),
                ObjectValue(attribute_name='date_creation', left=3*cm,
                    get_value=lambda instance: instance.date_creation.strftime('%m/%d/%Y')),
                )

The attribute **band_detail** is locally declared as a class, but it could alternatively be set
with an external class.

The important thing here is that the band has the attribute **height**, fixed with a
defined height in centimeters.

The second thing to observe is the **elements** list, with 2 widgets representing
2 model class fields: **id** and **date_creation**. The last one is customized
with the **get_value** attribute, for date field formatting.


 	  	 
