Unkillable zombie blog

This blog just won't die

Apr 30, 2015

Dynamic choices in wtforms SelectField.

I’ve built a Django app that is used internally at my job. I’m migrating it to Odoo (formerly OpenERP).

One of the things i had to do was move from django forms to wtforms (since Odoo does not supply a forms system)

Now the official wtforms documentation states that in order to provide dynamic forms to a SelectField you have to supply the assign the choices argument at instantiation time (instead of the class definition). This simply would not do for my setup because it is a complicated ‘plugin’-like system. The class that instantiates forms knows knothing about the form definition or where it came from.

A quick Google and Stackoverflow search did not turn up a solution so i dug into the wtforms sourcecode and found out that choices is treated as an iterable. No type checks or anything.

So i simply created a class for my data that implemented the __iter__ method:

from wtforms import Form , SelectField
class RandomChoicesIterable(object):
    def __iter__(self):
        from random import randint
        for i in xrange(0,5):
            pair = (randint(1,100),randint(1,100))
            yield pair

class MyForm(Form):
        randomchoices = SelectField(
            choices = RandomChoicesIterable(),
            label = "Random choices every time!"
            )

Now, you would get your database stuff in the __iter__ method instead of giving random data but this is only an example.

For example:

>>> form = MyForm()
>>> for field in form:
...    print field
...
<select id="randomchoices" name="randomchoices"><option value="40">9</option><option value="41">15</option><option value="11">6</option><option value="1">95</option><option value="39">33</option></select>
>>> for field in form: print field
...
<select id="randomchoices" name="randomchoices"><option value="82">81</option><option value="43">7</option><option value="97">87</option><option value="100">48</option><option value="85">59</option></select>
>>> for field in form: print field
...
<select id="randomchoices" name="randomchoices"><option value="29">89</option><option value="10">37</option><option value="58">37</option><option value="14">97</option><option value="10">22</option></select>
>>> for field in form: print field
...
<select id="randomchoices" name="randomchoices"><option value="22">18</option><option value="96">92</option><option value="27">81</option><option value="59">94</option><option value="28">42</option></select>
>>> for field in form: print field
...
<select id="randomchoices" name="randomchoices"><option value="17">5</option><option value="73">31</option><option value="21">68</option><option value="82">11</option><option value="81">61</option></select>
>>> for field in form: print field
...
<select id="randomchoices" name="randomchoices"><option value="37">53</option><option value="13">18</option><option value="69">73</option><option value="16">51</option><option value="86">14</option></select>
>>> for field in form: print field
...
<select id="randomchoices" name="randomchoices"><option value="81">42</option><option value="86">50</option><option value="29">71</option><option value="45">74</option><option value="43">35</option></select>

Big Fat Warning

Since this is not the documented method, it might break in future versions of wtforms.

Click to read and post comments

May 15, 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.

Click to read and post comments

May 11, 2010

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.

Παράδειγμα:

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

Click to read and post comments

Feb 28, 2009

Δημιουργία ImageSurface στο pycairo με χρηση της PIL.

Πρόσφατα δοκίμαζα το cairo (με την χρήση των python bindings, pycairo). Μια 2D vector graphics βιβλιοθήκη γραμμένη σε C με bindings για πολλές γλώσσες.

Ήθελα να μπορώ να φορτώνω εικόνες απο αρχεία με διάφορα format και να τα βάζω στο canvas μου.

Η PIL (Python Imaging Library) είναι μια πολύ καλή βιβλιοθήκη που μας επιτρέπει να διαβάζουμε και να επεξεργαζόμαστε πολλά αρχεία είκόνας.

Η PIL μας δίνει την δυνατότητα να πάρουμε τα “raw” δεδομένα μιας εικόνας, το cairo μας επιτρέπει να φτιάξουμε ένα ImageSurface απο τα “raw” δεδομένα μιας εικόνας αρα το πρόβλημα θα έπρεπε να είναι απλό. Μετατρέπουμε μια εικόνα σε RGBA format (γιατί μπορεί πχ να είναι pallete ) , παίρνουμε τα raw δεδομένα και τα δίνουμε στο cairo. Δεν είναι ακριβώς έτσι όμως. Η PIL χρησιμοποιεί RGBA format στα raw δεδομένα ενώ η cairo ARGB. (δηλαδή πρώτα το alpha channel αντί για τελευταίο όπως στο RGBA). Το αποτέλεσμα είναι οτι αντιστρέφονται τα χρώματα στο B και R color bands.

Για να το λύσουμε αυτό πρέπει να αντιστρέψουμε τα B και R color bands (blue και Red δηλαδή). Το οτι χρειάζεται να γίνει αυτό, όπως και το πώς γίνεται το βρήκα με αρκετό ψάξιμο ψάξιμο στα intertubes και για αυτό το καταγράφω εδώ. Υπάρχει στο cookbook του cairo σχετικό θέμα αλλα δεν λύνει την μετατροπή RGBA->ARGB και αυτό που αναφέρει σαν λύση (οτι το PIL μπορεί να μετατρέψει ARGB) δεν δουλεύει.

