Unkillable zombie blog

This blog just won't die

Feb 06, 2008

IBAN validating code

Recently i needed to validate some IBAN numbers (International Bank Account Number). I quick search revealed the wikipedia article on the subject.

Though i had initially hacked up a Greek-only IBAN validator i though it might be of use to other people as well and modified it to validate  every possible IBAN (as long as the wikipedia article is complete).

 

So here it is:

 

#!/usr/bin/python
# -*- coding: utf-8

# iban.py
# http://kill9.eu/
#
# Copyright (C) 2008 Konstantinos Metaxas 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 2 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

import string

class IBAN(object):

    def __init__(self,iban):
        """Represents an IBAN number.
        iban must be supplied as a string"""
        self.iban = iban.strip().upper()
        #List retrieved from http://en.wikipedia.org/wiki/IBAN
        self.error_list = []
        self.country_length_table = {
                'AD':('Andora',24),
                'AT':('Austria',20),
                'BE':('Belgium',16),
                'BA':('Bosnia and Hetzegovina',20),
                'BG':('Bulgaria',22),
                'HR':('Croatia',21),
                'CY':('Cyprus',28),
                'CZ':('Czech Republic',24),
                'DK':('Denmark',18),
                'EE':('Estonia',20),
                'FO':('Faroe Islands',18),
                'FI':('Finland',18),
                'FR':('France',27),
                'DE':('Germany',22),
                'GI':('Gibraltar',23),
                'GR':('Greece',27),
                'GL':('Greenland',18),
                'HU':('Hungary',28),
                'IS':('Iceland',26),
                'IE':('Republic of Ireland',22),
                'IL':('Israel',23),
                'IT':('Italy',27),
                'LV':('Latvia',21),
                'LI':('Liechtenstein',21),
                'LT':('Lithuania',20),
                'LU':('Luxembourg',20),
                'MK':('FYROM',19),
                'MT':('Malta',31),
                'MC':('Monaco',27),
                'ME':('Montenegro',22),
                'MA':('Morocco',24),
                'NL':('Netherlands',18),
                'NO':('Norway',15),
                'PL':('Poland',28),
                'PT':('Portugal',25),
                'RO':('Romania',24),
                'SM':('San Marino',27),
                'RS':('Serbia',22),
                'SK':('Slovakia',24),
                'SI':('Slovenia',19),
                'ES':('Spain',24),
                'SE':('Sweden',24),
                'CH':('Switzerland',21),
                'TR':('Turkey',26),
                'TN':('Tunisia',24),
                'GB':('United Kingdom',22),

                }

    def is_valid(self):
        """checks if it is a valid IBAN"""

        if not self._is_valid_for_countrycode():
            return False
        if not self._swift_check():
            return False
        return True

    def _is_valid_for_countrycode(self):
        code = self.iban[:2]
        try:
            max_length = self.country_length_table[code][1]
        except KeyError:
            return False
        if len(self.iban) != max_length:
            return False
        else:
            return True

    def _swift_check(self):
        """
        http://en.wikipedia.org/wiki/IBAN
        """
        head_to_tail_iban = self.iban[4:] + self.iban[:4]
        numerical_ascii_map = dict( zip(string.ascii_uppercase,range(10,36)) )

        translated_iban = ""
        for c in head_to_tail_iban:
            if c in numerical_ascii_map:
                translated_iban += str(numerical_ascii_map[c])
            else:
                translated_iban += c

        try:
            integer_iban = int(translated_iban)
        except ValueError:
            return False

        modulo = integer_iban % 97

        if modulo == 1:
            return True
        else:
            return False

Using it as as simple as:

 

