About

Archive for May, 2010

Tools for generating PDFs from django.

For a Django project i’m working on i needed to generate PDF reports. For example a “job ticket” that displays the client data, the material list and a list of “processing” actions to be performed, or an invoice (classic use case).

What i wanted was a way to generate these PDF files from a template using Django’s templating language. I had previously done something similar using PISA but i was not satisfied. There documentation was rather bad, generating HTML/CSS is not my strong point and automatic tools produce unreadable code. It does work though.

After some heavy and masterful googling i came up with several options.

  1. Reportlab. Very well known, very powerful, but the good parts are not free. I did not want to write the layout using Python code. I’d rather use some high level abstraction on top of Reportlab. Their RML framework (which does exactly that) seems great but as far as i can see it is not free. There are open source alternatives though, which i’ll talk about later.
  2. Pod Uses OpenOffice in server mode to generate documents in PDF. I’ll be running my software in a memory limited VPS and running OpenOffice (even in server mode) seems very scary. I did not investigate the exact memory requirements though because it seemed like a waste of time.
  3. rst2pdf. RST is very awesome, Sphinx uses it but from my reading of rst it seems that generating accurate and custom layouts seems somewhat difficult (for a report that needs specific elements in specific locations).
  4. trml2pdf. This is a tiny and free implementation of RML. This seemed to be a better solution, but what i wanted was a way to generate RML documents using a GUI tool (i’m a lazy bastard). The solution is tiny_sxw2rml which produces an RML document from an OpenOffice .odt file.
  5. JasperReports. This is a very impressive Java package and GUI designer that can generate beautiful reports from various data sources. This was a serious contender (** especially** with their very nice GUI designer) but i chose to avoid it because i could not use the Django ORM to do templating (i can use raw SQL but that is severely suboptimal in templating , especially on complex templates). I could use the Django ORM to produce an XML file with all the information but the prospect of using an XML file (using XPath ) seemed dreadful (but it would work nicely). It is a Java package with Python bindings available for it and a web interface that can be used to access it using a standard interface (HTTP)

The fourth solution (trml2pdf) is the one i chose. These tools are developed by the openerp framework for their reporting needs.

However, it is far from a perfect solution. There is no official repository for this package with various versions floating around the web with different features. More specifically, i was only able to use barcode generation using the version from openerp server source code, which i had to patch to make it work (because it relied on other modules of OpenERP).

Additionally, i had to patch it to make it work with unicode fonts as per the instructions from the OpenErp Docs. Not something you would want to depend on.

Nevertheless i was able to make it all work. I could use the tool tiny_sxw2rml to produce an rml template that would first be processed by the Django templating system and then fed into trml2pdf to produce a PDF. It works.

All in all i would have to say that JasperReports is the best solution from a technical standpoint since it has a great GUI designer that produces beautiful documents with perfect layout, has a lot of features, a large community, can be used as an independent server that produces PDF documents and much much more. The main problem is that you lose that advantage of integrating your workflow into the philosophy of Django (ORM/templates etc) and have to walk the extra mile.

I’ve already integrated trml2pdf but i’m keeping my eye open for a way to switch to JasperReports.

I’d like to hear what solutions others may have used/considered for this.

Deployment with Fabric

Τί είναι:

To fabric είναι ένα εργαλείο για αυτοματοποίηση εργασιών μέσω SSH που διευκολύνει το deployment ή την διαχείρηση συστημάτων.

Συγκεκριμένα, πρόκειται για ένα command line εργαλείο και μια βιβλιοθήκη (API) γραμμένη σε Python που επιτρέπει την εκτέλεση προγραμμάτων command line ή και Python κώδικα, σε τοπικό η και απομακρυσμένο server(s).

Παράδειγμα:

Έστω οτι έχω ένα Django site που το αναπτύσσουμε στον υπολογιστή μας, και τρέχουμε ένα production server σε ένα VPS.

Οταν έχω μια νέα έκδοση του site μου, τρέχω μερικές εντολές στο τοπικό PC (πχ το test framework, κάνω push τις αλλαγές κλπ), μετά κάνω login στο VPS μου μέσω SSH και κάνω τις αναβαθμίσεις. Αυτά τα βήματα μπορούν, αφού καταγραφούν, και μετά να αυτοματοποιηθούν κάνοντας χρήση του fabric.

Εγκατάσταση.

Η εγκατάσταση είναι πολύ έυκολη. easy_install fabric ή απο το package manager της διανομής μας

Το fabfile

