Archive for the ‘Ordenadores’ Category

Y ya van 5

Friday, July 4th, 2008

Escribo este post desde la última charla de la quinta Guadec Hispana. Quién lo diría desde aquel primer encuentro en Almendralejo, pero lo cierto es que hemos pasado ya por La Coruña, Vilanova, Granada y Fuenlabrada. Afortunadamente yo he tenido la suerte de asistir a todas ellas y ya es casi una cita obligada para mí. Más aún cuando sólo he podido ir a la Guadec internacional una vez.

Han sido dos días muy agradables viendo a viejos conocidos como Germán, los Carlos, Álvaro, Palomo, Daniel, Claudio, Roberto, Domingo y muchos otros que posiblemente olvide.

Al igual que GCubo, el grupo de personas de GNOME-Hispano es ya un grupo de amigos primero y de desarrolladores/usuarios de GNOME después. Tristemente, y al igual que GCubo otra vez, cada vez se ven menos caras nuevas. Aquí en la Guadec se ha hablado de esto y se han intentado buscar causas y soluciones sin llegar a un acuerdo concreto.

Al final, para mí no deja de ser un par de días muy agradables en un ambiente distendido y divertido en los que siempre aprendes cosas nuevas pero sobre todo mantienes el contacto con un puñado de personas que vienen todos los años porque les apasiona esto del software libre. Como a mí.

Algunas caras de esta Guadec-es

Libros que leí en el 2007

Thursday, January 17th, 2008

Siguiendo un poco el ejemplo de Malglam, aunque con un poco más de retraso, aquí os dejo la lista de libros que leí el año pasado:

Conan de Cimmeria. Volumen 1. 1932 - 1933

conan-1.jpgConan siempre ha sido uno de mis héroes desde pequeño y aunque no tuviera muchos comics de él, tras ver la película Conan el Bárbaro, siempre que podía imitaba sus mandobles con una fregona. Ahora tengo la edición de coleccionista de la película y estos dos tomos que Ana me ha regalado este año. Lo que no sabía de pequeño es que Robert E. Howard, el autor del personaje, era coetáneo de H. P. Lovecraft, uno de mis autores preferidos. Eso se nota en algunas descripciones y sensaciones que transmiten los relatos de Conan, quién puede matar a un gorila de un puñetazo pero cuando se enfrenta a hechiceros y a lo desconocido en general tiene miedo y prefiere evitarlo. A través de los relatos cortos que componen el libro te haces una idea de la personalizad de Conan: bruto y primitivo pero al mismo tiempo inteligente y justo. Un libro muy recomandable si tienes los 60 eurazos que cuesta esta edición de super lujo :-)

Conan de Cimmeria. Volumen 2. 1933 - 1934

conan-2.jpgMás de lo mismo, es decir, genial. Esta vez hay menos relatos pero son más largos. El último, “La Hora del Dragón”, me ha parecido el mejor relato de Howard de los que he leido. Es casi una novela por su extensión y habla de los días en los que Conan ya era rey de Aquilonia, de cómo le arrebatan el trono y de cómo consigue recuperarlo. Ojalá alguién haga un día una película de este relato.

Añoranzas y Pesares

anoranzas-y-pesares.jpg Añoranzas y pesares es una trilogía compuesta de 4 libros. Es trilogía porque el tercer y cuarto libro originariamente eran uno solo pero por su tamaño las editoriales decidieron dividirlo en dos. Me lo he leido porque Ana me lo ha recomendado y porque en general soy muy aficionado a los libros de fantasía. El libro engancha mucho porque la historia es rápida y dinámica (me leí los 4 libros en dos meses) pero creo que eso es todo lo bueno que puede decir sobre él. Es bastante predecible y algunas cosas parecen copias literales de El Señor de los Anillos pero bueno, junto con Ken Follet o Stephen King, creo que el autor es uno de esos que hacen libros como churros y libros que enganchan mucho.

Paper Prototyping

paper-prototyping.gifEste libro trata de una técnica que me llamaba la atención desde hace algún tiempo: hacer prototipos en papel de programas informáticos. Básicamente viene a decir que es tan poco lo que cuesta hacer un prototipo en papel y tanto lo que se aprende de él que todos los proyectos de software deberían hacerlos en sus fases iniciales. El libro hace especial hincapié en los tests de usabilidad usando prototipos de papel pero yo creo que hacer prototipos de papel es también de enorme utilidad para etapas de análisis y posterior implementación.

Es un buen libro pero quizá excesivamente largo para las 4 o 5 ideas claves que explica. La verdad es que al final se me hizo algo pesado.

User Interface Design for Programmers

ui_for_programmers.jpgEste libro lo leí durante mi estancia en Brasil en la magnifica biblioteca de Async. Me gustó tanto que he decidido comprarlo y releerlo en este año pasado. Como la mayoría de los libros que me gustan, es un libro corto y escrito en un lenguaje informal y muy atractivo, como casi todo lo que escribe Joel. Se basa en la premisa de que los programadores y los usuarios tienen modelos distintos de los programas informáticos y que un programa será tanto más usable cuanto más se acerque su modelo al modelo del usuario. Este concepto tan difuso es explicado con suma claridad en los capítulos del libro, que en total, no me llevaron más de 3 tardes en leerlo.

Don’t make me think

dont-make-me-think.jpgAl igual que el User Interface Design for Programmers, este libro también lo leí en Brasil y también me fascinó. Se lee en el mismo tiempo que se lee un Mortadelo, uno se rie casi igual y se aprende un montón sobre usabilidad de páginas web. Como el anterior, el libro gira en torno a una premisa: cualquier elemento de una página web (o cualquier interfaz de usuario) que haga pensar al usuario y le obligue a tomar una decisión, sobra. En los siguientes capítulos desarrolla esta idea y pone numerosos ejemplos con webs existentes de cosas que se deben hacer y cosas que no se deben hacer. Especial mención merece un apéndice al final del libro donde encontramos una carta modelo para tu jefe o tu cliente explicándo por qué no se deben hacer páginas web con gifs animados, introducciones en flash y demás aberraciones que sólo podemos perdonarle a Homer Simpson.

The C Programming Language

the-c-programming-language.jpgHe de confesar que a estas alturas de la vida no había leído este libro y tenía una mezcla de curiosidad y mala conciencia por ello. Afortunadamente ha sido fácil de remediar y he de decir que el libro no me ha decepcionado lo más mínimo, cosa que suele pasarme por ejemplo con ciertas películas, que todo el mundo recomienda y luego la ves y dices “no es para tanto”. Pienso que este libro debería ser de obligada lectura en la carrera de informática ya que explica de forma clara y cristalina conceptos difíciles de coger la primera vez que empiezas a programar como los arrays, las cadenas de caractéres, los punteros, etc. Lo único que me falta para completar esta lectura es desarrollar un programilla sencillo pero útil donde pueda poner en práctica lo que he aprendido y/o recordado con este libro.

