Tutorial 1
==========

Once you have Geraldo installed and its dependencies resolved (read
Installation document to know about dependency for ReportLab and Python
Imaging Library) you can start opening the **settings.py** from your
project to edit.

    **Attention:** this tutorial is pressuposes you are going to use Geraldo
    with a Django project, but in fact you can just ignore the Django parts
    and all the rest is exactly the same for Django and non-Django cases.

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

To install Geraldo in your Django project, you must change the setting
**INSTALLED_APPS** in your **settings.py** file and this shoul be enough.

But let's create a new URL to load a report PDF online.

    **Attention:** Geraldo can work with online or not onfile ways to get PDF.
    You are no dependent for a URL or view to generate reports from Geraldo.
    This is just one of many ways you can do it.

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 classes:

::

    class Product(models.Model):
        name = models.CharField(max_length=100)
    
    class Customer(models.Model):
        name = models.CharField(max_length=100)
    
    class Purchase(models.Model):
        customer = models.ForeignKey('Customer')
        delivery_address = models.CharField(max_length=70, blank=True)
        date_creation = models.DateField(blank=True, default=date.today())
        date_bill = models.DateField(blank=True, null=True)
        date_delivery = models.DateField(blank=True, null=True)
        taxes = models.DecimalField(max_digits=12, decimal_places=2, blank=True, default=0)
        sub_total_price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, default=0)
        total_price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, default=0)
    
    class PurchaseItem(models.Model):
        purchase = models.ForeignKey('Purchase')
        product = models.ForeignKey('Product')
        unit_price = models.DecimalField(max_digits=12, decimal_places=2)
        quantity = models.DecimalField(max_digits=12, decimal_places=2)
        total_price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, default=0)

First you declare a new URL, this for example:

::

    urlpatterns = patterns('purchases.views',
        url('^purchase-report/$', 'purchase_report'),
    )

The next step is you have a *view* for the created URL, like this:

::

    from django.http import HttpResponse
    
    from reports import ReportPurchase
    from geraldo.generators import PDFGenerator
    from models import Purchase
    
    def purchase_report(request):
        resp = HttpResponse(mimetype='application/pdf')
    
        purchases = Purchase.objects.order_by('customer','id')
        report = ReportPurchase(queryset=purchases)
        report.generate_by(PDFGenerator, filename=resp)
    
        return resp

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 next step) and the generator class
**PDFGenerator** you will need to generate a PDF file. In the future we will
have other generators, for other formats of file.

The next thing is you note you are going to return a PDF format **HttpResponse**,
look:

::

        resp = HttpResponse(mimetype='application/pdf')
    

Now you are going to work your report instance:

::

        purchases = Purchase.objects.order_by('customer','id')
        report = ReportPurchase(queryset=purchases)
        report.generate_by(PDFGenerator, filename=resp)

- The first thing you did is get all purchases, ordered by 'customer' and 'id'
  fields;
- After, you got a **report** instance from your report class
  **ReportPurchase**, using purchases queryset as report driver queryset;
- And latest, you called the 'generate_by', using the generator class.

Now you have a prepared Django 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'

Going 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 most we can from ReportLab libraries, their units, page
sizes, stylizing, etc.

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

A band is a row with elements in the report canvas. Bands in essence are the
same but their use vary depending 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 existency of every report.

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

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

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_dateil** is locally declared as class, but it could be setted
with an external class else.

The important thing here is 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 **get_value** attribute, for date field formatting.

Page Header and Page Footer bands
---------------------------------

Page header and Page footer bands are respectively designed to appear on the header and
footer on every pages in the report.

In most of reports, a page header is used to show the report title and its page number,
page count and maybe the columns header if you are creating a list or grid report.

In the same way, page footer band is used to show footer informations, like the print
date/time, software name, company's mark, etc.

Add this code block to the end of report class to have both bands:

::

        class band_page_header(ReportBand):
            height = 1.3*cm
            elements = [
                    SystemField(expression='%(report_title)s', top=0.1*cm, left=0, width=BAND_WIDTH,
                        style={'fontName': 'Helvetica-Bold', 'fontSize': 14, 'alignment': TA_CENTER}),
                    Label(text="ID", top=0.8*cm, left=0.5*cm),
                    Label(text=u"Creation Date", top=0.8*cm, left=3*cm),
                    SystemField(expression=u'Page %(page_number)d of %(page_count)d', top=0.1*cm,
                        width=BAND_WIDTH, style={'alignment': TA_RIGHT}),
                    ]
            borders = {'bottom': True}
    
        class band_page_footer(ReportBand):
            height = 0.5*cm
            elements = [
                    Label(text='Geraldo Reports', top=0.1*cm),
                    SystemField(expression=u'Printed in %(now:%Y, %b %d)s at %(now:%H:%M)s', top=0.1*cm,
                        width=BAND_WIDTH, style={'alignment': TA_RIGHT}),
                    ]
            borders = {'top': True}

Now you have to change the file top lines with imports to import some new elements:

::

    from geraldo import Report, landscape, ReportBand, ObjectValue, SystemField,\
            BAND_WIDTH, Label
    
    from reportlab.lib.pagesizes import A5
    from reportlab.lib.units import cm
    from reportlab.lib.enums import TA_RIGHT, TA_CENTER

The new elements for you now are:

- **SystemField** - shows a system field, like current date/time, page number,
  page count, report title, report author, etc.
- **Label** - show a free text
- **BAND_WIDTH** - sets the width automatically to its parent band width;
- **TA_RIGHT** and **TA_CENTER** - they sets the alignment of a widget

Other good use of Page Header and Page Footer bands is to compose or inherit
reports layout. Use your imagine ane do it!

Groupping
---------

The next thing now is group our purchases by a field. Lets choose the field
**customer**, ok?

Geraldo allows you to group report by how many levels you want. This means you
can group the report objects by 1, 2 or how many fields do you want and have
header and footer to show their aggregation informations and other features.

But for a while, just add the following code to the end of **reports.py**:

::

        groups = [
                ReportGroup(attribute_name='customer',
                    band_header=ReportBand(
                        height=0.7*cm,
                        elements=[
                            ObjectValue(attribute_name='customer', left=0, top=0.1*cm, width=20*cm,
                                get_value=lambda instance: 'Customer: ' + (instance.customer.name),
                                style={'fontName': 'Helvetica-Bold', 'fontSize': 12})
                            ],
                        borders={'bottom': True},
                        )
                    ),
                ]

In the same way you did before, you have to change the top imports to have the
new elements:

::

    from geraldo import Report, landscape, ReportBand, ObjectValue, SystemField,\
            BAND_WIDTH, Label, ReportGroup

The two important things now are:

- **attribute_name** - this attribute in ReportGroup defines what field we are
  going to use to group the objects. You must before order the queryset by this
  attribute to have it working properly. Here you can inform a field name, free
  attribute, property or method you have in queryset objects;
- **band_header** - this band is for you show things on the top of group. You
  could also use **band_footer**.

SubReports
----------

SubReport is the way you have to show object children data in a report. As you
know, a **purchase** has **many items**, and they are available in attribute
**purchase.purchaseitem_set.all()** os something like that.

Geraldo allows you to have how many subreports you want, and they will appear
in the same sequency you inform them in the class declaration.

Just add the following block with code to the end of **reports.py**:

::

        subreports = [
                SubReport(
                    queryset_string = '%(object)s.purchaseitem_set.all()',
                    detail_band = ReportBand(
                        height=0.5*cm,
                        elements=[
                            ObjectValue(attribute_name='product', top=0, left=1*cm),
                            ObjectValue(attribute_name='unit_price', top=0, left=5*cm),
                            ]
                        ),
                    ),
                ]

And now you have to change the top imports line:

::

    from geraldo import Report, landscape, ReportBand, ObjectValue, SystemField,\
            BAND_WIDTH, Label, ReportGroup, SubReport

The new thing and most intersting thing here is this:

::

                    queryset_string = '%(object)s.purchaseitem_set.all()',
    

This is the attribute that you use to join the detail object to the subreport.
It is a **string** because you can do a relationship to get objects using not
only one way.

So, you just use the macro **'%(object)s'** to set the current object!