Το πρώτο βήμα για την χρήση του fabric είναι η δημιουργία του αρχείου στο οποίο βάζουμε τις “εντολές” μας. Το αρχείο αυτό το ονομάζουμε fabfile.py γιατί με αυτό το όνομα το βρίσκει αυτόματα το εργαλείο fab (το command line εργαλείο του fabric)

Στο fabfile αρχείο καταγράφουμε τις διάφορες “λειτουργίες” που θα εκτελεί το Fabric.

Παράδειγμα:

:::python
def hello():
    print "Hello world"

Τώρα μπορούμε να τρέξουμε την “hello” τρέχοντας:

arcanum@localhost ~ $ fab hello

Το fabric αυτόματα θα ψάξει για ένα αρχείο με όνομα fabfile.py και μέσα του για ένα callable με όνομα hello και θα το εκτελέσει.

Φυσικά αυτό είναι υπερβολικά απλό και όχι ιδιαίτερα χρήσιμο απο μόνο του. Η χρησιμότητα του Fabric έρχεται απο το API του.

Παράδειγμα με το API του Fabric

Όπως είπαμε θέλουμε να τρέξουμε μερικές εντολές στο τοπικό PC και μερικές στο απομακρυσμένο. Το fabric μας παρέχει ένα API για αυτή την δουλειά Αυτό είναι ένα (τροποποιημένο για πολλούς λόγους) script που χρησιμοποιώ σε ένα project μου.

def import datetime
from fabric.api import local, run, cd, env

# The app directory of metaxas.gr
PROJECT_DIR='/home/arcanum/django_projects/myproject'

env.hosts = ['arcanum@metaxas.gr:2000']

def deploy():
    backup_database()
    sync_remote_repo()
    run_migrations()
    restart_wsgi()

def push_updates():
    """
    Push updates from local repo to remote repo
    Does *NOT* commit uncommited modifications
    """
    # TODO check for uncommitted modifications and give warning
    local("hg push")

def sync_remote_repo():
    """
    Sync the remote stable repository on the production server
    """
    with cd(PROJECT_DIR):
        run('hg pull')
        run('hg up')

def run_migrations():
    """
    Run any migration on the remote server
    """
    with cd(PROJECT_DIR):
        run('./manage.py migrate')


def restart_wsgi():
    """
    Restart the WSGI app by 'touch'ing the wsgi file.
    Thi means updating the last modification date of that file, which is
    monitored by the WSGI app server
    """
    with cd(PROJECT_DIR):
        run('touch wsgi/django.wsgi')


def backup_database():
    """
    Make a backup of the database.
    """
    with cd(PROJECT_DIR):
        now = datetime.datetime.now()
        now_str = now.strftime("%d-%m-%Y")
        BACK_CMD = 'pg_dump --format=t mydatabase |bzip2 > mydatabase-%s.sql.bz2'%(now_str, )

        run(BACK_CMD)

Θεωρώ οτι είναι αρκετά απλό παράδειγμα και κάποιος με εμπειρία στην Python θα καταλάβει αμέσως τι γίνεται όμως θα ανεφέρω μερικά πράγματα:

  1. Τρέχοντας fab deploy το fabric θα βρεί την function με όνομα deploy και θα την εκτελέσει. Αυτή με την σειρά της καλεί διάφορες άλλες functions μέσα στο fabfile.

  2. Απο το API του fabric χρησιμοποιούμε μερικές τυπικές λειτουργίες όπως local , run, cd κλπ.

Η local μας δίνει την δυνατότητα να τρέξουμε μια εντολή (σαν να είμασταν στο shell) στο τοπικό μηχάνημα.

Η run τρέχει μια εντολή (πάλι shell) στο remote μηχάνημα. Αυτό γίνεται transparently μέσω SSH. Το remote host τον βρίσκει απο το την env.hosts λίστα. Θα μπορούσαμε να συνδεόμαστε σε πολλούς hosts (αν για παράδειγμα είχαμε πολλούς web server σε ένα scalable site).

Το cd κάνει αυτό που φαντάζεσται. Η ανάγκη του προέρχεται απο την έλλειψη state της των shell-less SSH συνδέσεων. Το cd έχει ένα context manager (with statement ) οπότε οτι τρέχει μέσα στο context του with γίνεται prepend το path.

Γενικά το fabric λύνει πολλά προβλήματα έυκολα και γρήγορα. Απο οτι διαβάζω υπάρχουν πιο ευέλικτες αλλά πιό πολύπλοκες λύσεις αλλά δεν έχω ασχοληθεί γιατί το fabric είναι ικανότατο.

Το API του fabric είναι, προφανώς, πολύ μεγαλύτερο με δυνατότητες για prompts, file transfers, κλπ.

Για περισσότερα ανατρέξτε στο fabric documentation