The Mythical Man Month

the-mythical-man-month.jpgEste es el tercer y último libro de este post de los que leí en Brasil. Es considerado un clásico en la ingeniería del software y comprende un montón de artículos sobre el desarrollo de aplicaciones software de complejidad y tamaño considerables. En él viene a decir que la tarea más compleja de hacer una aplicación es la comunicación entre las distintas partes involucradas: el cliente, el arquitecto de software, los analistas, los programadores, etc. A consecuencia de esto añadir más mano de obra a un proyecto de software que lleva retraso según la planificación original no sólo no resuelve el problema sino que lo agrava. Otro artículo interesante incluido en el libro es “No silver bullet” en el que viene decir que en la informática no hay una herramienta/tecnología/metodología mágica que mejore sustancialmente la creación de software tal y como han ido vendiendola distintos fabricantes desde finales de los años 60. Es curioso (o aterrador si se quiere) comprobar que casi 40 años después, este paper sigue siendo tan verídico como en la fecha en la que se publicó. Lo de silver bullet (bala de plata) es porque compara el proceso de creación de software con un hombre lobo por los peligros que ello conlleva y la bala de plata es lo único que mata al hombre lobo. En informática no hay balas de plata.

El tío Petros y la conjetura de Goldbach

tio_petros.jpgEsta novela la ví comentada en barrapunto y un día que estaba en Alcampo, estaba en la sección de novedades y me hice con ella. Es un libro fácil de leer y bastante realista. La verdad es que me costaba creer que fuera una novela en lugar de una biografía y estoy seguro de que está inspirada/basada en la vida de un célebre matemático. La historía trata la vida de un matemático griego con una capacidad extraordinaria que dedica su vida a resolver la conjetura de Goldbach todo contado desde la perspectiva de su sobrino, el verdadero protagonista del libro. No cuento más detalles para no fastidiarle el libro a nadie. En definitiva, es un libro ameno y muy interesante.

Snow Crash

snow-crash.jpgSnow Crash se considera uno de los libros que iniciaron el género de Ciber Punk. Tiene un estilo ágil y rápido con las descripciones justas y diálogos llenos de dobles sentidos y argot. Esto hace que se lea muy rápido pero al mismo tiempo requiere concentración por parte del lector para que no se le pase ningún detalle de la enrevesada trama. Su visión del futuro no es muy tentadora pero su gran dosis de humor negro y cinismo hace que al final sea una experiencia divertida, por lo menos al leerla. Vivir en ese mundo me imagino que y no es tán divertido. La portada de mi edición es la de la foto y es bastante más cutre: una chica en 3D encima de una patineta voladora. Es bastante dificil que te tomen en serio cuando te ven en el autobus leyendo un libro con dicha portada….

Hegemonía o Supervivencia. La estrategia imperialista de Estados Unidos

hegemonia-o-supervivencia.jpgEste libro me lo regalaron Mario y Montero el año pasado y es un poco denso de lee, lleno de referencias a otros libros, de cifras y estadísticas. Todo para poner a Estados Unidos en su lugar. Después de leer el libro lo único que se te queda es una sensación de mala leche e impotencia enorme tras ver que todas tus sospechas sobre este país eran ciertas y Chomsky simplemente las ha investigado y plasmado aquí. No apto para personas sensibles a las injusticias mundiales.

El ecologista escéptico

el-ecologista-esceptico.jpgEste libro me lo recomendó mi buen amigo Jaime, el escéptico por excelencia :-) He decir que aún no lo he terminado porque si bien el de Chomsky era denso esté es más compacto que un mantecao de maritoñis. Lo bueno es que esta dividido en distintas partes y puedes leerlas en el orden que quieras porque son más o menos independientes excepto la primera que es la introducción y que es la que deberías leer primero. El libro viene a decir básicamente que los medios de comunicación tienden a darnos malas noticias sobre el mundo y que las buenas casi nunca nos llegan. Así es normal que siempre estemos en estado de alerta y paranoia esperando que llegue el siguiente cataclismo. El autor insiste en que es necesario hacer un análisis detallado de los problemas que asolan el mundo (porque haberlos haylos, eso está claro) para poder decidir en qué emplear nuestros (escasos) recursos de la mejor forma posible. No creo que nadie pueda estar en contra de está idea tan racional.

The Alphabet of Manliness

alphabet-manliness.jpgEl alfabeto de la masculinidad es un libro escrito por el autor de la mejor página del universo y que no te decepcionará. Por favor, abstenganse las mujeres en general y las feministas en particular porque el libro es muy politicamente incorrecto. Cada letra del alfabeto la asocia con un sígno claro de la masculinidad. Algunos ejemplos son la H de Heavy Metal, la C de Costillas de Cerdo, la T de tetas o la P de patada en el culo. Como se puede ver, todos ellos claros símbolos de masculinidad. Si te gustaron las frases de Chuck Norris, este libro te encatará. El que quiera saber más, el libro tiene hasta su propia web.

Los Hijos de Húrin

hijos-hurin.jpgPor último, este año pasado me leí los Hijos de Hurín, de Tolkien padre aunque editado y juntado por Tolkien hijo. He de decir que he leido más de un libro de los que ha publicado Tolkien hijo y sólo este me ha parecido bueno. El Libro de los Cuentos Perdidos, el Retorno de la Sombra y otros como estos me parecieron un mero trabajo de corta y pega. Pero en esta ocasión parece que se ha esmerado. La historia que cuenta no es nueva si has leido el Silmarilion, eso sí esta mucho más desarrollada y se puede leer sin tener que memorizar 50 nombres para no perderte. La gente que lee el Silmarilion y no les gusta porque esperan que sea otro Señor de los Anillos se sentirán un poco menos decepcionados con este libro. Aunque la verdad, a mí el Silmarilion me encantó. Este libro me ha gustado tanto que lo he comprado 3 veces este año: uno para mí y dos para hacer regalos, a Ana y a Daniel, las otras dos personas en el mundo que conozco que están casi tan flipadas con Tolkien como yo.

An address form

Tuesday, November 20th, 2007

In the Zope 3 app I’m working at we have person objects that can have a list of addresses associated to them. No rocket science so far. So I needed to create an add and an edit forms for this. Something with url like these:

  • http://yourhost/app/people/john/addresses/+/@@addAddress.html
  • http://yourhost/app/people/john/addresses/3/@@editAddress.html

Now you wonder how an address looks like. Quite simple, let’s look at its interface:

class IAddress(IContained):

    line = zope.schema.Text(

        title=u'Line',

        description=u'Street type and number information',

        required=True

    )
    country = zope.schema.Choice(

        title=u'Country',

        description=u'Country',

        vocabulary='CountriesVocabulary',

        required=True,

    )

    state = zope.schema.Choice(

        title=u'State',

        description=u'State',

        vocabulary='StatesVocabulary',

        required=False,

    )

    postalCode = zope.schema.Choice(

        title=u'Postal Code',

        description=u'Postal Code and City',

        vocabulary=u'PostalCodesVocabulary',

        required=False,

    )

    contact = zope.schema.TextLine(

        title=u'Contact',

        description=u'Contact information',

        required=False

    )

    notes = zope.schema.Text(

        title=u'Notes',

        description=u'Other notes',

        required=False

    )

As you can see the country, state and postal code are choices taken from different vocabularies. I have models for those objects and the structure is hierarchical: A country contains state and a state contains postal codes.

I could just store the postal code inside the address since I can retrieve its state and its country just from the postal code. We decided not to do so to because of two reasons:

  • To keep our queries and business logic simpler. For example, consider you want a report of all your customer living in Spain…
  • So we could use the hierarchical relationships to aid our users in the addresses forms.

I’ll explain the second reason in more detail in this post. What I wanted to accomplish was something like what you see in this small screencast:

addres-form-screncast.gif

As you see, the user first select the country in the first dropdown list, this will fill the state dropdown list with all the states belonging to that country. Similarily, when the user chooses a state the postal codes
dropdown list will be filtered.

I started implementing this with the wrong approach which I will also describe here for the record.

Wrong approach

In Zope 3 a choice field gets it list of possible values from something called a vocabulary (or more recently, a source). This can be anything that returns a list of terms. In our case the country vocabulary will
get the list of country objects from the database. I have a countries folder registered with an ICountries interface so the task of getting the list of country objects is quite easy:

class CountriesVocabulary(SimpleVocabulary):
    zope.interface.classProvides(IVocabularyFactory)

    def __init__(self, context):
        terms = []
        for name, countries in zope.component.getUtilitiesFor(ICountries):
            terms += [SimpleTerm(country, country.__name__, country.name)
                      for country in countries.values()]
        super(CountriesVocabulary, self).__init__(terms)

Building the states and postal codes vocabularies is a little more difficult since they depend on a context. This mean that the states vocabulary will depend on a specific country and the postal codes vocabulary will depend on a specific state. But the context won’t be the country neither the state in this two cases but the address object since the vocabularies are used in fields of the IAddress interface. Using this idea I coded these vocabularies:

class StatesVocabulary(SimpleVocabulary):
    zope.interface.classProvides(IVocabularyFactory)

    def __init__(self, context):
        terms = []
        if IAddress.providedBy(context):
            country = IAddress(context).country
            if country:
                terms = [SimpleTerm(state, state.__name__, state.name)
                         for state in country.values()]

        super(StatesVocabulary, self).__init__(terms)

class PostalCodesVocabulary(SimpleVocabulary):
    zope.interface.classProvides(IVocabularyFactory)

    def __init__(self, context):
        terms = []
        if IAddress.providedBy(context):
            state = IAddress(context).state
            if state:
                terms = [SimpleTerm(pc,
                                    pc.__name__,
                                    u'%s - %s' % (pc.code, pc.city))
                         for pc in state.values()]
        super(PostalCodesVocabulary, self).__init__(terms)

So far so good but here is my problem: when you create or edit an address object you need to hit the save button three times to store the country, state and postal code. Why is this? Let’s try to reproduce these three steps:

  1. Step 1: You select a country and save the address
  2. Step 2: Now the states vocabulary will be filled with the list of states for the country you choosed in step 1
  3. Step 3: Finally you can choose a postal code from the postal codes of the state you choosed in step 2.

Note that until you saved the country the vocabulary for states won’t have that information and thus, will return an empty term list. Same thing happens with the postal codes.

Even if you can get the proper information using AJAX in the form and fill the dropdown lists with the right information you won’t be able to save the address object since the vocabularies are also used for validation
purposes.

No need to say this was a pain in the ass and kepts my brain busy for a few days until I found the right solution.

Right approach

The lesson learned from the previous approach was that something was wrong with dictionaries which depended on a context in such a strict way. In the IAddress interface I should keep the fields simple enough to let the form set any state on the state field and any postal code in the postal code
field. As I still want some integrity in my data it’s time to delegate that to an invariant. In other words, I will any value of the right type into the state and postal code attributes and after that I will validate these
fields with an invariant that will make sure the state is inside the country and the postal code is inside the state. An invariant like this:

def postalCodeInsideStateInsideCountry(address):
    country = address.country
    state = address.state
    postalCode = address.postalCode
    if country is not None and state is not None:
        if state not in country.values():
            st = state.name or state.__name__
            co = country.name or country.__name__
            raise zope.schema.ValidationError(
                u"The state %s does not belongs to country %s" % (st, co)
                )

        if postalCode is not None:
            if postalCode not in state.values():
                pc = postalCode.code or postalCode.__name__
                st = state.name or state.__name__
                raise zope.schema.ValidationError(
                    u"The postal code %s does not belongs to state %s" % (pc, st)
                    )

Then I just add this declaration inside the IAddress interface:

class IAddress(IContained):
    ...

    zope.interface.invariant(postalCodeInsideStateInsideCountry)

Next I’ll have to rewrite my vocabularies to be context-free and return the full list of objects in each case:

class PostalCodesVocabulary(SimpleVocabulary):
    zope.interface.classProvides(IVocabularyFactory)

    def __init__(self, context):
        terms = []
        for name, countries in zope.component.getUtilitiesFor(ICountries):
            for country in countries.values():
                for state in country.values():
                    prefix = country.__name__ + '/' + state.__name__ + '/'
                    terms += [SimpleTerm(RSP(pc),
                                         prefix + pc.__name__,
                                         u'%s - %s' % (pc.code, pc.city))
                              for pc in state.values()]

        super(PostalCodesVocabulary, self).__init__(terms)

class StatesVocabulary(SimpleVocabulary):
    zope.interface.classProvides(IVocabularyFactory)

    def __init__(self, context):
        terms = []
        for name, countries in zope.component.getUtilitiesFor(ICountries):
            for country in countries.values():
                terms += [SimpleTerm(RSP(state),
                                     country.__name__ + '/' + state.__name__,
                                     state.name)
                          for state in country.values()]

        super(StatesVocabulary, self).__init__(terms)

You may note that the token of each term has a prefix now. This is because the token should be unique inside that vocabulary. I’m using the country code and the state code with a slash separating them because I will use
this token later in javascript.

This fixed my problem as now I’m able to create an address object with just a save button hit without losing data integrity but I still have one more problem: my forms list all states and postal codes no matter which
country is selected in the case of states and what state is selected in the case of postal codes. Seems like all these efforts didn’t help me to accomplish what I initially wanted.

It’s time for some javascript to make things truly dynamic.