Ορίστε λοιπόν:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import Image
import cairo
import numpy

img = Image.open('our_file.jpg')
img = img.convert("RGBA")
r , g , b, a  = img.split()
img = Image.merge("RGBA"  ( b, g, r ,a ) )
buff = numpy.asarray(img)
buff.flags.writeable = True

width,height = img.size
stride = cairo.ImageSurface.format_stride_for_width(
                  cairo.FORMAT_ARGB32,width)

surface = cairo.ImageSurface.create_for_data(buff,
                    cairo.FORMAT_ARGB32, width,height,stride)

Εδώ χρησιμοποιώ την numpy για writeable buffer που χρειάζεται η μέθοδος create_for_data() αλλα γίνεται το ίδιο απλά και με το array module.

Click to read and post comments

Dec 03, 2008

Django CSRF tag for AJAX POST calls.

Το Django μας παρέχει (προαιρετικά) το CSRF middleware του οποίου η δουλειά είναι να αποτρέπει τις επιθέσεις CSRF. Για να το πετύχει αυτό, στα response object βρίσκει τις φόρμες με POST method και “εισάγει” ένα ακόμα κρυφό field που περιέχει ένα token μοναδικό για το session. Στα POST request επιβεβαιώνει οτι το token υπάρχει και είναι σωστό.

Όλα ωραία και καλά, αλλα αυτός ο τρόπος έχει το πρόβλημα ότι όταν θέλουμε να κάνουμε AJAX κλήσεις με POST requests δεν θα δουλέψουν γιατί το CSRF middelware δεν θα βρεί tokens.

Για να λύσω αυτό το πρόβλημα με τον πιό απλό τρόπο έφτιαξα ένα template tag που παρέχει ένα token στα template μου.

Έτσι, για παράδειγμα στο YUI όταν κάνω το request είναι κάπως έτσι:

1
2
3
4
{%csrf_token as token    %}
params = "ham="+ham+"&eggs="+eggs + "&csrfmiddlewaretoken={{token}}" ;
var transaction = YAHOO.util.Connect.asyncRequest("POST", {%url spam%},
    callback, params);

Μιας και βασίζεται στην ύπαρξη του sessionid στο cookie, αυτό το tag δεν θα μας δώσει token όταν ο χρήστης έχει απενεργοποιήσει τα cookies (κάτι που σπάει ένα σκασμό site οπότε δεν με πολυνοιάζει) και όταν είναι το πρώτο request στο site μας. (Μπορούμε όμως να ελέγξουμε αν υπάρχει το token και να πράξουμε κατάλληλα)

Ορίστε και το tag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from django.conf import settings
from django.utils.hashcompat import md5_constructor
import re

class CsrfNode(template.Node):
"""
Tag that provides a CSRF token in the context.
Usage: {% csrf_token as token %}
If the user has no cookies (cookies disabled, or this is
the first ever page view on the site, this tag will provide
an empty token. You can test that (eg to refresh the page)
"""

def __init__(self,var_name):
    self.var_name = var_name

def render(self,context):
    SESSION_COOKIE_NAME = getattr(settings,
            "SESSION_COOKIE_NAME","sessionid")
    sess_key = context['request'].COOKIES.get(SESSION_COOKIE_NAME,None)
    if not sess_key:
        # User has no cookie. Empty token.
        token = ""
    else:
        val = settings.SECRET_KEY + sess_key
        token = md5_constructor( val  ).hexdigest()
    context[self.var_name] = token
    return ''

@register.tag(name='csrf_token')
def do_csrf_token(parser,token):
    try:
        tag_name , params = token.contents.split(None,1)
    except ValueError:
        msg = "%r tag requires arguments" % token.contents.split()[0]
        raise template.TemplateSyntaxError(msg)
    m = re.search(r'^as (\w+)$', params)
    if not m:
        msg = "%r tag had invalid arguments" % tag_name
        raise template.TemplateSyntaxError(msg)
    var_name = m.groups()[0]
    return CsrfNode(var_name)

Ελπίζω να είναι bug free :p

Click to read and post comments

Aug 30, 2008

Τρικ στρογγυλοποίησης ακεραίων στην Python

Στην Python για στρογγυλοποίηση αριθμών υπάρχει η build-in function round().
Η round(number,[ndigits]) παίρνει ένα αριθμό και επιστρέφει ένα float αριθμό στρογγυλοποιημένο. Αν δώσουμε το όρισμα ndigits (αριθμός παλι) η στρογγυλοποίηση γίνεται στα ndigits ψηφία. Πχ.
>>> round(1.123456,3)
1.123

Ο αριθμός στρογγυλοποιήθηκε στα 3 δεκαδικά ψηφία.
Υπάρχει όμως ένα undocumented feature της round() που μπορείς να δώσεις αρνητικό ndigits όρισμα!. Τότε αντί να μετράει δεξιά απο την υποδιαστολή, μετράει αριστερά.
Παράδειγμα:
>>> round(12345.123456,-1)
12350.0
>>> round(12345.123456,-3)
12000.0