Python 2.5.1 (r251:54863, Oct 14 2007, 19:24:49)
[GCC 4.2.2 (Gentoo 4.2.2 p1.0)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import iban
>>> myIBAN = iban.IBAN('GRXXXXXXXXXXXXXXXXXX') # Actual IBAN omitted for privacy reasons
>>> myIBAN.is_valid()
True

 

It should be easy to make it into a custom Django Form Field , and i might post the relevant code later.

posted at 17:53  ·   ·  python
Click to read and post comments

Aug 17, 2007

Captcha widgets in Django

This blog was getting stormed by comment spam so i decided to use some captcha protection. I chose pycaptcha for this and had to integrate it nicely with Django. I wanted to make it an easy widget that does it’s own validation and does not require view code for validating the captcha. So here is what i came up with:

EDIT: Updated for SVN trunk version of Django ( value_from_datadict has an extra parameter)

First, the custom form field:


class CaptchaField(CharField):
widget = CaptchaWidget
def __init__(self, *args, **kwargs):
super(CaptchaField, self).__init__(*args, **kwargs)


def clean(self,values):
value,captchaid = values
#Check if the value matches that in the session
factory = PersistentFactory('/tmp/captcha_persistence')
test = factory.get(str(captchaid))
factory.storedInstances.close()
if (not test) or (not test.valid):
raise ValidationError(u"""Captcha does not exist or is
invalid. Please refresh to get new captcha""")
if not test.testSolutions([value.strip()]):
raise ValidationError(u"""You did not type the correct
captcha text""")
return ""

The Widget used by this form field:


class CaptchaWidget(Input):
input_type = "text"
def render(self,name,value,attrs=None):
#Always generate a new captcha on rendering
factory = PersistentFactory('/tmp/captcha_persistence')
test = factory.new(Tests.PseudoGimpy)
factory.storedInstances.close()
captchaid = test.id
superclass_output = super(CaptchaWidget,self).render(name,"",attrs)
output = """<img src="/captcha/%s" border="0"/><br/>
<input type="hidden" name="captchaid" value="%s" />
%s
""" %(captchaid,captchaid,superclass_output)
return output

def value_from_datadict(self,data,files,name):
return [ data[name],data['captchaid'] ]


We also need a view to deliver the image for the captcha so here it is:

def captcha_image(request,id):

factory = PersistentFactory('/tmp/captcha_persistence')
test = factory.get(str(id))
factory.storedInstances.close()
if not test:
return HttpResponseNotFound()

response = HttpResponse(mimetype='image/jpeg')
test.render().save(response,'JPEG')
return response


Dont forget to add a urls.py entry for our view. something like:

(r'^captcha/(?P<id>\w+)','blogon.blog.views.captcha_image'),

should be enough (change the view to your view. If you change the url then be sure to change it in the html generated by the widget.

. Notice that in our code we use a file in /tmp for writing the persistence of pycaptcha. This may be a security problem depending on our environment. So be careful, or us a trusted location.
I should use a template for the HTML generated by the widget so as to make it more easily customizable but i just got bored after i got it working.

Click to read and post comments

Jul 11, 2007

doesn’t pownce.com support unicode properly?

I was invited to pownce.com today. It’s a hot new Web 2.0 messaging applications that is also developed by a really hot developer in a hot language using a hot framework.
It’s still in BETA and the new accounts are invite only. I suspect that most people have accounts and the whole ‘invite’ thing is a way to make it sound cool. After all, upon registration you get 6 invites automatically. It’s not like they are controlling how fast they grow like google did by giving invites using their own criteria to control growth.
Being in BETA it means there are bugs, and features missing. It seems that one particular bug is rather significant. My Greek friends tried to send messages in Greek (in unicode encoding) and all they got was question marks. Not being able to communicate in my native language is a major bug. I am also a little curious as to why this is happening. Django and Python have excellent unicode support, Mysql (horrible as it may be) has unicode support, so what gives?
Click to read and post comments

Jun 26, 2007

Lambda Lives!

It seems that Guido Van Rossum has decided to keep lamda for Python 3000 (or as it will be called when release.. Python 3.0.
I can’t say i like all of the proposed changes. It starts to feel more and more like Java, though that may not be necessarily a bad thing.
It’s backwards incompatibly in a significant way, so don’t expect major applications to be ported right away (or at all!)
posted at 00:19  ·   ·  python
Click to read and post comments

Jun 17, 2007

Validating Greek VAT code (έλεγχος Α.Φ.Μ)

I recently had to validate the VAT code entered in an internetl Django application. Searching around lead me to this site. The implementation there is Visual Basic though, which wouldn’t suit me. So i translated it to Python as best as i could (given i don’t actually know any VB :p )
Here it is:
def isValidAFM(afm):
    if len(afm) != 9:
        return False

    try:
        int(afm)
    except ValueError:
        return False

    AFMpart = afm[:-1]
    afmsum = 0
    multiplier = 2
    for c in AFMpart[::-1]:

        afmsum += ( int(c) * multiplier )
        multiplier *= 2

    if not afmsum:
        return False

    afmmod = afmsum % 11

    lastdigit = int(afm[-1])
    if lastdigit == afmmod:
        return True
    elif (lastdigit == 0) and (afmmod ==10 ):
        return True
    else:
        return False

Caveats:
  • I don’t know if the actual algorithm is valid since my search for official documentation turned up nothing. It seems to be accepted by a large on-line community of accountants though, and that’s good enough for me.
  • I have not tested it much. You at your own risk, as always
posted at 13:54  ·   ·  python
Click to read and post comments

Jun 07, 2007

A stupid Sudoku solver in Python (and PyGTK)

During the holidays i was bored out of my mind. Holidays in my family means that we gather some close relatives and friends at our house and eat. Not once. Not twice, but every freaking day. So when a sudoku magazine fell in my hands i thought it would be fun to try and write a sudoku solver. So here it is:
PySudoku UPDATE Because of a server move the tarball is unavailable. Get it using Subversion from svn://arcanum.homelinux.org/sudoku

This stupid little program is written in Python and PyGTK so you need both to run it.

There are some bugs:

  • It can be slow in certain puzzles.
  • It is either too slow or in a loop when the puzzle has no solution.
  • It won’t check if you enter an invalid puzzle from the start (which is easy to do. i am lazy)
  • Probably more things i can’t remember now :)

It does not generate puzzles, not because i don’t know how to do it but because my backtracking solver it so slow that it would be painful to try generating a solution with the “try a random number / see if there is a solution/ try another etc etc.” method.
There are far better solver/generators out there if you are looking for one. Mine is just a toy i did for fun.

Click to read and post comments
← Previous Page 2 of 2