Adding javascript for a richer user experience

My solution was to add a small jQuery script and a couple of related views to fill the dropdown lists every time the user changes them. This mean that if the user selects a country, an ajax request will be made to retrieve the list of states for that country and will populate the state select tag with those options. Similar behaviour happens when the user changes the state.

Let’s see the jQuery code:

(function($){

$(document).ready(function () {

    function fillSelect(data) {
        var options = $.map(data, function (obj, index) {
            return '<option value="' + obj.id + '">' + obj.name + '</option>';
        });
        return '<option value="">(no value)</option>n' + options.join("n");
    }

    $("select#form.country").change(function () {
        var value = $(this).val();
        if (value) {
            var currentState = $("select#form.state").val();
            var url = '../../Countries/' + value + '/@@states.json';
            $.getJSON(url, function (data) {
                $("select#form.state")
                  .html(fillSelect(data))
                  .val(currentState)
                  .change();
            });
        } else {
            $("select#form.state").html(fillSelect([])).change();
        }
    });

    $("select#form.state").change(function () {
        var value = $(this).val();
        if (value) {
            var currentPostalCode = $("select#form.postalCode").val();
            var url = '../../Countries/' + value + '/@@postalCodes.json';
            $.getJSON(url, function (data) {
                $("select#form.postalCode")
                  .html(fillSelect(data))
                  .val(currentPostalCode);
            });
        } else {
            $("select#form.postalCode").html(fillSelect([]));
        }
    });

    /* initialize combos */
    $("select#form.country").change();
});

})(jQuery);

Final thoughts

The last bit of love that this form need is some support for adding countries/states/postalCodes when the user is filling an address and that information is not yet in the database. It shouldn’t be too hard to add some javascript buttons that ask the user for that and post it to the server. Then update the selects and let the user choose the new created object.

Pycha 0.1.0 released. Just another pet project

Wednesday, October 17th, 2007

I’ve been writing Pycha in the last days. It is a Python library for drawing Charts as its name suggests :-) You can learn more about it at http://www.lorenzogil.com/projects/pycha/

Sample pycha chart

What I’d like to comment here is the amount of work needed for releasing a piece of free software. We usually think that’s not too much but I always get surprised about how many things you need to do besides writing the code.

You need to choose a license, this will probably means reading for a while about the options out there and try to make a balance between a license that will make your software easy to distribute and a license that won’t make you (or your users) lose freedom.

You need to package the software. I chosed setuptools for this task as I like the idea of Python Eggs. As this is my first egg, I needed to read the manual, do some tests, try again and so on.

You need to write some decent docs. This means installation instruction, examples, general overview, etc. I did it in English and Spanish as some times I think I do too many things in that foreign language forgetting about my own language. Writing this blog is one of them :|

You need to setup a website with the docs, some instructions for downloading and getting the software, a bug tracking system and probably a wiki. Fortunately we have Trac which makes this pretty easy. I got a problem with Trac in Fedora 7 but reading Trac’s Wiki solved it (tip: you need the python-sqlite2 package).

You need to write some tests, otherwise nobody with a sane brain will want to use your risky library. This is something I still need to do but hopefully I’ll be able to do it before the weekend.

You need to do the actual release. This involve writing an entry in the CHANGES.txt file, updating the version, tagging the repository, uploading to PyPi, etc. I used this document as a release guide.

Nothing is really mandatory but if you want to get some value from releasing a project with a free software license you pretty much need to do all of the above. In other words, no docs, no tests, no bug tracking system means no people interested, no community, no feedback, no bug reports, no patches, nothing.

So go and play with pycha and tell me what you think!

A captcha for Chuck Norris

Monday, October 8th, 2007

My friend Juan sent me this captcha today.

captcha-ruso.jpg

I guess it’s only for Chuck :-)

 Some other funny captchas here

The Making Of AnaLogica.es

Thursday, October 4th, 2007

It’s been almost a month since my last post and now it’s when I say it’s because I’ve been busy and think I meant lazy. For this time I have a proof: I’ve been making a website for a friend’s pop band, Ana Lógica It’s not completely finished but most of the work is already done.

I’ll try to describe the proccess of creating such a site so beware of a very boring post if you are not into web development.

First Steps: requirements gathering

The first thing I did was talk with my friend/client about what they wanted. We decided to take another website as a model for a start point. That website is http://www.lombardafolk.com/ and I did it too, some years ago.

As this was very vague talking I had the feeling I lack more information from them about what exactly they wanted. The model website was about an old folk band and this one was about a new pop band and some requirements are different.

I drew all the screens in paper in a fairly detailed manner (10 screens in total). Then I photocopied them and got 5 copies and scheduled a meeting with all the members of the band. We met in a bar and while drinking some beers we discussed my design. They liked it generally but gave me some new ideas and spotted minor things they didn’t like or I did fogotten. We also made a list of the multimedia and information material they needed to give to me. I was really happy with the results of that initial meeting because:

  • They got a pretty realistic view of how their website was going to look like
  • The paper prototype allowed us to speak about something touchable and not about abstract concepts
  • That made possible to discuss improvements and changes of my design and also to make a good checklist of the material needed

It took us less than one hour and a half (including other chats, drinking and eating) and I got everything I needed to get me started. Lesson learned: always meet the client before you make any coding and if you can, make a paper prototype, you can’t imagine how much work and frustration you will save by doing so.

Mockup 1

Mockup 2

Mockup 3

Mockup 4

Mockup 5a

Mockup 5b

Mockup 5c

Choosing the technology

Now that I knew what I had to do I needed to choose how to do it. In other words, the technology. I choosed Django over Zope or PHP for this project for the following reasons:

  • It was a well defined project that is not going to change or grow a lot in the future. No need for a component architecture here.
  • Most of the information is read only (except for the guest book) so no fancy forms were needed. This is one of the weak points of Django in my opinion but I didn’t needed them for this project.
  • The users (the members of the band) are young savvy computer users so the Django Admin interface is perfectly usable for them. I didn’t want to maintain the content of the website so this is a big +1 for Django’s admin interface. These three reasons pushed me to decided Django over Zope3 and for PHP…
  • I only choose PHP if the application is already done in PHP (and I need to extend it or fix it) and/or the hosting is fixed and it doesn’t allow my other favourite technologies. Neither the case for this project.

Coding, the fun part

It was time to get my hands dirty so I created a subversion repository to put my code in and started to make a Django 0.96 application. I only worked in my (not so frequent) spare time. I started on the 5th of September and got a beta version yesterday. Acording to my commits I worked 13 days and I estimate an average of 2-3 hours each day: around 30 hours in total. Not too bad, huh?

First I designed the models and created the database. For development I used SQLite and for deployment I used MySQL. I set up some fixtures so I didn’t have to input all the data again and again. By designing almost all the models initially, rebuilding the database was something I didn’t have to do very often.