Στο documentation δεν αναφέρουν τίποτα για αυτό.
posted at 19:10  ·   ·  python  greek  cool
Click to read and post comments

May 07, 2008

PyMOTW and stuff

Ίσως το καλύτερο blog σχετικά με Python.

Ο Doug Hellmann έχει στο blog του μια κατηγορία που την ονομάζει PyMOTW - δηλαδή Python module of the week.

Κάθε εβδομάδα έχει ένα αναλυτικότατο, απόλυτα κατανοητό με πολλά παραδείγματα αφιέρωμα σε ένα module της Python.

Είναι must read. Ακόμα και σε modules που τα ξέρεις βλέπεις features που δεν τα γνώριζες ή νέα tricks που δεν φανταζόσουν.

Αλλα φυσικά η βιβλιοθήκη της Python είναι τεράστια (για αυτό εξάλλου έχουν σαν motto το ‘batteries included’) και τα πιό πολλά modules είναι άγνωστα (ή έχω μια ιδέα, ή τα έχω χρησιμοποιήσει λιγάκι κάποτε).

 

 

Μια και λέμε για Python,

Κάποιοι  βλαμμένοι τύποι (βλαμμένοι με την καλή έννοια) έγραψαν ένα Ruby source code σε Python bytecode compiler (και python decompiler).  Awesome. Απο ότι διαβάζω αυτό είναι εφικτό γιατί το bytecode της ruby είναι εμπνευσμένο απο αυτό της Python. Έτσι εξηγείται αυτή η φωτογραφία  του Matz (δημιουργός της Ruby)

Click to read and post comments

Apr 19, 2008

Python list comprehensions nasty behavior

Python’s list comprehension syntax is amazingly concise and easy to use and understand. It make Python even more beautiful than it already is.

However, i came upon some weird behavior today that caused a strange bug in my application.

The common way to use a list comprehension is with a variable named x eg:

my_new_list = [ x.name for x in people ]

The behavior most of us would expect (or assume to be reasonable) is for the scope of variable x to be limited within the execution of the list comprehension.

This is not the case however. If you already have defined an x variable and expect it to be the same after the list comprehension you will be disappointed (not to mention bewildered at your new,  seemingly unexplained, bug ).

I am not sure why this feature it was designed that way. I think this is a source of confusion and, as such, un-pythonic, if i may use the term :-)

Be careful when using list comprehensions to use a completely new variable.

posted at 18:38  ·   ·  python
Click to read and post comments

Feb 26, 2008

sorting using keys in python.

I’ve used comparator functions while sorting in python quite often. I had a complex object today that i had to sort on using non-standard comparator functions.

My object was a dictionary that contained a list of 2 dictionaries that in turn containd lists of objects. I wanted to sort the first dictinary based on the len() of the objects of the lists at the other end of the hierarchy.

using a cmp function i had to do something like:

def comparator(x,y):
#4 lines do stuff to get the len() of data inside x


#4 lines do stuff to get the len() of data inside y

if len_x > len_y:
return 1
elif len_x == len_y:
return 0
else:
return -1

 

However i came upon the sorting howto on wiki.python.org and a way to do sorting that i did not know. Using the key funciton.

Instead of sorted(iterable,cmp=my_cmp_func) you can do sorted(iterable,key=my_key_func).

my_key_func is called with each element of the iterable as argument. It must returns a value that is used in sorting (I assume using standard cmp).

This meant that instead of my cmp function i could do only:

def my_key_func(x):
#4 lines do stuff to get the len() of data inside x
return len_x

 

It makes more sense because basically we are not sorting x and y based on their own value, but we are sorting x and y based on some remote data connected to x and y.

Certainly nicer.

posted at 21:51  ·   ·  python
Click to read and post comments

Feb 21, 2008

Resolver is a great idea.

I was reading about Resolver quite often, mostly on planet python. I had made a mental memo to try it out for my self sometime but always seemed to be delaying it. I tried it out yesterday i was really impressed by the concept. 

With Resolver you have a spreadsheet with tightly integrated python programmability. The window has 3 panes. The ‘spreadsheet’ pane, the Python editor, and the ‘output’ pane. You can define any python function you like and make it available for use in your spreadsheet. This means you can do database connectivity using python, or advanced data manipulation that is either no possible with Excel, or very difficult.  I can’t tell you how many times i wished i could do that easily with Excel. In fact, i very often needed to pull data from the database of a Django project and do some manipulation using Excel , and present it to some users. But i wanted to use the Django ORM instead of raw SQL. With Resolver it should be very easy.

At least for me, it was often the case that i needed to solve a problem that was too complex for Excel, and too simple for writing a full-blown application. I needed something that would combine the best of both worlds.

Resolver is written in IronPython and uses Python for programming.  It might be possible to run it using mono but i haven’t tried anything of the sort. Ironpython supposedly runs on linux , but mono has an incomplete Forms implmementation.

Best of all, it free for non-commercial use, so you need to try it out.

Click to read and post comments
Next → Page 1 of 2