Then I started to write views for each tab page. It’s important to note that I didn’t write any CSS code yet. The pages were really ugly but at the same time were pretty clean and allowed me to focus on the functionality and structure without distractions. I made 80% of all view code before even thinking on any visual design issue.

Then my friend sent me the material. He sent me the graphics for all their home-made records and reminded me that they want the website to look similar to the last record graphical design. So I opened my vector graphics application (Inkscape) and designed the homepage of the website. I thought about doing that in a raster graphics application (GIMP) but happily I used Inkscape which let you change a lot of stuff much more easily than with GIMP.

When I finished the graphical design I sent it to my friend for validation and after some minor corrections they all agreed with it so I could continue. Then I started moving that desing into the website using CSS and some graphics. I’m pretty proud of the final result since it’s quite elastical and adapt to a wide range of screen resolutions. Of course its XHTML and CSS validates and it’s quite accesible. Long life to web standards!

Next challengue was to make a template tag for the navigation tabs so it could automatically detect which one was selected in each view. In the settings.py file I had a list of tabs:

NAVTABS = (
('/', 'Noticias'),
('/songs/', 'Canciones'),
('/pictures/', 'Fotos'),
('/concerts/', 'Conciertos'),
('/band/', 'El Grupo'),
('/contact/', 'Contacto'),
)

Each tuple having an url prefix and the label of the tab. Then my custom templatetag looks like this:

@register.inclusion_tag('navtabs.html', takes_context=True)
def navtabs(context):
    request = context['request']
    path = request.get_full_path()
    tabs = [dict(url=url, title=title, current=path.startswith(url))
            for url, title in settings.NAVTABS]
    active_tabs = [t for t in tabs if t['current'] is True]

    # We may have two or more current tabs if prefixes are the same
    # In this case the longest matching prefix wins
    if len(active_tabs) > 1:
        # sort the tabs by its length
        def sorter(tab1, tab2):
            return cmp(len(tab1['url']), len(tab2['url']))
        active_tabs.sort(sorter)

        # The only current one is the longest one (the last)
        for tab in active_tabs[:-1]:
            tab['current'] = False

    return {'navtabs': tabs}

And the template navtabs.html looks like this:

<ul id="nav-tabs">
{% for tab in navtabs %}
<li {% if tab.current %}class="current"{% endif %}><a href="{{ tab.url }}">{{
tab.title }}</a></li>
{% endfor %}
</ul>

Now, all I have to do in my master template file is add this piece of code to have automatic navigation tabs! :

{% load navtabs %}
{% navtabs %}

And if I change, add or remove a tab I just need to update the settings.py file.

Automatic downloads

Something the band wanted is that the users could download their songs. I thought about two options: a) downloading each songs individually so you can get a grasp of how the band sounds like and b) download the whole album if you liked them. This way everybody saves bandwidth :-) But I didn’t want to store the mp3 files twice in the storage server. Each one individually and a ZIP for each record with the mp3s inside it.

So I decided to create the zip files on the fly:

def record_download(request, record_id):
    record = get_object_or_404(Record, id=record_id)
    buffer = StringIO.StringIO()
    zip = zipfile.ZipFile(buffer, "w")
    for song in record.song_set.all():
        filename = song.get_audio_filename()
        zip.write(filename, os.path.basename(filename))
    zip.close()

    name = u'Ana Logica - %s.zip' % unicode(record.title, 'utf-8')
    name = name.encode('utf-8')
    response = HttpResponse(mimetype="application/zip")
    response['Content-Disposition'] = 'attachment;filename="%s"' % name
    response.write(buffer.getvalue())
    return response

Layout problems and JQuery rescue

My layout is the typical header - main content with right sidebar - foot layout and I wanted to apply a soft background image tileable vertically in the sidebar. Usually you go for a liquid layout where you fix the width of the website and create a big horizontal image (something like 800×5 pixels) and repeat it all over the main content. That way, when you float right the sidebar everything looks like the height of the sidebar is the same as the height of the main content even when that is not actually the case. But I had two problems:

  • My width was not fixed but a percentage of the screen width resolution
  • My background image was not 5 pixel tall but 356 pixels height

So how did I solved it? Using a javascript oneliner :-) :

$(document).ready(function () { $("#sidebar").height($("#body").height()); });

Now my sidebar is always as tall as the main content (div id=”body”) of my website and the background image is applied properly.

I had a problem with 800×600 screen resolutions: as the main content has a width of 80%, in those resolutions it was not big enough and the tabs collide with the logo. Some more javascript and problem solved:

function fix_layout() {
  /* If the window is too small, no margins */
  if ($(window).width() < 880) {
    $("div#page-wrapper").css("margin", "0");
  } else {
    $("div#page-wrapper").css("margin", "0 10% 0 10%");
  }
  /* Make the sidebar as tall as the #body element */
  $("#sidebar").animate({height: $("#body").height()}, 1000);
}

$(document).ready(fix_layout);
$(window).resize(fix_layout);

Now you can see that when the user has an old screen he/she can still view the website properly but with no margins. I love the way javascript looks after dressing it with JQuery :-)

Automatic thumbnailing

As the band is going to update the content and not me I wanted the design to stay as good as initially. This mean no big images popping suddenly in the middle of a news item or a gallery. This also means I can’t trust my users to resize the images because they are humans and humans usually forget those tedious things. So I stolen this small template tag to get automatic thumbnailing for free:

def thumbnail(file, size='200x200'):
    # defining the size
    x, y = [int(x) for x in size.split('x')]
    # defining the filename and the miniature filename
    basename, format = file.rsplit('.', 1)
    miniature = basename + '_' + size + '.' +  format
    miniature_filename = os.path.join(settings.MEDIA_ROOT, miniature)
    miniature_url = os.path.join(settings.MEDIA_URL, miniature)
    # if the image wasn't already resized, resize it
    if not os.path.exists(miniature_filename):
        filename = os.path.join(settings.MEDIA_ROOT, file)
        image = Image.open(filename)
        image.thumbnail([x, y], Image.ANTIALIAS)
        image.save(miniature_filename, image.format)
    return miniature_url

register.filter(thumbnail)

This is an example of how to use it:

{% if newsItem.picture %}
<img alt="" src="{{ newsItem.picture|thumbnail:"100x100" }}" width="100" height="100"/>
{% endif %}

Stop spam, read books

Something I had learned from the past is this rule: every public HTML form you put in a website is going to be used for spam sooner or later. You need to assume it and live with it: either you don’t put public forms or you fight spammers with a captcha or similar techique. I decided to go with recaptcha.net as it looks quite nice and they use the information the users write for digitalizing books. Really smart.

Adding support for recaptcha is fairly easy:

  1. Register in recaptcha.net and get a pair of keys. Put them in the settings.py file
  2. Download the python package for recaptcha and just put the captcha.py file inside your application
  3. Write a template tag stupidly simple using the displayhtml function of the previous captcha.py file:
    @register.simple_tag
    def recaptcha():
        return captcha.displayhtml(settings.RECAPTCHA_PUBLIC_KEY)
  4. Add this to your template, inside the form:
    {% load recaptcha %}
    <form method="post" action=".">
    {{ form.as_p }}
    {% recaptcha %}
    <p class="center"><input type="submit" value="Enviar" /></p>
    </form>
    {% endblock %}
  5. Add this function to your view:
    def captcha_is_valid(request):
        challengue = request.POST.get('recaptcha_challenge_field', '')
        response = request.POST.get('recaptcha_response_field', '')
        ip = request.META.get('HTTP_X_FORWARDED_FOR', '')
        captcha = submit(challengue, response, settings.RECAPTCHA_PRIVATE_KEY, ip)
        return captcha.is_valid
    
    def contact_form(request):
        if request.method == 'POST':
            form = ContactForm(request.POST)
    
            if captcha_is_valid(request) and form.is_valid():
                # send the mail
                ...

The problem with recaptcha is that you need to type two words instead of one and the band members have complained to me saying it may be too difficult for their visitors. Sigh :-(

Conclusion

I probably forget to mention some stuff but I guess this post is already way too long so I will stop now. I’ll just mention that I developed the whole thing using Firefox 2.0 and I tried the page in Internet Explorer 7.0 I was happily surprised since it looked pretty good. I know it doesn’t look as nice in Internet Explorer 6.0 and I need to do some more work to fix it but the success with IE 7.0 made me happy and I have hopes in a web development future where we can finally say, write one surf everywhere :-)

Besides using strict standards I recommend you using ‘em’ instead of ‘px’ in your stylesheet as much as you can to get best results.

Well, that’s all for now, I hope you learn something from my experience. I certainly did with this project and get some fun with it!

Zope3 for Djangoers. Part 3: Views and templates

Wednesday, September 12th, 2007

I’ve been talking about how to make a web application in my last two posts but still no single html code has been written so far. What an hoax you may think. Well don’t panic, in this article you will finally get some pixels on the screen. :-)

As usually I’ll start remembering how to write a view in Django. We’ll use the models of our last article to keep things simple. So, let’s say you want a view that renders a list of your records, each one with a list of songs. Not too hard. You start writing your view function in a file called views.py inside your app directory:

from django.shortcuts import render_to_response
from myapp.models import Record

def all_my_records(request):
    records = Record.objects.all()
    return render_to_response('record_list.html', dict(records=records))

Now you need to write your template file, in this case we’ve called it record_list.html and it will live inside a templates directory in the root directory of your Django project:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><title>Record list</title></head>
<body>
  <h1>My records:</h1>
  {% for record in records %}
    <h2>{{ record.title }} - {{ record.artist }}</h2>
    <ul>
    {% for song in record.song_set.all() %}
      <li>{{ song.title }} - {{ song.length }} seconds</li>
    {% endfor %}
    </ul>
  {% endfor %}
</body>
</html>

Now we need to do a couple of administrative tasks to make this work. First, we need to tell Django that our templates directory is that one. We said so in the settings.py file of our project:

TEMPLATE_DIRS = ('/home/lgs/DJProject/templates',)

Next, we should write a urls.py file inside the project and another one inside the application. Let’s start with the one inside the application directory:

from django.conf.urls.defaults import patterns

urlpatterns = patterns('',
    (r'^my_records/$', 'DJProject.DJApp.views.all_my_records'),
)

As you can see we associate a url (/my_records/) with a view function. Now we just need to include this urls from the master urls.py file in the project directory:

from django.conf.urls.defaults import patterns, include

urlpatterns = patterns('',
(r'^/', include('DJProject.DJApp.urls')
)

And finally a line in the settings.py file:

ROOT_URLCONF = 'DJProject.urls'

Even that Django does not have a configuration language (ZCML) it needs quite a few of configuration settings. This is good indeed since it makes it a lot more powerful and flexible. Note that the settings.py changes and the urls.py of the project directory needs to be changed only onced. If we were to write another view for the DJApp only the urls.py of this directory would need to be changed.

Another good point for Django is its template system. It’s quite readable and pretty powerful. But as any other template system, simple examples like this one are no real issue for it. We will push it harder later on and we will see how good it actually is.

You can test your view by starting the web server and pointing your browser to http://127.0.0.1:8000/my_records/
You will probably get an empty list since there is no records in your database yet :-)

Time to move to Zope3. Let’s try to write the same view but first let’s do an assumption. Our record objects will be stored in the root of the ZODB tree. This will make things simple enough to concentrate in the view issue.

As a convention we will put our HTTP view code in a subpackage called ‘browser’. So let’s create that directory inside our project package and put this code in its __init__.py file:

from myapp.interfaces import IRecord

class AllMyRecordsView(object):
    def records(self):
        for obj in self.context.values():
            if IRecord.providedBy(obj):
                yield obj

This time it’s not a function but a class. Not much harder than the Django version, huh? It just have a method that returns an iterator of the records in the context object. Here the context will be the root container. We need the extra if just in case there is something else in the root container.

Now time for the template, introducing the ZPT (Zope Page Template) language:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><title>Record list</title></head>
<body>
  <h1>My records:</h1>
  <div tal:repeat="record view/records" tal:omit-tag="">
    <h2 tal:content="python:'%s - %s' % (record.title, record.artist)">Sticky Fingers - Rolling Stones</h2>
    <ul>
      <li tal:repeat="song record/values"
             tal:content="python:'%s - %d seconds' % (song.title, song.length)">Wild Horses - 220 seconds</li>
    </ul>
  </div>
</body>
</html>

I will tell you that the final html is the same in both versions. I think the Django one is easier to read but we have an advantage with the Zope one is that it is a valid xhtml file that can be edited and previsualized in any HTML editor so you can split your work between programmers and web designers. As you have seen in the previous example we even provided some default values (they will be replaced with the real ones in the runtime version) so we can get a grasp of how it will look like at the end.

Supposed we save the previous template in a file called record_list.pt. We will need to plug the pieces with a bit of configuration code. Put this code in a configure.zcml file inside your browser package:

<configure xmlns="http://namespaces.zope.org/browser">

<page
    name="my_records"
    for="zope.app.folder.interfaces.IFolder"
    class=".AllMyRecordsView"
    template="record_list.pt"
    permission="zope.Public" />

</configure>

Now include this browser package from your main configure.zcml file in the root of your app:

<include package=".browser"/>

And you are ready to go. Start the server and go to http://127.0.0.1:8080/my_records

But having no way to add content to our web application is not really fun. We could add it from a pure Python app but we should expect something else from this great frameworks. See you in part 4 of these series where we’ll discover the black magick mistery of automatic web forms for adding and editing content!

Zope3 for Djangoers. Part 2: Writing your models

Wednesday, September 12th, 2007

Yesterday I wrote an (introduction) article about how to start developing webapps with Zope coming with a Django background. We got to the point where a development environment for both frameworks was ready to start coding. It’s time to get our hands dirty.

As any modern web framework, both Django and Zope are based on a more or less formal variant of the MVC pattern. Usually in this pattern you start writing your models. You do this in Django creating a models.py file inside your app and writing classes that inherit from django.db.models.Model. For example if you are modeling a multimedia library application you will probably have the following models:

from django.db.import models

class Record(models.Model):
    artist = models.CharField('Artist', maxlength=80)
    title = models.CharField('Title', maxlength=150)
    length = models.PositiveIntField('Length', blank=True)
    date = models.DateField('Date', blank=True)

class Song(models.Model):
    title = models.CharField('Title', maxlength=150)
    length = models.PositiveIntField('Length', blank=True)
    record = models.ForeignKey(Record)

class Movie(models.Model):
    title = models.CharField('Title', maxlength=150)
    director = models.CharField('Director', maxlength=80, blank=True
    date = models.DateField('Date', blank=True)
    length = models.PositiveIntField('Length', blank=True)

Ok, that’s enough to have a semi-realistic example. Let’s see how to do the same with your Zope3 app. As pretty much everything else in Zope3 a model is a component, made up by an interface and an implementation. The interface says how the model looks like and what kind of things you allow others on your data. The implementation is an example of something that support your interface. You write your interfaces in a interfaces.py file and the implementation in another file or files. Note that the name of the file ‘interfaces.py’ is just a convention. The ‘models.py’ file in you Django app need to be called that way. Not a big deal though. Let’s write our interfaces.py file:

import zope.app.container.constraints
import zope.app.container.interfaces
import zope.schema

class IRecord(zope.app.container.interfaces.IContainer):
    artist = zope.schema.TextLine(title=u'Artist', description=u'Author of the record')
    title = zope.schema.TextLine(title=u'Title', description=u'Title')
    length = zope.schema.Int(title=u'Length', description=u'Length in seconds', min=0, required=False)
    date = zope.schema.Date(title=u'Date', description=u'Release date', required=False)

    contains('.ISong')

    def length_in_minutes():
        """Return a pretty string with the length in the mm:ss format"""

class ISong(zope.app.container.interfaces.IContained):
    title = zope.schema.TextLine(title=u'Title', description=u'Title')
    length = zope.schema.Int(title=u'Length', description=u'Length in seconds', min=0, required=False)

    containers(IRecord)

    def length_in_minutes():
        """Return a pretty string with the length in the mm:ss format"""

class IMovie(zope.app.container.interfaces.IContained):
    title = zope.schema.TextLine(title=u'Title', description=u'Title')
    director = zope.schema.TextLine(title=u'Director', description=u'Director', required=False)
    date = zope.schema.TextLine(title=u'Date', description=u'Release date', required=False)
    length = zope.schema.Int(title=u'Length', description=u'Length in minutes', min=0, required=False)

Now, time for some comments. Even that our Django models.py and our Zope3 interfaces.py have a lot of things in common as they are defining our models they have some important differences:

  • The classes in interfaces.py are not classes but interfaces. In Python there is no support for interfaces (as there is in Java) so we use the ‘class’ word but they are not clases. You can see they are not classes as the methods we defined doesn’t have the ’self’ or ‘cls’ arguments.
  • In Zope3 every string you put in your code must be unicode. Looks like a drawback but believe me, it will make your life much easier later on.
  • Both in Django and Zope3 a field is mandatory by default. If you want to make a field optional you use the blank=True or required=False argument respectively. I prefer the wording of Zope3 much more.
  • In Django you have a ORM that finally use a Relational Database (hint: ForeignKey field is no surprise here). In Zope3 you use an object oriented database (ZODB). To represent the 1:n relationship between Records and Songs you justs say a Record is a container for Songs. This is much more intuitive and easier to model with an object oriented database. Having said that I must tell you can use a relational database with Zope3. We will do that in following articles.
  • The ‘I’ prefix for our interfaces is a common convention to remind you they are interfaces, not classes.

We still need to implement our interfaces in Zope3 so we’ll write a file called multimedia.py where we can put the implementations. Usually I use several files, one implementation in each one but we’ll put everything in the same file for the sake of simplicity:

import persistent
import zope.interfacefrom zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained

import myapp.interfaces

class Record(BTreeContainer):
    zope.interface.implements(myapp.interfaces.IRecord)
    def __init__(self, artist, title, length=None, date=None):
        super(Record, self).__init__()
        self.artist, self.title, self.length, self.date = artist, title, length, date

class Song(Contained, persistent.Persistent):
    zope.interface.implements(myapp.interfaces.ISong)
    def __init__(self, title, length=None):
        super(Song, self).__init__()
        self.title, self.length = title, length

class Movie(Contained, persistent.Persistent):
    zope.interface.implements(myapp.interfaces.IMovie)
    def __init__(self, title, director=None, date=None, length=None):
        super(Song, self).__init__()
        self.title, self.director, self.date, self.length = title, director, date, length

Ok, that’s it. Not as scary as you thought thanks to the existence of a lot of useful base classes we can use. We use BTreeContainer and Contained to make our models aware of the relationship between Records and Songs. We also use Persistent to allow our objects to persist in the ZODB. We could use SQLAlchemy, Storm or even Django ORM here if we wanted to persist our objects in a Relational Database.

Now we must hook the models into the main app. We do this in Django with a two steps process:

  1. Include our application in the settings.py file, in the INSTALLED_APPS tuple
  2. Run the manage.py script with the syncdb argument. This will create a bunch of tables in our database backend. If you also installed the django.contrib.auth, here this command will ask you for a admin username and password. Something Zope3 did the first time the server was run.

In Zope3 we need to register the interfaces. In the next article, we’ll also write some security declarations to our implementations. The database is not affected until we create objects. So we open the configure.zcml file in the root of our application source directory and add this lines:

<interface interface=".interfaces.IRecord"
    type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.ISong"
    type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.IMovie"
    type="zope.app.content.interfaces.IContentType" />

What we are doing is fill some records for the Zope3 component registry, which is a small database of components that is kept globaly in memory but can be overriden with values in the ZODB database. Remember, it’s all about the component architecture: that’s what gives you real power. But every great power comes with great responsability and here the responsability is to correctly fill the configure.zcml file. I hear you little boy crying and winning about that ugly XML file. As we will see later, having a registry of components and the relationships between them allows you to do pretty amazing things.

So I’d say this is the first really important (and probably biggest) difference between Django and Zope3: the component architecture. Love it or hate it, that’s one thing that Django lacks. Before you complaint about this let met tell you a little history: I onced had to explain Django to a PHP experienced developer. As many PHP developers he used to write the <?php > tag scattered in his html files. No templates, no models, no views. Controller? what’s that?. I tried to explain him that using the MVC pattern was a good thing that will help him to design and maintain his applications. He was not sure that this extra learn effort was worthy. I ask the Django developer the same question: is the Component Architecture twist of mind worthy for you?

The answer will depend on your project. As a rule of thumb all I can say is: the more complex your project is the more value you will get from the CA. Every time I hear Zope3 is too hard, it has a very sloppy learning curve, etc, etc. I can’t say that’s not true. That is indeed true for every software project with a component architecture: think about Mozilla XUL, Microsoft COM or OpenOffice.org UNO. Zope3 looks easy next to them :-)

See you in the next article, where we will see how to render pixel on the screen: the exciting part!

Zope3 for Djangoers. Part 1: Installation

Monday, September 10th, 2007

I’m been working on a couple of Django projects for two months and I must say it’s a great platform for making web applications. Even when this is not the first time I play with Django. I tend to forget things too easily…

Having said that I must say I still prefer another Python web framework. One that’s pretty big but at the same time it’s small. One that it’s new but at the same time quite old. Yes, I’m talking about Zope. Zope3 precisely.

But I still think there is no perfect framework and each one has its strengths and its weaknesses. So, as always, you should study your requirements and your problem and decide which tool is best for you. As an overly simplified statement I’d say take Django for small 3-5 months projects that won’t change in the future and take Zope3 for everything else. Don’t use the same hammer for every nail.

Now, one of the things Django gets much better than Zope3 is documentation. It’s much easier to get started with Django not only because it doesn’t have a component architecture but because its tutorial and the rest of documentation really rocks.

I’ll try to write a series of posts to give the Zope3 newbie a little bit of knowledge so he/she doesn’t get lost in the Zope3 forrest. And more important, I’ll write them from a Djangoer perspective so you can always compare both frameworks.

Note of caution: in the zope3 world there are a lot of ways to do the same things. You may like this or not but that’s the way it is. In these articles I’ll just choose one of such ways. Probably not the best and hopefully not the worst. Just don’t think it is the canonical zope3 development process. I don’t think there is such one.

In this first article we’ll talk about installation. In Django you just need to have Python installed in your box and a database management system like sqlite, Mysql or Postgresql. That’s pretty much everything you need. Then you write this command:

django-admin.py startproject DJProject

and it creates an environment where you can start writing your code. Actually your are supposed to create applications inside of your project. I don’t like this wording since for me, a project is an application most of the time. Usually I split my project in components following best practices but they are not applications by their own. I know, Django lets you live without creating apps but things get out of the main road and you start living by your own. So let’s be good citizen and create our application:

cd DJProject
./manage.py startapp DJApp

Finally if you want to start your development server all you have to do is type this command:

./manage.py runserver

Ok, let’s jump into the zope3 side. In the old days Zope was a big fat framework and your app was just a module that you hooked into it. Now recently Zope3 has been splitted into lots of small components distributed as Python eggs. This approach has the advantage that you only use what you need keeping it simpler. The disadvantage is that usually you don’t know what you need :-) At least at the beginning. With some practice everything get easier.

Zope3 doesn’t currently work with Python 2.5 due to some changes in the C extensions mechanism that the 2.5 version introduced. There is quite a lot of C code in Zope3 to optimize things: some examples are the ZODB and the security system. That’s why you should stick to 2.4 until the 2.5 support is finished.

To work with Python eggs you need Easy Install which is the tool than manages them. Installing it is super simple:

wget http://peak.telecommunity.com/dist/ez_setup.py
python ez_setup.py

This will download everything needed to get started. Next step is install zopeproject. It is a great tool to start a Zope3 project. Its philosophy is that Zope3 is just a library and your app should use that library, not the other way around. It creates a WSGI application with a lot of default configuration made for you. Check out this great article to learn why WSGI is such a great thing. Ok, let’s get back to our project:

easy_install zopeproject
zopeproject Z3Project

The last command will ask you for a directory to place the zope3 eggs. This way other applications can use those eggs without duplicating them. It will also ask you for an administrator username and password. Something similar to what you get when you syncdb your Django project after adding the django.contrib.auth application.

Now you have a Z3Project directory pretty similar to your myproject Django directory. Running an http development server is quite simple:

cd Z3Project
bin/paste serve deploy.ini

But wait, I’ve got a bunch of files I don’t understand!, what are they for? And where is my code supposed to live? Well, don’t panic. I’ll try to answer these questions. The environment we just created is managed by buildout. This software handle the dependencies for your project and create/update the starting points for running, testing and debugging it among other things. These are the files you get:

  • setup.py: it’s a regular distutils file that says how to make an egg for your application. The important thing is that it lists the list of eggs your app depends on.
  • zope.conf: them main zope configuration file. You put your database here and the ZCML file the application will load on startup. Kind of a part of the settings.py file of Django.
  • site.zcml: the file which configures the component architecture of Zope. More on this later.
  • buildout.cfg: the buildout configuration file that basically tells buildout to build the egg each time it is executed taking care of the dependencies and to update the scripts in the bin directory.
  • deploy.ini: as we use Paste for the http server, this file tell it which port and IP to use.

And your code is supposed to live in the z3project subdirectory inside the Z3Project that zopeproject created. You will see a configure.zcml and an application.py files inside that directory, that is a Python package actually. The configure.zcml is included from the site.zcml we already saw and it loads a bunch of sane defaults for Zope3. It’s also the place where you should register your own components. The application.py defines a WSGI application factory that Paste will use to run the http server.

We’ll see how to write our models in the next article. See you then!

Repeat after me: Javascript is not Python

Wednesday, August 29th, 2007

I guess I’m too used to the Python programming language that whenever I have to write simple Javascript functions I keep making the same mistakes. I tend to write as much functionality as possible in the server side (Python :) ) but in order to create rich user interfaces I have to use the client side (Javascript :( ) and things like this happens:

function addTopic(topic) {
    var input_attrs = {
        type: "checkbox",
        checked: "checked",
        name: "topics",
        value: topic,
    };
    var li = Builder.node('li', [Builder.node('label', [Builder.node('input', input_attrs), topic.title])]);
    $("topic_list").appendChild(li);
}

For those of you who are curious, yes I’m using Script.aculo.us and Prototype libraries here but here is the point: There is a syntax error in the previous code, one that Firefox (and thus Firebug) won’t catch but one that Internet Explorer will. The funny thing is the Internet Explorer error message:

Error: Expected identifier, string or number

So, anybody can answer me: is the trailing comma a real Javascript syntax error or just another Internet Explorer flaw?