Dajaxproject, primera part


Escrit per Aaloy a 30 de January , 2012 a les 11:38 p.m.

Una de les preguntes més recurrents en les llistes de Django, junt amb la de quin editor fer servir és la de com fer cridades AJAX i quina llibreria utilitzar.

Django és agnòstic en el que fa a les llibreries javascript, tot i això la part de l'admin fa servir jQuery, però oficialment no hi ha cap llibreria especialment recomanada, Django és pot utilitzar amb qualsevol llibreria i dependrà del projecte que en triem una o altra.

El problema de que no es faci cap recomanació de com integrar cridades AJAX amb Django ens dóna molta llibertat, però també te l'emperò de que no hi ha cap guia de bones pràctiques sobre com fer-ho, des d'on posar la llibreria, quin nom li hem de donar, si la posam al views.py o no.

El la comunitat de codi obert quan hi ha un buid així i una necessitat ben aviat sorgeixen projectes que intenten donar una resposta. Un d'aquests projectes és el que us present en aquest article el dajaxproject.

Djaxaproject són de fet dues llibreries, dajaxice i dajax, la primera podríem dir que respon a la necessitat que us comentava de fer més senzilla la comunicació asíncrona entre les aplicacions Django y el codi javascript, la segona, dajax està molt més orientada cap a la capa de presentació i sobretot està lligada a un bastiment de javascript en concret, encara que tenim certa llibertat a l'hora de triar-lo.

En aquest apunt veurem primer Dajaxice i en un següent apunt faré també cinc cèntims de dajax.

Instal·lació

Suposaré com tantes vegades que teniu un virtualenv creat i Django instal·lat. També hem de partir d'un projecte base.

El primer que hem de fer és instal·lar la llibreria, per això bastarà fer un

pip install django-dajaxice

La documentació explica força bé el procés, així que resumeixo:

  1. Posar dajaxice dins la secció INSTALLED_APPS de settings.py
  2. Assegurar-nos que tenim eggs.Loader descomentat als TEMPLATE_LOADERS
  3. Assegurar-nos que django.core.context_processors.request és dins el TEMPLATE_CONTEXT_PROCESSORS, si no ho heu fet ja, el meu consell és que copieu tot el TEMPLATE_CONTEXT_PROCESSORS que hi ha a la documentació, ja que a poc que l'aplicació cresqui segur que necessitareu afegir-ne algun.
  4. Afegim als settings.py el prefixe que farem servir per distingir les urls de dajaxice de les de urls normals de la nostra aplicació, per exemple la documentació ens diu posar DAJAXICE_MEDIA_PREFIX="dajaxice".

Amb això ja hem fet par de la fontaneria, ara ve la part més interessant i la màgia d'aquesta aplicació.

Organitzant l'AJAX

Dajaxice el farà és utilitzar el prefixe que hem configurat per tractar certes urls com a cridades AJAX. Djaxice el que ens permet és registrar automàticament les funcions que es publicaran com a cridades AJAX utilitzant un mecanisme semblant al que fa dervir Django per a l'admin.

D'aquesta manera, una vegada modificat l'arxiu urls.py podem anar creant les funcions a publicar dins un arxiu ajax.py i registrar les funcions. Així l'arxiu urls.py ens ha de quedar com:

#!/usr/bin/python

from django.conf.urls.defaults import patterns, include, url
from dajaxice.core import dajaxice_autodiscover
from django.contrib import admin
from django.conf import settings

admin.autodiscover()
dajaxice_autodiscover()

urlpatterns = patterns('',
     url(r'^admin/', include(admin.site.urls)),
    (r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, 
        include('dajaxice.urls')),
)

Utilitzant AJAX a les nostres plantilles

Dajaxice es capaç de posar dins la nostra plantilla el codi que necessitam per fer les cridades AJAX i publicar les funcions que farem servir a partir del codi Python (ja ho veurem un poc més endavant). Per fer-ho fa servir una llibreria que s'ha de carregar.

Així a la part de càrrega de llibreries de la nostra plantilla haurem d'afegir: dajaxice_templatetags. Si encara no heu carregat cap llibreria doncs quedaria com {% load dajaxice_templatetags %}.

Una vegada carregada la llibreria posarem el tag que genera el codi javascript {% dajaxice_js_import %}. Ho podeu posar a la capçalera o al peu de la plana. Personalment, com que ho sol fer servir junt amb jQuery, ho pos al peu, abans de l'utilització de les llibreries, per tal de millorar la velocitat de càrrega de les planes.

Aquest codi es gener dinàmicament mentre estam desenvolupant. Us avís que de tant en tant es queda carregat en memòria i certs canvis requereixen reiniciar el servidor de desenvolupament. Si veis que heu publicat una funció i no surt, reiniciau el servidor.

En producció la llibreria té una opció per generar directament l'arxiu javascript i poder tractar-lo com un fitxer estàtic més.

L'estructura d'una plantilla bàsica amb djaxice seria doncs

{% load dajaxice_templatetags %}

<html>
  <head>
    <title>My base template</title>
    <!-- també al peu depen de l'aplicació -->
    {% dajaxice_js_import %} 
  </head>
...
</html>

Creant la nostra primera cridada AJAX

Com us comentava més amunt, una de la gràcia de dajaxice és que ens permet organitzar les nostres cridades AJAX. Així el que farem serà crear un arxiu ajax.py dins la nostra aplicació, de manera que podem agrupar cridades per aplicació.

Suposem que hem creat una aplicació anomenada main, la posam al settings.py del projecte i cream l'arxiu ajax.py

Crear una funció i publicar-la per a que la servim via AJAX és tan senzill com això:

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

from django.utils import simplejson
from dajaxice.decorators import dajaxice_register

@dajaxice_register
def hello_world(request):
    return simplejson.dumps(
        {'message':'Hello World'})

És a dir, importam simplejson per a fer la transformació cap a json, que és el que volem servir (o xml, o html, tant fa), cream una funció com ho feim al views.py de Django i la decoram amb
dajaxice_register. Això junt el autodiscover que hem posat a urls.py fa que ara la nostra aplicació pugui respondre a cridades AJAX.

Cridant des de l'html

Per cridar a la nostra funció es pot fer directament amb l'event onclick directament a l'HTML,però a mi particularment em fa molta grima tenir aquesta mescladissa, així que prefereixo tirar de jQuery encara que per fer la crida AJAX no el faci servir. Recordem que l'important no és el mètode amb que es fa la crida, sinó l'organització i facilitat de mantenimient (i depuració) que ens dóna la llibreria.

Així, en el meu cas quedaria:

<script src="https://ajax.googleapis.com/
    ajax/libs/jquery/1.7.1/jquery.min.js">
</script>
<script type="text/javascript">
    $(function(){
        $('#testme').click(function(event){
            event.preventDefault();
            Dajaxice.main.hello_world(test_callback);
        });

        function test_callback(data){
            if (data==Dajaxice.EXCEPTION){
                alert('Opps, alguna cosa dolenta ha passat');
            } else {
                alert(data);
            }
        }
    });
</script>

on #testme fa referència a un enllaç al qual hem assignat l'event.

L'interesant d'això és que tenim a la nostra disposició una funció javascript que mapeja la que tenim al codi Python i a la qual podem cridar amb uns paràmetres. El primer d'ells és la funció de tornada (el callback) que utilitzarem per gestionar la resposta i l'altra és un paràmtre codificat com json que passarà com a paràmetre cap a la funció.

Hem de dir que dajaxice sempre fa un POST contra la url, gestionant els problemes de CSRF.

Si posam DAJAXICE_DEBUG=True al settings.py o millor dit, ve així per defecte, a la consola de desenvolupament de Django podem veure tant la cridada que fem com la resposta i els errors que hi pugui haver.

La funció de tornada es defineix amb un paràmetre que contindrà el valor de retorn de la nostra cridada AJAX. Si és un json podrem accedir als valors directament, però com he comentat abans, no estam limitats a tornar json, podem tornar una cadena de text, xml, html, ... el que necessitem i poguem tractar amb javascript.

És interessant veure com es tracten els errors o les excepcions. Si hi ha problems al nostre codi Python i es llança una excepció, dajaxice la capturarà i llavors data contindrà el valor Dajaxice.EXCEPTION, amb la qual cosa sabem que quelcom ha anat malament.

He de fer servir sempre Dajaxice?

Depèn, algunes vegades serà més convenient fer una cridada directa amb jQuery, per exemple quan volem tenir més control del que passa quan s'inicia la cridada, o volem poder-la cancel·lar.

El cert, però, és que dajaxice ens permet organitzar millor el codi i gestionar d'una manera sistemàtica les nostres cridades AJAX. Com sempre no hi ha cap veritat absoluta, potser servirà en el 80% dels casos i això ja significa una gran ajuda.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Fakedata i Faketpv


Escrit per Aaloy a 20 de January , 2012 a les 6:07 p.m.

Fa temps que estic fent feina a hores mortes amb un projecte que m'ajudi (que ens ajudi) en el nostre desenvolupament d'aplicacions, en una de les tasques més ingrates: tenir que testejar l'aplicació i afegir dades de prova.

Un dels projectes amb que estic jugant és SST un envolcall per Selenium que fa molt senzill fer scripts per testejar webs des del navegador i de manera desatesa. He fet les proves inicials i la veritat és que promet molt el projecte.

En aquests moments tenim moltes de les nostres aplicacions monitoritzades fent servir Twill i connectades a Nagios, de manera que podem saber quan l'aplicació cau o no dóna la resposta esperada i que ens avisi mitjançant una alarma de Nagios. SST no està cridat a substituir Twill de moment, però si que ajudarà molt a evitar repeticions de tasques ja que és força més àgil que Selenium, i al meu punt de vista més entenedor.

L'altra línia de treball va dirigida a la tasca de generar dades de prova per les aplicacions. Supòs que també us heu vist amb la necessitat de testejar una aplicació en condicions semblants al que ens podem trobar a la realitat, i això vol dir omplir la base de dades amb cents o mils d'usuaris, d'adreces, ...

Vaig estar cercant eines que em deixessin fer això però no vaig trobar res que m'agradàs totalment, així que vaig optar per l'opció del mig, és a dir, tenir un bon conjunt de funcions que pugui cridar quan vulgui, que pugui créixer sense massa problemes i que pugui utilitzar per fer els programets per omplir de dades les meves aplicacions. Així va néixer Fakedata.

Fakedata és un conjunt més o manco organitzat de funcions per generar dades aleatòries però amb criteri per a les aplicacions, orientat sobretot a Espanya. Hi ha funcions per a generar noms de persona, per a generar CIF, NIF, nombres de telèfon, codis postals, targes de crèdit, ... El gruix de rutines les he anat trobant d'Internet o a altres llibreries i les he pogut adaptar, algunes les he creat des de zero. Em sembla bona idea tenir-ho tot organitzat, agrupat i a un únic punt, de manera que es pugui fer servir ràpidament.

Amb la mateixa idea de testejar aplicacions he afegit un mòdul nou al projecte anomenat Faketpv, amb la idea d'emular les principals passarel·les de pagament espanyoles. De moment ja tinc CECA i SERMEPA.

La idea és que quan estam en mode desenvolupament tenir que connectar a les passrel·les de pagament, encara que sigui al seu entorn de test, et fa perdre un temps preciós i sovint dificulta la depuració. Faketpv està pensat per ajudar al desenvolupament: mostra els paràmetres que reb el TPV i torna a generar la signatura per a que es pugui comparar amb la que generaria el TPV si li passàsim aquests paràmetres. A més retorna la resposta com si la venda s'hagués produït realment, sense tenir que posar cap tarja i fent-ho tot en local.

Com que funciona com a un servei es pot fer servir des de qualsevol aplicació, talment seguint la documentació de cada passarel·la però substituint el seu entorn de test per l'aplicació local.

El projecte està a Bitbucket i està obert a contribucions. A veure si entre tots en feim un de bo.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django Codi lliure


Resum del 2011


Escrit per Aaloy a 24 de December , 2011 a les 5:53 p.m.

Ara que ja estam a punt d'acabar el 2011 faré el que toca fer per aquestes dates no és més que fer un resum de l'any que acaba i fer un poc d'avançament del que esper en el 2012.

Com bé sabeu, darrerament per mi parlar d'informàtica i feina és parlar d'APSL, el projecte que iniciàrem fa uns anys amb un grapat de socis, parlar de Python, de Django i de GNU-Linux.

APSL

El febrer de 2012 ja farà tres any de la posada en marxa del projecte APSL, que per qui no ho sap és l'acrònim d'Advanced Programming Solutions SL. El nom el pensarem a un dinar entre en Bernat, en Xus i jo mateix, a partir d'un domini de quatre lletres que vaig poder comprar gairebé de casualitat. Primer va ser el domini i després va ser el nom. Ara no record ben bé qui va proposar la versió final, però record clarament que en parlàvem amb Xus.

Sigui com sigui enguany ha suposat passar dels 2 membre amb que iniciàrem el 2010 a passar a quatre i tenir que canviar d'oficina, passant de la incubadora del Parc Bit a un despatx compartit a l'edifici NTIC del mateix parc.

De fet, gairebé es podria dir que hem acabat l'any amb 5 persones, ja que Xus s'ha incorporat como a freelance també al projecte.

El 2011 ha estat un any dur i divertit a la vegada. Hem tingut prou feina per mantenir-nos ocupats amb el que més ens agrada. Hem fet feina amb projectes grans, que implicaven a tot l'equip durant mesos. Però no tot han estat alegries, com per tot la crisi s'ha fet notar i els plaços de pagament s'han allargat algunes vegades fins a límits que particularment m'han fet passar més pena de l'habitual, i qui me coneix ja sap que sóc passador de pena de mena.

En la part de projectes estic molt satisfet de la feina que hem fet amb la gent d'e-comerce de Fiesta, l'encaix ha estat molt bo, i en aquests moment tenim la web que desitjava el client i a més corrent damunt Django com a CMS. Passaren de un gestor de continguts genèric de PHP a un gestor de continguts fet a mida de les seves necessitats. El canvi no va ser gens traumàtic i ens ha permès no tenir que dir "això no es pot fer" o "això durà molt de temps". Ens ho passam molt bé amb aquest projecte, tant pel client/amic com perquè tenim una gran llibertat per fer optimitzacions. La darrera ha estat l'actualització de Varnish a la darrera versió, amb plugins especials adaptats a les necessitats de Fiesta.

Encara que més petitó, també ens ho passàrem molt bé amb la web de Sa Talaia del mateix Fiesta, aquí perquè va suposar poder retirar una web feta en flash que hi havia i desenvolupar-la amb Django i jQuery. Unes fotografies magnífiques de l'hotel fan que sigui una web que crec que s'ho paga visitar, i ja no dic rec d'anar a l'hotel (llàstima que no m'hagi tocat la loteria).

També estic molt content de la col·laboració assolida amb Xavi i el seu equip en el desenvolupament de la plataforma Txerpa. Content pel que significa particpar en projectes emprenedors i pel muntatge tècnic que hi ha al darrera. La web de Txerpa ja pot donar una idea del que hi ha al darrera, però la part més important és la que no es veu, i que permet donar d'alta i gestionar els usuaris de l'aplicatiu i la seva interacció amb el sistema.

El projecte que s'inicià l'any passat de Globalbooking pareix que poc a poc es va consolidant. En Rafa, l'emprenedor que hi ha al capdavant del projecte és un caramull d'idees i periòdicament anam fent canvis a la web. També s'ha convertit en un amic i company de batalletes. Tant amb ell com amb l'equip de Jesús de Valadis fa molt bon fer feina. Una vegada més ens sentim molt lliures de proposar millores i optimitzacions cercant el millor pel seu negoci. Sé que insisteixo amb això de que proposis millores i que la gent se les escolti, però si heu fet feina per grans empreses sabreu que sovint això no és així. Per mi estar a APSL està significant a més d'un repte personal, una gran satisfacció en veure que pots aportar solucions.

A finals d'estiu llançarem propietarios online, una aplicació web destinada a la gestió de comunitats de propietaris, que decidírem fer al vaixell de tornada d'Eivissa. Obrirem un període de proves i dins el 2012 es començarà amb l'explotació comercial del producte amb col·laboració amb una altra empresa. És un projecte propi que esperam que sigui útil a molta gent i que ens ha permès fer feina provant noves idees a l'hora de programar.

El final del trimestre ha estat mogudet. Projectes que potser s'aniran concretant dins el 2012, molt d'ells lligats a emprenedors, tant en el sentit d'aquell qui creu en el projecte i es llança a l'aventura com projectes novedosos que volen posar en marxa empreses ja consolidades. M'encanten aquests tipus de projectes, sempre suposen un repte, hi ha molta cosa nova, molta implicació tecnològica. Són projectes en els que t'impliques molt personalment, en algun d'ells si l'economia m'ho permetés fins i tot seria capaç d'invertir-hi, però per ara ens hem de conformar en fer-lo el millor possible per a que puguin arribar bé a port.

Dels darrers projectes destacar un de molt divertit Regala unas vacaciones, va ser un mini-deathmarch que em va deixar fora pont, però on m'ho vaig passar molt bé, tant pel bon rollo amb el client com per la web en si mateixa. El projecte va suposar posar a prova una nova manera de generar pdfs que no havíem provat fins a les hores. Ideal per quan hi ha poc text i molt component gràfic. Miraré de parlar-ne en aquest blog.

El 2012 es presenta també mogudet, projectes en marxa que han de finalitzar dins el primer trimestre, projectes propis com el de Propietariosonline que volem dur endavant, i un projecte nou que es va perfilant a poc a poc gràcies a l'ajuda i els comentaris de gent com Sebas o Jordi. Volem posar en marxa un servei d'instal·lació i configuració d'equips per a l'explotació d'aplicacions web fet a mida de les necessitats de programadors, dissenyadors i clients. És a dir, enlloc d'anar a serveis de hosting tipus macdonals, anar al restaurant a la carta, configurant un servidor a la mida de l'aplicació, amb configuracions diferents depenent de l'aplicació o aplicacions que hagi de dur el servidor: configuracions per Django, per Rubi, per Drupal o Wordpress, ... Pens que pot ser un servei molt útil, però el temps dirà si té la resposta que esper.

No puc acabar el capítol APSL sense fer referència a la quantitat d'amics que ens van donant suport i que s'interessen pel que feim. Menció especial per la gent de l'Ibit que ens ha convidat als seus saraus, als quals sempre hi anam amb la màxima il·lusió. A amics com Pau, Benjamí, Ricardo, Marcos, Joan, Sebas, Jordi, Pep, Xus, Eugeni, Paco, Eduard, Suki, Bruno, Xisco, ... (me deix gent, segur) que de tant en tant s'han passat per l'oficina a fer un cafè i a compartir amb nosaltres penes i alegries. He comanat cafè per un grapat de mesos, ja sabeu que ca nostra és ca vostra. Moltes gràcies pel vostre suport amics!.

El 2012 ja veurem com anirà, com us dic, molts projectes a concretar, però després ja veurem. El que sí m'agradaria és poder consolidar bé el projecte APSL i d'una manera o d'altra poder anar aglutinant al voltant del projecte al tipus de gent que m'agrada: gent apassionada per la seva feina i pel codi lliure i la informàtica i amb unes ganes d'aprendre coses noves que no s'acaba mai.

Python i Django

Encara que mantenim aplicacions amb la versió 1.2 de Django ja desenvolupam les noves aplicacions amb la versió 1.3, fent us de les "generic class views" tant com podem, i de utilitats com crispy forms. Que Twitter alliberàs el seu bastiment css i utilitats com crispy han fet que desenvolupar aplicacions de backoffice sigui molt més senzill que abans.

Hem incorporat un bon grapat de llibreries i utilitats a la nostra borsa de programació durant el 2011. Una de les més interessants és Redis, una base de dades NoSQL que poc a poc va reemplaçant a Memcached en els nostres projectes i que ha resultat un complement ideal pel sistema de coes Celery.

Sentry ha resultat un complement ideal per a la monitorització de les web i la gestió dels error no controlats (que sempre n'hi ha). El django-constance ens ha anat fantàstic per a mantenir webs on es tenia que canviar la configuració molt ràpidament sense reiniciar, django-compressor ens ha permès arribar al plus de velocitat de descàrrega que necessitàvem en algunes aplicacions empaquetant css i javascript.

Al primer trimestre del 2012 ja es preveu que surti la versió 1.4 de Django. Hi ha força novetats però la majoria són correcció de bugs. La que més ens afectarà en positiu serà la possibilitat d'interncionalitzar els noms de les urls. Fins ara ho fèiem amb una utilitat apart. Encara que el soport per a la internacionalització de Django sempre ha estat molt bo, trob que li faltava aquesta part dins el nucli del bastiment per passar de ser bo a excel·lent.

Hi ha eines que s'han consolidat com a imprescindibles aquest 2011: south per a la migració d'estructures de dades, pip per a la insal·lació dels paquets associats a una aplicació, virtualenv i virtualenvwrapper per aïllar aplicacions dins el seu propi entorn i fabric, que hem configurat per a que el desplegament i actualitzacions sigui cosa de segons.

Al 2012 esperam molt més de Python i Django. Hem començat a desenvolupar webs per a dispositius mòbils i un dels propers projects ha d'estar adaptat a tablets. Això a més del repte del projecte ha suposat tenir que adquirir una tablet per hom, però sincerament crec que sols en necessitàvem l'excusa :D

Adéu al PPC

Feia estona que volia "jubilar" el PowerPC G5. És un ordenador que no m'ha donat el rendiment que m'esperava, la relació qualitat preu trob que ha estat força dolenta si la comparam amb un portàtil de la mateixa època. El rendiment de l'equip amb dos processadors i 3 Gb de RAM sempre ha estat molt per davall del rendiment d'un portàtil Dual Core amb 2 Gb de RAM.

Fa quinze dies el G5 va decidir que no es posava en marxa. No tenc ni idea del que li passa, però sospit que pot ser la fon d'alimentació, i això he llegit que pel cap baix eren 300 - 400 eur. Sumat a les ganes que li tenia varen motivar la renovació d'equip de casa, i com que la mitja de compra d'equips és d'un cada 6 anys, doncs ja he aprofitat per fer el canvi complet i posar-me dues pantalles planes de 24" que fan goig. Una oferta molt bona de Dell en té la culpa. L'ordinador és un T1600, potent, però que no m'està deixant molt satisfet pel renou que fa, es nota molt que està encès i el disc dur també és molt renouer.

Un amic al que li agraden molt els Range Rovers em va dir que la solució per a que un cotxe d'aquests no faci renou és posar-li uns altaveus més potents a l'equip de música. Potser m'hauré d'acostumar a fer el mateix amb l'ordinador i acostumar-me a fer feina amb els cascs de música posats, però la veritat és que m'esperava més per la qualitat d'equips a la que me tenia acostumat Dell.

I la joguina nova es diu Asus Transformer

Seguin la rocomanació de Sebas el meu tablet va ser un Asus transformer. La relació qualitat-preu és bona i per ara n'estic content. M'agradava molt també el Xoom, però això significava haver de comprar un portàtil addicional per als meus desplaçaments fora de l'Illa. El Latitude 2100 que tinc encara va bé, però amb 4 hores de durada de la bateria fa que tengui que anar carregat amb el transformador si vull llegir el correu sense passar pena.

L'Asus té una càmera pitjor que el Xoom i no té flash, però la incorporació del teclat i una durada d'unes 16 hores em van fer decidir-me. Soluciona tres problemes: tenir una tablet per a poder provar les apliciacions web, poder disposar d'un equivalent a una portàtil per escriure sense problemes i tenir una bateria amb una durada prou gran per no haver de passar pena.

Bones festes

I això és tot per ara, si no ens tornam a llegir al 2011 esper que passeu unes bones festes i un bon començament d'any. Gràcies per ser per aquí i passar l'estona amb mi. Esper que ens anirem retrobant tant per la xarxa com en persona. Volia dir en la vida real, però estic pensant que la xarxa és real i també ho són els amics que hi fas, si va bé ja ens coneixerem cara a cara i si no pot ser això no ha de servir d'excusa per minusvalorar la gent que coneixes mitjançant la web.

Acab, en serio,

BONES FESTES!!!!!


Traducciones/Translations by apertium

5 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL


Reflexions


Escrit per Aaloy a 20 de November , 2011 a les 2:13 a.m.

Reflexionant

Aprofitant que avui és una jornada de reflexió me pareix que aprofitaré per repassar un poc l'anecdotaria personal d'aquestes darreres setmanes, a veure si en puc treure també algunes conclusions.

No sé el que vull

Donc així ens va venir una persona a veure'ns i d'entrada em va amollar aquesta frase. Bé, res a dir, això és bastant típic quan es comença un projecte. El client no sap massa bé què vol, i la meva feina sovint consisteix en anar fer preguntes, clarificant el que el client vol. Al final, després d'hores de conversa vaig definint el que podria ser el projecte: gran, molt gran. El client resulta que nos sap què vol, però sap que ho vol tot!

Paralant parlant ens diu que ja té un altre pressupost. Diu que ens ho enviarà per a que li poguem pressupostar el mateix. Fantàstic, al manco hi ha un punt de partida. Al dia següent em pos a repassar les notes i començar a fer el pressupost. Quan m'arriba l'e-mail del client jo ja duc com a 8 fulles del pressupost, explicant què tindrà el programa d'acord amb les especificacions del client.

El pressupost que reb em deixa de pedra. Es tracta de dues fulles ròniques que sols inclouen un llista de manteniments, sense entrar en cap tipus de detall. Ja me fa mala espina que en la maraca d'aigua de la fulla hi ha un disquet (are you from the past?), doncs pareix que sí. Però el que em sobta més és el preu que hi han posat, com a deu vegades menys que el que a mi m'està sortint. Li faig una ullada, no s'incluen caps de les característiques que fan més complexa l'aplicació, pareix un conjunt de planes estàtiques amb un editor html al darrera.

Faig una ullada a la web de l'empresa i veig la feina que han fet. En aquests casos sempre pens que potser sóc jo qui m'he errat. També confirm amb el client que el que vol és realment el que m'ha demanat i no simples planes estàtiques o picades a un cms.

La web de l'empresa és per sucar-hi pa. Webs de fa un any o dos que estan fetes fent servir cgis. La plana 404 de les aplicacions apunta al proveïdor del hosting, que obviament es un d'aquests superpoblats, que aprofiten el darrer cicle de CPU per posar-hi usuaris. Anam a veure una de les webs i li coment a Juan lo del cgi i les petades. Ell a més me comenta que ha canviat un paràmetre de la url i li ha sorti un dump de base de dades. Aquesta gent no ha sentit mai parlar d'injecció de codi ni llegit Exploits of a Mom. Som bona gent i el seus clients no es mereixen tanta incompetència, així que ho deixam anar.

Ara ja tenim un problema important, que és el factor psicològic de la fixació de preu. Aquesta gent ha fet un pressupost sense tenir la mínima idea d'on s'estava ficant i del que realment volia el client i ha donat un preu fruit de la seva inexperiència. Ara quan el client vegi el que jo li presentaré es pensarà que li estic prenent el pèl encara que és just al contrari.

Primera conclusió: els clients en general estan molt verds a l'hora de demanar un programa. Quan un demana una casa s'informa del constructor, demana el que ha fet, en quin tipus de projectes ha participat, demana a les amistats i s'assegura que les cases que ha fet no han caigut. En el desenvolupament sols pareix que es fixa amb el color de la façana. Com a informàtics ens fa falta fer molta feina de pedagogia, d'ensenyar als nostres clients a comparar, a saber que hi ha feines més i menys complexes, a poder destriar amb qui se la juga.

El meu cap diu que té un conegut que ...

Aquesta també és una altra història en línia amb l'anterior. Faig un pressupost força ajustat, el projecte m'interessa, pot ser divertit. El client vol donar el pas cap el negocio online. Vol canviar la seva web actual potenciant-la i afegint-li venda on-line. Es preveuen cents de milers de planes servides al dia i la web hauria de funcionar 24x7 pràcticament.

Tot està tancat, els tècnics del clients entenen el que farem hi saben que ho podem fer bé. Han vist el tipus de feina que fem i s'ha generat aquella confiança que t'anima a col·laborar i a entendre el projecte com si fos teu. Però al darrer moment reb una telefonada: el meu cap ha decidit que ho farà algú que ell coneix. Deman per la tecnologia: punt net, amb asp, internet information server, Windows 2003 i Microsoft Sql 2008. Ni en els meus pitjors malsons recomanaria una tecnologia així pel projecte del client. A més jo li havia oferit un servidor dedicat, és el mínim per anar tranquils amb el tipus de dades que es tractaran i el tràfic previst.

Faig una ullada al mercat nacional de servidors. El fiera del Information Server els hi ha dit que molt millor un servidor nacional per millorar el posicionament. Obviament no ha tingut en compte que això és un factor de tercer ordre, i que si primer la plana no va prou ràpida i té prou ample de banda, ja no hi ha res a fer. De totes maneres faig una ullada al mercat nacional, a veure què hi ha amb aquestes configuracions. Vaig a parar a Arsys, que per 605 eur/mes t'ofereix una plataforma així pel`tot just 605 eur/mes amb llicència sql server express edition, si vols la "bona" són 350 €/mes.

Jo els estava oferint si també un servidor dedicat, amb el doble de prestacions que Arsys i també gestionat per nosaltres per 200 €/mes. Em diuen que la gent que els fa la web els hi han dit que el hosting serà de 60 eur/mes. Per aquest preu i a Espanya em temo que serà un hosting compartit, potser del proveïdor, amb el més nou de la versió patapalo-edition de Microsoft.

És una llàstima, la gent amb la que he tractat em cau molt bé i són els primers que no entenen la decisió. Sospit que pot ser un projecte molt problemàtic i que els acabarà costant sang i llàgrimes. Tant de bo no els costi l'empresa.

Segona conclusió Senyors directius, convindria que de tant en tant i en questions tècniques es fes cas als tècnics, que amb les coses de menjar no es juga.

Tercera conclusió Com el el cas anterior hi ha molt de risc de que el projecte fracassi i el client surti escaldat. Part de la responsabilitat és del client, que hauria de saber què compra, però també hi ha una gran responsabilitat del proveïdor, que està enganant al client.

Pero mira que som frikis

Aquesta setmana també m'han demanat un pressupost per a una connexió amb un servei web. Llegint la documentació veig que el WSDL (argh!) sols està certificat que funcioni (que és el mateix que dir que sols funcionarà) amb Java i .Net. Això se diu fer coses estàndard, sí senyor. És un servei complex i delicat, així que abans de pressupostar res convé fer un petit prototip. Li aplic suds, el client SOAP que feim servir habitualment per Python, i les sospites es confirmen, no és capaç de consumir el WSDL i transformar-lo amb Python. Odio els WSDL la S se suposa que és de Simple, no? Han conseguit fer un protocol infumable i que gairebé sols ho pot consumir la mateixa llibreria que l'ha creat.

Però bé, l'interessant de tenir una capça d'eines farcida és que hi ha alternatives. Feim un poc de brainstroming amb Juan. La primera opció és modificar el WSDL per a que el mapejador s'ho mengi, o bé anar donant ajudes a la llibreria. És una opció que no m'agrada, ja que si hi ha problemes el proveïdor del servei se'n rentarà les mans, fins i tot si la culpa és seva.

Una possibilitat seria fer el projecte an Java, però això significaria un cost molt més alt per al client, i sobretot una manca de flexibilitat a l'hora de fer modificacions, ja que el servei sols és una petita part del projecte. Python i Django ens permeten tenir un temps de resposta molt bo davant canvis i això és fonamental pel tipus d'aplicacions que fem.

L'altra possibilitat és fer un servei Java/J2EE que faci de proxy cap a l'aplicació Python. Amb un protocol de comunicació compartit com xml, json, yalm o un binari com el de Google la cosa pot funcionar. En Juan suggereix fer una ullada a jython, que ell li va fer una ullada i pintava molt bé. Li fem una ullada, el projecte està mantingut i és compatible amb Python 2.5, que ja ens va prou bé.

Fem el primer prototip. Cridam a la libria Java des de jython i fem la cridada al servei. Funciona a la primera. I això provant des de la consola de línia de comandaments de jython. Ja tenim part del problema arreglat, però encara no ens satisfà del tot. En nexe d'unió ha de ser net i jython, com aplicació Java que és necessita un temps considerable per iniciar-se.

Però vet aquí que Celery, el sisteme de coes de Python del qual ja us n'he parlat, resulta que soporta Jython. Podem crear el mapeig de la llibreria amb jython i fer que les peticions sian bé síncorones o asíncrones, però que s'executin com a una tasca Celery. Com que la petició es farà dins un worker i aquest està sempre aixecat, no tindrem problemes de temps d'inici una vegada pugi l'aplicació. Es monta el prototip amb Celery, Redis, Python i Jython, en Juan ho està disfrutant i jo també.

Ara puc fer el pressupost tranquil. Sé que el que podria representar el risc més gran ja no representa el problema, hem descober el boogeyman del projecte (l'home del sac) i l'hem exposat a la llum.

No sé si el projecte es farà, però tenc la conciència tranquila de que aquesta és la manera de fer les coses. A l'hora de fer un pressupost per a un client no es tracta sols de pegar una pedrada i a veure si hi ha sort, sinó en estudiar el projecte i veure'n els riscs que hi pugui haver. Per això sovint sóc un perepunyetes demanant informació abans de fer un pressupost. Sé perfectament que en aquesta fase tot pressupost té un marge d'error gran i que hi haurà variacions en el projecte, però crec que no és professional tirar-se a la piscina a l'hora de presentar un pressupost si hi ha un punt crític que no es té clar com es farà. Preferesc invertir uns dies de feina més (amb risc que el pressupost no surti i perdre la feina) que exposar-nos a nosaltres, al projecte i sobretot al client als maldecapts d'un projecte la viabilitat tècnica del qual no s'ha pensat a l'hora de fer el pressupost.

Ho deix aquí, que aìxò s'ha fet molt llarg. Em deixo parlar de coses igualment divertides, com la telefonia IP que estam posant a l'oficina amb Asterisk i el bé que se sent amb els mòbils Android, o la potència de Bacula per configurar les còpies de seguretat. Potser un altre dia ...


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django Java Gestió de projectes Codi lliure Linux APSL


Generar arxius xls amb Python III


Escrit per Aaloy a 29 de October , 2011 a les 11:28 a.m.

En aquesta tercera entrega veurem com podem fer que des de la nostra aplicació Django es puguin generar i servir els arxius que hem generat amb la llibreria xlwt.

La mecànica és la mateixa que la que hi ha als tutorials de Django que mostren com servir un arxiu csv o un pdf, així que per a que serveixi d'alguna cosa més us mostraré como ho feim utilitzant les generic class views incorporades a Django 1.3.

El que volem fer és poder mostrar els resultats per pantalla i després poder afegir un link que ens retorni les dades que tenim en pantalla en forma d'arixu descarregable.

ListView

Per el nostre propòsit farem servir la classe ListView que es troba a django.views.generic

La utilització més típica d'aquesta classes implica sobreescriure el mètode get_query_set de tal manera que ens retorni les dades que mostrarem a la plantilla, i, com no, indicar-li el nom de la plantilla a la qual s'han de mostrar els resultats.

El codi seria quelcom semblant a això per la part de views.py

class TestView(ListView):
    """Classe de proves per l'article"""

    template_name='tests/user_list.html'

    def get_queryset(self):
        """Listam tots els usuaris que no són superusuaris"""

        return User.objects.filter(is_superuser=False)

a l'arxiu urls.py crearem la url que apunti a la nostra classe, afegint

url(r'^test/$', TestView.as_view(), name='trespams-test'),

i important TestView des del mòdul views.py

la plantilla

{% extends "base.html" %}

{% block main %}
<table>
    <a href="{% url trespams-test %}?exporta=1">Exporta</a>
    {% for user in object_list %}
    <tr>
        <td>{{user.username}}</td><td>{{user.email}}</td>
    </tr>
    {% endfor %}
</table>
{% endblock %}

A la plantilla com podeu veure hi ha un object_list que no hem definit enlloc. Això ho fa la pròpia classe, que passa tot el que ve del get_querset al contexte de la plantilla dins una variable anomenada object_list. Podem canviar aquest nom si no ens agrada.

<a href="{% url trespams-test %}?exporta=1">Exporta</a>

Hem definit un enllaç que apunta a la mateixa url i per tant a la matea vista, però afefint-hi un paràmetre que ens indicará que el que volem no és tornar a generar la plana sinó exportar el fitxer.

Si creau una aplicació de proves i provau el codi veureu que encara no fa res, tranquils, ara hi anam...

Canviam la renderització

El gran avantatges de les class views és que estan construïdes de manera molt modular, de manera que podem sobreescriure tot allò que convingui per a modificar-ne el comportament.

En el nostre cas volem que es renderitzi un contingut o un altre segons tinguem o no un paràmetre concret a la url, i per axiò el que farem serà sobreescriure el mètode render_to_response

class TestView(ListView):
    template_name='tests/user_list.html'

    def get_queryset(self):
        return User.objects.filter(is_superuser=False)

    def render_to_response(self, context, **response_kwargs):
        if self.request.GET.get('exporta'):
            return self.render_to_fitxer(context, **response_kwargs)
        else:
            return super(TestView, self).render_to_response(context,
                **response_kwargs)

El que deim és, si hi ha el paràmetre exporta al GET llavors fas un render_to_fitxer (que encara no hem definit), en cas contrari, fas el que es suposava que havies de fer.

Definim el render_to_fitxer

Ja sols ens queda definir el render_to_fitxer per fer que enlloc de l'HTML obtinguem un arxiu

class TestView(ListView):
    template_name='tests/user_list.html'

    def get_queryset(self):
        return User.objects.filter(is_superuser=False)

    def render_to_fitxer(self, context, **kwargs):
        "Sortida cap a un fitxer xls"

        import xlwt
        from django import http

        response =  http.HttpResponse(
            content_type='application/vnd.ms-excel; charset=utf-8',
            **kwargs)
        response['Content-Disposition'] = 'attachment; filename=usuaris.xls'
        usuaris = self.get_queryset()
        wb = xlwt.Workbook()
        ws = wb.add_sheet('usuaris')
        fila = 0
        for usuari in usuaris:
            ws.write(fila, 0, usuari.username)
            ws.write(fila, 1, usuari.email)
            fila += 1
        wb.save(response)
        return response

    def render_to_response(self, context, **response_kwargs):
        if self.request.GET.get('exporta'):
            return self.render_to_fitxer(context, **response_kwargs)
        else:
            return super(TestView, self).render_to_response(context,
                **response_kwargs)

Els imports que hi ha al mètode render_to_fitxer poden estar a la capçalera del mòdul perfectament, els he posat aquí per mostrar explícitament els mòduls que es necessiten dins el mètode.

De la resta de codi, fixau-vos com podem modificar les capçaleres de resposta per indicar que el que volem és generar un arxiu el content_type del qual és de tipus excel i que ho tornarem en format utf-8.

A més al Content-Disposition hem indicat que el contingut es servirà com a un adjunt i amb un nom específic.

Per a la generació de les dades hem reaprofitat get_querset, tanmateix el que volem és el mateix que hi ha en pantalla. Es pot arribar a optimitzar per posar el contingut dins una caché i evitar que es torni a repetir la consulta, però això és una optimitzaició prematura.

Finalment, cal notar que no es genera un arxiu temporal, sinó que la resposta, que tenim dins la variable response és comporta ella mateixa com si fos un fitxer. És la màgia del duck typing.

I això és tot! Fins la propera!


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Generar arxius xls amb Python II


Escrit per Aaloy a 20 de October , 2011 a les 9:27 p.m.

A l'article anterior havíem fet un petit "hello world" en format xls, ara toca fer alguna cosa més de profit, així que veurem com podem generar un full de càlcul a partir de dades.

En altres temps, quan donava formació ofimàtica, un dels exercicis que feia fer als alumnes quan tocava el tema de les fulles de càlcul era el de fer una taula de multiplicar, serveix per perdre la por a la cosa aquesta dels nombres i mostra els conceptes fonamentals, ara podem fer el mateix. Per no fer-ho molt complicat anem per la taula del dos

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import xlwt

fmt = xlwt.easyxf

h1 = fmt('font: name Arial, height 200, bold on;')
wbk = xlwt.Workbook()
full = wbk.add_sheet('Taula del 2')
full.write(0, 0, "Taula del 2", h1)
for x in range(1, 11):
    full.write(x, 0, 2)
    full.write(x, 1, "x")
    full.write(x, 2, x)
    full.write(x, 3, "=")
    full.write(x, 4, xlwt.Formula('A%s*C%s' % (x+1, x+1)))
wbk.save('taula.xls')

Podem veure com hem creat fàcilment una fórmula per a fer la multiplicació. També podem veure com per posar les fórmules hem de tenir en compte que s'ha de fer en la notació de files i columnes pròpia de les fulles de càlcul, és a dir, les columnes s'anomenen a partir de la lletra A. Això xoca un tant amb tot el maneig de la llibreria que utilitza una notació de coordenades o la l'origen està en la cel·la superior esquerra.

Per tractar amb això la llibreria proporciona un mòdul anomenat Utils que ens proporciona les eines per fer les transformacions entre la notació de fulla de càlcul i la notació de coordenades de la llibreria.

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import xlwt

fmt = xlwt.easyxf

h1 = fmt('font: name Arial, height 200, bold on;')
wbk = xlwt.Workbook()
full = wbk.add_sheet('Taula del 2')
full.write(0, 0, "Taula del 2", h1)
for x in range(1, 11):
    full.write(x, 0, 2)
    full.write(x, 1, "x")
    full.write(x, 2, x)
    full.write(x, 3, "=")
    num = xlwt.Utils.rowcol_to_cell(x, 0)
    taula = xlwt.Utils.rowcol_to_cell(x, 2)
    formula = xlwt.Formula("%s*%s" % (num, taula))
    full.write(x, 4, formula)
wbk.save('taula.xls')

Formats

Quan tractam amb informació és important a més de presentar-la, fer-ho en un format entenidor. Els fulls de càlcul ho fan prou bé a això, i des de fa molt temps tenen la capacitat de presentar-nos les dates en formats legibles i no en el seu format intern, i les quantitats numèrics ben formatejades, per exemple.

Amb la llibreria xlwt podem utilitzar els formats de cel·la de la fulla de càlcul, podeu consultar-los anant a les propietats d'una cel·la o bé fer una ullada a l'exmemple num_formats.py que ve amb la llibreria.

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import xlwt
import decimal
fmt = xlwt.easyxf

wbk = xlwt.Workbook()
full = wbk.add_sheet('Caixa')
moneda = fmt('font: name Times' ,
              num_format_str=u'#,#0.#0 "€";[Red]-#,#0.#0 "€";')
full.write(0, 0, decimal.Decimal("11133"), moneda)
full.write(0, 1, -3939.93, moneda)
wbk.save('nombre.xls')

A l'exemple podem veure com xlwt tracta perfectament tant el format numèric sencer (o float) com el Decimal.

De la mateixa manera podem fer feina amb diferents formats de dates o formats de temps.

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import xlwt
import datetime
fmt = xlwt.easyxf

wbk = xlwt.Workbook()
full = wbk.add_sheet('Avui')
data = fmt(num_format_str=u'D MMM YYYY')
full.write(0, 0, datetime.date.today(), data)
wbk.save('dates.xls')

Ho deix aquí per avui, a la propera entrega veurem com passar tot això la web i fer que Django ens doni un petit informe creat amb xlwt amb les dades del model.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Generar arxius xls amb Python I


Escrit per Aaloy a 17 de October , 2011 a les 9:25 p.m.

Una de les coses que volia que l'aplicació de gestió de comunitat de propietaris que fem és la de tenir algun tipus de via per a poder exportar la informació introduïda. D'aquesta manera el client no està captiu i les seves dades són realment seves, però a més també volia que aquesta informació es presentàs de manera que també pogués ser útil.

El format csv està prou bé, però és massa simple a l'hora de presentar les dades. Necessitava quelcom del format del qual fora prou potent per poder aplicar estils i format bàsic, i al mateix temps que no implicàs una despesa addicional per al client.

Cercant, cercant vaig arribar a la llibreria xlwt. Aquesta llibreria permet crear fitxers en format xls (Excel) d'una manera prou senzilla i ràpida com per a ser just el que necessitàvem. El format es pot llegir tant mitjançant aplicacions privatives, que de d'aquí no recomanaré, o des d'aplicacions lliures com l'OpenOffice o LibreOffice.

Per a qui vulgui saber-ho tot podeu fer una ullada al codi font, però si us conformau amb un poquet menys, podeu llegir-vos el tutorial. Si voleu anar per feina podeu seguir llegint i us ne faré cinc cèntims de la llibreria.

Hello world

El primer que hem de fer és instal·lar la llibreria. Així que si no heu instal·lat pip ja tardau i després no deixeu de fer el mateix amb el virtualenv i el virtualenvwrapper, ja que sempre, sempre, farem feina des d'un entorn virtual.

Així doncs:

mkvirtualenv fc
workon fc
pip install xlwt

El que farem serà crear un full de càlcul amb les paraules "Hello world" a la primera cel·la (A1)

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import xlwt
wbk = xlwt.Workbook()
full = wbk.add_sheet('full 1')

full.write(0,0,'Hello world')
wbk.save('hello.xls')

Ja està! Hem importat la llibreria, creat el llibre, afegit un full que hem anomenat "full 1" i segidament escreit a aquest full la frase mítica.

Podem afegir tans fulls com volguem sense problemes, però ens hem de fixar bé que la cel·la A1 correspon a les coordenades zero, zero del full de càlcul.

Un poc de format

Podem aplicar estils a una cel·la fent servir dos mètodes: podem utilitzar la classe XFStyle que ens permet crear l'estil dessitjat pas a pas, o bé aplicar un format molt més legible pels humans i molt més proper al que seria un full d'estil css: easyxf

En aquest article faré servir aquest darrer mètode, ja que està prou ben documentat i com us dic, és molt més legible. Així, el que faré serà fer que el nostre "Hello world" es present un poc més vistós.

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import xlwt

fmt = xlwt.easyxf

h1 = fmt('font: name Arial, height 400, bold on;')
wbk = xlwt.Workbook()
full = wbk.add_sheet('full 1')
full.row(0).height = 800
full.write(0,0,'Hello world', h1)
wbk.save('hello2.xls')

He creat un estil anomenat h1 amb font Arial i negreta. L'alçada l'he posada com a 400, és a dir 20px. Com a base preneu que un tamany de font 200 equival a una alçada de 10px.

Com que volem que la fila tengui més alçada que la font, hem establert la propietat height de la fila a 800.

Això és tot per avui, al proper article (demàs segurament) ja un full complet, amb format numèric i alguna fórmula.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Propietariosonline, la visió tècnica


Escrit per Aaloy a 08 de October , 2011 a les 11:01 a.m.

Aquesta setmana és obligat parlar del llançament de propietariosonline.com, una aplicació web per a la gestió de comunitats de propietaris que hem llançat des d'APSL. Junt amb la web de trobacasa forma el gruix dels projectes propis. Actualment estam en fase beta, és a dir, mirant de netejar tots els possibles errors que puguin haver passat els nostres testeigs inicials i sobre tot, copsant les opinions dels beta-testers, per tal d'anar incorporant els suggeriments de millora que ens facin. Personalment una de les coses que més em motiven a l'hora de fer un programa és que aquest sigui útil, i per això res millor que fer-lo en col·laboració amb els usuaris potencials del programa.

Com que supòs que no llegiu això per a que us parli de la gestió de les comunitats de propietaris, aniré entrant en matèria.

L'aplicació està desenvolupada amb Django en la seva versió 1.3. És la primera aplicació grossa en la que feim un ús intensiu de les generic class views. Com molts sabreu abans de la versió 1.3 la manera de passar del la petició (el request) cap a la sortida html era mitjançant una funció que es posava a l'arxiu views.py. Amb la versió 1.3 això també es pot fer, però hi ha la possibilitat de que aquestes funcions siguin classes.

Per una aplicació com propietariosonline això ha significat poder fer herència de classes i reutilitzar moltíssima funcionalitat. Ahir llegia un apunt on un usuari de Django es queixava que la quantitat de codi escrit utilitzant les classes era major que sols utilitzant les funcions. En casos puntuals potser veritat, però quan es pot fer ús de l'herència, la reutilització de codi i sobretot l'estructuració que tens compensa de sobres tenir que aprendre una nova manera de fer les coses.

Com es tracta d'un projecte propi, hem aprofitat per fer experiments i provar noves utilitats i llibreries. Ja se sap, els experiments a casa i amb gasosa, així que res millor que un projecte com aquest. Es prou gran com que l'experiència sigui extrapolable a altres projectes i no hi ha la pressió externa que representa un client que està pagant per hores.

Una de les llibreries que hem utilitzat és django-tables2 que ha significat passar de crear les taules de dades amb html a utilitzar codi Python per a enllaçar estructura i visualització. A l'aplicació es fan servir molts llistats tabulats i aquesta utilitat ens ha permès reutilitzar estructures de taula, definir formats de columnes i sobretot estalviar-nos una gran quantitat de codi HTML. De retruc les modificacions també són més senzilles, ja que per exemple modificar com apareixen les quantitat numèriques és tant senzill com modificar un tipus de columna que hem definit amb Python. Per exemple, per posar un check hem definit una columna com:

class BooleanColumn(Column):
    def render(self, value):
        valor = "on" if value else "off"
        return mark_safe('<img src="%s/img/check-%s.png" />' \
           % (settings.STATIC_URL, valor))

D'aquesta manera quan a un llistat es necessiti un camp booleà utilitzarem aquest tipus de columna. Si pel que sigui volem canviar com es presenta doncs no hem d'anar taula a taula a fer els canvis, sinó que podem anar directament al tipus de columna.

Una altra de les utilitat que hem fet servir es diu Sentry una utilitat que utilitza la gent de Quora per a monitoritzar els errors 500 i logs d'error. Fins ara aquests tipus d'errors els controlàvem amb els missatges d'e-mail que ens envia la pròpia aplicació de Django. El problema amb això és que no escala bé. L'altra dia de pagès ens trobàrem amb un problema on això es veu perfectament:

Un dels nostres clients està connectat amb una web amb molt tràfic que li envia peticions. Aquesta web va sofrir un atac i va començar a enviar peticions mal formades a tort i dret, i un dels afectats va ser el nostre client. Una de les màximes de Python és que les excepcions no ha de passar desapercebudes, així que personalment program de manera que si una cosa no ha de petar i peta, doncs me'n vull assabentar. Això va fer que rebéssim milers de missatges en poques hores. L'aplicació no es va veure afectada, però la bústia d'avisos feia goig! Al mateix temps un altra client va tenir una petada a l'aplicació, que també va enviar el corresponent e-mail. En condicions normals haguéssim vist l'error i l'haguéssim pogut solucionar en pocs minuts, però l'avís es va perdre entre els milers de missatges anteriors i no ens n'adonàrem fins passats un dia o dos. És veritat que es pot configurar el sistema de correu per a que distribueixi els missatges per client i per aplicació, però en condicions normals això no es tan còmode com tenir-ho tot centralitzat a un punt.

Aquí és on Sentry ens soluciona la vida. Hem configurat una aplicació amb Sentry que centralitza tots els missatges d'error. D'aquesta manera a la consola sols apareix el missatges i el nombre de vegades que s'ha produït l'error. Així encara que es produeixi una situació com la que explicava és molt més difícil que l'error passi desapercebut, ja que cada error idèntic s'agrupa dins Sentry. A més el format de visualització dels errors és fantàstic, molt semblant a com Django presenta els errors en mode depuració, la qual cosa fa que identificar el problema sigui encara més fàcil.

Propietariosonline va servir com a excusa i experiment, però a hores d'ara ja tenim el client de Sentry instal·lat al 90% de les aplicacions i la consola de Sentry com a una eina fonamental de monitorització, sols comparable en utilitat a Nagios en la monitorització de sistemes.

La resta d'utilitats que hem fet sevir ja són vells coneguts: django-nose per als tests unitaris, django-redis-cache, south, sorl-htumbnail, django-debug-toolbar, django-extensions, ipdb, robots etc. no s'han convertit en part fonamental de les nostres aplicacions.

Hores d'ara duim un mes just de dedicació al projecte. Fet i fet podem dir que hem dedicat dues persones a temps complet durant un mes. De fet és un poc menys, ja que hem fet manteniment d'altres projectes, però si sumam la feina de maquetació de la web principal i la feina de sistemes, doncs els resultat és si fa no fa aquest: 2 mesos-home de dedicació (encara que diferents perfils).

Posem-hi xifres! He utilitzat el programa cloc per comptar les línies de codi. He llevat la totalitat de codi javascript i css (i less), ja que fonamentalment hem utilitat llibreries jQuery i el bootstrap de twitter, el resultat és:

285 text files.
282 unique files.
1348 files ignored.

http://cloc.sourceforge.net v 1.53  T=1.0 s (248.0 files/s, 19589.0 lines/s)
-------------------------------------------------------------------------------
Language          files     blank   comment      code    scale   3rd gen. equiv
-------------------------------------------------------------------------------
Python              147      1593       872     11995 x   4.20 =       50379.00
HTML                 90       298        32      4088 x   1.90 =        7767.20
XML                  11         7         0       704 x   1.90 =        1337.60
-------------------------------------------------------------------------------
SUM:                248      1898       904     16787 x   3.54 =       59483.80
-------------------------------------------------------------------------------

És interesant veure que aquesta eina (encara que agafat amb pinces) ens dóna també l'equivalent en línies de codi si haguéssim fet servir un llenguatge de programació menys potent que Python. Donat que el nombre de línies de codi que pot escriure un programador és constant, llavors això vol dir que el mateix programa d'haver-ho fet en un altra llenguatge ens hagués duit 4 vegades més de temps. Per pensar-hi!

A la part de sistemes hem aprofitat també per potenciar el Fabric per a tota la gestió i desplegament de l'aplicació, que corre amb uWSGI i ngnix. Això ens permet pujar modificacions a diari, mantenint el cicle d'entorns de desenvolupament, preproducció i producció.

Encara hi ha coses que es poden millorar (de fet sempre n'hi haurà). Quan l'economia ens ho permeti volem incorporar un servidor per a la integració contínua, però a poc a poc esperam anar arribant-hi. M'agrada pensar que hem aconseguit un procés de millora contínua, on a cada projecte aconseguim tenir les coses millor que al projecte anterior i anar incorporant el que hem après també a les aplicacions que ja hi ha en producció.

Si tot això tindrà viabilitat econòmica i ens permetrà sobreviure com a empresa el temps ho dirà. Però el que sempre he tingut el convenciment que conformar-se i no aspirar a millorar sols duu a l'empobriment espiritual i és tan perillós o més que el risc que corres intentant millorar.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django APSL


Petit resum d'estiu


Escrit per Aaloy a 21 de September , 2011 a les 8:15 p.m.

Fa un grapat de setmanes que no escric res a aquest blog, tenc moltes coses pendents, però entre unes coses i altres el temps va passant i fins avui no he trobat una estona per a tornar-hi.

Han estat unes setmanes molt estranyes: problemes personals, alguns bons, com les noces de la germana i altres no tan bons, molta feina i com no, el començament de l'escola dels menuts i la festa de la Vermada que ja és aquí.

En el terreny de la feina tenc dues bones notícies: hem posat ja en producció un projecte que em fa una especial il·lusió txerpa i una web presencial fiscontrol.

txerpa és un projecte que hem desenvolupat per a una coneguda gestoria/assessoria de l'illa. Integra una web, un backoffice per a la gestió d'empreses via OpenERP i la integració amb el mateix OpenERP de manera que es poden crear molt fàcilment instàncies personalitzades per a poder dur una comptabilitat i gestió.

El projecte des del punt de vista tècnic ha estat molt engrescador. És un projecte amb un llarg recorregut en el qual esperam poder-hi afegint millores i nova funcionalitat. L'interessant, però, és veure que des d'aquestes illes nostres iniciatives com aquestes poden tirar endavant. Django i Python han permès que la integració dels diferents serveis que integren la plataforma es pogués fer d'una manera ràpida, mantenible i escalable.

Fiscontrol és un altre projecte que ha vist la llum aquesta setmana de manera oficial. En aquest cas és una web presencial, però ens ha fet il·lusió ja que és un projecte on el feeling amb el client ha estat molt bo i on les incorporacions del client han estat sempre per millorar el resultat final (ja sabeu que això no sempre passa). És de les poques webs on s'han demanat n idiomes i s'han posat continguts en aquests idiomes. En fi, que tot i que el projecte no té un component tècnic novetós, sí que ens hem divertit molt fent-la i veient els resultats.

Ja és la nostra tercera web d'assessoria, pareix que ens estam començant a especialitzar :)

Esper que en les properes setmanes hi haurà més novetats (com sempre en forma de projectes Django), ja que tenim un grapat de webs pendents de sortir.

Com sabeu els que us dedicau a la informàtica en el sector turístic, els mesos de temporada alta turística són mesos de temporada baixa en informàtica. He aprofitat aquest mig gas per engrescar a la gent d'APSL (o emmarronar-los, vet a saber) en un projecte propi. Permeteu-me de moment que us mantengui a l'espera de més notícis. L'interessant és que el projecte l'hem fet ja amb les noves generic class views de Django, incorporades a la versió 1.3.

En aquest projecte hem pogut veure la meravella de les class views, com tot el codi ens queda molt més compacte, com es pot aprofitar l'herència de vistes per escriure molt menys codi (sí, encara menys!).

De les class views en vull escriure un bon apunt un dia d'aquests. La documentació de Django que hi ha i els posts que he trobat sols fan referència al TemplateView, però la potència dels views de Forms, d'edició i borrat no s'han de menystenir. Així que es cosa de trobar-ne el temps per escriure l'apunt/tutorial i fer el codi d'exemple.

Per cert, que aquest blog ja corre damunt un altre servidor. Abans ho feia a un servidor dual core amb 2 Gb de RAM, però fa unes setmanes hem començat a migrar-ho tot a un servidor molt més potent, un i7 quadcore amb 12 Gb de RAM. Pingdom diu que la renderització ha passat de 4 segons a 1.5 segons. No he canviat el codi, sols és l'efecte d'un servidor que va més sobrat i d'una optimització de l'arquitectura: hem passat del WSGI de CherryPy que hi havia a l'inici al WSGI de uWSGI, de tenir la caché a memcached a tenir-la a Redis.

En Bernat ha fet molta feina deixant un entorn molt optimitzat per a les aplicacions Django. Tant que estam pensant en oferir un hosting gestionat per la gent que desenvolupi aplicacions Django i necessiti un entorn ben afinat. Temps al temps. El que tinc clar és que no vull que facem un projecte tipus Gondor, sinó quelcom on el valor afegit sigui la disponibilitat d'un tècnic de primer nivell com Bernat per a poder deixar ben fina l'aplicació.

Res, això és tot, i que no és poc.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Redis vs Memcached [en]


Escrit per Aaloy a 05 de August , 2011 a les 6:17 p.m.

With some help of our friend "Google Translate" :)

As we all know that if you want a web application goes faster there is a secret cache as much as you can. Avoid to generate the page each time and search for the content in the database.

To archieve this the today standard is Memcached. Memcached allows us to scale our application a simple way. We can think it as a big hash table, written in C, very fast and with libraries to access to it in almost any language. But thre is e a new competitor in this kind of applications: Redis, and I've already talked about it in the Celery post.

Memcached is an application aimed at dealing with cache, Redis is a noSQL general purpose key-value database in a similar way as Memcached, but with possibilities that go far beyond a cache. Let's see a few differences:

  • We can not see the keys we have in Memcached. With Redis can do a search for keys, or see all the command KEYS *

  • Redis has persitence.

  • Memcached is limited to memory you allocate, Redis can also swapt to disk and just put the keys in memory.

  • In Memcached we have no true replication. Redis replication is real and configurable with a simple line.

  • Redis is quite configurable, we can define the page size of cache, store to disk, have a password protected database...

  • With Redis we can define as many databases as you want. We can clean all the keys in a database without affecting the others.

So the next step is to ask if we could use Redis instead of Memcached for our web applications. Redis plus Django could partially solve one of the biggest problems we have: cache invalidation. As we can have an independent database for each application or for each purpose, we can FLUSHDB the database our application to invalidate the whole cache, or just delete the keys with a single Redis command.

But in Science hypotheses have to be confirmed. So what I did was to build a little sandbox application to see how if Redis was as good as it seems.

The Sandbox

The machines availables to us for the experiment follows:

  • Dell Laptop Core 2 at 2:16 GH and 2 GB of RAM with Ubuntu 11:04 This is the Web application server and has the address 192.168.1.35

  • Virtual Server Ubuntu 10.04 with 512 MB of RAM on Virtualbox running on the laptop at 192.168.1.38. This server is 32 bit and will host Redis as Memcached.

  • Apple PPC 64 Dual Core with 3 GB of RAM, it will launch the tests.

The application is the one I created for creantbits. That is, to run it has to read BD for the last events and presents them in the page.

We will use two of Gunicorn workers to start the application, and we'll test the performance from the PPC with Apache Benchmark

ab -n 1000 -c 5 http://192.168.1.35:8000/

For each test case two consecutive tests are thrown and we discarded the first.

To test the Redis cache we have installed the application django-redis-cache

The test

First we have to determine the starting point. So what we did is to clear our application's cache and see how many requests we get.

no-cache: 120 req / s Mean: 41.4 ms

We set up cache site for Django configured as locmem. Locmem is not recommended in production environments as it can't share the cache, but as before, we used to set the starting point:

locmem: 1380 req / s Mean: 3.6 ms

We configure the the cache as memcached

memcached: 626 req / s Mean: 8.0 ms

Cache Redis configured with persistence to disk

Redis: 623 req / s Mean: 8.0 ms

Cache Redis without persistence. Is the nearest Memcached equivalent.

Redis: 632 req / s Mean: 7.8 ms

We install the hiredis package

Redis: 650 req / s Mean: 7.7 ms

Conclusions

I think the results speak for themselves. If we use persistence Redis is comparable in speed to Memcached, and for the same price we have a NoSQL database at our disposal.

If we don't need persistence Redis shows a 4% improvement over Memcached. This percentage is not significative, but at least we can see that Redis is in the same leage as Memcached.

For me Redis it too good to not use it!


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python Django


Redis vs Memcached


Escrit per Aaloy a 04 de August , 2011 a les 6:57 p.m.

Com tots sabem si un vol que una aplicació web vagi ràpida hi ha un secret: posar en caché tot el que puguem. Evitar tenir que fer càlculs i anar a la base de dades a cercar la informació.

Per fer això l'estàndard avui en dia és Memcached. Memcached ens permet escalar la nostra aplicació d'una manera molt senzilla. És una gran taula hash, del tipus clau valor, escrita en C, molt ràpida i amb llibreries d'accés en pràcticament qualsevol llenguatge.

Però en els darrers temps ha sortit un nou competidor dins les bases de dades de tipus hash, aquest competidor reb el nom de Redis, i ja us n'he parlat quan tractàvem el tema de Celery.

Així com Memcached és una aplicació orientada a tractar amb caché, Redis és una base de dades noSQL de propòsit general, amb format clau-valor com Memcached, però amb unes possibilitats que van molt més enllà de un simple magatzem de memòria. Anem a veure un parell de diferències:

  • No podem veure les claus que tenim dins Memcached. Amb redis podem fer una cerca per claus, o veure-les totes amb la comanda KEYS *

  • Podem configurar Redis per a que sigui persistent.

  • Memcached està limitat a la memòria que li assignem, Redis pot utilitzar també el disc i sols posarà en memòria les claus.

  • Memcached no té una vertadera replicació, encara que podem fer que hi hagi molts servidors Memcached. Redis té replicació real i configurable amb una simple línia.

  • Redis és força configurable, podem definir el tamany de pàgina de caché, quant guardarem a disc, si volem tenir la base de dades protegida, ...

  • Amb Redis podem definir tantes bases de dades com vulguem. Podem netejar totes les claus d'una base de dades sense afectar a les altres.

Amb tot això és lògic demanar-se si enlloc de Memcached no podríem fer servir Redis per a les nostres aplicacions web. Si ho ajuntam amb Django tenim resolt un dels problemes més grans, que és la invalidació de cachés. Separant cada una de les nostres aplicacions dins una base de dades, podem fer un FLUSHDB a la base de dades de la nostra aplicació per invalidar la caché, amb la seguretat que no afectarà a la resta.

Però a la ciència les hipòtesis s'han de confirmar. Així que el que he fet ha estat muntar un petit entorn de proves per veure com se comporta una aplicació senzilla.

L'entorn de proves

El maquinar de que disposam per l'experiment és el següent:

  • Laptop Dell Core 2 a 2.16 GH i 2 Gb de RAM amb Ubuntu 11.04 Aquest servidor té l'aplicació web, i té l'adreça 192.168.1.35

  • Servidor virtual Ubuntu 10.04 amb 512 Mb de RAM damunt Virtualbox, executant-se damunt el servidor anterior amb l'adreça 192.168.1.38. Aquest servidor és de 32 bits i tindrà tant Redis com Memcached.

  • Apple PPC 64 Dual Core amb 3 Gb de RAM, ens servirà com a màquina per a llançar els tests.

L'aplicació és la que vaig fer servir pel creantbits (http://creantbits.com). És a dir, fa un accés a BD per obtenir els darrers esdeveniment i presenta la plana.

Utilitzarem dos workers de Gunicorn per iniciar l'aplicació, i la testejarem des de el PPC amb l'Apache Benchmark

ab -n 1000 -c 5 http://192.168.1.35:8000/

Per a cada test es llancen dos tests ab consecutius i es descarta el primer. Es repeteix 2 pics i es fa la mitja, arodonint cap avall en nombre de peticions per segon.

Per la caché de redis es fa servir l'aplicació django-redis-cache instal·lada des de PyPi.

Inici dels tests

Per començar hem de determinar el punt de partida. Així que el que farem serà desactivar la caché de la nostra aplicació i veure quantes peticions aconseguim.

no-cache    :   120 req/s    Mean: 41,4 ms

Activam la caché per site de Django i posam la caché a locmem. Locmem fa que la caché no pugui ser compartida entre processos i no és un opció recomanada per entorns de producció, però com abans, ens serveix per fixar el punt de partida:

locmem      :   1380 req/s  Mean: 3,6 ms

Posam la caché a memcached

memcached   :   626 req/s   Mean:   8,0 ms

Posam la caché a Redis configurada amb persistència a disc

Redis       :   623 req/s   Mean:   8.0 ms

Configuram Redis sense persistència. És l'equivalent a Memcached.

Redis       :   632 req/s   Mean:   7,8 ms

Utilitzam Redis amb el client hiredis

Redis       :   650 req/s   Mean:   7,7 ms

Conclusions

Crec que els resultats parlen per sí mateixos. Si volem persistència Redis és comparable en velocitat a Memcached, i a més pel mateix preu tenim una base de dades NoSQL en el nostre entorn i a la nostra disposició.

Si no volem persistència Redis supera per molt poc a Memcached i si aplicam totes les optimitzacions per tenir un entorn el més semblant possible a Memcached arribam a un 4% de millora. Aquest tant per cent no és significatiu, però si més no ens serveix per veure que Redis està al mateix nivell de rendiment que Memcached i ve amb tot el paquet d'opcions afegit.

Massa bo per no fer-ho servir!


Traducciones/Translations by apertium

8 comentaris, 0 trackbacks (URL) , Tags: Python Django


De la web al model


Escrit per Aaloy a 29 de July , 2011 a les 6:04 p.m.

L'scrapping

Ahir ja vàrem veure quin era el model de dades i el bé que va sorl per a manipular imatges, així com la utilització de la llibreria requests ens quedava veure una altra part important: com agafar el contingut de la web, parsejar-lo i obtenir-ne la informació que necessitam.

Per fer això hi ha diverses utilitats, algunes molt especialitzades com scrappy, i amb més solera és BeautifulSoup. Aquesta llibreria té la qualitat de ser molt permisiva amb l'HTML i hi ha poques planes que no pugui tractar d'una manera o altra.

La plana de Meneame té las notícies de portada dins un div anomenat news-summari, així que el primer que farem serà carregar la plana dins una instància de BeautifulSoup i cercar aquestes notícies.

page = requests.get('http://www.meneame.net')
if page.status_code != 200:
    print "Ups! pareix que hi ha un petit problema"
    return page.status_code
soup = BeautifulSoup(page.content)
noticies = soup.findAll('div', 'news-summary')

amb això BS ens haurà donat tots els divs que tenen la classes 'news-summary' amb la qual cosa ja és sols cosa d'aplicar un tractament semblant per a obtenir la informació de cada notícia

for noticia in noticies:
    titular = noticia.find('h1').text
    texto = noticia.find('p').text
    img = noticia.find('img', 'thumbnail')
    if img:
        src = img['src']
        match_obj = compile_obj.search(src)
        id = match_obj.group('id')
        try:
            NoticiasPortada.objects.get(identificador=id)
        except NoticiasPortada.DoesNotExist:
            print u"Tractant la noticia: %s" % id
            noticia = NoticiasPortada(
                identificador = id,
                texto = texto,
                titular = titular,
                thumbnail = download_image(src,           
                                     'thumb-%s.jpg' % id)
            )
            noticia.save()

El mètode find ens permet a accedir al primer tag que compleix la condició i text ens en dona el contingut. Si com a segon paràmetre hi possam una classe ens retornarà el primer element d'aquell tipus que tengui la classe que li hem donat.

El problema ve quan volem identificar d'alguna manera les notícies. Volem guardar sols les que tenen una imatge. Podem veure que Meneame genera el thumbnail de la notícia amb l'identificador de la mateixa, així que podem fer us d'una expressió regular:

rawstr = r"""(?P<id>\d+).jpg$"""
compile_obj = re.compile(rawstr)

així

src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')

ens donarà l'identificar de la notícia a partir de la informació de la url de la imatge. Hi ha una utilitat fantàstica per a la depuració i testeig d'expressions regulars anomenada Kodos, no us la podeu perdre.

El toc final

En una aplicació com aquesta la CPU està molt de temps sense fer res, esperant que li arribi la informació. És un bon candidat per a que l'aplicació faci ús dels Threads o del multiprocés. Una de le maneres més senzilles de fer-ho és fent servir la llibreria Queue, d'aquesta manera sols hem de definir quants Threads farem servir en el processament i que aquests consumeixin el contingut (cada notícia) de la cua.

Així el codi final quedaría si fa no fa:

import re
import cStringIO
import requests
from Queue import Queue
from threading import Thread
from BeautifulSoup import BeautifulSoup

from django.core.management.base import BaseCommand
from django.core.files.uploadedfile import SimpleUploadedFile

from t1.models import NoticiasPortada

rawstr = r"""(?P<id>\d+).jpg$"""
compile_obj = re.compile(rawstr)

class Command(BaseCommand):
    """Divertimento. Permet posar les notícies amb foto
    de meneame dins una BD.
    """

    def handle(self, *args, **options):
        page = requests.get('http://www.meneame.net')
        if page.status_code != 200:
            print "Ups! Pareix que tenim un problema"
            return page.status_code

        #cream les coes
        self.q = Queue()
        for i in range(5):
            t = Thread(target = self._importar_portada)
            t.daemon = True
            t.start()

        soup = BeautifulSoup(page.content)
        noticies = soup.findAll('div', 'news-summary')
        for noticia in noticies:
            self.q.put(noticia)
        self.q.join()

    def download_image(self, img_url, filename):
        r = requests.get(img_url)
        if r.status_code == 200:
            return SimpleUploadedFile(content=r.content,
                name=filename,
                content_type=r.headers.get('content-type'))
        else:
            return None

    def _importar_portada(self):
        while True:
            noticia = self.q.get()
            titular = noticia.find('h1').text
            texto = noticia.find('p').text
            img = noticia.find('img', 'thumbnail')
            if img:
                src = img['src']
                match_obj = compile_obj.search(src)
                id = match_obj.group('id')
                try:
                    NoticiasPortada.objects.get(identificador=id)
                except NoticiasPortada.DoesNotExist:
                    print u"Processant la notícia: %s" % id
                    noticia = NoticiasPortada(
                        identificador = id,
                        texto = texto,
                        titular = titular,
                        thumbnail = self.download_image(src, 
                          'thumb-%s.jpg' % id)
                    )
                    noticia.save()
            self.q.task_done()

I això és tot, com sempre amb Python l'explicació sol ser molt més llarga que el codi a executar, fins i tot amb els fils.

Esperant que us hagi agradat el divertimento.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Imatges de la web al model Django


Escrit per Aaloy a 28 de July , 2011 a les 5:42 p.m.

L'altra dia estava fent una aplicació web part de la qual consisteix en aprofitar els continguts de la web anterior, continguts als que no tenim accés directe.

La part de text va ser senzilla, però obtenir una imatge de la web i associar-la a un model Django va resultar una mica més interessant del que suposava. Tant que vaig pensar que potser convenia posar-ho en forma d'apunt.

Per posar-ho en context vaig començar a fer una aplicació Django, la qual tenia que obtenir la imatge i guardar-la, però ja que hi era, vaig voler fer quelcom més educatiu, així que l'aplicació es va anant transformant amb codi per a descarregar-se les fotografies associades a la plana principal de meneame. Si provau el codi mirau de canviar la url que ja que amablement Ricardo em va dir que no hi havia problema en fer algunes proves, tampoc és cosa que anem putejant la web.

Així doncs, aquest apunt serà un poc mescladissa d'utilitats, d' screen scrapping, de thread, processos i altres herbes. Miraré d'anar a poc a poc, però ja us aviso que hi ha de tot i molt!

Definim el model

El més habitual quan hom tracta amb imatges és tenir-ne que fer algun tipus de conversió, si més no per presentar-les a l'administrador de Django. És tan habitual que quan he de fer servir un camp ImageField de Django ja ho converteixo directament a un camp ImageField d'una llibreria que va ideal per a la manipulació d'imagtes sorl.thumbnail.

Així doncs, el nostre model serà molt senzill, tindrà un identificador per poder distingir les notícies, el titular, el texte de la notícia i com no la imatge.

M'ha queda talment així:

!/usr/bin/env python
# -*- coding: UTF-8 -*-
from django.db import models
from sorl.thumbnail import ImageField
from sorl.thumbnail import get_thumbnail

class NoticiasPortada(models.Model):
    """Base de datos de noticias"""

    def get_upload_path(instance, filename):
        return "fotos/%s" % filename

    identificador = models.IntegerField()
    titular = models.CharField(max_length=200)
    texto = models.TextField()
    thumbnail = ImageField(upload_to=get_upload_path, 
              blank=True, null=True)

    def img(self):
        if self.thumbnail:
            try:
                im = get_thumbnail(self.thumbnail, 
                                     '50x50', crop='center', quality=80)
                return '<img src="%s">' % im.url
            except IOError:
                return "error"
        else:
            return "%s" % self.identificador
    img.allow_tags = True
    img.short_description = 'img'

    def __unicode__(self):
        return self.titular

Com es pot veure Sorl incorpora una sèrie d'utilitats per a la conversió d'imatges que ens van molt bé. Així podem definir el mètode img dins el model per tal de poder-ne fer referència a l'administrador de d'Django.

Fixem-nos també com li podem passar un mètode per tal de poder calcular a quin lloc es deixarà la imatge. Aquest mètode ha de tenir la signatura nomfuncio(instància, nom) on instància és la instància de l'objecte al qual s'associarà la imatge i el nom és el nom de la imatge en sí. Això és molt útil per poder deixar les imatges a diferents carpetes segons convingui.

A l'admin.py podem fer

from django.contrib import admin
from models import NoticiasPortada

class NoticiasPortadaAdmin(admin.ModelAdmin):
    list_display = ('img', 'identificador', 'titular')
    search_fields = ('titular', 'texto')

admin.site.register(NoticiasPortada, NoticiasPortadaAdmin)

Sorl té opcions per a que al formulari se'ns presenti també la imatge, però per ara ho deixarem així.

La càrrega de les imatges

La càrrega de les imatges la podríem haver fet de moltes maners, però com que ja vaig posar un article de com fer comandes de Django, doncs ho aprofitaré. Crearem una comanda anomenada importar que es podrà cridar com

python manage.py importar

Per això s'ha de crear un paquet Python dins l'aplicació amb l'estructura

app
    models.py
    management
        __init__.py
        commands
            __init__.py
            importar.py

Si feis servir django-extensions recordau que hi ha una comanda anomenada create_command que crea directament aquesta estructura.

Per a importar les imatges utilitzarem la classe InMemoryUpladedFile que es troba a django.core.files.uploadedfile. Podem passar una instància d'aquesta classe al nostre model dins l'atribut thumbnail i Django se n'encarregarà de la resta. La complexitat addicional està en que ens hem de davallar la imatge de la web.

def download_photo(self, img_url, filename, field_name):
    img = cStringIO.StringIO()
    image_on_web = urllib.urlopen(img_url)
    while True:
        buf = image_on_web.read(65536)
        if len(buf) == 0:
            break
        img.write(buf)
    image_on_web.close()
    return InMemoryUploadedFile(file=img,
            field_name= field_name, name=filename,
            content_type="image/jpeg", size=img.tell(), charset=None)

Per davallar-nos la imatge farem servir la llibreria urllib. Passant-li una url es capaç de llegir-la i donar-nos el contingut. Com que la memòria de moment no és un requeriment, el que farem serà deixar el contingut dins un buffer que es comporta a tots els efectes com un fitxer. Feis una ullada al duck typing per saber com és això possible.

InMemoryUploadedFile espera un fitxer (d'aquí el truc de fer servir StringIO), el nom del fitxer, el content_type, el tamany i el charset, així com el nom del camp.

De fet, però ho podem fer una mica més senzill, ja que no necessitam el nom del camp. Django té una altra classe que fa el cid més senzill, és el SimpleUploadedFile de manera que el codi queda un poc més net:

def download_photo_simple(self, img_url, filename):
    img = cStringIO.StringIO()
    image_on_web = urllib.urlopen(img_url)
    while True:
        buf = image_on_web.read(65536)
        if len(buf) == 0:
            break
        img.write(buf)
    image_on_web.close()
    return SimpleUploadedFile(content=img.getvalue(),
            name=filename,
            content_type="image/jpeg")

Però encara així és molta línea per a tan poca cosa. Em feia ganes provar una llibreria que promet simplificar el procés d'accés als recursos web. La llibreria requests. Teniu en compte que el codi amb urllib encara es complicaria més en una aplicació real, ja que convé controlar les excepcions que hi pot haver. El tema està força ben explicat a l'apunt urllib2 - The Missing Manual, així que no m'estendré més.

L'avantatge de requests és que ens permet abstreure'ns d'aquests excepcions i tractar únicament amb codis d'estat web. Així el que ens interessarà és obtenir la imatge si el codi és 200 (tot bé) o retornar un None si hi ha problemes. Així el codi es simplifica encara més.

def download_more_simple(self, img_url, filename):
    r = requests.get(img_url)
    if r.status_code == 200:
        return SimpleUploadedFile(content=r.content,
            name=filename,
            content_type=r.headers.get('content-type'))
    else:
        return None

Amb això, crear una instància de NoticiasPortada pot quedar com

noticia = NoticiasPortada(
    identificador = id,
    texto = texto,
    titular = titular,
    thumbnail = self.download_more_simple(src, 'thumb-%s.jpg' % id)
                )
 noticia.save()

on id és l'identificador de la notícia i src representa la url de la imatge.

Com ho treim a això? Doncs és una bona pregunta. A la segona part de l'article us ho explico. Fins demà!


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Slicing en Python


Escrit per Paurullan a 17 de July , 2011 a les 1:04 p.m.

Slicing en Python

O com l[::-1] gira una llista.

Resum

En Toni va comentar al darrer creantbits que es pot girar un string fent cadena[::-1]. Vos puc adelantar que no sols és la forma més clara pythonica d'escriurer-ho sinó que aquesta és la més ràpida. En aquest petit apunt exposarem els usos i perquè dels slices.

Què són els slices

Un slicing és el procés de seleccionar un rang d'elements d'un objecte de tipus seqüència. Aquest subconjunt pot ser creat de vàries maneres però la gràcia és que un cop generat es converteix en un objecte més i podem manipular-lo de manera tradicional. Cal recordar que els iteradors en python s'indexen des de zero i que podem usar un signe negatiu per començar a contar des del darrera. També és important fixar-se que els límits inferiors són tancats i els superiors oberts.

La sintaxi és:

iterable[inici:final:passa]   # la gramàtica
iterable[0]                   # el primer element
iterable[-1]                  # el darrer element
iterable[-3:]                 # els darrers tres elements
iterable[:-3]                 # fins als darrers tres
iterable[::2]                 # de dos en dos
iterable[::-4]                # de quatre en quatre des del darrera

Exemples

D'aquesta manera podem mostrar que:

# range per defecte fa de 0 fins al l'entorn obert
In [1]: l = range(10)
In [2]: l
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Des del tercer fins al quart no inclòs (sols el tercer)
In [3]: l[2:3]
Out[3]: [2]

# Des del tercer fins al cinquè
In [4]: l[2:5]
Out[4]: [2, 3, 4]

# Des del segon fins a l'antepenúltim
In [5]: l[1:-2]
Out[5]: [1, 2, 3, 4, 5, 6, 7]

# De dos en dos
In [6]: l[::2]
Out[6]: [0, 2, 4, 6, 8]

# Des dos en dos començant pel darrera
In [7]: l[::-2]
Out[7]: [9, 7, 5, 3, 1]

# Des del segon fins a l'octau de tres en tres
In [8]: l[1:7:3]
Out[8]: [1, 4]

I no cal olvidar que les cadenes de text són al cap i a la fi llistes de caràcters, per la qual cosa podem fer:

In [1]: s = "Cadena de text"

# Inverteix la cadena
In [2]: s[::-1]
Out[2]: 'txet ed anedaC'

Conclusions

Hem vist que els slices són una ràpida i còmode manera de manipular objectes iterables, podent fer operacions comunes com invertir llistes o extreure elements pegant bots de tres en tres de manera molt simple. A més de ser la forma més pythonica és especialment lleugera i cal tenir-los en compte pel dia a dia.

Referències

http://docs.python.org/whatsnew/2.3.html#extended-slices

http://docs.python.org/reference/expressions.html#slicings

http://stackoverflow.com/questions/509211/good-primer-for-python-slice-notation


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Creantbits un 15 de juliol


Escrit per Aaloy a 16 de July , 2011 a les 11 a.m.

Ahir divendres 15 hi hagué una nova edició del creantbits. Aquesta vegada enlloc de que la inscripció es fes en un comentari al blog, ho ferem amb una aplicació creada ad-hoc i que serví per experimentar amb un hosting de Python. El hosting va caure un pic en el procés d'inscripció (després de tot encara està en beta), però la gent d'Eldarion va respondre i en poques hores estava una altra vegada operatiu.

L'aplicació en si crec que ha respost bastant bé, tot i estar feta en quatre potades. Ha permés a la gent que s'havia inscrit prest i després no ha pogut venir, fer-ho saber ràpidament i comunicar-ho al següent de la llista d'espera. Tot d'una que tengui una estona més miraré de documentar l'aplicació (que el codi ja hi està) i posar-ho a bitbucket per tal que si hi ha més gent que s'animi entre tots poguem fer un bon programa de gestió d'events.

De l'event en sí poca cosa a dir, la sala plena d'amics, gent que ja coneixia personalment i gent que he pogut desvirtualitzar per primera vegada. Però la part important és amics, això fa que parlar en públic sigui molt menys estressant i també molt més divertit. Va venir molta gent que fa coses interessants en programari lliure, Python o no, però que té pànic escènic. Encara que ja sabeu no tenc cap problema en parlar de Python hores i hores, també m'agradaria que el Creantbits fos un punt de trobada on els amics s'expliquen tecnologies interessants. Pensau que no xerrau davant un auditori estrany, sinó a un grupet de gent, d'iguals, i allò que per vosaltres pot ser el dia a dia potser sigui una revelació per altra gent. Així que animau-vos, que llevar-se la por escènica és important i res millor que fer-ho entre gent de confiança.

La valoració de les xerrades no seré jo qui les faci, esper que us resultessin interessants. Era la primera xerrada de @morenosan, l'home passava pena per si els nirvis el traïen, però ja vàreu veure que se'n va desfer prou bé. En Juan té moltes coses a dir en el món de la programació i noves tecnologies i esper que ara que ja sap que no passa res, s'animi a preparar-nos altres matèries.

En Bernat també tenia els seus dubtes, al matí va començar a dir que igual si no hi havia temps la conferència de Varnish no era tan important, que a lo millor no calia, ... Però no em vaig deixar convèncer i crec sincerament que va ser una de les exposicions més interessants que hem fet al creantbits.

Ja veis, el que costa més d'un event d'aquest és que la gent perdi la por, però és important fer-ho, perquè personalment estic convençut que en aquesta Illa nostra hi ha molta gent que fa coses molt interessants i que no les valora prou. Contava l'anècdota d'un conegut empresari madrileny que es dedica a fer webs per hotels, anava dient a tort i dret que havien fet un cms que admetia llenguatges no llatins, i això ho deia al 2011, nosaltres mateixos ja havíem fet webs en xinès al 2006, i ja no us cont Juan que estigué 5 anys al Japó. És, però un bon exemple de que potser no li donam la importància que es mereix a fer aquestes coses.

I una altra anècdota de l'event. Ens vàrem deixar els curetes per remenar el sucre. Però potser la millor anècdota de totes va der la de @SebaSj , que ens va dir que no podia venir perquè la filla estava en camí. Hem estat molt contents de saber que n'Helena ha fet gairebé tres kilos i que han passat bona nit. L'enhorabona!

El proper creantbis no sé quan serà. Depèn de la feina i de les ganes de la gent, però al manco ja sabem que n'Antònia està disposta a parlar-nos de Python i càlcul numèric, que potser en Xesc també s'animarà a fer alguna coseta i que en Pau quan aprovi la pràctica, té moltes ganes de parlar de Haskell.

Una abraçada a tots el que vinguéreu i disculpes al que quedàreu fora. L'aforament de la sala és el que és i per ara no tenim un lloc millor. La sala gran de l'auditòrium estareu d'acord amb mi que imposaria massa a l'hora de parlar. La por escènica és molt menor quan tens la gent propera i els oients estan agafant gominolas!


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL


Redis


Escrit per Aaloy a 26 de June , 2011 a les 10:55 a.m.

Ja fa un grapat de mesos estic fent cosetes amb Redis, una base de dades de les anomenades noSQL, molt semblant en funcionament a Memcached.

Val a dir que a Redis hi vaig arribar a partir de Celery, la utilitat per crear i gestionar tasques per Python i Django. Vaig trobar la combinació Celery més Redis molt bona quan no necessites tota la potència, ni tota la complexitat que et dóna RabbitMQ.

La idea, una vegada hagi finalitzat les proves, és anar substituint Memcached per Redis com a sistema de caché per les aplicacións Django. També hi ha un projecte per fer que les sessions també puguin estar damunt Redis, així que crec que també li tocarà. A més, d'aquesta manera ja tenim una base de dades addicional per fer-la servir quan sigui necessari. Redis ofereix una cosa que Memcached no té, la persistència de la informació.

En el cas de la caché, Redis pot ajudar a solucionar un dels problemes més importants que hi ha quan un fa aplicacions grans, la invalidació de les cachés. Amb memcached la invalidació sovint és un tot o res, és complexa fer que s'eliminini sols una part de la informació si no saps ben bé quines són les claus exactes que s'han fet.

Redis té una velocitat de resposta i suport a la concurrència tan bona o millor que memcached, però a més ens permet fer consultes sobre claus que comencin per algún prefix, o per claus concretes, podem saber quin tems d'expiració té cada clau, veure les claus que hi tenim al sistema, etc.

Això ens dóna tota una nova via per dissenyar el sistema de cachés, on tant l'usuari com el propi sistema poden decidir invalidar la caché en funció de les necessitats de l'aplicació.

Podem eliminar completament el contingut de la base de dades amb una simple comanda, FLUSHDB. Posau-ho per exemple a l'abast de l'administrador de l'aplicació web. Quan fa un canvi important i no vol esperar podem posar-hi aquest link per a que llanci aquesta comanda contra Redis.

Si ho feim servir dins Django podem veure com aquest va generant les claus, així podem decidir millor què invalidam. És a dir, tenim tot el que teníem amb Memcached i un món nou de possibilitats.

A més l'API per Python és molt bona, tant que amb l'iPython i l'API no hi ha pràcticament necessitat d'anar a la pròpia consola de Reids (el redis-cli), per acabar-ho de rematar s'ha millorat molt el parseig de la resposta amb hiredis-py.

En resum, estic gratament sorprès amb aquesta eina i us animo a fer-li una ullada.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Un creant bits d'estiu


Escrit per Aaloy a 17 de June , 2011 a les 8:09 p.m.

Fa just una estoneta he fet l'anunci per Twitter de que ens han confirmat des del Parc Bit que podem disposar de la sala de formacio pel #creantbits el dia 15 de juliol a les 16:00.

Aprofitant que és estiu i fa molta calor, i la gent tampoc està per xerrades molt llargues, hem pensat que estaria bé fer xerrades curtes i d'un bon grapat de temes.

La primera per anar fent boca serà de Python. Una introducció als nousvinguts en aquest llenguatge que serveixi per perdre-li la por. En una horeta es pot veure bastant bé el llenguatge i tenir un idea suficient per poder seguir la resta de xerrades, o al manco per adonar-se de la potència que s'amaga darrera el llenguatges.

Després en @morenosan ens parlarà mitja horeta de South i de les seves possibilitats quan fas desenvolupaments amb Django. South ens permet fer modificacions a la nostra estructura de base de dades, de manera que passar de desenvolupament a preproducció i després a producció sigui poc traumàtic, de fet per a que sigui gairebé automàtic. Per la gent que no ha fet servir mai una eina com aquesta segur que serà una revel·lació.

Mirarem que @bercab perdi la por escènica i ens faci una introducció a Varnish. Un sistema de caché del més potent que hi ha i que ben duit ens permet aguantar una quantitat ingent de visites. Estam parlant de 15.000 o més peticions per segon en servidors de gama baixa. Com tot sistema té avantatges i inconvenients. No es tractarà de dir com muntar Varnish, sinó de que la gent que ho vulgui montar per les seves webs tengui sàpiga amb què juga.

Com heu pogut veure en alguns apunts m'agrada molt Celery, així que m'haureu d'aguantar un poc presentant-vos aquesta llibreria. Parlaré de quan i com es pot fer servir, de diferents configuracions que es poden emprar i d'escalabilitat.

Estam mirant de tancar una altra ponència, això de parlar en públic, encara que sigui entre amics pareix que imposa bastant. Ja veurem, si no un poc de trobada social que tampoc no està malament.

No es tracta de fer un curs o una classe magistral, es tracta de perdre la por a la tecnologia, de veure llibreries i programes que potser us sonaran i potser no, però sobretot es tracta de conèixer-nos i trobar-nos de tant en tant.

I ja sabeu que si algú està interessat en preparar-se un tema, doncs a aquesta mateixa trobada o per la propera. Quan més rotació hi pugui haver en les presentacions millor, que si no sempre parlam els mateixos, i no em queix, el que és complicat és aturar-me una vegada he començat a xerrar. :-P

En aquesta ocasió i aprofitant l'entorn de Gondor, he creat una petita aplicació per les inscripcions, està en beta i serà la primera prova baix foc real que es fa.

Inscripcions: http://creantbits.com

Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django


Avaluant Gondor


Escrit per Aaloy a 15 de June , 2011 a les 9:08 p.m.

Estic a la segona tongada de beta-testers de Gondor un hosting per Django montat al núvol desenvolupat per la gent d'Eldarion.

L'aplicació vol simplificar la manera de desplegar les aplicacions Django a força de forçar una determinada configuració. És a dir, es simplifica el desplegament, però sempre que juguem amb les cartes que ens donen, que he de dir que no són dolentes.

La posada en marxa és senzilla, el programa que fa la interacció amb Gondor (una vegada t'has donat d'alta a la web) és un paquet que està al PyPi, així que és sols cosa de pip install gondor i gairebé ja pots començar a fer-hi feina.

Per provar he fet servir un codi que ja tenia mig fet i que ara s'ha convertit en la web d'inscripcions de creantbits. La conversió d'una aplicació tal com la desplegam a APSL habitualment a una aplicació capaç de ser desplegada amb Gondor és prou senzilla, i està ben explicada a la documentació. Si més no però, convé fer uns petits avisos per si algú també s'hi troba:

  1. El teu projecte ha de ser gestionat per Git o Mercurial. Tant fa si no hi ha un repositori remot, però s'ha de tenir en compte que el desplegament es fa damunt una versió commitada del codi.

  2. S'ha de crear una carpeta anomenada requirements on hi hagi un arxiu anomenat requirements.txt amb els requeriments de l'aplicació amb un fortat llegible per pip. No s'hi valen paquets que s'hagin de compilar llevat de lxml i PIL, la connexió a BD, Postgres per més senyes ja ens la donen ells.

  3. S'ha de crear una carpeta deploy amb la connexió WSGI. S'ha d'anar alerta amb els paths. En el meu cas no m'agrada tenir dependències del nom del projecte a les aplicacions ni a les urls, així que he modificat el WSGI (per cert, sabeu que es llegeix com a Whisky?).

  4. Alerta amb els STATIC_URL i MEDIA_URL Gondor he comprovat que dóna menys problemes si es fan servir les convencios d'static files, així que una adaptació que s'ha de fer a les aplicacions és substituir a les plantilles els MEDIA_URL per STATIC_URL (i configura el settings) i posar el contingut estàtic lligat a una aplicació per a que funcioni el collectstatic.

Amb això i amb un parell (dues o tres) de proves la primera versió de l'aplicació ja funcionava, els que em seguiu per Twitter ja ho haureu notat, ja que he estat donat la murga amb això els darrers dies.

Gondor desplega l'aplicació creant un tar.gz del master del nostre repositori (el tip en terminologia Mercurial) i l'envia al seus servidors. Allà es desplega, es creea un virtualenv, s'instal·len les dependències i es crea la BD o es migra si és una actualització.

Això fa que el procés d'actualització, fins i tot per una modificació a una plantilla sigui un tant lent, ja que les velocitats de pujada de les ADSL són de vergonya. Estic mal acostumat als servidors dedicats que tenim, bé directament o amb fabric les actualitzacions ens duen segons. Està clar que la gent de Gondor ha tingut que arribar a un compromís entre velocitat de desplegament i estabilitat de l'entorn. Els vaig fer arribar aquesta "queixa" i em contestaren que estan treballant en un mètode diferencial per fer les actualitzacions, la qual cosa pareix que pot millorar molt el conjunt.

Encara no tenen a l'abast Celery, el sistema de cues, que és força important ja que no es té accés al cron del servidor i sovint són necessàries tasques periòdiques, com per exemple per netejar les sessions caducades.

Llevat d'això, la plataforma per ara és molt estable, quan hi ha errors les missatges se t'envien a la pròpia consola i quan fas l'actualització surt una plana 503 ben informativa.

Quan feia les proves vaig enviar uns quants missatges per aclarir conceptes o suggerir millores i em contestaren ràpidament, ja mitjançant e-mail o pel Twitter d'Eldarion.

Encar no sé el cost de la plataforma, ni les funcionalitats que tindrà en el futur. Però les impressions inicials són bones. Per molts dels projectes que feim Gondor no és una alternativa, però per projectes tipus la web del creantbits i semblants va prou bé.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Geany, editor per a programació


Escrit per Aaloy a 25 de April , 2011 a les 12:23 p.m.

Aquestes festes he estat mirant-me l'editor Geany per veure com responia a les meves necessitats de tenir un editor per a programació fonamentalment Python i Django que fos a la vegada potent i que tingués baixos consums de memòria.

En un editor per a programació crec que és important seguir el principi de Pareto o regla del 80-20, és a dir, vull un editor que amb el 20% de comun de recursos tengui el 80% de les funcionalitats d'un IDE com Eclipse+Pydev o PyCharm per exemple.

Avui en dia el meu editor de capçalera és Vim/Gvim totalment personalitzat amb la configuració que podeu trobar a trespams-vim, crec que és una bona configuració, però tot i la potència de Vim tanmateix no faig servir ni un 20% de les possibilitats que m'ofereix l'editor. El que sí que crec que és important és dominar-lo, ja que de tant en tant convé editar via ssh i saber fer anar vim amb condicions és un avantatge.

La cosa doncs és que vaig decidir donar una altra ullada a Geany, ja que vaig veure a la web que havien llançat una nova versió, la 0.2 més moderna que la que tenia ja instal·lada a Ubuntu. Una cosa que a mi en va molt bé és el ressaltat de plantilles per Django (cosa que Gvim té), així que per a ser justs, el primer que vaig fer va ser mirar si Geany també ho tenia: requereix una mica de configuració, però sí, us explic:

Cercant per Google vaig arribar a la plana de drevans, on explica com podem activar el ressaltat, és prou senzill, basta fer

cp /usr/share/geany/filetypes.html  ~/.config/geany/filedefs/

editar l'arxiu que acabam de copiar i cercar la secció lexer_properties i afegir

lexer.html.django=1

Amb això ja tenim colorejat per la sintaxi de plantilles Django. Una cosa ja feta.

Superat el primer obstacle ja sols és cosa de veure quin conjunt de característiques té Geany comparat amb altres editors o IDEs i quines no té, i el grau d'importància relativa que tenen per mi. Deix de banda coses que podem donar per pressuposades a un editor modern: ressaltat de sintaxi per múltiples llenguatges (fonamental ressaltat de javascript dins html) i edició de múltiples documents.

  1. Gestió de projectes. És una característica que classificaria com a important. Estalvia molt de temps que l'entorn et situi directament al directori i mantengui la llista dels darrers arxius que es van obrir al projecte.

  2. Treball amb UTF-8 i format Unix. La nostra configuració de feina per defecte és UTF-8, quatre espais per tabular i salts de línia en format Unix. Si ja no té això no me mir l'editor, així que és una característica obligatòria.

  3. Autocompletat. Realment no necessit que tengui un autocompletat de llibreries (al cap i a la fi Python no és Java) però si ho té millor, i sobretot el que va molt bé és tenir un autocompletat basat en que un ja ha escrit en el document, ja que evita molts errors tipogràfics.

  4. Reasaltat de sintaxi per HTML i Javascript dins el mateix document El ressaltat de sintaxi va molt bé a l'hora de programar, pots detectar errors sols pel colorejat de l'editor, per això és important que a l'hora d'editar HTML on cada cop és més comú que hi pugui haver javascript, el ressaltat sigui prou inteligent per detectar que estic a la part javascript del codi i adapti també el ressaltat.

  5. Integració amb un comprovador de sintaxi per Python com pylint o pyflakes i pep8. Es pot fer la comprovació per línia de comandaments, però és interessant no tenir que sortir de l'entorn. És doncs una característica interessant però no fonamental.

  6. Parseig de símblos Va molt bé que un editor per a programació sigui capaç de parsejar el codi font i et mostri les classes i funcions que tens definides dins el document, estalvia molta feina a l'hora de navegar pel codi o trobar el que t'interessa. No és una característica fonamental, però pot ajudar a decidir.

  7. Maneig de bookmarks Cada cop faig servir més aquesta característica a Vim ja que em permet navegar ràpdament entre distintes seccions del codi. No és fonamental però també molt convenient.

  8. Cerca potent De les millors que he vist són les d'Eclipse i Netbeans que et permeten cercar per tot el projecte.

  9. Baix consum de recursos. Per mi és important poder fer servir l'editor en qualsevol dels equips que tinc. Fer feina sempre amb el mateix editor fa que a poc a poc un s'ho vaig fent seu i n'aprofiti millor les funcionalitats. Si triam un editor gràfic hem de tenir en compte que a més convindrà dominar un poc les quatre coses d'un editor en moda consola com Vim. Si l'entorn consumeix molts recursos ens podem trobar que no hi hagi prou memòria per engegar altres aplicacions que necessitam, sobretot en equips més vells. Vaig deixar de fer servir Eclipse i després Netbeans per aquest motiu. Per fer modificacions xorres necessitava esperar gairebé un minut per posar tot l'entorn en marxa. Netbeans a més ha deixat de suportar Python, així que ho he descartat tot i les darrers millores.

  10. Integració amb control de versions Interessant però com en el cas del comprovador de sintaxi o precompilador és quelcom que sovint és més ràpid fer per línia de comandaments.

La resta de característiques que pot tenir un IDE poden estar molt bé i potser un les fa servir un cop o dos al llarg d'un projecte, però per mi i amb el tipus de projectes que feim, crec que no compensen el tenir que carregar amb un entorn feixuc.

Geany compleix amb gairebé totes les funcionalitats que he exposat aquí, fins i tot l'autocompletat va un poc més enllà i es capaç d'autocompletar a partir de les llibreries Python. La integració amb Pyflakes y Pep8 es pot fer i els missatges d'error o avís apareixen a la finestra de missatges. Tot i això he de dir que li faltaria ponder fer clic damunt un missatge d'error i anar directament a la línia, per això s'ha de configurar un poc, anirem a la secció Munta i a on diu "Error regular expression" posarem (.+):([0-9]+):[0-9]+ això ens servirà tant per pyflakes com per pep8, de fent a la meva configuració actual tinc com a compilador pyflakes "%f" associa a la tecla F8 i associat a F9 pep8 "%f" per saber si es compleixen les convencions de codi.

El que he trobat molt úlil a Geany és que té una secció on pots veure tots els documents que tens oberts classificats en la carpeta on es troben. Quan fas feina amb Django on tots els models són models.py et permet navegar ràpidament pels arxius que tens oberts.

Genay també ve amb un conjunt de plugins per estendre la funcionalitat de l'editor. El més interessant que he trobat és un formatejador d'XML, ja que m'estalvia tenir que fer servir un altre programa i realment no carrega gens l'editor.

Geany té també la possibilitat de definir plantilles de codi, com entorns molt més grans i que és una de les característiques que m'agraden més de Vim. D'entrada el conjunt de plantilles que duu per defecte són molt poques, però és molt bo de fer crear-ne més.

En conclusió, Geany és un editor potent, senzill de fer anar i amb un consum de recursos de màquina realment petits. Me pareix que serà el meu proper editor de capçalera.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Celery, Redis and Django


Escrit per Aaloy a 03 de April , 2011 a les 9:46 p.m.

Disclaimer: This is my first English post is a free translation of the original catalan post

In previous posts have written about Celery and Django Celery, a system to manage queues and tasks in Python and Django.

Celery in its documentation recommends RabbitMQ as message broker, that is, as the application that receives and distributes the tasks that the application sends between the different workers we have configured in our system.

Once the worker has done the task it leaves the result (if we have configured to do it) to the results backend, usually it is the same one as the message broker, that is RabbitMQ acts as a message broker and as a result backend.

The architecture of Celery is very powerful in the sense it allows us to scale up and down and replace the parts we need to configure the application to our needs. So we could have applications that needs some sort of message or task distribution, but they don't need to deal with the complexity nor the system requirements of RabbitMQ. With Celery we can even use a database as a message broker where could save the results, we can replace the serialization routines, the results storage. So, although in the documentations we have a prefered configuration we can change it the needs of our application.

In this post I'll try to present is a configuration that fits in the middle of the complexity of a RabbitMQ solution but enough powerful to fit most application needs.

What's the problem we'll try to solve?

We have an application that we want to run in an small server or in a shared server that needs some sort os distributed tasks or periodic task than we want to manage inside the application itself. We would like:

  • Minimum complexity to install and configure the task system.
  • We'd like not to have a dedicated broker.
  • We'd like to monitor what's happening in our application
  • We'd like to manage our system easily.
  • We'd like to debug our application and run everything on a local server before run the application using a distributed task configuration.
  • We'd like our task broker could have very low memory requirements

We can imagine lots of scenarios in what that requirements would fit, a news aggregation, an e-comerce application that needs to send the invoices, an small document management system that makes some sort of format translation. That is, systems that need a small response time to the user and that could make the heavy task in an asynchronous way, where the reliability of the tasks system is not critical for the application

For that kind of applications Celery with RabbitMQ is overkill, so we're going to diet it a bit

The broker

We want the distribution of tasks to be powerful and flexible, but without the complexity of RabbitMQ. So what will be do is install Redis , a NoSQL a database that works in a similar way memcached does.

Redis is very fast and comsumes few machine resources, allows the persistence of periodic data and the application is generic enough to be used in our applications in addition to task management. A [presentation by Simon Wilson] (http://simonwillison.net/static/2010/redis-tutorial/) summarizes very well the possibilities of this database.

Redis has, however, and important requirement that we must know: in its standard configuration requires that all data has to fit in memory and that periodically synchroniZes the changes to disk. So we must monitor our application to be sure that Redis does not grow without notice consuming all the available memory.

Installing Redis

Readis is present in major Linux distribution, and in Debian based distros is enough to type

sudo apt-get install redis-server

as we're going to use Redis in Celery we must install also the Python API

pip install redis

inside our virtualenv (I suppose everybody is using virtualenv ...)

In a brand new installation in a Ubuntu 10.10 redis consumes 3271B of virtual memory and 1516B of resident memory in a single process.

In a production environment for sure we would like to configure some parameters:

  • bind, to link the redis instance to an IP
  • loglevel is verbose in the default configuration, in production notice or warning would be enough.

The configuration file for redis is in /etc/redis/redis.conf in the Ubuntu, is extensively documented to allow us to adapt it to our needs.

The results storage

As mentioned Celery also allows us to define where to store our data. Redis is a general purpose database, so in addition to the tasks broker we can use it to save the results of the tasks. As pointed before, we have to monotor Redis if we plan to store lot of data o if we store big results. Redis mantains all the database in memory.

Usually in a task/queue system we want to keep the results a just the time enought to see that everything is going well and then we don't need the results anymore.. That is, the results do not necessarily have to remain in the database, the amount of time we need to keep the results in the database would greatly depend on our application.

Let me explai myself. We use Celery in a B2C application to update the information we have about the hotels. We launch the update information periodicaly to update a the information and each task is able to run another taks. Once the information is received the information is processed. So the results just needs to be in the database the time that a worker needs to process it, after that we can delete it. As the process is quite fast is much simpler to make the results expire in 60 seconds than to write the code to delete it.

If we're need to create a task to send an invoice we do not need to save the invoice in Redis, we just need to update our database to mark the invoice as sent once the worker has finished the pdf generation and the mail is sent.

So if we want to mantain our low memory requirements we have to tune our application to not store a lot of information in the Redis database.

Using Redis as a broker and as a database makes us to reach our objective of reusing the technology, but we can use Redis as a cache backend for Django and to [store sessions]((https://bitbucket.org/dpaccoud/django-redis-sessions/src).

Our settings.py

First at all in our INSTALLED_APPLICATIONS we have to add djcelery and now we have to configure Redis as a broker and database backend.

BROKER_HOST = "192.168.1.33"
BROKER_BACKEND="redis"
REDIS_PORT=6379
REDIS_HOST = "192.168.1.33"
BROKER_USER = ""
BROKER_PASSWORD =""
BROKER_VHOST = "0"
REDIS_DB = 0
REDIS_CONNECT_RETRY = True
CELERY_SEND_EVENTS=True
CELERY_RESULT_BACKEND='redis'
CELERY_TASK_RESULT_EXPIRES =  10
CELERYBEAT_SCHEDULER="djcelery.schedulers.DatabaseScheduler"

import djcelery
djcelery.setup_loader()

192.168.1.2 is the virtual image in which I have installed a fresh Ubuntu and that runs Redis, as in this post I'd like to emulate a simple production environment with two servers. As you can see I have no password protection and Redis runs in its default port.

Note that on BROKER_VHOST we have to configure the database Redis will use for the broker system. It can be the same one as the REDIS_DB but we could choose to have the results and the task communication in different databases. CELERYBEAT_SCHEDULER CELERY_TASK_RESULT_EXPIRES is just 10 seconds, time enough for our purposes. CELERYBEAT_SCHEDULER is configured to allow us to create periodic task from our Django application. As this needs new database tables, we would need to run syncdb to create the tables that the scheduler needs.

Development mode

On development on of the first goals is to be sure everything works properly, so we don't need the noise that the broker and storage puts on our development process. Celery has a special configuration

CELERY_ALWAYS_EAGER=True

so our application would not use nor the worker neither the broker and is executed as a common application, just invokes the task in a synchronous way.

Lets start the workers

When you start with Celery it's important to have a global vision about what's happening in our application. I have found that terminator is a good tool to run our console commands, splitting our terminals in order to see what's happening.

So lets open a console in our application environment and run

python manage.py celeryd -E -B --loglevel=INFO -n w1.d820

This will run a worker, configured to run the default number of processors, which depends on the number of CPUs available on our server. We have configured the worker to send monitoring signals (-S) and to run an additional process to deal with the periodic tasks (-B).

It's important to remark that just one worker can have the -B parameter, so perhaps is better to make this fact more visible and run the periodic task process using a dedicated command

python manage.py celerybeat --loglevel=INFO

Running celerybeat as a standalone process it will inform us about its configuration

[2011-04-03 11:16:46,808: WARNING/MainProcess] celerybeat v2.2.5 is starting.
[2011-04-03 11:16:46,863: WARNING/MainProcess] __    -    ... __   -        _
Configuration ->
    . broker -> redis://@192.168.1.33:6379/0
    . loader -> djcelery.loaders.DjangoLoader
        . scheduler -> djcelery.schedulers.DatabaseScheduler

As we want to monitor the tasks and have more than just one worker is important to name them. This can be done with the -n parameter. I like to add the worker number and the server name. In the example the name of my laptop.

Run a second worker is as easy as:

python manage.py celeryd -E --loglevel=INFO -n w2.d820

 -------------- celery@w2.d820 v2.2.5
---- **** -----
--- * ***  * -- [Configuration]
-- * - **** ---   . broker:      redis://@192.168.1.33:6379/0
- ** ----------   . loader:      djcelery.loaders.DjangoLoader
- ** ----------   . logfile:     [stderr]@WARNING
- ** ----------   . concurrency: 2
- ** ----------   . events:      ON
- *** --- * ---   . beat:        OFF
-- ******* ----
--- ***** ----- [Queues]
--------------   . celery:      exchange:celery (direct) binding:celery

As we can see we have not add the -B parameter and Celery informs us that the beat process is off.

We can increase or decrease the number of default processes that the worker is going to star with the --concurrency parameter. The final number is a matter to test and see.

python manage.py celeryd -E --concurrency=10 -n w3.d820

Monitoring: what's happening in my application?

If we have added logs on our applications in each worker we can check and see the output, but perhaps we have no logs o our workers could be distributed in different servers. Celery provides us with some monitoring that are nice to know. To use such tools the first step is to start the monitoring service:

python manage.py celerymon

celerymon 2.2.5 is starting.
Configuration ->
    . broker -> redis://@192.168.1.33:6379/0
    . webserver -> http://localhost:8989
celerymon has started.

As we can see Celery has started a server on port 8989. We can connect to that server and see the registered workers and tasks. It's some sort of raw information but it could be enough

[{"heartbeats": [1301824037.784225],"hostname": "w1.d820"}, 
{"heartbeats": [1301824018.90294], "hostname": "w2.d820"}]

As we have configured the DatabaseScheduler we could see the tasks in the Django application itself, but there is another tool on colole mode that give us nearly realtime information, the celeryev

With python manage.py celeryev we will start an console application that will show us what tasks are being processed, we can see the result of each tasks and even revoke a task. If we want more control about the monitoring tools Celery provides an API to get the information, and of course you can look at the source code for celeryev.

It's important to monitor also the Redis server

sudo tail -n 100 -f /var/log/redis/redis-server.log

we'll see what's happening on the Redis side,

==> /var/log/redis/redis-server.log <==
[677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38923
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38924
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38925

Redis provides also a client console, redis-cli that we could use to get more information and make a lot of management task. Some useful commands are:

  • KEYS * shows us the active keys
  • DBSIZE informs us about the size of the active database
  • INFO give us a lot of information about our database, it's really useful to check the memory consumption

    process_id:677
    uptime_in_seconds:5349
    uptime_in_days:0
    connected_clients:14
    connected_slaves:0
    blocked_clients:4
    used_memory:1551228
    used_memory_human:1.48M
    changes_since_last_save:1111
    bgsave_in_progress:0
    last_save_time:1301824662
    bgrewriteaof_in_progress:0
    total_connections_received:509
    total_commands_processed:7060
    expired_keys:0
    hash_max_zipmap_entries:64
    hash_max_zipmap_value:512
    pubsub_channels:1
    pubsub_patterns:0
    vm_enabled:0
    role:master
    db0:keys=8,expires=0
  • FLUSHDB cleans all the database removing all the keys

  • MONITOR shows us in real time what's happening, what commands are being executed, and the keys and information that is stored in the database.

To summarize

With Django, Celery and Redis we have a simple task distribution, scalable and with very small server requirements.

We can use Redis as a broker, as as data store and in other tasks of our Django application: as another database, as a session database, as a cache server.

If we want to work using tasks to split the work we have to

  • Develop our application thinking in tasks and asynchronous processes.
  • Install and configure Redis
  • Run the workers
  • Run celerybeat if we have periodic tasks
  • Run the monitor

And of course we have to monitor all the application. Enjoy!


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Celery i Redis


Escrit per Aaloy a 03 de April , 2011 a les 12:11 p.m.

Link to Celery i Redis

En anteriors apunts he parlat ja de Celery i de Django Celery, un sistema per a la gestió de cues i tasques per Python i Django.

Celery a la seva documentació recomana RabbitMQ com a gestor de missatgeria, és a dir, com a aplicació que reb i distribueix les tasques que li envia l'aplicació entre els diferents worker que tenguem al nostre sistema.

Una vegada el worker ha realitzat la tasca deixa el resultat (si així ho hem indicat) al contenidor de resultats, que normalment serà el mateix que el broker de missatgeria, és a dir RabbitMQ.

L'arquitectura de Celery és molt potent en tant que ens permet escalar cap a baix i substituir peces segons necessitem. Així per aplicacions que necessitin d'un sistema de distribució de tasques però no de la complexitat de RabbitMQ podem utilitzar altres sistemes de notificacions, fins i tot fer servir una base de dades. On es guarden els resultats o com es serialitzen els missatges també es pot canviar. En definitiva, encara que hi hagi una configuració recomanada per a entorns que necessitin de molta potència en el sistema de distribució de tasques i coes, podem personalitzar Celery al nostre gust i a les nostres necessitats.

En aquest article el que presentaré és una configuració a mig camí entre el que seria la configuració més complexa de Celery i una configuració simple basada sols en base de dades.

Plantejant el problema

Tenim doncs una aplicació que no és molt gran, però a la que aniria molt bé tenir un sistema de distribució de tasques.

  • Volem que la instal·lació i configuració sigui senzilla.
  • El que instal·lem si pot ser hauria de poder aprofitar-se per quelcom més que no el sistema de tasques.
  • Volem poder monitoritzar el que està passant.
  • Volem poder gestionar fàcilment l'entorn
  • Hem de poder depurar fàcilment el que està passant i assegurar-nos que l'aplicació funciona sense tenir que muntar tot l'entorn.
  • El gestor de tasques ha de consumir pocs recursos i ser bo de controlar.
  • El gestor de tasques ha de permetre l'execució de tasques periòdiques.
  • Volem poder tenir accés als resultats.

Podem imaginar molts escenaris en que això s'ha de complir, per exemple un agregador de notícies, en un sistema de e-comerce l'enviament de factures, en un petit gestor documental la conversió de formats, ... És a dir, sistemes en els que volem que la resposta cap a l'usuari final sigui el més ràpida possible i que la feina feixuga no necessàriament es tengui que fer de manera síncrona, però on tampoc passa res si el sistema de missatgeria cau i perd alguna tasca, simplement es pot tornar a executar.

Amb aquestes condicions la configuració estàndard de Celery i de Django Celery és massa complexa, així que el que farem és aprimar-la un poc.

El broker

Volem que el sistema de distribució de tasques sigui prou potent i flexible, però sense la complexitat de RabbitMQ. Per això el que farem serà instal·lar Redis una base de dades NoSQL molt semblant al que seria un memcached.

Redis té l'avantatge de que és molt ràpid, ocupa molt pocs recursos de màquina, permet la persistència periòdica de les dades i és una aplicació prou genèrica com per poder ser utilitzada en les nostres aplicacions a més de per fer la gestió de les tasques. Una presentació de Simon Wilson crec que resumeix molt bé les possibilitats d'aquesta base de dades

Redis té un emperò important que hem de conèixer, és una base de dades que en la seva configuració estàndard requereix que totes les dades hi càpiguen en memòria i que aquesta es sincronitzi de manera periòdica al disc. Així doncs hem de procurar monitoritzar la nostra aplicació i l'ús que en fa de Redis per a no consumir més memòria de la que ens vulguem permetre.

La instal·lació de Redis

Redis està com a paquet en les principals distribucions, així que per distribucions basades en Debian bastarà fer:

sudo apt-get install redis-server

i com que també l'utilitzarem des de la nostra aplicació convé instal·lar també el l'API de connexió per Python, amb pip mateix

pip install redis

dins el nostre entorn virtual (perquè està clar que feis servir virtualenv a les vostres aplicacions, no?).

En una instal·lació neta dins una màquina virtual Ubuntu redis està ocupant 3271B de memòria virtual i 1516B de memòria resident a un únic procés.

En producció segurament ens interessarà tocar alguns paràmetres de Redis per fer la configuració més segura:

  • bind ens permet lligar redis a una IP concreta.
  • loglevel per defecte està a verbose, a producció amb notice o warning serà suficient.

l'arxiu de configuració de redis es troba a /etc/redis/redis.conf i està molt ben documentat per a poder adaptar-lo a les nostres necessitats.

On guardam els resultats?

Com hem dit Celery ens permet definir també on guardar els nostres resultats. Donat que Redis és una base de dades de propòsit general, a més de fer les tasques de missatgeria l'aprofitarem per a que guardi els resultats de les tasques.

Aquí hem de tenir en comptes el que dèiem de Redis, si guardam moltes dades i no pensam en anar fent neteja la memòria de Redis anirà creixent fins a ocupar tot el que tinguem.

Normalment a un sistema de tasques i cues ens interessarà guardar els resultats un temps per veure que tot està anant bé i després ja no els necessitam. Es a dir, els resultats no tenen perquè quedar forçosament dins la base de dades Redis, això ja dependrà de la nostra aplicació.

M'explicaré. Una de les aplicacions on feim servir Celery ens permet actualitzar la informació que tenim d'hotels. Es van llançant les peticions d'actualització i el worker se n'encarrega de baixar-se la informació llançant a la seva vegada una altra tasca i de processar-la una vegada l'ha rebuda. La informació sols necessita estar al magatzem el temps just i necessari per poder monitoritzar que tot va bé i que el worker ha rebut i processat la informació.

En el cas de la generació d'una factura no hem de guardar necessàriament el pdf de la factura dins Redis, basta poder dir que la factura s'ha realitzat correctament o directament suposar que ha anat tot bé i periòdicament tornar a llançar les tasques d'enviament per a tots aquells clients que han sol·licitat la factura i no se'ls ha enviada.

Així doncs utilitzarem també Redis per guardar els resultats i configurarem la nostra aplicació per a que no els guardi per sempre.

Amb això ja hem aconseguit que Redis faci una doble funció, però és que a més podem utilitzar Redis per a guardar les sessions de Django o fer-la servir com a Cache. Així doncs estam aprofitant els recursos que és un dels nostres principals objectius.

La configuració del settings.py

A INSTALLED_APPS hem afegit djcelery i per configurar Redis com a broker i com a magatzem de resultats feim:

BROKER_HOST = "192.168.1.33"
BROKER_BACKEND="redis"
REDIS_PORT=6379
REDIS_HOST = "192.168.1.33"
BROKER_USER = ""
BROKER_PASSWORD =""
BROKER_VHOST = "0"
REDIS_DB = 0
REDIS_CONNECT_RETRY = True
CELERY_SEND_EVENTS=True
CELERY_RESULT_BACKEND='redis'
CELERY_TASK_RESULT_EXPIRES =  10
CELERYBEAT_SCHEDULER="djcelery.schedulers.DatabaseScheduler"

import djcelery
djcelery.setup_loader()

El 192.168.1.33 és la màquina virtual on tenc instal·lat Redis, per tal d'emular un entorn de producció amb dues màquines. El port és el port per defecte i no he protegit redis amb usuari i password.

Important, el BROKER_VHOST és per redis el nom de la base de dades que es farà servir. Per defecte Redis fa servir la base de dades zero (0) però res ens impedeix tenir una base de dades diferent per les tasques i una altra pels resultats.

A CELERY_TASK_RESULT_EXPIRES podem veure com hem posat que els resultat expirin als 10 segons, més que suficient per la configuració de l'aplicació.

CELERYBEAT_SCHEDULER ens permet utilitzar la nostra aplicació d'Django per a gestionar les tasques periòdiques. Necessitarem fer un syncdb per a crear les estructures de dades, però a canvi podrem definir períodes i tasques.

Desenvolupant

En desenvolupament ens interessarà tenir molt present que tenim un sistema de coes i tasques, però realment el que més ens interessa és provar que tot funciona

CELERY_ALWAYS_EAGER=True

Aquesta configuració fa que l'aplicació s'executi com si no tingués coneixement del gestor de tasques, la qual cosa ens permetrà crear i depurar la nostra aplicació com sempre hem fet.

Posant en marxa els workers

Per tal de monitoritzar millor el que feim podem utilitzar una aplicació com terminator, una consola que ens permet agrupar consoles i veure d'una ullada el que està passant. A una d'aquestes consoles farem:

python manage.py celeryd -E -B --loglevel=INFO -n w1.d820

Això posa en marxa un worker, configurat amb un nombre de processos per defecte (2 en el meu cas) i li deim que envii els senyals de monitorització (això és el paràmetre S) i que a més engegui un procés addicional per a que gestioni les tasques periòdiques.

Hem de tenir en compte que sols hi pot haver un procés de gestió de tasques periòdiques, així que o bé es llança des d'un sol worker o bé podem fer servir l'aplicació celerybeat

python manage.py celerybeat --loglevel=INFO

Recordau! O una manera o l'altra però no les dues i mai per duplicat a dos workers.

Si l'executam per separat celerybeat ens informa de que està engegat i de las configuració que farà servir:

[2011-04-03 11:16:46,808: WARNING/MainProcess] celerybeat v2.2.5 is starting.
[2011-04-03 11:16:46,863: WARNING/MainProcess] __    -    ... __   -        _
Configuration ->
    . broker -> redis://@192.168.1.33:6379/0
    . loader -> djcelery.loaders.DjangoLoader
        . scheduler -> djcelery.schedulers.DatabaseScheduler

Com que volem controlar i monitoritzar bé el que passa i potser tenir més d'un worker és convenient posar-los nom, això es fa amb el paràmetre -n, a mi m'agrada donarlos un nom junt amb el nom de la màquina.

Arrancaré un segon worker:

python manage.py celeryd -E --loglevel=INFO -n w2.d820

 -------------- celery@w2.d820 v2.2.5
---- **** -----
--- * ***  * -- [Configuration]
-- * - **** ---   . broker:      redis://@192.168.1.33:6379/0
- ** ----------   . loader:      djcelery.loaders.DjangoLoader
- ** ----------   . logfile:     [stderr]@WARNING
- ** ----------   . concurrency: 2
- ** ----------   . events:      ON
- *** --- * ---   . beat:        OFF
-- ******* ----
--- ***** ----- [Queues]
--------------   . celery:      exchange:celery (direct) binding:celery

Fitxem-nos que en aquest cas ens diu que els events estan activat però que no hi ha el beat (el responsable de les tasques periòdiques actiu per aquest worker).

Podem ampliar o limitar el nombre de processos que arranca cada worker amb el paràmetre --concurrency, la configuració per defecte que depèn del nombre de CPUs disponibles sol anar bé, però sempre dependrà de la nostra aplicació i de les limitacions de recursos que li vulguem donar. Per exemple:

python manage.py celeryd -E --concurrency=10 -n w3.d820

Monitoritzant el que passa

Si a la nostra aplicació hem posat logs a cada worker podem veure el que està passant, però potser no sigui així, o tinguem els workers distribuïts a vàries màquines. Per això Celery ens dóna vàries eines de monitorització.

El primer que hem de fer és engegar el servei de monitorització:

python manage.py celerymon

celerymon 2.2.5 is starting.
Configuration ->
    . broker -> redis://@192.168.1.33:6379/0
    . webserver -> http://localhost:8989
celerymon has started.

Si anam al servidor web que ha engegat Celery, podem veure un resum de les tasques i dels workers. Per exemple, en el nostre cas:

[{"heartbeats": [1301824037.784225],"hostname": "w1.d820"}, 
{"heartbeats": [1301824018.90294], "hostname": "w2.d820"}]

Podem veure que hi ha dos workers actius.

Si hem fet servir el DatabaseScheduler veurem les nostres tasques dins la pròpia base de dades de Django, però a més hi ha una eina de consola d'allò més interessant celeryev

Amb python manage.py celeryev posarem en marxa una consola en la que ens informarà del que està passat, el nombre de tasques que hi ha executant-se i podrem veure informació damunt la tasca o eliminar (revoke) una tasca de la cua.

Si volem saber més coses o fer les nostres pròpies eines de monitorització Celery ens dóna tot una API per fer-ho, així que no estam limitats a les eines de sèrie.

El que ens queda també per monitoritzar és què està passant amb Redis

   sudo tail -n 100 -f /var/log/redis/redis-server.log

ens informarà del que està passant i de les connexions que es realitzen.

==> /var/log/redis/redis-server.log <==
[677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38923
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38924
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Client closed connection
[677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38925

Si volem anar més enllà podem fer servir la consola de Redis, redis-cli

Algunes comandes molt útils:

  • KEYS * - ens monstra les claus que tenim actives.
  • DBSIZE - ens diu com està el tamany de la nostra base de dades
  • INFO - ens dóna tot un conjunt d'informació de com està la nostra bd, és especialment interessant veure i monitoritzar el tamany de la memòria.

    process_id:677 uptime_in_seconds:5349 uptime_in_days:0 connected_clients:14 connected_slaves:0 blocked_clients:4 used_memory:1551228 used_memory_human:1.48M changes_since_last_save:1111 bgsave_in_progress:0 last_save_time:1301824662 bgrewriteaof_in_progress:0 total_connections_received:509 total_commands_processed:7060 expired_keys:0 hash_max_zipmap_entries:64 hash_max_zipmap_value:512 pubsub_channels:1 pubsub_patterns:0 vm_enabled:0 role:master db0:keys=8,expires=0

  • FLUSHDB - per netejar tota la base de dades activa de Redis.

  • MONITOR - ens mostra què està passant, les claus que es generen, els resultats i les sentències que va executant Redis.

Conclusions

Amb Django, Celery i Redis hem aconseguit tenir un sistema de distribució de tasques simple, escalable i amb molts pocs requisits de màquina.

Redis es pot aprofitar tant pel sistema de cues com per la nostra aplicació, obrint-nos tot un món de possibilitats i mantenint baixa la complexitat de tota l'arquitectura.

Posar en marxa tot el sistema implica:

  • Desenvolupar l'aplicació pensant en les tasques
  • Configurar Redis
  • Executar els workers
  • Executar celerybeat per les tasques periòdiques si en tenim
  • Executar el monitor

I òbviament anar monitoritzant-ho tot. Que ho disfruteu!


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django


Fer servir un ORM o no


Escrit per Aaloy a 19 de March , 2011 a les 12:49 p.m.

Arrel del darrer post s'ha establert un nou fil relacionat amb la conveniència o no de fer servir un ORM en les nostres aplicacions quan l'sql directe pot ser més eficient. En Jan Carreres, el "responsable" d'aquest article fa una sèrie d'apreciacions que crec que donen per un nou post, més que res per poder mantenir la discusió en baix el seu propi concepte.

Així doncs acomodeu-vos al seient, que començam ...

Però què dimonis és un ORM

ORM són les sigles de Object Relational Mapping, és a dir, s'anomena ORM a aquella llibreria o procés que fa la conversió d'estructures de dades relacionals cap a estructures de dades orientades a objectes. És a dir, passam de fer feina amb taules i files a fer feina amb classes i instàncies d'aquestes classes. Passam de fer feina de camps a fer feina amb propietats dels objectes.

No hi ha una única aproximació als ORM. Christian Bauer i Gavin King al llibre Hibernate in Action, anomenen quatre característiques comuns a tot ORM:

  • Una API per executar operacions CRUD (Create, Retrieve, Update, Delete) en els objectes.
  • Un llenguatge o una API per especificar les consultes que fan referència a les classes i a les propietats de les classes.
  • Utilitats per especificar les metadades del mapeig entre el model relacional i el model d'objectes.
  • Una tècnica dins l'ORM per crear transaccions, fer assosicacions lazy i altres tipus d'optimitzacions.

A partir d'aqui la implementació de cada ORM potser completament diferent. Els autors, citant a Mark Fussel proposen la classificació dels ORMs en quatre tipus.

  1. Purament Relacionals. L'ORM es construeix al voltant de la base de dades i es específic per a la nostra aplicació. Té l'avantatge que es pot optimitzar molt, però és difícilment portable entre bases de dades i presenta dificultats de manteniment. És típic d'aplicacions que tenen molta lògica dins la BD en forma de procediments amagatzemats.

  2. Mapeig fi. Les entitats són representades com a classes i es mapegen manualment cap a les taules del model relacional. Un exemple d'aquest cas serien les aplicacions Java que fan ús del SQL/JDBC o bé en el cas de Python les que fan servir directament la DB-API de Python.

3. Mapeig mig. L'aplicació es dissenya al voltant del model d'objetes. És a dir, la base de dades és ara un suport per a la persistència dels objectes quan abans era gairebé la peça fonamental de l'aplicació. Aquest tipus d'ORM són especialment interessants quan l'aplicació té un tamany mitjà, amb transaccions complexes i sobretot quan la portabilitat entre bases de dades és un requeriment de l'aplicació.

4. Mapeig complet. S'hi arriba quan l'ORM suporta les característiques més importants i pròpies del model orientat a objectes: composició, herència, polimorfisme, ... i les implementa de manera transparent cap a la base de dades.

Si estam dins el món Java podríem dir que Hibernate està gairebé en el 4 d'aquesta classificació, Ibatis entre el 2 i el 3. En el cas de Python l'ORM de Django estaria començant a entrar en el 4 i SQLAlchemy estaria fregant el nivell d'Hibernate.

Per què fer servir un ORM

Per començar dir que un ORM no és una excusa per no saber SQL. Quan un fa feina amb aplicacions de bases de dades relacionals s'ha de conèixer al manco l'SQL estàndard, tenir molt clars els conceptes de taula, registre, selects, unions, claus primàries, etc. Es dóna per suposat que el tema de la normalització ja ho tenim ben clar.

El que fa un ORM és facilitar-nos la vida. Quan la nostra aplicació ja no es trivial, sinó que fa un ús intensiu de consultes, manipulacions de resultats, insercions, ens trobarem repetint una i una altra vegada el procés de conversió dels nostres objectes cap a sentències SQL. L'ORM fa tota aquesta feina per nosaltres, així que una de les raons més importants per a fer servir un ORM enlloc de sentències SQL a pèl per la nostra aplicació és la productivitat i evitar el DRY al llarg de la nostra aplicació.

Fer servir un ORM implica escriure menys codi nosaltres mateixos i sovint que el codi que hem escrit sigui més bo de llegir. Això es tradueix en una millor mantenibilitat de l'aplicació. Jan diu que si un es dedica a fer apliccions de BD ha de conèixer l'SQL i és veritat, però no té per què tenir a la ment tots els camps que hi ha a la taula. Imaginem-nos un entorn amb molts programadors fent feina a la mateixa aplicació. Aplicació amb moltes taules i amb taules amb molts de registres. Picant SQL a pel és tenim molts números de deixar-nos un camp a l'hora de fer una consulta. L'ORM manté la llista de camps per nosaltres, quan accedim a un objecte el ja se n'encarrega de fer les selects oportunes a la base de dades i obtenir-ne tots els camps.

Encara que hi ha casos en que escriure el codi SQL directament pot significar un guany considerable en el rendiment d'una consulta concreta, el més habitual és que l'ORM que facen servir ja tengui considerat dins la seva estructura les optimitzacions més habituals, de manera que l'sql que es genera pot ser fins i tot més eficient que el que generaríem a mà. Pensar que nosaltres sempre generarem millors sentències SQL és si més no agoserat, i per mi representa una optimització prematura. L'avantatge de productivitat i mantenibilitat que ens dóna l'ORM és el que s'ha de considerar primer. Després sempre hi som a temps d'optimitzar, però els nostres esforços s'han de concentrar en aquelles consultes o accions que torben més temps o que s'executen més sovint. És a dir, l'ORM i l'augment de productivitat que implica ens permet guanyar temps i poder optimitzar allà on interessar realment.

Molt relacionat amb tot això ens trobam la navegabilitat. La navegabilitat que ens permet el model orientat a objectes és d'aquelles coses que xoquen més amb el que ens proporciona el model relacional. Els ORM permeten navegar entre les associacions d'objetes de manera transparent, generant les sentències necessàries i sovint permetent-nos optimitzar aquesta navegabilitat, els select_related de Django n'és un exemple, o les cachés d'Hibernate. Pensem amb el senzill que és posar un objecte dins una plantilla i anar accedint a les seves propietat, anar fins a la propietat que representa una clau forana de la base de dades, fer-ne referència i obtenir-ne les propietats de l'objecte associat. Ara pensem en la quantiatat de codi SQL que ens hem estalviat.

I finalment l'altra gran motiu per fer servir un ORM és el d'independitzar-nos de la base de dades. Podria parèixer que quan un coneix SQL ja pot fer anar qualsevol base de dades, però realment això no és així, cada BD té petites diferències en la implementació de l'SQL que fan que sovint el codi SQL no sigui directament portable entre bases de dades. Un ORM crea una capa d'abstracció entre la nostra aplicació i la base de dades, generant i utilitzant les sentències SQL més adients a cada una.

L'argument que "un tria una BD i l'utilitzes sempre" per la meva experiència passa poc. Una empresa gran pot triar Oracle, però segurament per motius de cost algunes aplicacions les voldrà amb un altra BD. Si comercialitzam una aplicació limintar-nos a una base de dades concreta limita les possibilitats de comercialització. Fent servir un ORM podem desplegar la nostra aplicació contra diferents clients i fins i tot contra diferents versions d'una base de dades.

Resistir la temptació

Quan un comença a programar aplicacions de gestió és difícil resistir la temptació d'optimitzar-ho tot, d'escriure sql a pel optimitzant-ho per la nostra base de dades, és cert, però resistiu!

Si feim sql a pel a poc que l'aplicació creix el procés serà el seguent:

  1. Ens adonarem que tenim sql repartits per tot. Així que farem una refactorització i posarem tot l'sql dins un sols arxiu que contindrà totes les funcions.

  2. Després ens adonarem que aquest arxiu té molt de codi repetit, feim sempre la conversió de taules a objectes i ho refactoritzarem per a no repetir codi.

  3. Després ens adonarem que feim sovint les mateixes consultes i que també es pot refactoritzar.

  4. Seguirem i seguirem refactoritzant....

Al final acabarem amb un ORM del tipus 2 que ens haurà duit una feinada en refactoritzacions i que no té encara els avantatges d'un ORM del tipus 3 ó 4.

Costa resistir la temptació, fa peresa estudiar una llibreria d'ORM, però pensem que probablement el camí que seguirem serà aquest i s'ho paga l'esforç inicial.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Java Python Django PostgreSQL


Solapament d'intervals ara amb Django


Escrit per Aaloy a 12 de March , 2011 a les 12:05 p.m.

Segueixo amb la vena friki, ahir damunt el solapament d'intervals de mode genèric i avui veurem la implementació d'aquesta idea a un model Django.

class Oferta(models.Model):
    """Una oferta que va per rang"""
    nom = models.CharField(max_length=200)
    inici = models.DateField()
    fi = models.DateField()
    activa = models.BooleanField(default=True)

    class Meta:
        verbose_name="Oferta"
        verbose_name_plural="Ofertes"

    def __unicode__(self):
        return self.nom

El model com veis és molt senzill, una oferta pot estar activa o no i pertany sempre a un rang de dates. Queda fora de l'exemple la determinació de si admetem solapament de dates per una oferta, que es fareia de manera semblant al la cerca que ara farem.

La idea és que hem d'aconseguir que amb l'ORM de Django nodelar una consulta del tipus not ((x1<y0) or (y1<x0)).

Els filtres per defecte el que fan es un and, per la qual cosa no ens serveixen directament. El que farem és utilitzar una característica de l'ORM anomenada funció Q que ens permete afegir condicons de filtratge múltiples i fer que vaign per OR enlloc de per AND.

from django.db.models import Q

Ara sols queda fer la consulta. Particularment si són consultes que poden ser susceptibles de fer-se servir a varis llocs o que estan molt relacionades amb el model, m'agrada lligar-les al model amb un manager ad-hoc o bé amb un mètode estàtic, que és el que farme ara. Així:

@staticmethod
def ofertes_entre(pInici, pFi):
    return Oferta.objects.filter(activa=True). \
             exclude(Q(fi__lt=pInici)|Q(inici__gt=pFi)).order_by('-inici')

Fitxem-nos que el NOT de la consulta s'aconsegueix amb l'exclude i que feim servir la fució Q per afegir les dues condicions de comparació dins aquest filtre i les lligam per OR (|).

He afegit un grapat de dades d'exemple, i executat la consulta, que queda com

Oferta.ofertes_entre(f1, f2)

Ho he fet des de línia de comandes, de manera que ara puc veure la query que es genera:

>>from django.db import connection
>>>connection.queries

'sql': u'SELECT "oferta_oferta"."id", "oferta_oferta"."nom",
"oferta_oferta"."inici", "oferta_oferta"."fi", "oferta_oferta"."activa" FROM "oferta_oferta" WHERE ("oferta_oferta"."activa" = True AND NOT (("oferta_oferta"."fi" < 2011-03-20 OR "oferta_oferta"."inici" > 2011-04-20 ))) ORDER BY "oferta_oferta"."inici"

Com m'agrada Django!


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Solapament d'intervals i.e. Interval match


Escrit per Aaloy a 11 de March , 2011 a les 8:32 p.m.

Avui em fa ganes dedicar un parell de línies a tractar un problema força comú que ens trobam amb programació. A saber, la necessitat de saber si dos intervals solapen o no.

El problema es pot presentar en diverses formes, però un cas força típic és el que tenim una taula que conté una data inicial i una data final i ens demanen que a partir d'un rang de dades, trobem tots els registres que tenen dades compreses entre les donades. Un cas molt típic es del de tenir un preu d'oferta que es vàlid per un rang de dades concret.

Anem a concretar el problema. Tenim un producte que presenta ofertes. Pels qui feis feina al sector turístic això us pot sonar als preus d'un hotel, però el problema és prou genèric, com diuen els matemàtics, podem considerar sense pèrdua de generalitat que tenim una sèrie de dies en que tenim ofertes i promocions de productes

dia     descompte 
1-8     10 €      
9-10    20 €
11-25   15 €
25-31    5 €

El nostre client ens demana un rang de dies i volem donar-li quines ofertes hi ha actives per aquests dies

10 -> 20
11 -> 15
12 -> 15

és a dir, li diríem que hi ha dues ofertes dins el rang cercat, una que correspon a l'interval 9-10 i l'altra a l'interval 11-25

El problema es redueix a determinar quins intervals del nostre rang de consulta solapen amb l'interval d'ofertes i mostrar l'oferta corresponent.

El mètode més directe per comprovar si dos intervals es trepitgen és mirar-ne les condicions en que es solapen, suposant que anomenam a l'interval base [x0,x1] i a l'interval de comprovacio [y0, y1] tendríen que es dona solapament en aquests casos

   [yyy]
[xxxxxxxxxx]      cas 1     x0>=y0 and y1<=x1

[yyy]             
  [xxxxxxxx]      cas 2         y1>=x0 and y0<=x0

       [yyyyy]
[xxxxxxxxxx]      cas 3     y0<=x1 and y1>x1

[yyyyyyyyyyy]
    [xxx]         cas 4         x0>=y0 and x1<=y1

[yyyyyyyy]
    [xxxxx]   cas 5         x0<=y1 and y1<x1

   | y0 | y1 | x0 | x1 | cas |
   | 10 | 12 | 1  | 8  | -   |
   | 10 | 12 | 9  | 10 | 3   |
   | 10 | 12 | 11 | 25 | 2   |
   | 10 | 12 | 25 | 31 | -   |

Si ajuntam casos semblants el codi que tindrem és

def overlaps(x,y):
   x0, x1 = x
   y0, y1, preu = y
   return (
      (x0 <= y0 <= x1) or
      (x0 <= y1 <= x1) or
      (y0 <= x0 <= y1) or
      (y0 <= x1 <= y1)
   )

Però el més interessant és que ho podem pensar d'una altra manera, pensem en la inversa, demanem-nos quan els intervals no és solapen

[xxxxxxx] [yyyyyyyyyy]
[yyyyyyy] [xxxxxxxxx]

és a dir, dos intervals no és solapem si x1<y0 o bé y2<x0, podem cercar dons quan no no es solapen els intervals, i per tant tindrem not (x1<y0) or (y1<x0)

def overlaps2(x, y):
   x0, x1 = x
   y0, y1, preu = y
   return not ((x1<y0) or (y1<x0))

divertit, veritat?


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Informàtica Python


Karma


Escrit per Aaloy a 28 de February , 2011 a les 11:51 p.m.

Aquests dies estic bastant engrescat i enfeinat amb projectes molt relacionats amb Python i Django. Per una banda estam definit i creant el que serà la segona fase del projecte Clickote, una aproximació simpàtica a les reserves d'hotel en la qual estam passant molt de gust fent-hi feina. Per una altra banda estam migrant un aplicació web feta inicialment amb ezPublish, un CMS dels de tota la vida, cap a Django.

Clickote és un projecte en el qual m'està agradant molt fer-hi feina perquè a més del projecte i la tecnologia en sí, ve a demostrar que client i proveïdor de serveis no són antagonistes, sinó que són col·laboradors. Amb l'equip de Clickote ens hi sentim molt bé fent-hi feina. Hem topat amb gent que no tan sols coneix el negoci, sinó que a més és conscient de la part tecnològica i de les implicacions en temps i esforç del que demanen, i sempre hem arribat a solucions beneficioses per al projecte.

La migració d'un lloc web des del CMS ezPublish cap a un CMS fet a mida amb Django és també tot un repte i representa també tot una aposta de futur per part del client. A les primeres etapes del canvi ja hem notat un augment del rendiment en temps de renderització de les planes d'un 30% de l'aplicació Django, motivat fonamentalment pel gran grau de control que es té a Django damunt el que es passa a la plantilla i com es passa. ezPublish no és un sistema MVC i quan hi ha lògica complexa pel mig, es queda molt curt comparat amb les possibilitats que et dóna Django. La creació d'un middleware ad-hoc, tags, l'herència de plantilles, la facilitat per crear serveis Ajax, ... Django com a CMS no es posa en el teu camí, ben al contrari, t'ajuda a crear l'aplicació i a optimitzar-la. Quan fas una migració com aquesta entens ben bé la frase del que el bastiment Django és per a "perfeccionistes amb dates d'entrega". Encara ens hi estarem un grapat de setmanes a la migració, en acabar esper poder posar-ho com a exemple de cas d'èxit, o si més no d'aprendre'n de l'experiència.

El cap de setmana passat vaig estar llegint "Maldito Karma" de David Safier, una lectura divertida amb cert tocs d'humor àcid que és llegeix molt bé, com un vi blanc jove fresquet a una tarda calorosa d'estiu.

No sé si serà cosa de la influència del llibre o no, però això de poder fer feia amb el que m'agrada, amb companys i clients com aquests, segur que em fa venir bon karma.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Llibres i revistes Python Django


Creant un web service de proves amb Bottle


Escrit per Aaloy a 06 de February , 2011 a les 11:46 a.m.

Els cap de setmanes mira que intento estar una mica allunyat de la programació, intent llegir, passejar un poc i estar amb la família, però quan arriba el vespre i l'opció és convertir-se ja en un xuclador passiu d'anuncis publicitaris ja no puc més. Amb el temps el meu nivell de tolerància a les pauses publicitàries ha baixat molt. Ja no puc aguantar més de tres minuts, mir el mòbil a veure quines piulades noves hi ha al twitter, llegeixo un poc, ... Però si la pausa ja es fa massa gran ja m'avorreixo, no m'agrada estar avorrit, així que sovint acab davant l'ordinador, a provar idees, ...

Aquest cap de setmana m'ha passat això. Estava provant una web que hem fet que connecta amb un web service i se m'ha passat pel cap emular el servei, creant el meu propi servei web a efectes de test. Si ho pensau bé és una xorrada: es fan les peticions contra el servei real i es guarden les respostes. Després sols hem de substituir el servei real amb el nostre i en funció de les peticions enviar les respostes predefinides que hem capturat.

La primera intenció era fer-ho amb Django, però fet i fet el servei és tan simple que no necessita de tot l'aparell i funcionalitat de Django. Era hora de cercar un micro-framework ·[1]. De micro-frameworks per Python n'hi ha per donar i remanar. En aquest cas vaig optar per provar-ne un que m'agrada força, el bottle, més que res perquè es un sol mòdul, no té dependències externes i està ben documentat. Un hello world és

from bottle import route, run
@route('/:name')
def index(name='World'):
    return '<b>Hello %s!</b>' % name
run(host='localhost', port=8080)

Com que es tracta d'obtenir i tractar peticions xml [2] vaig utilitzar també la llibreria lxml. Així es tracta de capturar una petició xml que es reb via POST i en funció del contingut d'aquest xml enviar una resposta predefinida com a resposta.

La cosa queda així:

#-*- coding: UTF-8 -*-
from bottle import route, run, request
from lxml import etree
import os
app_root = os.path.abspath(os.path.dirname(__file__))
@route('/', method='POST')
def fake_ws():
    body = request.body.read()
    root = etree.fromstring(body).getroottree()
    rq = root.getroot().tag
    if rq == 'op1':
        arx =  os.path.join(app_root, 'rta/op1.xml')
    elif rq == 'op2:
        arx = os.path.join(app_root, 'rta/op2.xml')
    #etc
    #ja es veu com pot anar no?
    return  open(arx, 'r').read()
run(host='localhost', port=9090, reloader=True)

Donant-li nom a l'arxiu i executant-lo tindrem un servei web xorra escoltant al port 9090 i que per testejar va més que bé.

Òbviament convertir això en un vertader servei que en lloc de respostes predefinides faci més feina és sols cosa de programar. Estendre el servei de proves a més opcions o contemplar altres casos, és també igual de senzill.

Està clar que això ho podem fer en gairebé qualsevol llenguatge. Python en aquest cas el que té és que ens dóna unes eines molt potents i simples. No hem de recompilar res, podem anar afegit nous exemples per testejar de manera trivial i el millor de tot, el servidor de test és tant senzill que no introdueix renou addicional a l'hora de provar. Com que les respostes les tenim predefinides podem optar per enviar-ne una o altra segons convengui, modificar-la per tractar casos frontera, o bé tractar-les com a una plantilla, Bottle té també un mini-llenguatge de plantilles, i que la resposta varii segons ens convengui.

[1] Bé, ho podria haver fet amb el servidor web que duu la llibreria estàndard de Python, però què voleu, m'encanten aquestes coses!

[2] També hi ha eines que farien bé la feina a la llibreria de Python, com l'xml.etree però m'agrada lxml.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Actualitzant coses


Escrit per Aaloy a 30 de January , 2011 a les 11:23 a.m.

Aquesta setmana he estat força entretingut, m'he estat mirant un grapat de llibreries i utilitats, algunes d'elles noves versions que han anat evolucionant i millorant al llarg del temps. També vaig tenir l'oportunitat d'assistir a la xerrada de Maria Antònia Mas Pichaco damunt la guia PMBOK®, gràcies a l'empenta de Paco que em va recordar que es donava la xerrada. A part de la conferència en sí, també va estar molt bé el cafetó i la xerrada amb Paco, això de contar batalletes i compartir opinions no s'ha de perdre Paco! :)

Xerrada de PMBOK®

Si anam per ordre el primer seria parlar de la xerrada de Maria Antònia. A diferència d'altres assistents que l'havien tinguda com a professora, era la primera vegada que l'escoltava. Em va agradar, la exposició va ser clara i entretinguda. M'hauré de llegir la guia per poder-ne fer una avaluació en profunditat, però pel que vaig entendre es tracta d'un conjunt de recomanacions i millors pràctiques que cobreixen el cicle de la gestió de projectes, però que en cap cas ens marca un camí a seguir o una metodologia. Supòs que la utilitat vindrà quan ja hagis elegit com gestionar un projecte.

En la part anecdòtica me va fer gràcia quan un dels punts de la gestió del projecte consistia en "triar l'equip de gent". Em va recordar a aquelles pel·lícules on l'heroi va cercant l'equip en els llocs més inversemblants, sempre els millors en el que fan, per conformar un equip invencible. Bé, la realitat és que poques vegades tens ocasió de fer això, però quan pots és molt engrescador. El més normal és que l'equip et vengui donat i llavors el que s'ha de fer és tractar d'esbrinar quines mancances i quines aptituds té cada membre de l'equip per arribar a dur a terme el projecte. Seguint amb el símil fílmic, és mes semblant a les pel·lícules on l'entrenador agafa un equip que no coneix i el duu cap a la fita proposada (sempre amb final feliç).

Em va dur molts records una altra frase "un projecte ha de tenir una data de finalització", ja que és una discussió que havia tingut en algunes ocasions amb certa gent. Per mi un projecte comença, es desenvolupa i acaba, tal com també va dir Maria Antònia, tot el que no té dada de finalització entra dins la part d'operacions.

LibreOffice

Al portàtil de casa he substituït ja l'OpenOffice per LibreOffice, la migració va ser molt senzilla i per ara no he detectat cap problema. Pareix més ràpid i no dóna problemes amb el Firefox. No sé si me passa a mi sols, però amb l'OpenOffice i el Firefox en marxa les operacions de tallar i aferrar es feien eternes, ara això no passa. També he trobat millores curioses, com la possibilitat de tenir colors a les pipelles de les fulles de càlcul, opció que ja tenia el Quattro Pro de Borland i que el pas en massa de les empreses cap a Excel va deixar a l'oblit. Com a altra avantatja la millora a la pantalla de càrrega que li dóna un aire un poc més fresc i la millora en la navegació.

VirtualBox 4

He actualitzat el programa VirtualBox a la darrera versió, l'actualització des de la 3.2 és trivial i no s'han perdut màquines virtuals. El temps d'arrencada de l'aplicació i de les màquines virtuals ha millorat molt i també ho ha fet la interfície gràfica d'administració. A més ara té la possibilitat de connectar-se en remot a una màquina virtual mitjançant el protocol RDP, bé en sessió única o en multi sessió. Així dins una Ubuntu 10.10 tenia dues màquines virtuals, una Ubuntu 10.04 i un Windows XP (l'original que va venir amb el portàtil), aquest XP estava connectat via RDP a la màquina virtual Ubuntu i s'hi podi interactuar veient l'arrancada i tot. Des de l'altra ordinador, el PPC, també vaig connectar via remote desktop a la màquina virtual Ubuntu. Divertit i amb moltes possibilitats.

Sorl thumbnail

Sorl es una llibreria per a la gestió d'imatges per Django. És una reescriptura d'una versió anterior pràcticament des de zero. Incorpora totes les possibilitats de la versió anterior però ara de manera molt més transparent i entenidora. Han millorat com es fa caché de les imatges incorporant Redis com a opció de magatzem de claus, opció per distingir el tipus d'orientació de la imatges i un control per determinar si la imatges existeix o no (per exemple en remot quan dóna un 404) i actuar en conseqüència.

A les properes setmanes esper poder parlar de la sortida de Django 1.3 i de Celery. No sé què em fa més patxoca. Per una part Django 1.3 significarà que ja s'avança cap al 1.4, versió que està previst que incorporarà moltes més novetats que la 1.3 que és una versió més de correcció de petits errors i en general petites funcionalitats. Celery, la llibreries de cues per Python i Django també treurà aviat una nova versió. N'estic molt pendent ja que incorpora compressió de missatges, autoescalat, un nou protocol més eficient, ... Per ara m'he llegit la documentació i he de començar a preparar algunes de les nostres aplicacions que fan servir la llibreria migrant a l'actual Release Candidate i veure'n el camí de migració. Ja en xerrarem!


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Django


Primer apunt del 2011


Escrit per Aaloy a 23 de January , 2011 a les 12:25 p.m.

Primer apunt del 2011

Primer apunt d'aquests any. Se diu aviat quan gairebé ja ha passat un mes del 2011. Aquesta aturada per festes ha suposat també una aturada en els pots d'aquest blog. No vull dir que escriure sigui una rutina més, però sí que requereix de moments de tranquil·litat davant de l'ordinador, de reorganitzar idees, i amb les festes i amb tot el que queda pendent a l'inici d'any per mor de les festes, doncs tot s'acumula i els moments propicis per escriure al blog són molt limitats.

Així que aquest primer apunt implica d'alguna manera tornar a la normalitat després de festes, posar en ordre els projectes i també fer un petit resum de l'any passat, que com a nota històrica doncs tampoc queda tan malament. Als soferts lectors ja us aviso que aquest apunt va de batalles, buabulància, projectes, ... Un sac on s'hi pot trobar de tot, bé si fa no fa com als resums anuals que fan per les teles, però sense els cops i les caigudes.

Com molts sabeu el 2010 va ser l'any en que vaig deixar una feina estable a Tui España (després Hotelbes) per dedicar-me junt amb altre(s) socis a fer feina a temps complet per a la nostra pròpia empresa). Vist amb un any de perspectiva he de dir que no tot són flors i violes, això de tenir un sou fix cada més està força bé, però també es veritat que poder fer feina en projectes amb la tecnologia que t'agrada i de la manera que t'agrada crec que ho compensa de sobres.

També he pogut veure que moltes de les coses que comenta la gent autònoma o les petites empreses és totalment cert. Crec que es creen micro-empreses i autònoms a pesar de l'administració i no gràcies a ella. Tenir que avançar l'IVA de factures que encara no has cobrat (i que potser no cobraràs) és l'exemple més clar, però també ens n'hem trobat més: disposicions vers la comunicació electrònica de l'administració que sols funcionen amb segons quins sistemes operatius, concursos públics que demanen un programa específic d'un proveïdor concret (no fa falta dir quin, veritat?), subvencions que se'n van a empreses que no ho necessiten, plans avanza que impliquen que siguis una gran empresa per presentar-hi i ja pensats per a que les empreses se n'aprofitin i que juguen al gat i al ratolí amb les normes. Pens que tot hauria de ser més fàcil, en Varsavsky a alguns apunts del seu blog quan parla de la crisi i de com està organitzada la creació d'empreses a Espanya dóna bastant al clau.

També potser totes aquests qüestions burocràtiques tenen una raó de ser, que la gent té per hobby defraudar tot el que pot a Hisenda i a l'Estat (els recents i nombrosos casos de corrupció que s'han destapat a les nostres Illes aquest darrer any així pareix que ho demostren), però també crec que molta paperassa que es fa té per objectiu justificar la pròpia burocràcia i la seva ineficiència. Amb la quantitat d'informació que té l'Estat damunt nosaltres no té sentit que cada organisme torni a demanar la mateixa paperassa, i que se suposi que tothom és un potencial defraudador i es legisli en conseqüència, és a dir, no amb la intenció de fer més fàcil la creació d'empreses i la seva gestió, sinó amb la intenció de fer més fàcil la feina d'inspecció i control, passant la càrrega de feina (i sovint de la prova) cap a l'empresa.

Per exemple, una de les normes suposa que per una micro-pyme com nosaltres hem de fer que els 85% de beneficis vagin com a factures dels socis, la conseqüència és que a principis d'any l'empresa es queda pràcticament descapitalitzada i sense marge de maniobra per escometre inversions (ja siguin de renovació de mobiliari o de projectes propis). Entenc que és una mesura per evitar el frau, però que fa molt difícil la vida a les micro-empreses que començam.

Però no tot és negatiu, la veritat és que la Incubadora del Parc Bit és una gran ajuda per gent com nosaltres i el personal d'allà està fent molt bona feina. També a poc a poc vas notant alguns canvis positius: que l'Ibit organitzi un curs de qualitat d'OpenERP va ser una notícia prou bona pels que creim que el programari lliure és una opció de futur pel nostre teixit empresarial. Les empreses ja no els estranya que els diguis que fas feina exclusivament amb programari lliure i que els desenvoluparàs una aplicació web que correrà damunt Linux. Potser és la crisi, potser és un canvi de mentalitat o tot plegat, però al manco es veu que alguna cosa està canviant de manera lenta però inexorable.

Els projectes del 2010 han estat força entretinguts, hem tingut un gran projecte: la migració i renovació de la web del grup Hoteler Fiesta. Tot i que algunes vegades hem tingut que adaptar-nos al que ens deixava fer el CMS "legacy" més que al que nosaltres ens hagués agradat, el treball va ser molt engrescador i va suposar un repte tècnic important.

Hem pogut fer feina amb Python i Django amb molts projectes. Des de webs presencials com la de Clima Insular a projectes b2c com són els projectes de Globalbooking i Clickote. Hem fet feina amb Python i Django, i afinat les configuracions de sistemes per fer que les aplicacions tenguin un rendiment semblant a webs que es gastes varis ordres de magnitud més en el seus desenvolupaments. Personalment m'ho he passat d'allò més bé en veure com una aplicació com Celery i RabbitMQ pot donar un joc tan gran en el desenvolupament web. En poques paraules, m'he divertit, ens hem divertit amb la feina.

El 2011 es presenta també molt interessant. Tenim en cartera projectes per fer webs de venda per alguns hotels que crec que poden quedar molt bé. No ens les donam de gurús i no tenim el marketing d'altres, però mira som així i no hi podem fer res. L'altra dia a un twitt un conegut gurú del marketing hoteler deia que eren els millors perquè el seu cms podia fer feina amb múltiples idiomes, com si l'Unicode l'hagués descobert ell. Fa ja prop de tres anys nosaltres amb Python i Django posàrem la web d'UltramarExpress en producció en xinès i ens va parèixer la cosa més normal del món, crec que no ens sabem vendre bé. També és veritat que potser també tenim més coneixements tècnics per a saber què és l'Unicode, l'UTF-8 i per distingir la traducció de la localització. Em sap greu, però aquests tipus de venda de fum m'indignen!

Em fa moltes ganes seguir fent feina amb l'arquitectura d'Amazon, amb els preus a que s'han posat les micro-instàncies, fer experiments o posar serveis dedicats és molt barat. Un dels primers que crec que posarem serà un servidor dedicat per Git. Estam investigant si hi ha alguna cosa opensource que ens faciliti la tasca d'administració, però el que és segur que tenir un servidor propi per control de versions damunt Amazon per a una empresa té un cost realment ridícul. En la nostra feina diària feim servir fonamentalment el subversion, però Git (o Mercurial) tenen molts avantatges com per a no intentar fer el canvi. Representa però una manera de fer feina que requereix una adaptació i aquesta ha de ser el menys traumàtica possible. Feim servir Trac per a gestionar projectes i hem d'avaluar el suport de Git o pensar en migrar a Redmine que pareix que el té molt més madur. Com dic, també estaria molt bé tenir una eina web potent d'aministració dels repositoris i dels usuaris, més que res com a una opció de futur.

El 2011 també es presenta molt interessant en el món Python. Supòs que les distribucions més populars ja vindram amb el Python 2.7 i això facilitarà la migració cap a la versió 3.x de Python. La setmana passada pareix que se va arreglar l'entrebanc més important que hi havia amb el mòdul wsgi per la versió 3 i això vol dir que els principals frameworks Python podran utilitzar Python 3 i desplegar-se sense problemes. Seria molt agoserat dir que el 2011 serà l'any en que Python 3 serà l'estàndard per al desenvolupament web, però sí que crec que es portara a Python 3 les principals llibreries i que el 2012 (si no s'acaba el món) un ja tindrà pocs dubtes de si programa amb Python 2 o 3, directament ho farà amb el tres.

El 2010 Python ha guanyat molt acceptació dins la comunitat de programadors. L'índex TIOBE i el premi al llenguatge de l'any són una bona mostra i el 2011 crec que anirà pel mateix camí. Cada cop veus més utilitats, aplicacions web i web fetes amb Python i algun framework (normalment Django). Les utilitats de desenvolupament web per Python cada cop són més potents i a la vegada fàcils de fer anar, amb línea amb la filosofia del llenguatge. He passat d'utilitats de testeig com pyUnit a fer feina amb nose, que fa que crear tests unitaris per testejar aplicacions no sigui molt més complexe que escriure els petits scripts que tanmateix ja feia. Nose permet reutilitzar la feina que ja feim i formalitzar les proves que hauríem fet de totes maneres. South per Django és una altra d'aquestes petites joies que ha arribat a la maduresa. L'actualització del model de dades per Django és trivial amb South i cobreix pràcticament el 99% dels casos. A l'autor li han proposat sovint que South formi part del Core de Django però ell diu que encara no està totalment satisfet i que per això vol mantenir-ho com a projecte separat. Pareix que vol arribar a la perfecció absoluta!

A més de per les ganes que me feia poder fer feina a la meva pròpia empresa, una de les raons que me dugueren a deixar Hotelbeds, no ho negaré, va ser que volia poder desenvolupar projectes amb Python i Django com fins aleshores ho havíem fet a TUI España. Crec que és un dels grans problemes de Python, que una vegada que hi fas feina tornar a altres llenguatges és molt difícil. Crec que va ser i és encara una decisió arriscada, però fer feina amb el que te grada sovint ho és.

La versió 1.3 de Django s'espera d'aquí uns mesos. Serà una versió de transició, orientada a corregir bugs i afegir petites millores de funcionalitat que quedaren com a tickets pendents d'integrar a la versió 1.2. Tot i això hi haurà millores com la millora en el maneig de contingut estàtic, que ha passat de ser una aplicació independent a estar integrada en el core.

Fa pocs dies al twitter i a la llista de desevolupament s'anunciava que es passava a Transifex per a les tasques de traducció. Això és una molt bona notícia. Transifex és d'aquests projectes que poc a poc van calant dins els món del desenvolupament per la seva senzillesa i eficàcia. Una altre bon exemple seria també el d'Sphinx per a la documentació d'aplicacions, o fins i tot una aplicació web molt recent, el readthedocs que s'està convertint en una web ideal on deixar la documentació dels nostres projectes online.

Al 2011 també hi haurà la possibilitat d'anar a la Django Conference que enguany es fa a Holanda, un destí relativament proper i econòmicament assequible. Si l'economia m'ho permet me fa moltes ganes anar-hi, serà una bona oportunitat de conèixer gent a la que segueixo per les llistes de Django i/o Twitter i veure com afronten ells la realitat empresarial.

També crec que pot ser un bon any per implantar OpenERP a l'empresa. El curset de l'Ibit ens va servir per adonar-nos realment de tot el seu potencial. Ja ha sortit la versió 6.0 i encara que no està completament localitzada trob que està prou madura per poder-hi començar a fer feina internament i potser d'aquí algunes setmanes/mesos començar a fer alguns projectes per tercers. OperERP està fet en Python i per tant moltes de les eines i tècniques que ja feim servir són aplicables al desenvolupament d'aplicacions de negoci damunt OpenERP.

Realment no sé si el 2011 representarà el final de la crisi, si tots els projectes que hi ha en expectativa es tancaran finalment i tindrem un any bo. Com que me conec sé segur que no deixaré de passar pena: per la feina, per les factures, pel local, pels projectes,... però és el meu tarannà. La diferència és que abans me creava molta ansietat el saber que les coses es podrien fer d'una manera millor i no tenir l'oportunitat de canviar les coses, ara aquesta ansietat no hi és, perquè al manco tenc l'oportunitat d'intentar-ho.


Traducciones/Translations by apertium

5 comentaris, 0 trackbacks (URL) , Tags: Informàtica Linux Python Django APSL


OpenERP


Escrit per Aaloy a 11 de December , 2010 a les 11:10 a.m.

Fa uns dies vàrem finalitzar la primera part del curs d'OpenERP. Aquesta part estava dedicada a la programació d'aplicacions i després, de fet la propera setmana, seguirà una part més dedicada al món de l'empresa i la gestió.

El curs és una iniciativa de l'Ibit d'aquestes que et tornen a reconciliar amb l'administració. Quan l'administració aposta per programes com OpenERP i la formació de programadors locals en aquestes tecnologies està apostant tant per l'empresa local de programació com per les PIMEs, que podran gaudir d'una programari avançat amb el sol cost de la seva implantació. Un ERP com OpenERP uneix omptabilitat, facturació, gestió de clients, etc amb la capacitat de programació i personalització. El que sigui slliure implica que el que pagaríem amb llicències se'n pot anar a millorar el programa i a adaptar-lo a les nostres necessitats.

Coneixia OpenERP des de fa temps, però els darrers dos anys el seu creixement en funcionalitat i mòduls ha estat espectacular. El programa està escrit en Python, una de les coses que el fan més atractiu, ja que fa que la corba d'aprenentatge sigui molt suau i a més fa que els requeriments de màquina que se necessitin per fer anar el programa siguin minims. La base de dades que utilitza OpenERP és Postgresql, una altra magnífica elecció al meu entendre ja que personalment he pogut comprovar la robustesa d'aquesta base de dades en entorns de negoci i la seva gran versatilitat.

Recordem que estam parlant d'un ERP, un sistema que integra tot el que el negoci pugui necessitar gairebé de sèrie i que permet personalitzar-ho de manera que sigui el programa el que s'adapti al negoci. Així podem verticalitzar l'aplicació creant nous mòduls i aprofitant els existents, o bé podem utilitzar-ho com a bastiment per a crear les nostres pròpies aplicacions. El sistema es prou modular per permetre ser utilitzat com un programa de comptabilitat i gestió potent, com un bastiment de programació per aplicacions de gestió o la combinació d'ambdues caracterísiques. Sols per la comptabilitat i facturació la instal·lació d'OpenERP ja es justifica sola, per una part tindrem un dels millor programes comptables que hi ha i a més a més tendrem control de les nostres dades. Supòs que més d'una ara recordarà algun amic o conegut que s'ha trobat amb problemes amb alguna de les distintes versions de contapluses pagades i amb llicència (canvi disc dur o d'ordinador són bastant habituals), i quan ha anat a consultar què passava li han dit que ha de passar per caixa i contractar un manteniment que li costa més que el que va pagar de llicència. Mentre no té accés al programa, no pot fer factures ni comptabilitzar, passa a ser hostatge de la seva compra. Amb OpenERP això no passa, no depens d'una única empresa que et pugui donar el mantenimient, si ho instal·les a casa les dades són teves i no hi ha limitacions artificials. Si el nostre negoci creix i necessitam més gent a administració o canviam de disc dur l'OpenERP seguirà funcionant.

Que OpenERP estigui escrit en Python i que hagi tingut aquest creixement per mi no és casualitat. Ho he vist en altres projectes Python, que comencen molt tímidament però que aviat gràcies al nivell d'entrada tan suau que té el llenguatge i a la seva legibilitat, va incorporant programadors i característiques fins a convertir-se en un referent dins el sector. Ho hem vist recentment a projectes com Django o Satchmo, i també a llibreries com pyQt, wxPython, sistemes de còpia de seguretat, ... OpenERP és potser un dels millors exemples del que es pot fer amb Python.

La part de programció pura, amb OpenObject i no lligada a la comptabilitat o facturació també es mereix la màxima atenció. És molt bo de fer crear formularis i aplicacions noves. La gent que sovint diu que necessita un Access per Linux li hauria de fer una ullada a com funciona el programa. Pel mateix preu tens el client gtk i el client web, i la feina que duu m'atreviria a dir que és menor que una aplicació Acces ben acabada.

Personalment ho tenc força clar, OpenObject passarà a formar part del meu caixò d'eines informàtique i l'opció preferent quan es tracti de crear una aplicació de gestió d'escriptori o web. A més és molt bo de fer connectar-ho amb Django o altres aplicacions gràcies a la seva filosofia oberta.

Esper que la propera setmana el curs sigui tan interessant como ho va ser el de programació. Crec que ens divertirem molt amb OpenERP i OpenObject.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python PostgreSQL


Celery: mini tutorial


Escrit per Aaloy a 28 de November , 2010 a les 6:55 p.m.

Configuració del servidor rabbitmq

El servidor Rabbitmq és qui rebrà les ordre d'execució de les tasques i les enviarà als workers que han de fer la feina i rebrà el resultats de les operacions de manera que el procés que ha donat l'ordre d'execució de la tasca el pugui fer servir.

És important destacar que a l'hora de donar l'ordre d'execució podem decidir si el resultat s'ha de guardar o bé s'ha de descartar. Si el resultat no és necessari (pensem per exemple en l'enviament d'un e-mail) llavors convé marcar la tasca per a que el resultat es descarti.

sudo aptitude install rabbitmq-server
Els paquets nous següents s'instal·laran:           
  erlang-os-mon{a} erlang-snmp{a} rabbitmq-server 
0 paquets a actualitzar, 3 a instal·lar, 0 a suprimir i 5 a no actualitzar.
Es necessita obtenir 1398kB d'arxius. Després del desempaquetat s'utilitzaran 3187kB.
Esteu segur de voler continuar? [Y/n/?] y
Obté:1 http://es.archive.ubuntu.com/ubuntu/ maverick/main erlang-snmp i386 1:13.b.3-dfsg-2ubuntu3 [581kB]
Obté:2 http://es.archive.ubuntu.com/ubuntu/ maverick/main erlang-os-mon i386 1:13.b.3-dfsg-2ubuntu3 [77,4kB]
Obté:3 http://es.archive.ubuntu.com/ubuntu/ maverick/main rabbitmq-server all 1.8.0-1ubuntu2 [739kB]
S'han obtingut 1398kB en 2s (480kB/s)   
S'estan preconfigurant els paquets...
S'està seleccionant el paquet erlang-snmp prèviament no seleccionat.
(S'està llegint la base de dades … hi ha 118931 fitxers i directoris instal·lats actualment.)
S'està desempaquetant erlang-snmp (de …/erlang-snmp_1%3a13.b.3-dfsg-2ubuntu3_i386.deb) …
S'està seleccionant el paquet erlang-os-mon prèviament no seleccionat.
S'està desempaquetant erlang-os-mon (de …/erlang-os-mon_1%3a13.b.3-dfsg-2ubuntu3_i386.deb) …
S'està seleccionant el paquet rabbitmq-server prèviament no seleccionat.
S'està desempaquetant rabbitmq-server (de …/rabbitmq-server_1.8.0-1ubuntu2_all.deb) …
S'estan processant els activadors per a man-db …
S'estan processant els activadors per a ureadahead …
S'està configurant erlang-snmp (1:13.b.3-dfsg-2ubuntu3) …
S'està configurant erlang-os-mon (1:13.b.3-dfsg-2ubuntu3) …
S'està configurant rabbitmq-server (1.8.0-1ubuntu2) …
Adding group `rabbitmq' (GID 123) ...
Fet.
Adding system user `rabbitmq' (UID 115) ...
Adding new user `rabbitmq' (UID 115) with group `rabbitmq' ...
Not creating home directory `/var/lib/rabbitmq'.
Starting rabbitmq-server: SUCCESS
rabbitmq-server.

Amb això hem aconsegui instal·lar i posar en marxa el RabbitMq en un sistema Ubuntu 10.10. Ara ens queda configurar, afegirem un usuari i un vhost per a que accepti els treballs, a més hem de donar permisos a aquest usuari per a que que pugui enviar treballs al vhost creat.

Afegim l'usuari:

$ sudo su
$ rabbitmqctl add_user django password
Creating user "django" ...
...done.

Cream l'vhost

$ rabbitmqctl add_vhost test
Creating vhost "test" ...
...done.

Assignam els permisos a l'usuari per a quest vhost

$rabbitmqctl set_permissions -p test django ".*" ".*" ".*"

Setting permissions for user "django" in vhost "test" ...
...done.

Ja tenim la part més complexa llesta. El servidor RabbitMQ pot estar en la mateixa màquina o en una totalment distinta de la que llançarà la tasca o de la màquina de l'executarà.

Preparant l'entorn

La gràcia del Celery és que ens permet llançar les tasques i executarles mantinguent la cohesió de la nostra aplicació. És a dir, si estam treballant amb Django Celery ens permet manetenir el codi de cridada i d'execució de la tasca des de la mateixa aplicació o des del mateix projecte.

Per començar convé que instal·lem Celery. Ho farem des de un entorn virtual, per això convé tenir instal·lats els paquets virtualenv i virtualenvwrapper.

sudo pip install virtualenv virtualenvwrapper

i configurar el virtualenvwrapper.

Crear l'entorn, de nom per exemple celery i instal·larem el paquet celery

mkvirtualenv celery
workon celery
pip install celery

Que sigui un projecte Django o Python pur té molt poques diferències. En el cas de Django hem d'instal·lar el paquet django-celery i configurar el projecte. Així a les nostres aplicacions afegirem "djcelery"

INSTALLED_APPS += ("djcelery", )

I també és important afegir les següents línies al settings.py

import djcelery
djcelery.setup_loader()

La resta es comú tant a un projecte Python que a un projecte Django, sols que les configuracions en el primer cas aniran a un arxiu anomenat celeryconfig.py i en el cas de django la configuració pot anar dins el settings.py

Per la nostra primera tasca crearem un project Python simple i l'arxiu de configuració celeryconfig.py contindrà

#-*- coding: UTF-8 -*-

BROKER_HOST = "192.168.1.10"
BROKER_PORT = 5672
BROKER_USER = "django"
BROKER_PASSWORD = "password"
BROKER_VHOST = "test"
CELERY_RESULT_BACKEND = "amqp"
CELERY_IMPORTS = ("tasks", )
CELERY_SEND_EVENTS=True

El prefixe BROKER fa referència al servidor RabbitMq que acabam d'instal·lar. Celery pot fer servir difernts tipus de Brookers pero RabbitMq és el més recomanat.

Fitxau-vos que el configuram amb els paràmetres amb els quals hem creat el vhost, i amb l'usuari i password creats a RabbitMq.

CELERY_RESULT_BACKEND serveix per a indicar a Celery on s'han de deixar els resultats. Podem tenir distints backends, des de Memcached, a una base de dades o una base de dades NoSQL.

CELERY_IMPORTS indica a l'aplicació que tasks.py conté les tasques que s'han d'executar. Bé, de fet el que fa és indicar al daemon de celery quines són les tasques a exeuctar quan es posi en marxa, però també pot fer-se servir per altres tipus d'inicialitzacions.

CELERY_SEND_EVENTS li diu a Celery que volem monitoritzar els events que es produeixin. Si no posam aquesta ordre i després volem saber què esta passant ho tendrem molt més complicat.

Creant la nostra primera tasca

Per cerar la nostra primea tasca crearem una arxiu anomenat tasks.py

#-*- coding: UTF-8 -*-

from celery.decorators import task

@task(name='tasks.add')
def add(x,y, **kwargs):
    logger=add.get_logger(**kwargs)
    logger.info('Entrant a la tasca ...')
    return x+y

Fitxem-nos que és codi Python del més normalet, sols que hem afegit un decorador tasks per marcar la funció com a tasca i posar-li el nom. Aquest nom és opcional, però convé posar-li ja que així evitam que celery li posi un nom per defecte que podria fer menys portable la nostra aplicació.

També podem veure como podem enviar informació a logger des de la tasca.

Executant el worker

Ara ja podem anar al nostre projecte, si tot ha anat bé tindrem dos arxius: tasks.py i celeryconfig.py

Executam: celeryd -l info

[2010-11-28 18:44:20,949: WARNING/MainProcess] celery@D820 v2.1.3 is starting.
[2010-11-28 18:44:20,950: WARNING/MainProcess]  
Configuration ->
    . broker -> amqp://django@192.168.1.10:5672/test
    . queues ->
        . celery -> exchange:celery (direct) binding:celery
    . concurrency -> 2
    . loader -> celery.loaders.default.Loader
    . logfile -> [stderr]@INFO
    . events -> ON
    . beat -> OFF
    . tasks ->
    . tasks.add
[2010-11-28 18:44:20,960: INFO/PoolWorker-1] child process calling self.run()
[2010-11-28 18:44:20,962: INFO/PoolWorker-2] child process calling self.run()
[2010-11-28 18:44:20,963: WARNING/MainProcess] celery@D820 has started.

amb això hem creat el nostre primer worker preparat per a executar la tasca que li diguem (i que estarà dins tasks.py). Per aquest article hem deixat el worker dins al consola, però es pot deixar com a dimoni. Consulteu el vostre administrador de sistemes de capçalera per a una configuració acurada. El paràmetre que li passa indica que volem que generi log en mode info. Fixem-nos que també ens informa de les tasques disponibles i del nivell de concurrència que tenim, 2 en el meu cas, que és el nombre de processadors del portàtil.

I executam la tasca

Des d'una altra consola executarem l'interpret de Python

>>>from tasks import add
>>>result = add.delay(2,3)

Si ara observam la consola del worker veurem alguna cosa semblant a això:

[2010-11-28 18:46:57,783: INFO/MainProcess] Got task from broker: tasks.add[c379421f-fa6a-4917-b45a-4833a9e30ef3]
[2010-11-28 18:46:57,830: INFO/PoolWorker-2] [tasks.add(c379421f-fa6a-4917-b45a-4833a9e30ef3)] Entrant a la tasca ...
[2010-11-28 18:46:57,879: INFO/MainProcess] Task tasks.add[c379421f-fa6a-4917-b45a-4833a9e30ef3] succeeded in 0.0486330986023s: 5

El brooker (RambbitMQ) ha enviat l'ordre d'execució de la tasca add que el worker té registrada i aquest es disposa a executar-la.

Des de la consola hem dit a la coa que volíem que la tasca s'executàs en modoe asíncron, d'aquí l'ús de delay.

Si ara volem obtenir el resultat farem

>>> print result.get()
    5

Com es pot veure l'exemple és molt senzill, potser massa per adonar-nos de la potència de Celery. Pensem que en lloc d'una suma podem fer la generació d'un pdf, l'enviament d'un e-mail, la càrrega en batch d'un xml, etc. etc. L'important és que el procés principal ja no té que realitzar la tasca i pot ser una altra màquina l'encarregada de fer-ho. Pensem en una màquina plena de targes gràfiques dedicades al processament numèric, en un sistema encarregat del data-mining dels logs, les possibilitats són infinites.

També és important notar l'escalabilitat que aconseguim amb Celery. No estam parlant ja d'escalabilitat amb nombre de processadors, sinó que parlam d'escalabilitat amb nombre de màquines. Mentre les nostres tasques puguin ser paralelitzades podrem fer servir Celery per a distribuir la càrrega de feina entre les màquines disponibles.

I això és sols el principi: celery pot substituir al Cron, permet la monitorització, la creació de tasques molt més complexes que les que ens permet una funció i un llarg etcètera d'opcions. La documentació és força completa, però llevat de casos molt especial, el grau de dificultat que trobarem és el que hi ha a aquest post.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Introducció a Celery


Escrit per Aaloy a 13 de November , 2010 a les 12:18 p.m.

Introducció a Celery

Celery es una aplicació que ens permet crear tasques de feina asíncrones gestionades per un gestor de cues que està basada en l'enviament de missatges de manera distribuïda. Es focalitza en operacions en temps real però també suporta la calendarització de tasques.

Les unitats d'execució, anomenades tasques, s'executen de manera concurrent en un o més nodes de treball. Aquestes tasques poden executar-se de manera asíncrona bé de manera síncrona (esperant fins que la tasca està llesta).

El sistema de missatgeria recomanat per celery és RabbitMQ encara que és bastant agnòstic en el tema i pot fer servir com a substitut Redis, MongoDB o una base de dades sql.

Encara que el programa va sorgir lligat a Django, actualment és una llibreria que es pot utilitzar de manera independent. S'han creat dos projectes a partir de l'original, Celery manté l'estructura bàsica de la llibreria i django-celery manté la integració amb Django.

Bé, fins aquí la parrafada d'introducció traduïda amb més o menys fortuna de la documentació de Celery, el que estam dien és que celery ens permet executar tasques de manera distribuïda, on el director d'orquestra (si seguim les recomanacions) és una aplicació anomenada RabbitMQ, escrita en Erlang, per cert, que se n'encarrega de rebre les ordres de les tasques que s'han d'executar i les distribueix entre els distints nodes que estan registrats per a executar dita tasca. Aquest director és el que se n'encarrega de saber com està cada tasca i d'obtenir-ne el resultat quan aquesta finalitza, de manera que el programa que ha encomanat la tasca.

Així dons hem de distingir tres actors en tot això, l'aplicació que comana la tasca, l'aplicació que executa la tasca (el worker) i l'aplicació que se n'encarrega de la comunicació entre qui comana la tasca i qui l'execute. La gràcia de tot això és que podem tenir més d'un worker a distintes màquines o a la mateixa màquina, de tal manera que el gestor se n'encarrega de repartir les tasques en funció de qui està lliure per executar-les.

Pensem un noment amb el que això significa: podem passar de tenir una aplicació que ho fa tot, a un conjunt d'aplicacions que poden estar en distintes màquines que col·laboren entre sí. Resumint: ens permet l'escalabilitat horitzontal.

Per què fer servir Celery?

Abans de Celery la meva experiència havia estat amb Beanstalkd, un sistema semblant, molt simple i ràpid. Ara us contaré una batalleta, estant de cap de projecte per Tui España un dels projectes que duia era el de la creació d'un servei web per a la venda on-line de transfers i excursions. La informació estava (i supòs que encara hi és) a una base de dades Oracle i amb una estructura pensada per a introduir-hi informació, de manera que fer una consulta un poc complexa com "vull totes les excursions que hi ha tal dia que es poden fer des de tal punt" era extremadament lent. La solució que trobàrem va ser desnormalitzar la informació passant-la cap a una base de dades Postgresql, un parell mallorquí de milions de registres que s'actualitzaven diàriament.

La primera versió del programa de càrrega estava fet amb Java, va ser complexe de crear, gairebé 3 mesos de feina, mal de mantenir i executant-ho tot amb fils estava gairebé 2 hores en tenir-ho tot carregat.

La segona versió la férem amb Python, una setmana i mitja, un 10% del codi, més funcionalitat i utilitzarem Beanstalk per a distribuir les tasques, ja que la càrrega en sí es podia particionar molt bé. Això ens permeté aprofitar la potència de servidors que estaven ociosos i aprofitar també els temps morts entre la consulta a Oracle, el parseig de la resposta i la introducció a Postgres. A una de les proves posarem en marxa processos a 4 servidors diferents, amb un total d'uns 20 workers i un temps de càrrega d'uns 3 minuts. Amb 2 servidors el temps era d'uns 5 minuts, una millora molt significativa respecte a l'aplicació Java.

Així doncs una de les utilitat que tenim és la de poder distribuir tasques molt pesades entre moltes màquines. Celery el que té molt millor que Beanstalk és una documentació molt acurada, sistemes de monitorització i gestió i en el cas de fer servir Django, la possibilitat de mantenir les tasques dins el mateix projecte.

El segon ús important de Celery és la web. En aplicacions web el que volem és donar la millor experiència d'usuari possible, i aquesta experiència es degrada si l'usuari ha d'esperar molt. El que ens interessa és donar la resposta a l'usuari de que la seva comanda s'està està executant i deixar el servidor web lliure per poder atendre altres peticions. Imaginem-nos per exemple una aplicació de comerç electrònic on l'usuari demana una factura i aquesta li arriba en pdf. En sistemes de molta càrrega la generació del pdf pot degradar la resposta global de la web, el que és interessant és poder enviar la petició de factura a un sistema extern a la web i que sigui aquest l'encarregat de la generació i l'enviament. Aquest sistema no té perquè estar ni tant sols a la mateixa màquina on hi ha l'aplicació web o poden aprofitar-se hores de poca càrrega per a fer-ne la generació i l'enviament.

Anar cap a la complexitat d'arquitectura que representa afegir un sistema de cues asíncron a la nostra aplicació implica voler anar cap a un aprofitament dels recursos i sobretot entendre com funciona la nostra aplicació, què s'ha de fer de manera immediata i què es pot fer de manera asíncrona, saber quines són les tasques més feixugues que ens afecten i com podem paralelitzar-les i distribuir-les. L'altra solució és posar més màquina amb n-mil cores i passar de tot, però a més de ser una opció econòmicament més cara i tècnicament discutible, és una opció a curt termini, ja que si la nostra aplicació té molt èxit pot arribar un moment on ja ni hi hagi màquines més grans o el cost de les mateixes es mengi tot els nostres beneficis. Podem acabar fent feina pel fabricant de hardware i per pagar-ne les llicències associades.

Convençuts? Esper que sí. Un sistema així no és per totes les aplicacions, la complexitat d'instal·lació i manteniment és més gran que tenir tot el sistema en una màquina i sense distribuir, però si teniu necessitats de fer càlculs intensius de manera puntual o tasques per les quals no voleu fer esperar l'usuari Celery és una molt bona solució. A més pensau amb sistemes com el núvol d'Amazon, puntualment podem aixecar-ne tantes instàncies com calgui i executar-ne workers, o imaginem un Datacenter propi on la majoria de servidors estan ociosos, podem posar workers a les màquiens amb mensy càrrega per a que ajudin en els processos pesats. Se'ns obre un ventall de possibilitat pràcticament il·limitat.

En el proper post, instal·lació de Celery, la creació d'una tasca i un exemple d'integració amb Django, així com problemes que ens podem trobar i com a evitar-los.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Gràcies a tots per venir al creant bits: eines


Escrit per Aaloy a 30 de October , 2010 a les 12:14 a.m.

Actualització: Pujats els documents al dropbox

Una vegada més, i ja van quatre el creantbits ens ha servit per carregar piles, per trobar-nos amb amics que feia temps que no veiem. Cares conegudes d'altres trobades (des d'aquí moltes gràcies per el detall de les galletes) i un quòrum impressionant. Se nota que en Pau té tirada :)

Feia estona que volíem fer una altra trobada d'aquestes. Personalment crec que és molt enriquidora pel que representa d'important per nosaltres veure que hi ha molta més gent que s'interessa pel mateix tipus de coses que nosaltres. Com ja he dit altres vegades això me fa tornar la fe amb la professió.

M'hagués agradat poder fer streaming de vídeo, l'hem intentant, però ens ha fallat la connexió. Ja sé que estam a un Parc tecnològic, però ja sabeu com van aquestes coses. A APSL necessitàrem prop de tres mesos en tenir una ADSL i és sols de les normaletes (i cares), res de connexions de 50 Mb que hi ha pels pobles. A veure si la propera vegada hi ha més sort!

Com a telonero de Pau he presentat un grapat d'eines que trob d'allò més interessants per fer feina amb Django, Python i potser fins i tot per complementar altres llenguatges de programació. En Pau per la seva banda ens ha fet una introducció molt clarificadora del que representa fer feina amb Git. M'ha agradat molt la frase de que Git representa una solució tècnica a un problema social, el de la comunicació. En pau m'ha enviat la presentació i la penjaré junt amb la meva tot d'una que pugui.

Moltes gràcies per l'assistència i el suport. En el nostre ànim està el seguir fent més trobades com aquestes. El suport del Parc Bit (Incubit) cedint-nos la sala val a dir que és una de les coses que fan possible que aquest esdeveniments siguin possibles. Ens queda un any i mig d'incubació encara, així que amenaçam amb tornar-hi :)


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL


Creant Bits: eines


Escrit per Aaloy a 19 de October , 2010 a les 6:55 p.m.

El divendres 29 d'octubre a les 16:00h a la sala de formació del Parc Bit tornam amb una nova edició del Creant Bits.

Aquest cop ens fa ganes xerrar un poc d'eines de programació Python, fent cinc cèntims d'utilitat com virtualenv, fabric, pip, ... És a dir, de totes aquelles eines que no poden faltar a la borsa d'un programador Python i que ens permeten ser encara més productius.

Després en Pau Rullan ens explicarà con fer servir Git en el dia a dia. Git, junt amb Mercurial són una de les millors eines de control de versions distribuït que podem trobar. En Pau s'ha ofert a explicar-nos perquè Git li agrada més i com fer-ho servir per a que la complexitat d'aquesta utilitat jugui al nostre favor.

Com sempre aforament limitat a 25 persones màxim. Us podeu apuntar deixant un comentari.


Traducciones/Translations by apertium

25 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python


Flat is better than nested


Escrit per Aaloy a 11 de October , 2010 a les 8:23 p.m.

L'altra dia i gràcies a Jordi Cabot vaig arribar a un article anomenat Groovy for Business Software. Why not?.

L'article és prou interessant i ve a mostrar els avantatges de fer servir Groovy per al desenvolupament web amb la unió amb Java. Groovy està prou bé si un no pot fer servir Python per les raons que sigui, però encara té moltes reminiscències de Java que fa que el codi sigui mal de llegir i lent d'escriure.

Si anam a la consola de Python i feim un

import this

Trobarem de les primeres la frase "flat is better than nested". La idea de que el més simple és millor, que el codi ha de ser el més pla possible i que els anidament s'han d'evitar.

El codi Groovy per crear un XML és

def writer = new StringWriter();
def builder = new groovy.xml.MarkupBuilder(writer);    
builder.carList {
    for (make in ["Honda", "BMW", "Cadillac"]) {
        car(make: make) {
            type("sedan")
            year("2010")
            used("false")
        }
    }
}
def xml = writer.toString();

És un codi prou simple, però quan vaig fer l'exercici de passar-ho a Python vaig tenir que repetir-ho. No vaig veure la tercera clau i vaig generar un XML que no era el de la sortida.

Veiem el codi Python

from lxml.builder import E
from lxml import etree
xml = E.carList()
for brand in ["Honda", "BMW", "Cadillac"]:
    car = E.car(make=brand)
    car.append(E.tipo("sedan"))
    car.append(E.year("2010"))
    car.append(E.used("sedan"))
    xml.append(car)
print etree.tostring(xml, pretty_print=True)

El nombre de línies que s'han fet servir és anecdòtic, l'important és que algú que segueixi el codi ho té molt més bo de fer per entendre'l. L'anidament ha desaparegut i com s'afegeix cada tag a l'xml es veu a la primera, és ben explicit a qui afegim què.

Quan programam ho hem de fer com si algú més que nosaltres tingués que entendre el codi passat un any, ja que quan el temps passa i encara que siguem nosaltres mateixos els qui hem de mantenir el codi, la filosofia de Python de fer les coses el més simple i explícites possible ens pots estalviar moltes hores de depuració.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python


Vídeos de la Djangocon inspiradors


Escrit per Aaloy a 03 de October , 2010 a les 11:29 a.m.

Aquests darrers dies he estat mirant els vídeos de la darrera Djangocon, com a tota conferència hi ha gent que vol una cosa més tècnica i gent que la troba massa tècnica, però particularment trob que al la quantitat de conferències i lightning talks que hi ha qui no ha trobat el que volia segurament és perquè no ha cercat prou.

Jo he trobat molt interessants dues conferències, Scaling the World's Largest Django Application de la gent de DisqUs i Switching addons.mozilla.org from CakePHP to Django de la gent de Mozilla add-ons. M'interessen molt perquè mostre tant els problemes com la capacitat de Django per escalar per aplicacions molt grans, molt més grans del que estam acostumats per les nostres contrades. En el cas de Mozilla les xifres que donen el rendiment de programació estan en la línia del les meves pròpies recerques: una aplicació Django necessita de l'ordre de 3-5 vegades més línies de codi que per fer-la en PHP un framework com Cake.

Aquests projectes tan grans fan que s'hagin de cercar solucions a problemes comuns d'escalabilitat. L'interessant d'aquests conferències és que et diuen tant els problemes que s'han trobat com les solucions. Pel Twitter he postejat algunes vegades referències al projecte Celery el gestor de cues que s'està convertint en l'estàndard de facto per Python i Django i que fan servir aquests projectes. Però també ens ha permet veure com utilitzen Sentry una eina per la gestió unificada de logs.

Sentry és una aplicació integrable amb Django que ens permet unificar els nostres logs en un únic servidor i integrar les notificacions d'error i incidències al nivell que vulguem i com vulguen. Ho podem posar integrat dins la nostra aplicació o tenir un servidor independent i fer que els n servidors que composen la nostra aplicació enviïn els missatges per http o per una coa Celery. La solució és força elegant i potent, de fet força més potent del que es pot trobar per Java/J2EE i és una solució a un dels problemes més comuns quan es tenen múltiples servidors: la necessitat de tenir logs unificats per saber què està passant i a on està passant.

Encara em queden força conferència a veure, però tampoc vull endur-me'n un empatx, i a més s'ha de tenir temps per avaluar altres projectes més propers. Per exemple, en aquests moments estic avaluant si per les aplicacions que feim és millor disposar d'un blog separat amb WordPress o bé d'un blog, potser visualment menys atractiu, però que s'integri totalment amb les aplicacions Django. Encara no tenc cap conclusió en ferm, però ara per ara la flexibilitat que ens dóna la integració.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Entorns virtuals


Escrit per Aaloy a 12 de September , 2010 a les 12:18 p.m.

Quan un es dedica a la programació seriosa amb Python (ja sigui amb Django o amb qualsevol altre framework) hi ha un parell de coses que s'han de tenir en compte: saber amb quina versió de Python programarem i si aquesta versió estarà suportada per la distribució del servidor de producció és una d'elles, potser la més simple. Una altra un poc més complexa és la de plantejar-nos des del principi com durem el mantenimient de l'aplicació.

Amb els servidors dedicats cada cop a més bon preu, no és genys extrany plantejar-se tenir un servidor dedicat per les nostres aplicacions web en Python per exemple. És també molt habitual que les aplicacions tenguin un cicle de vida molt més llarg que les llibreries que hem utilitzat per a crear-les. Ara podem fer servir la versió 1.2.3 de Django, però d'aquí tres o quatre mesos tindrem la 1.3, amb altres llibreries pot passar una cosa semblant.

Quan tenim una aplicació en producció la màxima sempre ha de ser la de si no està trencat no ho arreglis. Si la nostra aplicació funciona amb Django 1.1 i hem anat aplicant els pegats de seguretat, llavors plantejar-nos migrar a una versió major és quelcom que s'ha de plantejar amb cura i sempre tinguent en compte la relació risc benefici que comportarà l'actualització. Així doncs ens podem plantejar no actualitzar una aplicació, però no per això les seguents aplicacions que desenvolupem tenen que fer-se forçosament amb llibreries antigues.

Com manteneir doncs entorns separats? Si instal·lam les llibreries a nivell de la nostra distribució de Linux preferida l'actualització de llibreries i fins i tot del mateix Python es farà a nivell de tot el sistema. Necessitam quelcom que ens aïlli tot allò que utilitzam a la nostra aplicació. Aquesta eina té un nom: virtualenv creada per l'omnipresent Ian Bicking.

Aquesta utilitat ens crea un entorn separat de Python amb les llibreries que nosaltres hi instal·lem. Si no li deim res utilitzarà també les llibreries del sistema posant-les al PYTHONPATH, però també podem fer que la separació sigui del tot completa fent servir l'opció de '--no-site-packages'. Aquesta opció fa que l'entorn virtual no faci servir cap llibreria del path del sistema i ens permet instal·lar-ho to. És l'opció que a mi m'agrada més, encara que signfifiqui perdre més temps amb la instal·lació, em dona la seguretat de que sé exactament tot allò que necessit per la meva aplicació.

El manual del virtualenv està prou explicat, però tot i això jo no recomanaria fer-lo servir a pèl, sinó amb l'ajud de una altra utilitat anomenada virtualenvwrapper de Dough Hellman.

Aquesta eina ens permet gestionar de manera molt senzilla els nostres entorns virtuals, posar-los en un únic punt del sistema i ens proporciona una gran quantitat de hooks que ens permeten definir que volem fer cada cop que es crei un entorn virtual, o que s'elimini, o que s'activi o desactivi.

Així, per exemple, podem fer que cada cop que activem un entorn virtual s'actualitzi el repositori del control de versions, o que ens situi al punt exacte on tenim el codi. Si sempre feim servir Django als nostre projectes podem fer que cada cop que es crei un entorn virtual se'ns instal·lin també les llibreries que més utilitzam.

Per acabar-ho de rematar la utilitat pip instal·lador avançat de codi Python del que ja he parlat en altres apunts, s'acobla molt bé als entorns virtuals, fins i tot li podem dir que no instal·li res si no estam a un d'aquests entorns.

Si feim servir un entorn virtual i pip podem utilitzar llavors la comanda pip freeze quen es dirà exactament què hi ha a l'entorn virtual: paquets i nombre de versió o revisió quan s'ha instal·lant des de codi font. Així podem crear fàcilment un fitxer de requermients pip i asseguar-nos que quan posem l'aplicació a producció les llibreries que es facin servir siguin exactament les mateixes que hem fet servir a desenvolupament.

Si us plantejau fer servir Python en els vostres desenvolupaments un dels millors consells que us puc donar és que dediqueu unes quantes hores a estudiar virtualenv, virtualenvwrapper i pip, la vostra vida a partir d'aquest moment ja no serà la mateixa.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Quina calor!


Escrit per Aaloy a 28 de August , 2010 a les 12:24 p.m.

Aquestes darreres setmanes han estat d'allò més interessants, caloroses però interessants.

La calor ha vingut fonamentalment d'uns dies que hem passat a Menorca. Ja hi havia anat una vegada de vacances i me va encantar, aquesta vegada no ha estat diferent, encara que hi havia molta més gent que la darrera vegada.

Però no vull donar enveja, així que també toca parlar de programació. Aquests dies hem estat i encara estarem durant unes quantes setmanes més, fent feina amb un projecte que implica fer feina amb un cms anomenat ezPublish.

L'ezPublish és un CMS prou potent, orientat a treure continguts sigui com sigui. Tu escrius el que sigui a la part de l'administrador i l'eZ intentarà renderitzar-ho com pugui. Supòs que si el que importa és el contingut en sí això és fantàstic, però quan es tracta d'afegir-hi regles de negoci per damunt eZPublish és un malson comparat amb frameworks com Django.

Està clar que no podem fer una comparació justa ja que ezPublish no té la flexibilitat d'un framework però crec que l'experiència serveix per reafirmar-me amb l'apreciació de que si el teu negoci no és generar continguts sinó que vols controlar el que passa a la web i per exemple vendre el teu producte, construir l'aplicació directament damunt un CMS és un malson.

Moltes aplicacions necessiten d'un CMS, bàsicament per independitzar la creació de planes del programador que ha fet l'aplicació, però això no vol dir que el CMS tengui que ser l'aplicació. El CMS no ha de ser intrussiu, hauria de limitar-se a una backoffice potent i a ser possible integrable i una API per poder accedir als continguts de la manera que nosaltres vulguem.

El CMS ens permet començar molt ràpid a afegir texts, però com dic, si la nostra web no és estrictament de continguts aviat el CMS es converteix en un problema, ja que te condiciona la manera de fer les coses. Personalment preferesc sol.lucions com Django Page CMS que funcionen a mena de plugin per a les nostres aplicacions. Sóm nosaltres que decidim quin contingut es presenta i com. Potser la interfície no és tan virguera com altres CMS dedicats, però al manco el CMS no es posa en el nostre camí i no ens condiciona.

Un altre CMS per Django que darrerament m'està agradant molt és el projecte Django cms. Si bé és pot considerar ja un CMS en sentit habitual, segueix essent poc intrussiu, podem afegir tant plugins propis del CMS com seguir amb les aplicacions Django que vulguem i l'administrador seguirà funcionant.

Una altra aproximació per Django és lfcms però en aquest cas més que "per Django" hauríem de dir fet amb Django. Aquí ja parlar d'un CMS complet amb el seu propi administrador que és extensible mitjançant plugins fets amb Python. La interfície està molt cuidada i és un CMS a tenir molt en compte si la nostra aplicació és bàsicament de continguts. L'emperò més gran és que necessitam accedir a dues interfícies per fer algunes coses. Per exemple si tenim contingut estructurat i contingut textual, el contingut estructurat l'haurem de gestionar per l'administrador de Django (o fer-ne un nosaltres) i el textual pel backoffice de lfcms. He d'investigar-ho un poc més potser hi ha una manera d'aprofitar tota la fona feina que han fet la gent de lfcm a la interfície.

Un altre CMS prou interessant és un nouvingut anomenat Merengue presenta unes característiques prou interessants com la senzillesa amb que gestiona els temes o la edició col·laborativa (no l'he provada per això), però no té ben lligat el concepte de planes i contingut. No he vist cap vista al backoffice que ens permeti veure clarament la jerarquia de planes. El contingut s'estructura mitjançant una llista plana i perds la referència de l'estructura de la web. Potser s'ha duit a l'extrem la separació dels continguts de la presentació, però al meu entendre tenir una visió esquemàtica dins l'administrador del que hi ha a la web facilita molt la vida a la persona que ha de posar els continguts. Això però, no és un impediment no no s'han de crear moltes planes o el lloc web té una estructura molt fixa, ja que com altres opcions que he presenta aquí, el CMS permet gestionar el contingut directament des de la part de visualització. Merengue apunta maneres i convidrà fer-hi un seguiment de ben a prop.

Però un CMS no és res sense un bon disseny. Crec que l'equip ideal de desenvolupament està format per un dissenyador/maquetador, un parell de programadors i un tècnic de sistemes. En un grup així es maximitza la productivitat, però sempre se pot aspirar a més.

Per això quan tenc una estona estic jugant també amb un micro-framework anomenat bottle amb la idea de facilitar la feina de maquetació amb html pur. És veritat que es podria fer amb Django però la idea és comptar amb una via de visualitzar planes web i trocejar-les amb seccions que no requereixi pràcticament instal·lació i permeti fer maquetes ràpides i tener un prototip funcional que sigui molt bo de modificar. Una vegada el prototip està funcionant passar-ho a plantilles Django és trivial.

Com trivial és gestionar una empresa amb OpenERP. Un complet sistema ERP desenvolupat amb Python i que particularment amb té el cor robat. Ja tenim vàries instàncies funcionant i com que no ha de ser allò que diuen de "en casa del herrero...", una de les primeres gestions que tindrem serà la d'APSL. Si us estau plantejant deixar el Contaplus o semblant o voleu saber en temps real l'estat dels vostres comptes, fer factures i gestionar la cartera de clients, OpenERP és ideal, ja que pots externalitzar tota la part de comptabilitat i fiscal i dur el dia a dia del negoci gràcies a la potent interfície web que té. És un d'aquests projectes que fa anys que segueixo, des de que era TinyERP i el darrer any i mig ha arribat al volum crític que l'ha permès créixer exponencialment en funcionalitat i potència.

El que deia, molta calor, però no sé si és el sol o la quantitat de 'hot software' que he ha al voltant del l'ecosistema Python.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Haanga vs Django vs Tornado


Escrit per Aaloy a 15 de August , 2010 a les 5:42 p.m.

L'altra dia es va alliberar un llenguatge de plantilles inspirant amb Django anomenat Haanga patrocinat per Meneame.net.

Crec que això és una bono notícia, tenc la mala sort que quan he de tocar codi PHP sempre m'he trobat en que la separació de codi i lògica de presentació directament no hi és. Supòs que perquè els llenguatges de plantilles que hi ha són massa feixucs com per a que un programador de PHP se'ls plantegi en el seu dia a dia.

La sintaxi de plantilles de Django és lleugera per al programador/maquetador: és bona d'aprendre i molt poc intrussiva. Així que Haanga al meu entendre és un gran avanç, ja que té la senzillesa de Django i lleva l'excusa de la velicitat que sovint et donen alguns progrmadors de PHP per posar-ho tot junt, ja que compila les plantilles a PHP, amb la qual cosa tenim tots els beneficis i molt pocs emperòs.

Ricardo al seu blog fa una comparació de velocitat però s'ha de dir que no és ben bé justa, ja que no estam comparant el mateix, no en el cas de Django i Haanga al manco, ja que la comparació s'està fent en peticions per segon i contra el framework i no directament contra la part de plantilles. El framework de Django és quelcom més que les plantilles, aquestes són una part important però anecdòtica en termes de codi, ja que és força senzill canviar de motor de plantilles cap a qualsevol altra. El realment important d'un framework com Django és la facilitat que et dona per escalar tant en màquines com en programadors. L'estructuració del codi i el model MVT fa que sigui molt més senzill reaprofitar codi i mantenir codi existent.

Així doncs una comparació més justa seria la de generar les plantilles sense tenir en compte la part del framework. Per a ser justs també hem de modificar un poc el codi, Haanga fa ús de la compilació, però Django pot fer quelcom semblant, és a dir, podem cachejar les plantilles i reutilitzar-les, això, que ja era pràctica habitual en versions anteriors s'ha estandaritzat en la 1.2.

Si volem més velocitat llavors hem de començar a sacrificar funcionalitat, potser haurem d'anar cap a llenguatges de plantilles que facin el mateix que Haanga, és a dir, compilar el codi, en aquest cas a Python. Un bon exemple d'aquesta mena de llenguatges és el llenguatge de plantilles de Tornado o el de Mako. El primer és força interessant, ja que és d'un nivell de senzillesa i extensibilitat comparable Haanga.

He fet uns petits benchmarks considerant el cas més real, que és el de la generació del header.html. Per a fer la comparació justa he cachejat la compilació de plantilles tant en Django com Tornado, i en el cas de Django he utilitzat sols la part de plantilles. Per exemple el test b2_tpl.py queda:

from django.conf import settings
from django.template import Context
from django.template.loader import get_template
import time
settings.configure(TEMPLATE_DIRS=('../templates',), DEBUG=False,
               TEMPLATE_DEBUG=False)

class Info(object):
    def __init__(self, i, epoch):
        self.i = i
        self.epoch = epoch

t = get_template('b2_tpl.html') 
x = ( Info(i,time.time()) for i in range(1,100))
for i in range(1,100):
    print t.render(Context({'objects':x}))

En poques paraules: no hem de programar amb Python/Django como ho faríem en PHP. Per cert pos l'exemple perquè consider que és ideal per veure com es pot fer un script fent us de Django però sols agafant les parts de configuració que ens interessin. Podeu trobar més informació a l'apunt de b-list.

La part de benchmarks de Tornado està inspirada en el treball de Rolando Espinoza sols que he llevat la part de Tornado i he utilitzat sols la part de plantilles.

He fet servir la instrucció time per obtenir els temps i n'he fet la mitja de 5 execucions enviant la sortida a un arxiu.

El resultat és que Haanga ens dóna un temps de 0,1798s, Tornado un temps de 0,099s i Django un temps de 0,3438s. En termes relatius Tornado és 1,8 cops més ràpid que Haanga i 3,5 cops més ràpid que les plantilles de Django. A la seva vegada Haanga és 1,9 vegades més ràpid que les plantilles de Django.

Que Tornado sigui més ràpid no vol dir que tenguem que deixar el llenguatge de plantilles de Django. Hi ha més coses a més de la velocitat pura. Un usuari realment no notarà si la plantilla es genera en 3 o en 1 milisegon però sí que ho notarem a l'hora de mantenir l'aplicació el poder haver fet ús dels avantatges de les plantilles de Django. S'ha d'optimitzar on calgui.

En el cas de PHP i Haanga el consell però seria el de tirar-s'hi de cap. La mantenibilitat que dona tenir el codi separat en plantilles compensa de sobres els pocs milisegons de retard que tindríem el primer cop que es generàs la plantilla en PHP i la gent que tengui que mantenir el codi PHP us ho agrairà.

Enhorabonoa a crodas i a la gent de Menéame per a esponsoritzar un projecte així i obrir-lo al món.

PS. Hi havia un projecte del Google Summer of Code que anava en la línea de Haanga però per Django d'Alex Gaynor, però pel que es veu al final Alex es va decidir cap a un projecte relacionat amb bases de dades no relacionals per Django.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Integració del TVP CECA


Escrit per Aaloy a 26 de July , 2010 a les 8:20 p.m.

Després de barallar-m'hi un bon grapat de dies he aconseguit integrar el TPV de CECA en una de les aplicacions que estic desenvolupant per APSL. Com en el cas de la integració amb el BBVA he de dir que la qualitat de la documentació és inversament proporcional al suport que tens dels tècnics, per a que quedi clar: la documentació és pèssima, plena de inconsistències i exemples que no funcionen. El suport dels tècnics de CECA molt bo. Poc temps per a contestat (llevat de si demanes en divendres, clar) i respostes clares i concises. Un deu! Amb la gent del BBVA el mateix, se coneix que els ha tocat bregar amb la documentació.

No acab d'entendre què costa mantenir la documentació actualitzada. Si ja no es fa servir un programa per a generar la firma, llavors es refà la documentació i se'n lleva la referència. Si s'incorpora un tag obligatori nou que s'ha d'enviar, llavors s'actualitzen els exemples.

Per si a algú més li serveix faig cinc cèntims del que m'he trobat i del que funciona a l'hora d'escriure aquest apunt.

Generació de la firma

La firma és diferent per l'enviament i per la resposta. En ambdós casos es fa servir el xifrat sha1

Per l'enviament hem de fer una concatenació formada per:

    clau_encriptació
    merchantid
    adquirerbin
    terminalid
    num_operacion
    importe
    tipomoneda
    exponente
    referencia
    SHA1
    url_ok
    url_nok

El que és cada cosa està a la documentació, aquí el que s'ha de saber és: SHA1 és una cadena de caràcters i és necessari posar-la. La referència apareix a la documetació, però NO s'ha de posar, millor dit, és una cadena buida. El número d'operació és el que identificarà la nostra operació i vendrà també a la confirmació així que convé (i és necessari) que sigui un codi numèric únic. L'import és un nombre on les dues posicions representen els decimals, així 12.23 passa a ser 1223. Damunt la concatenació s'ha de fer l'sha1 i passar-ho tot a una cadena hexadecimal i a minúscules. Per exemple:

 m = hashlib.sha1()
 m.update(s)
 valor = m.hexdigest().lower()

El calcul de la firma per la signatura de la resposta és diferent: clave_encriptacion merchantid adquirerbin terminalid num_operacion importe tipomoneda exponente referencia

Fixau-vos que no hi ha SHA1 aquí i com a cosa important l'import s'ha de posar tal com ve de la resposta, veureu que ve completat a zeros per l'esquerra. En aquest cas la referència sí que ve i està generada per la pasarel·la de pagament.

Enviament de les dades

 <form id="pago_form" action="{{url_ceca}}" method="post" accept-charset="utf-8" enctype="application/x-www-form-urlencoded">
 <input id="MerchantID" name="MerchantID" type="hidden" value="{{merchant_id}}"/>
 <input id="AcquirerBIN" name="AcquirerBIN" type="hidden" value="{{acquirer_bin}}"/>
 <input id="TerminalID" name="TerminalID" type="hidden" value="{{terminal_id}}"/>
<input id="firma" name="Firma" type="hidden" value="{{firma}}"/>
<input id="importe" name="Importe" type="hidden" value="{{importe}}"/>
<input name="TipoMoneda" type="hidden" value="978"/>
<input name="Exponente" type="hidden" value="2"/>
<input id="referencia" name="Referencia" type="hidden" value=""/>
<input name="URL_OK" type="hidden" value="{{url_ok}}"/>
<input name="URL_NOK" type="hidden" value="{{url_nok}}"/>
<input name="Pago_soportado" type="hidden" value="SSL"/>
<input name="Cifrado" type="hidden" value="SHA1"/>
<input name="Idioma" type="hidden" value="1"/>
<input name="Descripcion" type="{{descripcion}}"/>
<input type="submit" value="Comprar" />
</form>

Si mirau la documentació veureu que qualsevol parescut amb els exemples és pura coincidència. Tots els camps hi són però no hi ha cap exemple que funcioni amb els camps que hi ha. L'import ha d'estar en el mateix format que la firma, és a dir com a nombre sencer on les dues darreres xifres representen la part decimal.

És important notar que la referència viatja buida i que s'ha de posar el camp Cifrado. L'exemple està agafat d'una plantilla Django i preparat per a funcionar amb Euros com a moneda i en castellà com a idioma.

Rebre la resposta

CECA us enviarà la resposta en un POST a la url que l'indiqueu. Ja he comentat abans el tema de la firma. Convé comprovar sempre que la firma de l'operació calculada amb els paràmetres que ens ha donat CECA coincideix amb el que rebem, d'altra manera implicaria que algú altra està intentant validar l'operació de pagament i marcar-la com a correcta quan no seria així.

Si feis com nosaltres la integració fent servir Django heu de tenir en compte que la protecció CSRF evita que pugui enviar-se un post com el que necessitam, així que hem de marcar la url com a exempta d'aquesta protecció. No fa gaire gràcia, però com a protecció addicional convindrà configurar el firewall per a que sols accepti peticions cap a la url confirmació des de les IPs de CECA.

Com a consideracions importants heu de tenir en compte que CECA passarà l'import completat a zeros per l'esquerra i que heu de guardar la referència i el camp Num_aut, ja que són els dos camps que CECA us demanaria en cas de reclamació. El camp Num_operacion se correspon amb el nombre d'operació que nosaltres hem enviat i ens servirà per a localitzar i processar la venda.

Esper que la informació sigui d'utilitat.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python APSL Django


Comparant Python i PHP: Xor encryption


Escrit per Aaloy a 18 de July , 2010 a les 10:26 a.m.

Quan xerram de Python una de les coses en que es fa més èmfasi és la claretat i expressivitat del llenguatge, que ens permet posar les nostres idees en forma de codi de manera ràpida i a més entenedora.

També és important remarcar que Python sovint necessite moltes menys línies de codi per fer el mateix que altres llenguatges, ja no dic un C, C++ o Java, sinó que un PHP, ja sigui per la pròpia potència de Python o bé per que ja hi ha una llibreria estàndard que té implementada aquella funcionalitat que cercam.

Aquesta setmana estava fent una integració amb la passarel·la de pagament del BBVA i m'he trobat amb un d'aquests casos. Per a la comunicació de les dades BBVA signa l'enviament i ho encripta fent servir l'algoristme d'encriptació xor. A la docuentació hi ha un exemple de vàries planes de codi de com es fa això amb PHP i Java/JSP.

Podeu trobar un exemple de com es fa amb PHP al SicilianGirl o també a l'IES Puig Castellar

La implementació de l'encriptació XOR en PHP, treta d'un snippet de php és:

function XOREncryption($InputString, $KeyPhrase){ 
    $KeyPhraseLength = strlen($KeyPhrase);
    // Loop trough input string
    for ($i = 0; $i < strlen($InputString); $i++){
       // Get key phrase character position
       $rPos = $i % $KeyPhraseLength;
       // Magic happens here:
       $r = ord($InputString[$i]) ^ ord($KeyPhrase[$rPos]);
      // Replace characters
      $InputString[$i] = chr($r);
}
return $InputString;
}

En Python això es faria així:

from itertools import izip, cycle 
def xor_crypt_string(data, key):
   return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(data, cycle(key)))

Python és un llenguatge molt potent, i part d'aquesta potència és la possibilitat de fer servir la programació funcional i els iteradors.

Com el seu nom indica cycle va retornant elements d'un iterable de manera cíclica, és a dir que quan arriba al darrer torna a començar.

izip té un nom potser més confús, ja que no té res a veure amb el popular format de compressió, és un iterador que donats dos iterables (dues llistes de caràcters en el nostre cas) ens retorna cada vegada una parella d'elements on el primer element pertany a la primera llista i el segon element a la segona.

Fixau-vos que el cycle ens permet eliminar el codi que manté el comptador al PHP, és a dir $rPos i tot el relacionat amb ell.

Una altra raó més per fer servir Python! :)


Traducciones/Translations by apertium

7 comentaris, 0 trackbacks (URL) , Tags: Python


Django caché invalidation


Escrit per Aaloy a 04 de July , 2010 a les 4:32 p.m.

Un dels problemes més importants del desenvolupament d'aplicacions web que necessiten suportar una gran quantitat de visités és el de decidir com es farà l'arquitectura de caché i quan i com s'invalida el contingut de la mateixa.

Al respecte he trobat dues presentacions realment excel·lents de la Djangocon:

Les dues són molt bones, però especialment us recoman la segona si voleu passar una estona divertida a més d'aprendre com funciona el tema de les cachés. Jared Kuolt broda una presentació plena d'ocurrències, dobles sentits i acudits freaks al mateix temps que aconsegueix introduir-nos en la problemàtica de les cachés.

La primera presentació té moltíssima informació, Michel Malone de Pownce explica els problemes que s'han trobat a Pownce i com els han anat solucionant a Django.

El problema fonamental de les cachés a Django és el tema de la invalidació. Django oculta bastant el nom de les claus que genera i sense aquestes claus un no pot anar a Memecached per invalidar-les.

La sol·lució passa invariablement per generar les nostres pròpies claus de manera repetible i fer un us dels signals de Django per a realitzar la invalidació de les cachés.

Això vol dir ser conscients de com està construïda la nostra aplicació, saber com es genera cada plana i poder enviar el senyal adeqüat per a que s'invalidi la caché quan es modifica cert contingut.

Per llocs amb una càrrega mitjana, les eines per defecte que ens dóna Django són més que suficients. El problema ens ho trobam quan tenim llocs amb mil·lions de visites o bé, quan tenim un lloc web amb relativament poques visites però on l'usuari no és conscient de la importància que té servir les planes ràpidament i per tant de la importància de les cachés.

És el tipus d'aplicació que necessita que qualsevol tipus de canvi es vegi reflexat de manera immediata a la web. Si el lloc és petit, senzillament es pot invalidar tota la caché, si el lloc comença a tenir moltes visites aquest tipus d'invalidació no és convenient i ens trobam gairebé amb la mateixa necessitat d'invalidació de cachés que poden tenir llocs amb moltes més visites.

Jared diu a la seva presentació que el tema del rendiment d'una aplicació web és soluciona amb ^ca(sh|che)$. Personalment crec que les dues opcions van lligades, ja que encara que no es faci la despesa en hardware, un lloc web que requereixi um molt bon rendiment i a la vegada tengui poca inversió amb ferro, necessitarà d'una gestió molt fina de les cachés i de la seva invalidació.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


L'editor de codi perfecte


Escrit per Aaloy a 26 de June , 2010 a les 10:29 a.m.

Amb la nova versió d'Eclipse Helios vaig donar una nova oportunitat a aquest entorn de desenvolupament. A primer cop d'ull no hi ha cap novetat espectacular, més si un no es dedica a la programació Java. La primera cosa que sobta és que no duu suport nadiu per Python, ni tan sols com a resaltat de sintaxi. D'això ja n'estava acostumant versió rera versió, però donat la gran empenta de Python dels darrers anys, pensava que Eclipse ja ho hauria inclòs entre els llenguatges més populars. Les enquestes de popularitat es fan de manera ben estranya a ca'n Eclipse.

Vaig instal·lar doncs PyDev amb la seva versió de desenvolupament, que és compatible amb Helios. Suport de sintaxi Python, depuració, autocompletat, etc. etc. Tot el que un pot desitjar, fin i tot un tímid suport per Django.

Tot aquest entorn, aquesta integració, té un preu en forma de consum de memòria. Prop de 800 MB, massa pel portàtil amb 2 GB de RAM que faig servir a casa.

El fet, però és que això fa que em plantegi el perquè d'estar provant editors per a programació. Supòs que com en el cas de les eines, sempre estam cercant la bala de plata, l'eina perfecta que ens farà més productius. O potser és perquè ens agrada tafanejar, mirar què hi ha de nou i les noves virgueries que incorpora cada nova versió de l'entorn de desenvolupament.

El fet, però, és que la productivitat no ens la donarà canviar d'eina de desenvolupament, el més segur que el canvi impliqui una pèrdua de productivitat a curt i mig plaç. La vertadera productivitat la tenim quan agafam un editor/entorn potent (posau aquí el que us agradi) i ens dedicam a conèixer-lo en profunditat, a personalitzar-lo i adaptar-lo a les nostres necessitats diàries de feina.

Una cosa tan senzilla com que l'editor ens deixi definir macros i crear plantilles pot augmentar la nostra productivitat en un tant per cent molt major que la nova rentada de cara de l'IDE i les noves icones.

Per tant, i supòs que ja ho sospitàveu, l'editor perfecte no existeix. Potser existeix un editor que s'adapta a una persona i feines concretes i que és el millor per aquesta persona en aquell moment, però tot i això, aquesta perfecció no s'aconsegueix d'entrada, sinó una vegada dominat l'editor en profunditat.

Personalment m'agrada provar nous editors i entorns, no puc evitar aquesta recerca de l'eina perfecta com si es tractàs de la font de l'eterna joventut. Però pel tipus de feina que estic fent ara crec que he trobat el meu editor perfecte: Vim i un bon munt de complements.

Però que sigui "perfecte" per mi, no vol dir que sigui perfecte per un altre. Si un fa feina fonamentalmetn amb javascript i css potser i no toca per res la consola de línea de comandes potser un Eclipse+Aptana li anirà millor, o el Notepad++ si fa feina amb Windows.

El crec que és important és decidir-se i donar una oportunitat a l'editor o entorn que vulguem utilitzar durant un temps, estudiar-lo un poc, fins i tot llegir-ne la documentació. Sols d'aquesta manera l'editor es convertirà en un factor de productivitat més.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Informàtica General Linux Python


Migració a postgres des de sqlite


Escrit per Aaloy a 13 de June , 2010 a les 2 p.m.

Pels qui no ho sabíeu aquest blog corria damunt una base de dades sqlite3. La base de dades és prou ràpida per les necessitats d'un blog com aquest, però té un emperò considerable: consumeix molta memòria comparada amb un mysql o postgresql. Quan el blog duia una parell de setmanes amb visites que consultàven molts apunts, sqlite començava a cachejar i el consum de memòria de l'aplicació del blog es disparava fins als 160 Mb, mass si ho comparam amb altres aplicacions tant o més complexes que executant-se amb Postgresql estàven entre 30 i 50 Mb. El consum de Postgres és una altra cosa, però com que es reparteix millor entre les aplicacions el resultat final és un estalvi de memòria.

El procés per passar d'sqlite3 a Postgres ha estat el següent:

  1. Feim un dump de les dades cap a json. Això es pot fer des de Django amb la comanda dumpdata, per exemple:

    python manage.py dumpdata contenttypes > dumps/contenttypes.json

He fet dumps de sites, auth per la part d'usuaris, contenttypes, i després de tota la resta d'aplicacions que fa servir el blog.

  1. Cream la base de dades i l'usuari a Postgresql que farem servir, donant-li permisos de creació de taules.

  2. Anam a l'aplicació i canviam la connexió de base de dades de sqlite3 a postgres. Per aixòs basta canviar el DATABASE_ENGINE cap a postgresql_psycopg2 i establir el nom de la base de dades i el password.

  3. Executam la comanda syncdb. Això ens crearà les estructures de dades que necessitam i ara ja poden amar restaurant les dades.

En el meu cas hi ha aplicacions que modifiquen el contenttypes quan es fa el sycndb, de tal manera que abans de restaurar he fet un trunc contentypes cascade a la base de dades.

  1. Restauram, per exemple:

    python manage.py loaddata ./dumps/auth.json

Anam repetint el procés. Començam per contentypes, després per sites, després per auth i després per les nostres aplicacions segons l'ordre de dependències que tenguin.

Això és tot, comprovam que tot és al seu lloc i recarregam l'aplicació.

A més he aprofitat que és diumenge per provar més coses. Això implica que el blog pot veure's una mica afectat, ja que vull provar errors 404, 500, ping cap a google i altres coses que vull provar en real.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Darrera setmana de juny


Escrit per Aaloy a 30 de May , 2010 a les 9:36 a.m.

Aquesta setmana he tocat molt poca cosa de Python i Django, el projecte que ens consumeix gran part del temps és una feina gairebé de rellotgeria però no és un projecte Python. Tot i això he fet servir Django per al prototipat d'algunes part de l'aplicació. És molt més senzill i ràpid muntar un prototip i fer-hi feina que tractar amb l'aplicació real, per molt que tenguem varis entorns de proves.

Estic revisant també els vídeos de la Djangocon Europe 2010. He vist el de Django a l'empresa, el dedicat a la testabilitat i el de GUnicorn. Aquest darrer m'ha agradat especialment ja que tenim plans per anar incorporant GUnicorn com a mètode de referència de desplegament d'aplicacions Django.

En aquests moments estam fent servir CherryPy i la veritat és que funciona molt bé. Encara que la màxima és "si funciona no ho canviïs" GUnicorn és un sistema WSGI molt més orientat a la feina que CherryPy, amb possibilitat de gestionar connexions síncrones i asíncrones. Els benchmarks que hi posen Gunicorn un poc pitjor que altres sistemses WSGI, però per notar-ne la diferència hem de gestionar un lloc web amb moooltes visites. En aquests casos el que ens puguin dir els benchmarks serveix de poc, ja que sempre s'ha d'ajustar la tecnologia al nostre cas concret.

El que té bo GUnicorn a més de l'orientació a la feina és que fa molt senzill desplegar una aplicació Django amb WSGI, fins i tot provant-la en local, està molt ben documentat i amb manteniment actiu.

Parlant d'una altra tema, dir que entenc perfectament a Ricardo quan diu que Amazon l'avorreix! El sistema de contingència que ha desenvolupat Bernat funciona molt bé. Això sí, no hi ha por que els administradors de sistemes es quedin fora feina. Es necessita un bon administrador per fer que tot rutlli, el que sí que hi ha és la tranquilitat de saber que no hi ha problemes de hardware. El valor afeget d'un tècnic d'IT és a més d'entendre com funciona Amazon AWS, crear els scripts necessaris per a automatitzar-ho tot, crear les alarmes, els tests, etc. Amazon no llevarà feina als administradors de sistemes, sols canvia el tipus i la qualitat de feina.

L'altra dia vaig posar la primera alfa-alfa del motor de reserves per hotels a l'entorn de test. Encara està molt verd per a poder-ho alliberar com a codi font, i no he tingut temps per polir alguns errors que té la prova. Per ara és un "naked dessign" on es mostren els contractes, la cerca de disponibilitat i permet modificar algunes coses. Falta documentar, no el codi, que hi està força, però sí fer una documentació amb Sphinx que faciliti entendre tot el que comporta. Si algú vol fer una ullada a l'aplicació que m'ho digui i li facilitaré l'accés.

Seguim intentant trobar un finançament mínim per donar major impuls al projecte, però encara no hem aconseguit res, així que la cosa per ara va tira a tira.

L'altra dia a una trobada es parlava de la necessitat que tenia la gent de WP de comptar amb un motor de reserves semblant al que estam desenvolupant. Crec que no és tan imporant que estigui dins WP com que es pugui fer una integració neta mitjançant cridades Rest, XML o XML-RPC.

No tenc clar quin pot ser el model de negoci d'una aplicació així: cobrar pr suport, per tenir una versió estable, per hostejar el servei?. El que sí tenc clar és que ha de ser de codi obert.

Però no tot ha de ser informàtica, he començat a llegir "Si és dolent t'ho recomano" de Steven Johnson, mentres esper que m'arribin d'Amazon UK dos llibres de gestió de projectes. Per ara Johnson m'està agradant força, té un punt de vista molt fresc respecte a com ens influeixen els canvis com els videjocs, els realities, etc. La seva teoria és que aquests canvis no ens fan més estúpids sinó tot el contrari. Quan l'acabi en faré una ressenya més extensa.


Traducciones/Translations by apertium

7 comentaris, 0 trackbacks (URL) , Tags: Informàtica Llibres i revistes Python Django


PHP o Python


Escrit per Aaloy a 30 de April , 2010 a les 8:25 p.m.

Avui he tingut una interessant conversa telefònica, una mini-consultoria d'una hora podríem dir, com a presa de contacte per a un projecte que sembla força interessant i ambiciós.

Com és habitual una de les preguntes ha estat per què Python i no PHP? La veritat és que no hi ha una resposta única, sempre depèn del projecte. No hi ha una tecnologia única que encaixi a tots els projectes i sempre s'ha d'avaluar bé. Però quan el projecte no té únicament una vessant web, les possibilitat de que Python encaixi millor són més altes. Després de tot Python és un llenguatge de propòsit general, que ha demostrat les seves capacitats amb branques tan diferents com la programació web o el càlcul numèric, passant per la generació de gràfiques científiques i el control de robots.

També, com gairebé sempre, surt el tema de que amb PHP es més fàcil trobar gent. És veritat, però també és més fàcil trobar gent amb Java o amb PL/SQL. El tema està en que si el que volem no és gent, sinó dur endavant un projecte, el que no necessitam és gent sinó bons programadors i aquí la cosa ja es complica.

El PHP és un llenguatge que té un nivell d'entrada molt baix. És molt fàcil començar a fer coses, d'aquí que molta gent amb pocs coneixements de programació comença amb PHP. A més posar una web en producció també és molt senzill. Un ftp i un accés a una base de dades és suficient per fer la major part de les webs. Això ens dóna una gran quantitat de programador PHP aficionat, fa aplicacions i surten, però no hi ha una metodologia darrera i sovint les aplicacions no són mantenibles.

Una primera conclusió doncs: és molt més senzill destriar els bons programadors Python que els bons programadors PHP. La feina és més senzilla. Si algú s'ha atracat a Python per programar ja significa que té inquietud per fer les coses bé. El nivell d'exigència inicial per a fer webs amb Python també és més alt, has d'aprendre un framework, saber configurar un servidor web optimitzat per a la teva aplicació, ... En definitiva, no te pots haver quedat amb els coneixements bàsics "per a que la cosa funcioni i prou", s'ha d'haver anat més enllà.

Està clar que en PHP també es pot programar bé. Llavors segurament el programador que entrevistarem ens dirà que coneix or fa feina amb un framework. Bé, això està bé. Però llavors ja hem perdut la hipòtesi inicial de que era més fàcil trobar programador amb PHP perquè a més el programador PHP ha de ser bo en PHP i bo amb el Framework i l'àmbit de cerca es redueix molt.

Fent feina amb un framework PHP també hem perdut un dels "avantatges" del PHP: la capacitat d'escriure codi ràpidament i posar-ho en producció. Quan poses un bastiment PHP les velocitats d'execució són ridícules si les comparam amb les de Python i Django. Eps, potser suficients pel que volem, però per res comparables. Però la velocitat d'execució no és el més important, el més important és la velocitat de desenvolupament, i posant un bastiment PHP també perdem això. Resulta ara que el PHP no és tan directe com pensàvem, ni hi ha tanta gent que el conegui com fèiem comptes.

Segona conclusió: Si volem fer servir un framework amb PHP per programar no tindrem tants programadors a on triar i perdem molt en velocitat d'execució.

Però és que a més el projecte és gran i s'ha de mantenir al llarg del temps. Què triam PHP o Python llavors? Si el projecte és gran no importa tant que el programadors coneguin el llenguatge, basta que n'hi hagi uns quants experts que pugin fer el mentoring i la formació i deixar que la gent es vagi fent amb el llenguatge. El que sí necessitam són bons programadors, gent que sàpiga programar bé i no tengui por d'aprendre coses noves.

En aquest cas Python també sortirà molt afavorit. Aquí ja estam parlat de comparar PHP+Framework PHP amb Pyton + Django. Python té una cosa sempre present: el codi ha de ser clar i mantenible, si no ho és no és pythonic. El PHP segueix una altra filosofia... Si hem de mantenir un projecte gran en el temps Python és una de les millor eleccions que es poden fer: és molt més difícil escriure codi il·legible i una vegada el programador s'ha impregnat de la filosofia que hi ha dins el llenguatge el codi surt sols. És divertit escriure i mantenir codi, perquè és fàcil llegir-lo. Pensem que quan es tracta de mantenir codi ens passarem molt més temps llegint el que altres han escrit que creant nou codi. Tenir un depurador a Python, poder fer unit tests amb facilitats (recordem que la llibreria de unit test forma part de la pròpia distribució de Python), tenir un servidor web integrat com a Django. Tot això fa que la feina de manteniment correctiu i evolutiu sigui més senzilla. La gent que heu/hem tingut que mantenir codi PHP i que ha pasasat a Python i Django segur que està fent capades d'assentiment.

Tercera conclusió: Python fa que el codi que s'ha de mantenir durant molt temps sigui molt més mantenible. Dedicarem menys temps a la depuració. Si ho complementam amb la política de Django d'estabilitat de versions la cosa ja és per nota.

Podem entrar també en les interioritats del llenguatge. Python és un llenguatge madur, amb llibreries molt ben establertes, ben documentat, ben pensat. PHP fins fa poc no tenia orientació a objectes i encara així escriure un objecte i utilitzar-ho amb PHP és força farragós si ho comparam amb Python. Tot a Python és un objecte. Però el que trob més important és la quantitat de codi que es necessita per fer la mateixa tasca. Per programes grans el codi Python és entre dues i 5 vegades més curt que el codi PHP. Això vol dir menys codi que depurar, menys codi que llegir. En definitiva codi més mantenible.

Però sobretot el més important de tot per mi és l'experiència que he tingut amb programadors de PHP que han passat a conèixer bé Python i Django. S'han divertit en el procés i es segueixen divertint programant amb Python. Per mi és un factor important, ja que un programador motivat crea millor i és un factor decisiu en l'èxit de qualsevo projecte. Així que aquí va la meva

Quarta conclusió: perquè Python és un llenguatge fotudament divertit per a programar o en la versió en anglès: It's a hell of a lot of fun to code again!

Per a més referències hi ha un article força extens que compara PHP i Python a la wiki de Python.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python


Actualitzat vimrc


Escrit per Aaloy a 03 de April , 2010 a les 11:59 a.m.

He actualitzat la meva configuració de .vimrc i els pluggins i ressaltat de sintaxi que hi ha a .vim

  • El subversion: http://code.google.com/p/appfusedjango/source/browse/#svn/trunk/myvim
  • El .vimrc
  • El .vim

Novetats

  • Substitució de snipEmu per snipmate. SnipMate fa si fa o no fa el mateix però té una sintaxi més senzilla i clara i permet fer nous snippets molt més fàcilment.
  • He afegit un nou ressaltat de sintaxi per json.
  • El colors per defecte per gvim passa a ser ara wombat i he canvait el tipus de lletra a DejaVu Sans Mono, ja que té una bona distinció entre la vocal O i el zero, entre l'u i la i, bastant millor per programar.
  • Activació per defecte dels menús i de la toolbar a gvim
  • Neteja de la configuració a .vimrc
  • Pos els alias a un fitxer apart dins ~/.vim/abbr, feu alias per veure el que hi ha.
  • Integració de més codi de pycopia especialment dels snippets per Django.

Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Testejant Django amb Nose


Escrit per Aaloy a 02 de April , 2010 a les 10:19 a.m.

A poc a poc però sense pausa estic embarcat en la creació d'un motor de reserves orientat cap a hotels i cadenes hoteleres. És a dir, no es tracta de fer un sistema genèric orientat a la integració d'xml com els que poden necessitar agències i TTOO, sinó de tenir quelcom flexible i ràpid de personalitzar orientat a cobrir les necessitats més o manco complexes de la venda directa on line de nits d'hotel.

És a dir, el sistema ha de cobrir el bàsic (gestió del nombre d'habitacions disponibles, tarifes, descomptes per ocupació, aturada de vendes, etc) però també ha de permetre cobrir necessitat que en aquests moments no coneixem. Per tant, tenir una bona bateria de tests que ens assegurin que afegint noves característiques no ens estam carregant les que ja hi ha és fonamental.

La idea del Test Driven Development és que s'han d'escriure els tests abans d'escriure el codi. Jo no sóc tan purista i els tests els escric quan els necessit, unes vegades abans i unes vegades després d'escriure el codi. La raó és molt senzilla, quan estic immers en l'escriptura de codi per a que passi un test, sovint me trob afegint noves característiques per les que no tenc cap test encara. Llavors crec que el millor és seguir a la zona de programació pura i dura i després escriure els tests. Crec que no és tan important el moment en el que s'escriuen els tests unitaris com el fet de tenir-los.

Un motor de reserves com el que descric es pot fer en qualsevol llenguatge, en el meu cas hem triat fer-ho amb Python i amb Django, ja que ens dóna molta flexibilitat posterior a l'hora de fer adaptacions, que és el que cercam. Així doncs la definició del model de dades i l'ORM que s'utilitza és el de Django, la qual cosa fa que sigui important poder testejar-ho amb les eines que ens proporciona aquest bastiment.

Llegint la documentació de Django podem veure que aquest fa servir els units test de tota la vida i que a més per a que els tests siguin realment unitaris el que fa es utilitzar una base de dades nova i neta cada vegada que executam un test, d'aquesta manera ens asseguram que no hi ha dependències entre diferents execucions de casos de prova i per tant que els tests són realment unitaris respecte a les dades.

Tot i que sigui una solució totalment vàlida, consider que eines com nose fan l'escriptura de tests unitaris una tasca molt més divertida, ja que no has de passar pena de com estructura els test, sinó simplement els has d'escriure amb unes convencions de codi (per exemple els tests han de començar o contenir la paraula test). Per mi això significa menys complicacions i poder reaprofitar petits troços de codi que tanmateix hauria necessitat escriure per provar l'aplicació sense tenir que donar-los el formalisme que necessita un unit test. L'ideal per mi és tenir nose integrat dins el sistema de test de Django.

Afortunadament més gent ha tingut aquesta idea i per sort per mi, gent amb un coneixement més profund que jo de nose a nivell intern com per fer-ne una adaptació per Django, us present el django-nose. És una aplicació que s'instal·la amb pip o easy_install i que necessita molt poca configuració, afegirem 'django_nose' al settings.py a la secció INSTALLED_APPS i afegirem TEST_RUNNER = 'django_nose.run_tests' per dir-li a Django que farem servir el nostre motor de tests enlloc dels seus.

La gràcia d'aquest mòdul i de nose és que és compatible amb el que ja hi havia, però a més afegeix molta més funcionalitat i agilitat a l'hora de crear els tests. Basta fer un python manage.py test --help per adonar-nos de tota la quantitat d'opcions que ens afegeix el mòdul a l'hora de testejar. D'aquestes m'agradaria destacar-ne algunes:

  • --with-coverage Ens permet utilitzar la utilitat de coverage de Ned Batchelder que ens permetrà veure quines línies de codi no hem testejat encara. Amb opcions addicionals és capaç de generar-nos un html navegable per a que poguem veure exactament el context de les línies testejades i de les que queden sense provar.

  • --processes Ens permet aprofitar els nuclis del nostre ordinador, els tests s'executen en paral·lel (recordau que els tests unitaris no han de tenir dependències entre ells) accelerant notablement el temps de procés.

  • --with-xunit Fa que en lloc de tenir la sortida típica dels unit tests tenguen el format de xunit, el mateix que es fa servir als unit test de Java, per exemple, i que ens permetrà integrar els nostres unit tests amb eines d'integració contínua com Hudson.

A l'hora de testejar una aplicació com el motor de reserves és imprescindible partir de dades conegudes i controlades per poder determinar els resultats esperats. Per això la manera que té Django de carregar les dades és simple i elegant. A cada unit test i abans de l'execució de cada cas de prova es crea la base de dades (en el meu cas un sqlite3 en memòria) i es carreguen les dades inicials (o fixtures en la nomenclatura) que es defineixen a cada unit test. Els fitxtures no són més que arxius en format json, yaml o xml que representen els registres que hi ha d'haver dins la base de dades.

Per exemple: [{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}} ]

Aquests arixus els podem crear nosaltres directament amb un editor com vim o bé aprofitar-nos de l'entorns de Django i crear-los a partir de l'admin. Així podem introduir les dades a la nostra base de dades i fer un

 ./manage.py dumpdata applicacio.model

per exemple:

 ./manage.py dumpdata sites.Site --indent=4

ens proporcionarà el codi json per al model Site, l'indent el que fa es proporcionar espaiat addicional per a que sigui més bo de llegir i de modificar.

Si ens va millor tractar amb yaml, és prou senzill canviar el format:

 python manage.py dumpdata sites.Site --format=yaml --indent=4

-   fields: {domain: example.com, name: example.com}
    model: sites.site
    pk: 1

Dins un mateix unit test podem carregar tants arixus de fixtures com vulguem, ja que basta definir una llista del que s'ha de carregar, això vol dir no tenir que tractar amb grans arixus de dades de proves, sinó poder-los fer molt més manejables i sobretot reaprofitables.

La combinació de fixtures, unit test de Python, eines de testeix de Django i nose és molt difícil de batre. Per una part tenim que escriure tests amb Python costa una fracció del temps i de codi respecte a escriure'ls en un altre llenguatge, però a més nose fa que la tasca sigui molt més natural i la capacitat de generar els fitxtures des del mananager de Django fa que una tasca avorrida es convertesqui en trivial.

Tenir una bona bateria de test és fonamental per provar l'aplicació, per demostrar que els casos que s'estan considerant funcionen i tenen les sortides que deim que han de tenir. Però la utilitat dels tests unitaris va més enllà. Són una eina imprescindible en la refactorització.

Una vegada l'aplicació ja fa el que volem arriba l'hora de plantejar-se si es pot fer millor, si l'aplicació pot ser més eficient, de refer el codi per a evitar repeticions. La regla fonamental de la refactorització és que no hem d'introduir funcionalitat nova, quan refactoritzam tot ha de funcionar com abans. Els tests unitaris ens ajuden a demostrar que la nostra refactorització és bona, en tant en quant passin exactament els mateixos tets que teníem abans de fer cap modificació.

Faceu Test Driven Development o agafeu una aproximació més pragmàtica, el cert és que tenir bons unit tests és una inversió de futur que costa sols un poc més de temps quan estam desenvolupant, ja que amb nose els tests es poden reaprofitar de les petites rutines per proves que tanmateix hauríem d'escriure. Els beneficis el veurem a mig i llarg plaç: quan refactoritzem, quan canviem codi, quan les proves les llanci un sistema d'integració contínua. És massa bo per a no fer-ho servir.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Django


Pip pip hurra!


Escrit per Aaloy a 13 de March , 2010 a les 12:08 p.m.

Què voleu, estic content de veure que una eina com pip funciona tan bé.

Pip és una eina per a la instal·lació de paquets i dependències per Python, molt més avançada que easy_install ja que permet tot el que feia easy_install però a més, permet mantenir d'una manera fàcil un arxius amb totes les dependències del nostre projecte.

Ens permet instal·lar paquests de Python des dels repositoris de Pypi, des de subversion, git, mercurial a través d'un fitxer de requeriments simple i a la vegada funcional.

Mode batalleta on:

Ahir vespre estava fent feina amb un projecte mascota que tinc, un motor de reserves per hotels i cadenes hoteleres fet amb Python i Django.

El projecte necessita de força llibreries: la darrera versió de django, nose pels tests, sphinx per la documentació, django debug toolbar, django extensions, south per la migració de dades, ipython, ipdb, pep8, coverage, pytlint. Tot això (i algunes més que s'hi afegiran) constitueixen l'entorn de desnvolupament del projecte.

Actualment faig feina amb tres ordinadors: el fix (el PPC del que tant he parlat per aquí), portàtil un Dell D820 i un Dell de 10". Segons on estic i el que he de fer en faig servir un o altra, o duc dins la borsa el portàtil gran o el petitó.

Per això el pip m'ha solucionat tant la vida. Ara quan desenvolup un projecte me basta mantenir al repositori del control de versions un arxiu amb els requeriments del projecte. Cada vegada que afegeixo una nova dependència no la instal manualment sinó que la pos a aquest arixu i execut

pip install -E $VIRTUAL_ENV -r requirements.txt

dins el meu virtualenv a partir d'aqui i quan he de canviar d'ordinador i vull tornar a fer feina en el projecte, basta baixar-me els canvis i tornar i tornar a executar a aquest ordinador l'ordre que he posat un poc més amunt. Amb una bona connexió en pocs segons (potser minuts) tindrem l'entorn de desenvolupament del programa totalment operatiu.

Mode batalleta off

Pel que en tingueu interés deix aquí un parell d'enllaços:


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Consoles per Python


Escrit per Aaloy a 08 de March , 2010 a les 8:08 p.m.

Quan hom comença amb Python qualsevol ajuda és benvinguda i quan ja en saps un poc més llavors el que cerques és poder provar coses ràpid, flexibilitat i eines potents.

Sigui com sigui un dels avantatges de Python com a llenguatge de programació interpretat és que ens proporciona una consola de comandaments que ens permet provar les coses d'una manera pràcticament immediata. Tot i això, com us dic, quan comences a aprendre'n la consola que hi ha per defecte potser no és el més amigable del món.

Us presento unes quantes consoles més per poder triar i remenar:

bpython

És una consola molt amigable per la gent que comença: té autocompletat per defecte i ens mostra la documentació de les funcions que anam teclejant. Ens permet copiar el nostre codi a Pastebin, amb la qual cosa ens facilita la tasca de comentar el que surt (o no surt) amb altres persones. Té també una versió per gtk (bconsole-gtk) i s'integra amb Django fent servir eines de tercers. És ideal per a començar a jugar amb l'entorn i aprendre la sintaxi de les comandes.

dreampie

És també una shell força interessant per la gent que comença, surt del típic prompt i presenta una secció on es pot anar introduint el nostre codi. Amb ctrl+intro el codi passa a la shell i s'executa. És multiplataforma i s'integra amb matplotlib (pels qui necessitin fer gràfiques). Com a cosa interessant, ens permet guardar la feina en html, ideal per muntar tutorials.

pyShell

Junt amb Pycrust són utilitats que podem trobar al paquet wxPython. Tenen autocompletat i mostren la documentació. Són consoles potents però requereixen tenir instal·lat wxPython. M'agrada PyCrust perquè et mostra un poc les interioritats de l'intèrpret de Python, té una secció anomenada namespace on pots veure tot l'espai de noms que hi ha carregat i navegar-hi. Per exemple fer un import os posa el paquet os a l'espai de nom i podem moure'ns i veure'n les funcions que té, constants i la documentació.

ipython

La meva preferida. Una consola potent, gairebé pot reemplaçar una shell per les tasques més habituals. Té resaltat de sintaxi i autocompletat, però ja has de saber un poc més el que estàs cercant. S'integra molt bé amb Django i permet executar directament comandes de shell. El manual és molt complet i dóna una idea de la potència de la consola.

idle

Python ja ve amb les bateries incloses. A més de la consola habitual també podem trobar l'idle una consola gràfica i editor. Té resaltat de sintaxi i autocompletat. Li han fet una bona rentada de cara darrerament i també és una opció a considerar. A més recordau que és a més un editor força complet per Python.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python


Vim IDE per Django i Python


Escrit per Aaloy a 17 de February , 2010 a les 8:18 p.m.

Link to Vim IDE per Django i Python

Encara que faig servir distints editors i entorns integrats (IDE) per programar en Python i Django hi ha sempre la constant de retornar cap a Vim i gVim.

La cosa està però, en que per al desenvolupament normal no vull renunciar a un parell de coses que fan la vida més fàcil:

  • Resaltat de sintaxi amb colors personalitzables i/o una paleta de colors còmoda per fer-hi feina.
  • Autocompletat (dins cert límits, que això és un llenguatge dinàmic) i ajuda integrada.
  • Plantilles per no haver d'escriure molt. Per exemple els shebangs, o els models de Django.
  • Distints tipus de tabulació segons el llenguatge, quatre per Python, però 2 per HTML i Javascript.
  • Possibilitat de tenir oberts molts arxius a la vegada i accedir-hi fàcilment
  • Navegació pel sistema d'arxius integrada

I poca cosa més. Després quant més potent sigui l'editor millor, i per això Vim n'és de potent!! El problema és que ja m'agradaria poder fer servir amb agilitat un 20% de les seves capacitats.

En la meva recerca de l'editor perfecte he anat modificant el .vimrc i afegint plugins diversos, i configuracions que anat trobant d'aquí i d'allà. Per si a algú li va bé, he posat el meu .vimrc i .vim amb els plugins a l'appusedjango. Ja me contareu!

Eines per a la isntal·lació de plugins

Si feis un apt-get vim-addons obtindreu una petita utlitat que us permetrà veure quins plugins teniu instal·lats al vostres sistema i activar-los pel vostre usuari. En el meu cas tenc:

bufexplorer                 installed     
markdown-syntax             installed            
matchit                     installed            
python-indent               installed            
python_bike                 installed            
supertab                    installed            
surround                    installed            
taglist                     installed            
utl                         installed            
winmanager                  installed            
xmledit                     installed

En local (i instal·lats a mà) tenc també:

  • ftplugin
  • nerdtree_plugin
  • snippetsEmu
  • taglist
  • mathit
  • supertab
  • vcssvn

Hi ha altres plugins interessants com el nerdcommenter i altres, però encara m'he d'anar acostumant al altres.

Referències

La veritat és que em costa dir d'on ho he tret tot, la configuració és una feina orgànica, he anat agafant coses d'aquí i d'allà, així que pos els darrers consultats.

Disclaimer: NO sóc cap expert amb vim, així que moltes coses van per assaig i error.

Download

  • El subversion: http://code.google.com/p/appfusedjango/source/browse/#svn/trunk/myvim

  • El .vimrc

  • El .vim

Al svn trobareu un .vimrc que heu de posar al vostre home i un arxiu comprimit amb .vim que conté plugins, plantilles i demés, descomprimiu-lo també al vostre home.

No he de recordar la impirtància de fer còpies de seguretat de la configuració antiga abans de res, veritat?

Pels debianites i ubuntaires

Per a tenir l'entorn funcional necessitareu instal·lar

  • sudo aptitude ctags
  • sudo aptitude vim-addons-manager
  • sudo aptitude vim-python (segons versions...)

comprovat per bibigeek (gràcies!) pels ubuntaires amb PPC com jo, no hi ha vim-python i convé recompilar vim amb suport per Python.

Esper que us sigui d'utilitat!


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Ressenya de creant Bits, el déjà vu


Escrit per Aaloy a 30 de January , 2010 a les 10:41 a.m.

El segon creant bits dedicat a la introducció de Python i Django d'ahir va a tornar aplegar un bon nombre de gent interessada per la programació i per Python i Django.

Cares conegudes i gent que vaig poder desvirtualitzar. Em va fer especial il·lusió poder desvirtualitzar Guillem, ja que per un motiu o altre mai ens havíem pogut trobar personalment.

Aquesta vegegada preferírem no allargar molt la jornada i no es donà la xerrada damunt la posada en producció d'aplicacions Django. La passada jornada En Bernat va tenir molt poc temps i acabarem molt tard, així que ho hem deixat per a una millor ocasió.

Aquest pic el consum de gominolas per part dels assistents va ser menor, lluny del rècord de kilo i busques de l'altra vegada. :-P La idea és que si algú s'avorreix al manco se n'endurà un sabor dolç de boca.

Entre l'anecdotari comentar el mal cos que se'ns quedà a tots quan un tassó d'aigua va vessar damunt un portàtil Macbook Pro nou de trinca. Després de netejar-lo va tornar a la vida i esper que segueixi així. Hi va haver un segon intent de tragèdia, quan el que es va vessar va ser cafè i no aigua, afortunadament aquest cop no va tocar el portàtil.

Sols me queda agraïr l'assistència de tots i especialment dels companys que donàren l'assistència tècnica i ajuda. Esper que tothom se n'anàs amb una millor idea de la que tenia a l'entrada damunt el que és Python, el que es pot fer i de la potència de Django per al desenvolupament web.

Ara a encalentir motors per a la xerrada de Ricardo al proper creant bits.


Traducciones/Translations by apertium

7 comentaris, 0 trackbacks (URL) , Tags: Python Django


FireLogger per Python


Escrit per Aaloy a 15 de January , 2010 a les 3:42 p.m.

Quan hom fa feina amb Django una de les primeres coses que aprèn és a mirar la consola del servidor de desenvolupament. Al la consola hi apareixen els missatges d'error i els logs bé en forma de prints o com a logs de Python.

Convé evitar fer prints i fer servir els logs. Aprofitarem el funcionament del logger per tal de discrimitar els tipus de log i distingir entre els missatges que volem que es mostrin sols en depuració (DEBUG), errors o informatious.

Una configuració molt bàsica del logs és la que propòs a projecte base d'appfusedjango, molt ràpidament:

Al properties.py o al settings configuram el sistema de log

1
2
3
4
5
import logging
logging.basicConfig(
    format="%(asctime)s-%(levelname)s-%(name)s-%(lineno)s-%(message)s",
    level = logging.DEBUG,
)

i a cada arxiu on el volguem fer servir

1
2
import logging
log = logging.getLogger(__name__)

Això ens permte configurar a un sols lloc el nivell de log que volguem i a més saber des d'on s'estan generant els missatges.

Com a retruc, a més ens servirà per poder mostrar els logs a la consola del Firebug gràcies a l'aplicació FireLogger.

Aquesta aplicació té una instal·lació en dues parts, ja que hem d'instal·lar el plugin de Firefox que hi trobareu a la plana (la versió 0.7 en el moment d'escriure això) i després instal·lar les llibreries Python necessàries.

La plana recomana fer servir easy_install però en aquests moments el paquet que hi ha al PyPi està desactualitzat així que millor instal·lar-ho a mà:

 sudo easy_install paver
 sudo easy_install  jsonpickle
 git clone git://github.com/darwin/firepython.git
 cd firepython
 sudo python setup.py install

Amb això a una Ubuntu basta. Una vegada insta·lat anirem a la nostra palicació Django i a la secció del MIDDLEWARE_CLASSES afegirem

 firepython.middleware.FirePythonDjango

Podem obrir ara la consola de Firebug i trobarem una nova secció anomenada Logger. Aquí apareixeran els missatges de Log de la nostra aplicació. Podem filtrar per la criticitat del log i a la mateixa vegada tenim tota la potència del Firebug per a la depuració de l'aplicació.

Una bona eina per tenir al caixó de les de desenvolupament i depuració.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Creant Bits, el déjà vu


Escrit per Aaloy a 07 de January , 2010 a les 9:24 p.m.

Em complau anunciar una segona edició de Creant Bits destinada a tots aquells i aquelles que no poguéreu assistir a la primera.

Els contingut seran bàsicament els mateixos, en tot cas mirarem de resoldre algunes mancances de la primera presentació, però en un 99% serà tot el mateix:

  • Introducció bàsica al llenguatge Python, amb exercicis.
  • Introducció a Django: arquitectura i possibilitats
  • Instal·lació d'una aplicació Django a Apache.

La sala serà la mateixa que a la presentació anterior.

Creant Bits, el déjà vu

29 de gener de les 16:00 a les 21:00

Sala de Formació - Parc Bit

Pensau a dur el portàtil carregat amb el Python instal·lat. Hi ha connexió inhalàmbrica a la sala i el Parc Bit ens deixa un projector.

La sala té una capacitat per a 20 persones màxim. Per apuntar-vos deixau un comentari a aquest apunt.

Per cert, aquesta vegada tampoc hi ha catering! :)


Traducciones/Translations by apertium

23 comentaris, 0 trackbacks (URL) , Tags: Python APSL Django


Si House fos programador ...


Escrit per Aaloy a 04 de January , 2010 a les 7:35 p.m.

Ahir estava mirant la presentació de James Bennet a la DjangoCon anomenada "UR DOIN IT WRONG" i me'n vaig adonar que feia referència a una sèrie de màximes tipus:

#11919 No.  You must believe the ERROR MESSAGE.  You MUST believe the error message.

La conferència és molt bona, us la recoman. El cas, però, és que em va picar la curiositat i vaig seguir l'enllaç fins a arribar a una entrada de comp.lang.perl.misc del març del 2002 on Mark Jason Dominus feia una relació de consells que ell tenia a un arxiu anomenat File of Good Advice.

Les màximes, encara que plenes de sentit comú, tenen una mala llet considerable, i m'han recordat al nostre metge de la tele favorit. Supòs que no desapareixeran de la xarxa, però per si un cas les torn a escriure aquí. Ha passat temps, però la majoria són perfectament aplicables! Esper que les disfruteu tant com jo ho he fet.

#11900 You cannot just paste code with no understanding of what is going on 
 and expect it to work.
#11901 You can't just make shit up and expect the computer to know what you
 mean, Retardo!
#11902 You said it didn't work, but you didn't say what it would have done if
 it *had* worked.
#11903 What are you really trying to accomplish here?
#11904 Who the fuck cares which one is faster?
#11905 Now is the time in our program where you look at the manual.
#11906 Look at the error message!  Look at the error message!
#11907 Looking for a compiler bug is the strategy of LAST resort.  LAST resort.
#11908 Premature optimization is the root of all evil.
#11909 Bad programmer!  No cookie!
#11910 I see you omitted $! from the error message.   
It won't tell you what went wrong if you don't ask it to.
#11911 You wrote the same thing twice here.  The cardinal rule of programming 
 is that you never ever write the same thing twice.
#11912 Evidently it's important to you to get the wrong answer as quickly as
 possible.
#11913 Gee, I don't know.  I wonder what the manual says about that?
#11914 Well, no duh.  That's because you ignored the error message, dimwit.
#11915 Only Sherlock Holmes can debug the program by pure deduction from the
 output. You are not Sherlock Holmes.  Run the fucking debugger already.
#11916 Always ignore the second error message unless the meaning is obvious.
#11917 Read.  Learn.  Evolve.
#11918 Well, then get one that *does* do auto-indent.  You can't do good work
 with bad tools.
#11919 No.  You must believe the ERROR MESSAGE.  
 You MUST believe the error message.
#11920 The error message is the Truth.  The error message is God.  
#11921 It could be anything.  
 Too bad you didn't bother to diagnose the error, huh?
#11922 You don't suppress error messages, you dumbass, you PAY ATTENTION 
 and try to understand them.
#11923 Never catch a signal except as a last resort.
#11924 Well, if you don't know what it does, 
 why did you put it in your program?
#11925 Gosh, that wasn't very bright, was it?
#11926 That's like taking a crap on someone's doorstep and then ringing 
 the doorbell to ask for toilet paper.
#11927 A good approach to that problem would be to hire a computer programmer.
#11928 First get a book on programming.  Then read it.  Then write the program.
#11929 First ask yourself `How would I do this without a computer?'  Then have
 the computer do it the same way.
#11930 Would you like to see my rate card?
#11931 I think you are asking the wrong question here.
#11932 Holy cow.
#11933 Because it's a syntax error.
#11934 Because this is Perl, not C.
#11935 Because this is Perl, not Lisp.
#11936 Because that's the way it is.
#11937 Because.
#11938 If you have `some weird error', the problem is probably with your
 frobnitzer.
#11939 Because the computer cannot read your mind.  Guess what?  I cannot read
 your mind *either*.
#11940 You said `It doesn't work'.  The next violation will be punished by
 death.
#11941 Of course it doesn't work!  That's because you don't know what you are
 doing!
#11942 Sure, but you have to have some understanding also.
#11943 Ah yes, and you are the first person to have noticed this bug since
1987.  Sure.
#11944 Yes, that's what it's supposed to do when you say that.
#11945 Well, what did you expect?
#11946 Perhaps you have forgotten that this is an engineering discipline, not 
 some sort of black magic.
#11947 You know, this sort of thing is amenable to experimental observation.
#11948 Perhaps your veeblefitzer is clogged.
#11949 What happens when you try?
#11950 Now you are just being superstitious.  
#11951 Your question has exceeded the system limit for pronouns in a single 
 sentence.  Please dereference and try again.
#11952 In my experience that is a bad strategy, because the people who ask 
such questions are the ones who paste the answer into their program without 
understanding it and then complain that it `does not work'.
#11953 Of course, this is a heuristic, which is a fancy way of saying that it 
 doesn't work.
#11954 If your function is written correctly, it will handle an empty array 
 the same way as a nonempty array.
#11955 When in doubt, use brute force.
#11956 Well, it might be more intuitive that way, but it would also be useless.
#11957 Show the code.
#11958 The bug is in you, not in Perl.
#11959 Cargo-cult.
#11960 So you threw in some random punctuation for no particular reason, and 
 then you didn't get the result you expected.  Hmmmm.
#11961 How should I know what is wrong when I haven't even seen the code?  
 I am not clairvoyant.
#11962 How should I know how to do what you want when you didn't say what you
 wanted to do?
#11963 It's easy to get the *wrong* answer in O(1) time.
#11964 I guess this just goes to show that you can lead a horse to water, but 
 you can't make him drink it.
#11999 You are a stupid asshole.  Shut the fuck up.

Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django Conyes marineres


Connectant el blog amb Twitter


Escrit per Aaloy a 30 de December , 2009 a les 5:06 p.m.

Feia estona que volia connectar el blog amb Twitter, de manera que cada vegada que escrigui un post s'envii directament a Twitter sense tenir que fer-ho jo. No és gran cosa, però és la vagueria informàtica: si es pot automatitzar perquè fer-ho a mà? :)

De llibreries de connexió cap a Twitter des de Python n'hi ha un bon grapat, la majoria el que fan és crear un envolcall a l'API de Twitter per tal que sigui més manejable des de Python i evitar repeticions de codi.

La llibreria que jo he fet servir s'anomena twython i té el mèrit de ser la primera que he provat. Potser n'hi ha de millors o més completes, però pel que he de fer ja basta.

El que vull doncs és que cada vegada que escrigui un nou post, s'envii un nou Twitt. Per fer-ho hi havia dues opcions clares: sobrescriure el mètode save del model o bé utilitzar senyals.

He triat fer-ho amb senyals ja que m'estim més no fer massa complexe el mètode save, i a més la senyal post_save ja ens informa de si s'ha gravat un registre nou o un registre antic. El codi de fet és tan senzill com això:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Signals
def twitt_post(sender, **kwargs):
    is_new = kwargs.get('created')
    if settings.SEND_TWITT_ON_POST and is_new:
        instance = kwargs.get('instance')
        import twython
        try:
            twitter = twython.core.setup(username = settings.TWITTER_USER,
                password = settings.TWITTER_PASSWORD)
            twitter.updateStatus(_(u"Nou post al Blog de Trespams: %s %s") %
             (instance.headline, instance.get_absolute_url(), ) )
        except AuthError, e:
            logging.error("Error in twitter account")
            logging.error(e.message)
        except Exception, e:
            loggin.error('Error in twitter post %s' % e.message)

# Connect the signal with the callback
post_save.connect(twitt_post, sender=Entry)

twitt_post és el callback és a dir, la funció que es cridarà quan es gestioni la senyal, com a arguments passa la classe, la instància i si el registre es nou o no.

Obtenim primer si el post es nou o no, no vull empipar a la gent cada vegada que faci una correcció ortogràfica o tipogràfica, així que sols enviaré els twitts quan el post sigui nou. També he fet que sols s'envii si la variable de configuració SEND_TWITT_ON_POST així ho diu.

Després ja és cosa de utilitzar la llibreria (login i enviar, així de fàcil) i capturar les possibles excepcions.

Finalment el que feim és connectar l'event amb el model i la funció callback.

Si tot va bé aquest post ja s'hauria d'enviar automàticament.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


I trobaré gent que sàpiga Python?


Escrit per Aaloy a 30 de December , 2009 a les 1:06 p.m.

Sovint quan plantejam que un desenvolupament ho farem amb Python i Django, la gent de les primeres coses que demana és a veure si trobarà gent que sàpiga Python i Django per dur el manteniment posterior, o si nosaltres deixam el negoci, o si ha de contractar algú, ...

Qui està un poc aficat en el tema de la programació web sap que és relativament senzill trobar gent de coneix PHP o Java, però encara el tema Python no està tan arrelat al subconscient com per a que soni com a llenguatge de referència.

Idependentment del que al final s'opti per un llenguatge o un altre, el "trobar gent" serà tant o més complicat en PHP o Java como ho pot ser en Python, ja que conèixer el llenguatge no implica necessàriament poder-se fer càrrec de l'aplicació o que el manteniment de la mateixa sigui senzill. És més, m'atreviria a dir que molts programes web que hi ha en PHP o Java són molt complexes de mantenir per algú aliè al desenvolupament inicial.

Aprendre un llenguatge és relativament senzill, conèixer-ne la sintaxi i poder llegir el codi pot dur pocs dies, setmanes si m'apurau. Després la tasca és saber com està fet el programa, entendre què fa i començar a trobar on s'ha de modificar cada cosa, i és aquí on el llenguatge importa menys que com s'ha desenvolupat el programa.

El model Python i Django fa molt més senzill fer programes bons de seguir, la sintaxi de Python es legible per pròpia construcció del llenguatge, i un programa que utilitzi Django com a bastiment i que segueixi les convencions de codi de Django mateix i de Python serà molt menys propens a les "sorpreses" que un típic programa PHP que segueix sols les convencions del seu programador, i segurament que a un programa Java-J2EE on vet a saber quines llibreries o tecnologia s'haurà utilitzat.

Amb això no vull dir que no es puguin fer programes mantenibles en PHP o Java, sinó que si el que volem en un futur és prendre el control del desenvolupament d'un programa que hem comanat a un tercer, el més important no és tant el llenguatge de programació sinó com estigui estructurat el programa, la legibilitat del codi, el comentaris i la documentació.

Quan un desenvolupament està fet en Python i Django augmentant les nostres possibilitats de que sigui mantenible posteriorment. Potser necessitarem un temps previ de formació en el llenguatge (o formar als nostres programadors interns), però aquest temps es veurà de sobres compensat per la facilitat de manteniment posterior. Per mi és una inversió de futur.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Aquest 2009 s'acaba


Escrit per Aaloy a 20 de December , 2009 a les 1:38 p.m.

El 2009 ja gairebé s'ha acabat, així que com és habitual convé fer un poc de recapitulació del que ha estat l'any 2009 i definir el que esper del 2010.

Trespams

Sense haver acabat l'any aquest és l'apunt que fa 77, això vol dir que ha estat un any prou constant en el que és la publicació d'apunts en el blog, . M'agrada escriure, em relaxa i em permet posar idees en clar. Si a més aquests posts serveixen a algú més, encara que sols sigui per passar l'estona, crec que s'ho paga el petit esforç de posar-se davant l'ordinador i teclejar.

En aquest moments Trespams té uns 900 visitants més o manco habituals. D'aquest n'he pogut desvirtualtitzar un tant per cent molt petit, potser un 10%, alguns han deixat comentaris i hem pogut mantenir converses virtuals tant pel blog, correu o Twitter. Per mi és una de les experiències més gratificants del blog: poder compartir idees i pensaments amb comunicació bidireccional.

Des del principi el blog ha estat per mi essencial a l'hora de posar en ordre les meves idees i dèries. La gestió de projectes, la gestió d'equips de programació, l'estimació de projectes de programari, Python i Django. Tot sempre amb un fil conductor comú: el programari lliure.

El blog vol ser també part de la meva petita aportació al moviment del programari lliure. Programari lliure per mi significa no sols compartir codi, sinó compartir idees de com podem crear i gestionar aquest codi, eines, idees, ... El coneixement ha de fluir per a que tots com a societat ens en puguem beneficiar.

Python i Django

Per Python i Django també ha estat un bon any. Ha sortit l'esperada versió 3 de Python i Django ha assolit una velocitat de creuer que el consolida com un dels bastiments de referència en la programació web moderna. La llista de Django té uns 15.000 subscriptors, al repositori de projectes de Python, PyPi hi ha una cinquantena de projectes i actualitzacions de projectes diàriament.

Esper que el 2010 torni a ser un any Python, projectes com PyPy i Unladen Swallow poden donar encara més empenta a aquest fantàstic llenguatge. Esperem que l'onada Python arribi també a les empreses per a que tots ens puguem gaudir d'una programació més clara, mantenible i sobretot divertida, on el llenguatge no sigui un condicionant sinó un vehicle per a la creació de programes i la generació de valor per al negoci i en definitiva per a la societat.

Pel 2010 l'objectiu és anar creant més exemples a Appfusedjango, millorar-ne la documentació amb Sphinx. Voldria també millorar el codi d'aquest blog, fer-lo més accessible als dispositius mòbils. M'agradaria poder fer aportacions al projecte Basie, un projecte amb el qual he pogut coneixer noves formes de col·laboració, de control del codi, d'eines, nova gent.

Tot això farcit d'apunts en aquest blog, com a manera de presentar el que m'agrada, d'animar a la gent a participar, i com ja he dit, com a manera d'ordenar les meves pròpies idees. Es presenta doncs un any 2010 força interessant.

Creant Bits

Creant bits és la demostració del que es pot fer quan les idees es converteixen en accions. Un petit comentari al Twitter i la col·laboració de molta gent va fer possible que una vintena de persones ens trobàssim al Parc Bit per parlar de tecnologia, de Python i de Django.

La meva intenció és repetir-ho al llarg del 2010 i més si tenim disponibilitat de Sala. En aquests moments i baix el paraigües d'APSL tenim accés a les sales de formació del Parc Bit i convé aprofitar-ho. Quan no hi tenguem accés ja veurem que feim, però m'agradaria que fos quelcom que anàs perdurant en el temps fins que el cos aguanti i la gent no es cansi.

L'altra dia per un comentari que vaig fer al Twitter d'un curs de Python se'm va demanar si Creant Bits seguiria essent gratuït. La resposta es sí, Creant Bits és una aportació al moviment del programari lliure, com ho poden ser alguns dels apunts del blog, o altres projectes en els particip. No crec que es pugin considerar cursos en el sentit que l'objectiu del Creants Bits no és que la gent surti amb un coneixement profund de la tecnologia, sinó el de presentar el que es pot fer, parlar, reunir-nos i animar a la gent a provar coses, donant-los el primer impuls.

Els cursos sí que els cobr. Quan una empres em demana un curs de Python i Django els objectius és que la gent que participi surti amb un domini del temari que els faci ser productius una vegada acabat el curs. Són moltes hores de curs i moltes hores de preparació per la meva part. L'horari del curs, la localització i els assistents són responsabilitat de l'empresa que em contracta i és aquesta qui fitxa els objectius.

A Creant Bits ens reunim amics i coneguts, gent que ja ens coneixem de manera física i virtual o que tenim ganes de conèixer-nos, amb ganes d'aprendre coses i relacionar-nos. Potser al llarg del temps i amb diferents trobades la gent que participa es veurà amb un coneixements semblants als que tindria amb un curs formal, però això serà tant per l'impuls de la xerrada com per la seva iniciativa personal, i això crec que és la diferència fonamental amb un curs. A un curs vols que la gent surti preparada amb tants coneixements com sigui possible en un temps raonable, a una trobada com Creant Bit a mi el que m'agradaria és que la gent sortís amb motivació per poder començar, amb una petita llum a la foscor, que permeti, amb el seu esforça personal, avançar en el món del programari.

M'estic extenent molt amb aquest tros, però és que veure tanta gent reunida perquè sí per mi ha estat molt important, ja que ha representat passar del món de les idees al món de l'acció, del món de les intencions als fets. L'injecció de moral per mi (i esper que per als participants) ha estat grandiosa i tenc ganes de repetir l'experiència al 2010.

APSL

Al 2009 hem consolidat APSL, l'empresa de la que són CEO i soci. És una empresa atípica, feta a la nostra manera d'entendre els projectes, amb l'ètica al davant, sense voler tenir clients captius sinó essent-ne col·laboradors. Volem fer partíceps a les empreses del que significa el programari lliure, de com les coses es poden fer d'una altra manera fins i tot amb els pressuposts.

Estam intentant rompre amb la idea de pressuposts tancats per a projectes de programari. Creïem amb la idea de que el pressupost inicial ha de ser orientatiu, que després el que importa és que el programa que s'entregui representi el que necessiti l'empresa, que no és necessàriament el que l'empresa vol a l'inici del projecte.

No és una tasca senzilla, representa canviar un poc les regles del joc. Actualment les negociacions d'un projecte sempre estan encaminades a que el risc del projecte ho assumeixi una de les parts. El client intenta que sigui l'empesa desenvolupadora la que assumesqui el risc intentant tancar el mínim possible. L'empresa de programació intentant minimitzar el risc mirant de tancar-ho tot i de protegir-se en el pressupost. És una situació un tant perversa, en la qual tothom hi perd en un moment o l'altra, com en una ruleta russa.

Tot projecte té un risc i aquest hauria de ser compartit i minimitzat. Creim amb la idea d'Scrum com a metodologia de desenvolupament i com a manera de facturar a un projecte. El client assumeix un cert risc: el pagament anticipat d'una quantitat i l'empresa n'assumeix un altre: que l'empresa en qualsevol fita del projecte pugui tancar-lo, dient que el que té ja és el que volia o donar-lo a un altre proveïdor.

El 2010 m'agradaria que fos un any de creixement per APSL perquè voldria dir que aquesta filosofia de treball i gestió ha estat entesa, que una altra manera d'entendre la relació empresa-client és possible.

Gestió de projectes

A 2010 m'agradaria aprofundir en el tema de l'estimació de projectes des d'un punt de vista col·laboratiu. Una de les mancances que tenim com a col·lectiu és que estam massa encaixonats dins la nostra manera de veure les coses, potser sense tenir massa idea del mercat. Són les nostres estimacions raonables? Són les nostres maneres de pressupostar adients? Som competitius? Podem fer alguna cosa per millorar les nostres estimacions?

Crec que es un punt amb la cooperació hi té molt a dir. On podem col·laborar explicant projectes, explicant el perquè de les valoracions i el temps total, per a que serveixin de referència. També es podrien organitzar sessions d'estimació àgil, amb Planning poker estimation. És una idea a la que estic donant voltes però que encara no sé molt bé com organitzar.

També m'agradaria posar en marxa algun tipus de projecte col·laboratiu local, potser lligat al Creant Bits, que servesqui no sols per aprendre sinó també per tenir un producte que pugui beneficiar-nos a tots.

Moltes idees i molts projectes que m'agradaria fer. Tot això s'ha d'acompassar necessàriament amb la dedicació a la família, amb els projectes alimenticis i amb altres projectes que no estan lligats a la informàtica que m'agradaria assolir. Per exemple, no tenc cap coneixement de llenguatge musical, i és una cosa que des de fa anys m'agradaria aprendre. Potser el 2010 serà l'any...

L'any de la crisi

El 2009 passarà per ser l'any de la crisi, però tot i això crec que hem viscut temps interessants. El problema amb la famosa crisi és que tot s'ha enlentit, és com si haguéssim perdut un any, estant a l'expectativa, a veure-les venir. Per mi aquesta expectativa ha estat doble, ja que en hem trobat amb la fusió de TUIE i Hotelbeds, amb la qual cosa molts projectes s'ha paralitzat a l'espera del que passaria.

Com es diu "qui espera desespera", però crec que no ha estat el cas. La sensació de pèrdua de temps és intensa, però tot i això projectes com APSL, el Creant Bits, els passejos amb els nins amb bicicleta i els amics del Twitter no em queda la sensació d'any perdut, sinó d'any de reflexió, de tenir temps de descobrir coses noves i punts de vista diferents.

Encara que molts (la majoria) de pressuposts presentats encara estan al caixó d'algú, poder parlar amb els clients crec que m'ha enriquit com a persona, no des del punt de vista econòmic, però si des del punt de vista espiritual i tot suma!.

Com sempre un espera que l'any que començarà serà millor que l'anterior. Sigui com sigui serà també un any interessant de viure.


Traducciones/Translations by apertium

5 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django Gestió de projectes Codi lliure APSL


Experiment amb Python i CAVAL


Escrit per Aaloy a 16 de December , 2009 a les 8:36 p.m.

Turistec ha presentat recentment la iniciativa CAVAL com una seria d'especificacions per promoure la interoperabilitat de dades entre solucions TIC per al sector turístic.

Aquestes especificacions tenen una implementació de referència a la web de proves de Viajes Urbis, on podem trobar la documentació, l'API de Java, els WSDL, els endpoints per REST i una plana de proves que ens permet testejar el nostre client contra l'especifiació d'Urbis.

Es d'agraïr l'especificació de referència d'Urbis, però la veritat, crec que estaria molt millor com a iniciativa de Caval i no deixar l'especificació de referència a una web separada del domini principal. Tal com està pareix com si s'hagués acabat la subvenció i no es pogués pagar el hosting o el desenvolupament. A més es podria posar el codi de la implementació a la web, donant la possibilitat de que la gent hi faci aportacions, encara que fos Caval l'entitat encarregada de certificar finalment la solució.

Ara és cosa de no deixar-ho morir, falta una implementació real de referència (podria ser la d'Urbis però amb la marca Caval), el codi font d'aquesta, poder tenir accés al codi des d'un control de versions i per què no poder participar amb aportacions i correcció d'errors. En la implementació de referència crec que es tindria que tenir en compte que la majoria de possibles consumidors de l'especificació són petites i mitjanes empreses. Això vol dir fugir d'implementacions de referència que requereixin grans instal·lacions o coneixements tècnics avançats, la implementació ha de tenir un nivell d'entrada molt suau, amb pocs requeriments de servidor, de tal manera que es pugui tocar molt ràpidament.

No sé si arribaré a fer una implementació de referència en Python, potser seria un bon exercici per futurs creant-bits, però el que sí es pot fer és començar a jugar amb el que té Urbis.

Per això farem servir Suds, com que estam de proves, en lloc de la llibreria recomanada farem servir la beta. Suds és una llibreria per a consumir serveis webs SOAP. A diferència d'altres eines com ZSI no genera codi, sinó que construeix els objectes al vol.

Anem per feina!

El primer que farem es descarregar la beta i instal·lar-la al nostre entorn. En el meu cas faré servir un virtualenv de manera que no tendré dependències amb altres paquets.

Com que volem veure les cridades que es generen establirem el nivell de login de Suds a DEBUG

1
2
3
4
import logging
logging.basicConfig(level=logging.INFO)

logging.getLogger('suds.client').setLevel(logging.DEBUG)

Per a fer les proves mapejarem el servei d'Hotels, que té per url http://test.viajesurbis.com/serveis/caval/20091127/soap/HotelBookingService?wsdl

1
2
3
4
from suds.client import Client

url = "http://test.viajesurbis.com/serveis/caval/20091127/soap/HotelBookingService?wsdl"
client = Client(url)

ara podem fer un print client per tenir l'estructura de mapeig que ha fet Suds,

Suds ( https://fedorahosted.org/suds/ )  version: 0.3.8 (beta)  
build: R627-20091211

Service ( HotelBookingService ) tns="http://caval.travel/20091127/hotelBooking"
   Prefixes (1)
      ns0 = "http://caval.travel/20091127/hotelBooking"
   Ports (1):
      (HotelBookingServicePort)
         Methods (6):
            confirmHotelBooking(cavalHotelBookingConfirmRQ rq, )
            getAvailableHotels(cavalHotelAvailabilityRQ rq, )
            getDetailedValuation(cavalHotelBookingValuationRQ rq, )
            getEstablishmentDataSheets(cavalGetEstablishmentDataSheetsRQ rq, )
            getOffersList(cavalGetOffersListRQ arg0, )
            notifyHotelBookings(cavalHotelBookingNotificationRQ arg0, )
         Types (52):
            abstractAuthenticatedAgencyRQ
            abstractAuthenticatedRQ
            abstractRS
            ...
            roomOccupation
            roomType
            supplement
            valuatedLine
            valuatedOccupation
            zoneWithOffers

Fitxau-vos que ens està donant els mètodes del web service i els tipus que hi ha definits.

Per tractar amb arguments simples no necessitam gaire cosa més. Podríem cridar als mètodes directament. El problema és que els arguments no són simples, així que tendrem que crear-los així com els necessitem.

Per a les nostres proves farem utilitzarem getAvailableHotels, com que a la web d'Urbis hi tenim un client web ens anirà bé per validar el que feim.

Cream l'objecte que contindrà la petició:

1
rq = client.factory.create('cavalHotelAvailabilityRQ')

si feim un dir(rq) podrem veure les propietats que té l'objecte

 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
In [11]: dir(rq)
Out[11]: 
['__contains__',
 '__delattr__',
 '__doc__',
 '__getitem__',
 '__init__',
 '__iter__',
 '__keylist__',
 '__len__',
 '__metadata__',
 '__module__',
 '__printer__',
 '__repr__',
 '__setattr__',
 '__setitem__',
 '__str__',
 '__unicode__',
 'agentId',
 'airportIds',
 'boardGroupFilter',
 'checkIn',
 'checkOut',
 'cityIds',
 'establishmentClassificationFilter',
 'establishmentIds',
 'establishmentNameFilter',
 'excludeOnRequest',
 'fromRow',
 'gzipResponse',
 'hotelCategoryGroupFilter',
 'language',
 'login',
 'numRows',
 'occupations',
 'onlyOffers',
 'password',
 'removeHotelInfo',
 'roomGroupFilter',
 'rqId',
 'stateIds']

Que podem comparar amb l'exemple de la web d'Urbis

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <getAvailableHotels xmlns="http://caval.travel/20091127/hotel">
      <rq xmlns="">
        <login>xml</login>
        <password>xml</password>
        <agentId>1</agentId>
        <language>es</language>
        <gzipResponse>false</gzipResponse>
        <stateIds>569061</stateIds>
        <checkIn>01/1/2010</checkIn>
        <checkOut>05/1/2010</checkOut>
        <occupations>
          <adultsPerRoom>2</adultsPerRoom>
          <childrenPerRoom>0</childrenPerRoom>
          <numberOfRooms>1</numberOfRooms>
        </occupations>
<removeHotelInfo>false</removeHotelInfo>
      </rq>
    </getAvailableHotels>
  </soapenv:Body>
</soapenv:Envelope>

Però a més Suds ens dóna més informació damunt els paràmetres que el propi dir, així si feim un print rq

 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
In [13]: print rq
-------> print(rq)
(cavalHotelAvailabilityRQ){
   gzipResponse = None
   login = None
   password = None
   rqId = None
   agentId = None
   language = None
   airportIds[] = <empty>
   boardGroupFilter[] = <empty>
   checkIn = None
   checkOut = None
   cityIds[] = <empty>
   establishmentClassificationFilter[] = <empty>
   establishmentIds[] = <empty>
   establishmentNameFilter = None
   excludeOnRequest = None
   fromRow = None
   hotelCategoryGroupFilter[] = <empty>
   numRows = None
   occupations[] = <empty>
   onlyOffers = None
   removeHotelInfo = None
   roomGroupFilter[] = <empty>
   stateIds[] = <empty>
 }

Podem veure que ens dóna també informació sobre els objectes i sobre els alguns d'ells són col·leccions, llistes Python.

Assignem-hi valors:

1
2
3
4
5
6
7
8
rq.login = 'xml'
rq.password = 'xml'
rq.agentId = 1
rq.language = 'es'
rq.gzipResponse='false'
rq.stateIds=569061
rq.checkIn='01/01/2010'
rq.checkOut='05/01/2010'

Arribam ara a l'ocupació, aquí s'espera una llista del tipus availRQOccupation així que l'hem de crear l'objete primer tal com he fet abans al la petició

1
2
3
4
5
6
room = client.factory.create('availRQOccupation')
room.adutlsPerRoom = 2
room.childrenPerRoom = 0
room.numberOfRooms = 1
rq.removeHotelInfo ='true'
client.service.getAvailableHotels(rq)

Si heu seguit els passos fins aquí veureu el log amb el SOAP-XML que s'ha generat, i que falla miserablement amb un error:

1
2
3
4
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body><soap:Fault><faultcode>soap:Client</faultcode>
<faultstring>Unmarshalling Error: com/sun/xml/bind/v2/runtime/reflect/opt/Const </faultstring>
</soap:Fault></soap:Body></soap:Envelope>

Dont' panic!

Si comparam el que hem enviat (per això tenim el log a DEBUG) i ho comparam amb l'exemple que ens dóna Urbis veim que estam enviant tags buids i això a la implementació d'Urbis pareix que no li agrada gens.

Com que Suds fa la generació automàticament pareix que ho tenim un tant pelut, però no és així, supòs que Suds (si no ho ha fet ja) potser algun dia posarà l'opció de no generar tags buids, o potser la gent d'Urbis corregirà la implementació de referència per no petar amb aquests tags (que hauríen de ser vàlids, per una altra banda), però mentre és el que tenim, així que com a pas previ abans d'enviar-ho el que farem serà eliminar de la petició els atributs que estan buids.

1
2
for attr in dir(rq):
    if not attr.startswith('_') and not getattr(rq, attr): delattr(rq,attr)

si imprimirm el rq ara

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(cavalHotelAvailabilityRQ){
   gzipResponse = "false"
   login = "xml"
   password = "xml"
   agentId = 1
   language = "es"
   checkIn = "01/01/2010"
   checkOut = "05/01/2010"
   occupations[] = 
      (availRQOccupation){
         adultsPerRoom = 2
         childAges[] = <empty>
         childrenPerRoom = 0
         numberOfRooms = 1
      },
   removeHotelInfo = "true"
   stateIds = 569061
 }

Si ara ho tornam a enviar veurem que torna a pegar una petada, però aquest cop si ens fixam ens retorna la resposta, la petada diu

ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (129, 0))

Als tips de Suds ens avisa que alguns servidor posen caràcters de control a la responsta que fan que el parsejador SAX que fa servir Suds doni una excepció (ara ja sabeu perquè la gent fa una rialla d'aquelles d'incredulitat quan li dus que la S de SOAP ve de Simple), per això el permet Suds és afegir-hi un filtre a la resposta

1
2
3
import string
from suds.bindings.binding import Binding
Binding.replyfilter = (lambda s,r: ''.join([c for c in r if c in string.printable]))

Ara sí:

1
2
rs = client.service.getAvailableHotels(rq)
print rs.totalRows

En el meu cas m'han sortit 76 hotels. Si feim un print rs en veurem l'estructura de dades, i amb un dir(rs) les propietats i mètodes que tenim disponibles, ens fixam amb availableEstablishments

A partir d'aquí ja és sols cosa d'anar accedint als distints elements de l'estructura i decidir com presentam la informació que hem obtingut. Per exemple i per no fer-me massa llarg, per imprimir els noms dels hotels que hem trobat basta fer:

for hotel in rs.availableEstablishments:
    print hotel.establishmentName

Per acabar, si us demanau pel rendiment d'això, heu de saber que Suds manté el WSDL en caché, de manera que no necessita demanar-lo cada vegada. La caché és configurable.

Pel nostre exemple, la disponibilitat de la tenim en poc més de 3 segons.

2009-12-16 20:07:36,179-INFO-__main__-36-Iniciam la consulta
2009-12-16 20:07:39,215-INFO-__main__-38-Fi de la consulta

Considerant que en el entorn de test via web d'URBIS la resposta l'obtenim entre 10 i 14 segons, doncs jo dira que està d'allò més bé.

Pos tot el codi per a que sigui més fàcil fer les proves:

 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
43
44
45
46
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import logging
logging.basicConfig(level=logging.INFO,
        format="%(asctime)s-%(levelname)s-%(name)s-%(lineno)s-%(message)s",)
logging.getLogger('suds.client').setLevel(logging.INFO)
log = logging.getLogger(__name__)

from suds.client import Client
import string

from suds.bindings.binding import Binding
# Com que la resposta té problemes filtram 
Binding.replyfilter = (lambda s,r: ''.join([c for c in r if c in string.printable]))

url = "http://test.viajesurbis.com/serveis/caval/20091127/soap/HotelBookingService?wsdl"

client = Client(url)
rq = client.factory.create('cavalHotelAvailabilityRQ')
rq.login = 'xml'
rq.password = 'xml'
rq.agentId = 1
rq.language = 'es'
rq.gzipResponse='false'
rq.stateIds=569061
rq.checkIn='01/01/2010'
rq.checkOut='05/01/2010'
room = client.factory.create('availRQOccupation')
room.adultsPerRoom = 2
room.childrenPerRoom = 0
room.numberOfRooms = 1
rq.occupations = (room, )
rq.removeHotelInfo ='true' # no volem informació de l'hotel ara

# bug? de la implementació de referencia, eliminam tags buids.
for attr in dir(rq):
    if not attr.startswith('_'):
        if not getattr(rq, attr): delattr(rq,attr)

log.info('Iniciam la consulta')
rs = client.service.getAvailableHotels(rq)
log.info('Fi de la consulta')
# i la llista d'hotels
for hotel in rs.availableEstablishments:
    print hotel.establishmentName

Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python


Creant bits amb Django i Python


Escrit per Aaloy a 04 de December , 2009 a les 11:46 p.m.

Hem arribat a la sala als voltants de les 14:30, allà el responsable tècnic del Parc Bit (Gillem) ens ha explicat com estava tot, endollat el projector i ajudat amb les cadires. Un 10 per la gent del Parc Bit i de l'Incubit, tant per deixar-nos la sala com per l'ajuda.

Hem començat a preparar-ho tot. Respiram un poc més tranquils quan hem comprovat que la connexió a Internet funcionava. Després de 2 mesos i busques sense ADSL començava a pensar que teníem gafe.

Als projectors no els sol agradar el portàtil amb resolució de 1920px, així que m'ha toca configurar-ho un poc. El projector una passada, arriba a 1600x1200 px i des del fons de la sala es veu bé.

La sala de formació té capacitat per unes vint i tantes persones assegudes a la taula. No hi havia cadires abastament i la gent del Parc Bit ens ha ajudat a torbar-ne més.

Després d'això, quan al part tècnica ja funcionava hem anat a cercar la poca intendència que hi ha preparada: aigua i gominolas. Si tot falla, pens, al manco la gent que vengui se'n podrà anar amb un gust dolç.

Primera sorpresa del dia: Xus s'entrega amb una cafetera Nesspresso, cafè i galletes per tothom. No tenc paraules!

Son les 15:30, ja ho tenim tot llest i anam a fer una mossegada ràpida. Allà trobam gent coneguda que també assistiran a la trobada.

En Juan s'adelanta per anar rebent la gent, seguidament baixam, són les quatre i comença a omplir-se la sala.

Molta gent coneguda, amics virtuals que es desvirtualitzen. Em sap greu poder dedicar més temps a presentar-nos com toca, esper que hi haurà més ocasions.

A les 16:30 aproximadament començam: primer amb la part Python i després amb Django. L'objectiu és evitar la por a començar, donar la primera empenta. Qued admirat pel nivell que hi ha a la sala. Veure tants informàtics junts, interessats per aprendre em fa tornar l'esperança en la professió. Sols per això ja s'ho paga els nirvis de muntar una cosa d'aquestes.

No sé estar assegut, m'agrada més explicar i comentar passejant, les gominolas al manco ajuden a recuperar energies. El michelín ho notarà segur :)

Hem acabat amb més de mitja hora de retràs de la part Django, En Bernat tendrà menys temps. És una llàstima, perquè una de les gràcies de Django és la flexibilitat que té per a deplegar-se allà on més ens convengui a nosaltres. Ha de resumir molt la xarl·la i es limita a mostrar la configuració amb mod_python. A veure si a la propera ens dona temps de que mostri wsgi i la potència del balanceig.

Són les nou passades. Acabam. Anam saludant a la gent que marxa i començam a recollir. Encara queda temps per anar parlant amb la gent que queda de batalletes i tecnologia.

En Pau i na Sílvia han fet fotos, quan les tengui les penjaré junt amb les presentacions. Els twitts estan com creant_bits.

Fi de la crònica!

Gràcies a tots per assistir, perdonau els que no heu pogut venir per falta d'espai. La gent que ha vingut de ben segur que us contarà que la sala tampoc no donava per massa més. Però no serà la darrera!

Moltes gràcies a Juan (morenosan), Xus (m'has deixat sense paraules) i Pau per les labors d'assistència tècnica. Gràcies especialment per Bernat, que s'ha currat una presentació. A Paco per la idea del nom, i per damunt de tot a tota la gent que un divendres de pont s'ha desplaçat al Parc Bit i ha estat més de cinc hores compartint la nostra passió per la informàtica.

Si algú em diu que a partir d'una twittejada es podia muntar un sarau com aquest no m'ho crec. La màgia de la comunicació!

Com dic, esper que no sigui la darrera. Personalment he gaudit del contacte amb els assistents, de desvirtualitzar gent, de veure que la visió que tenc de la informàtica és compartida, de pensar que les coses es poden fer d'una altra manera.

Avui no dormiré! :)

--

He pujat les presentacions a SlideShare. Les fotografies són a Flicker, gràcies a Silvia i Pau.


Traducciones/Translations by apertium

8 comentaris, 0 trackbacks (URL) , Tags: Python Django


Desplegament de Django


Escrit per Aaloy a 29 de November , 2009 a les 1:53 a.m.

Una de les característiques que més m'agraden de Django per desenvolupar és que ja ve amb el seu propi servidor integrat. És un servidor no apte per entorns de producció, de fet a les planes de Django es recomana repetides vegades que NO es faci servir a producció, però que va molt bé al desenvolupament.

El que fa tenir aquest servidor és per una banda acursar el temps necessari per començar a desenvolupar. Una vegada creat el projecte i la primera aplicació, posam el servidor en marxa amb un python manage.py runserver i ja tenim accés a un complet servidor web.

A més ens permet depurar des del principi les nostres aplicacions. La consola del servidor que acabam d'executar mostrarà els missatges del servidor i els logs que li enviem com un servidor web qualsevol, però a més ens servirà de consola de depuració de Python.

Encara que hi ha entorns com Eclipse+Pydev que ens permeten posar punts de ruptura directament al codi, un dels mètodes més simples consisteix en escriure import pdb;pdb.set_trace() allà on vulguem que s'aturi el programa. Llavors, en arribar-hi a la consola on hem executat el servidor ens apareixerà l'entorn de depuració de Python (el pdb). Si volem fer un poc més de feina podem instal·lar l'ipdb i tendrem una consola de depuració amb autocompletat i resaltat de sintaxi.

A Python diuen allò de "batteries included" per indicar que amb Python hi ha tot el necessari per arrancar, Django segueix la mateixa filosofia, de manera que no necessitam configurar cap servidor web per començar a fer-hi feina i ens proporciona a més eines de depuració prou potents: la possibilitat de depurar amb pdb/ipdb o semblants, la depuració mitjançant els missatges d'error de les plantilles o la possiblitat de depurar evitantn la recàrrega amb l'opció de --no-reload del servidor de desenvolupament.

Una vegada ja tenim l'aplicació creada i depurada passam al desplegament. Pels experts de sistemes no ha de ser cap problema seguir les instruccions de les instruccions de desplegament de Django. Es pot desplegar en Apache (mod_python o mod_wsgi), damunt ngnix, lighthttp, Cherokee, ... En general no es tant cosa del servidor com de triar una tecnologia considerant el nostre entorn d'execució.

Perquè pensau que Django està pensat per ser escalable per amunt i per avall. Segons les restriccions del nostre entorn de producció ens convindrà elegir una tecnologia o una altra. Decidir si ens convé tenir un sols servidor http o separam l'execució de l'aplicació del servidor de continguts, quin tipus de caché farem servir, etc. etc.

Tot és molt flexible, però aquesta flexibilitat fa que ens tenguem que fer preguntes, Django no decideix per nosaltres com s'ha d'instal·lar. Fa recomanacions però en general s'adapta al que hi ha.

Si un disposa d'uns tècnics de sistemes com els que jo tenc la sort de fer feina, aquesta flexibilitat és fantàstica, segons l'aplicació decideixen la tecnologia que es fa servir, la caché, les instàncies que s'aixecaran o com es serveix el contingut estàtic. Si un s'ho ha de fer tot i no disposa d'un servidor propi (o al manco d'un servidor virtual) el desplegament vindrà marcat pel que ens deixi el nostre ISP.

Posar una configuració? Doncs no, dependrà de cada aplicació i de cada entorn. Sols un parell de recomanacions:

  • wsgi funciona molt bé
  • Si podeu separau el servidor d'aplicacions Django del servidor de contingut estàtic.
  • Amb un poc més de pressupost es pot tenir un servidor dedicat o virtual configurat i podrem treure tot el suc a la nostra aplicació.

Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Una ullada a Sphinx


Escrit per Aaloy a 27 de November , 2009 a les 11:36 p.m.

Si heu fet servir la documentació de Python, Django o potser alguna de les aplicacions més importants o darrerament li heu fet una ullada al Building Skills with Python us n'adonareu que comparteixen un estil comú. Django ha personalitzat les plantilles, però com podem veure a la part on explica com es crea i es contribueix a la documentació, podem feure que Django fa servir Sphinx.

Sphinx és l'eina amb la qual s'estan creant les documentacions dels principals projectes basats en Python (encara que ens permet escriure qualsevol tipus de documentació) i poc a poc esdevé la killer appplication de la documentació tècnica.

Començar amb Sphinx és prou senzill, sols necessitam tenir Python instal·lat i

$ easy_install Sphinx
$ sphinx-quickstart

i anar contestant les preguntes. Si heu compilat alguna vegada algun programa al món Unix veureu com quan acabin les preguntes s''haurà creat un directori amb un arxiu anomenat Makefile (make.bat per altres). Executant-lo ens crearà un directori build amb les bases del que pot ser la nostra documentació.

I és que Sphinx és bàsicament un compilador orientat a la creació de documentació. Agafa documents escrits en Restructured Text i crea un lloc web en HTML, genera un pdf o latex. Això vol dir que per poder treure'n el màxim partit s'ha de conèixer Restructured Text i fer-li una ullada a les comandes que Sphinx incorpora per crear les taules de contingut, fer el resaltat de sintaxi, incorporar imatges o fórmules matemàtiques. Una vegada passat el període d'aprenentatge (que ja us dic que és curt) veureu que el resultats que s'obtenen són espectaculars.

Ja he dit sovint pel blog que m'agrada escriure la documentació (i escriure en general) en text pla, utilitzant LaTeX, Markdown o ResTructured Text. Sphinx ajunta el fet de poder escriure la documentació com m'agrada amb un resultats realment professionals. És a la documentació hipertextual el que LaTeX és per a la documentació científica.

Potser ara us estareu demanant perquè fer la documentació amb aquesta eina i no limitar-nos a un document amb OpenOffice (o fins i tot en Word!!!), se m'acudeixen un parell mallorquí de raons:

  • El text pla es pot posar damunt un control de versions.
  • Permet que molta gent participi en la creació de la documentació.
  • Sphinx ens dóna unes plantilles que fan que la documentació llueixi.
  • El ressaltat de sintaxi Python ve de sèrie i costa ben poc fer el ressaltat per altres llenguatges.
  • El cercador està inclòs a la documentació

Però sobretot, perquè amb Sphinx escriure documentació és divertit. Pots distribuir-te la feina com vols, no has de passar ànsia per la maquetació final, ni per que les imatges se t'han descol·locat i no saps on han anat a parar (si heu fet documents de més de 20 planes amb un processador de text sabreu què vull dir). I que escriure documentació no sigui feixuc segurament farà que ens faci menys peresa documentar i tothom ens estarà eternament agraït. Jo ja he començat amb la documentació d'appfusedjango que no se digui que no predic amb l'exemple.

Personalment crec que Django ha marcat un abans i un després en el món de la documentació de projectes, de tal manera que cada dia més projectes i aplicacions emulen la manera de documentar de Django i Django (com Python) fa servir Sphinx. Demostrant que per a que un projecte tengui èxit no basta que sigui bo sinó que ha d'estar ben documentat.

No cerquem en Sphinx la bala de plata. Fer bona documentació no és senzill, Sphinx sols fa que sigui menys pesat i ens dóna un resultat final molt més amigable als nostres lectors. Un dels creadors de Django,Jacob Kaplan-Moss, ha escrit un conjunt de posts damunt com s'ha d'escriure bona documentació: Writing great documentation, de lectura imprescindible. Però tot i que no sigui fàcil s'ha de començar, ens hem d'avesar (i jo el primer) a que Sphinx sigui part de les eines de projecte, a escriure documentació i a millorar el nostre estil, tanmateix diuen que la pràctica fa el mestre, no?


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Ulipad 4


Escrit per Aaloy a 24 de November , 2009 a les 11:53 p.m.

Acab de devallar-me la darrera versió (la quatre) de l'editor Ulipad. És un editor fet en Python damunt les llibreries wxPython.

La primera impressió que m'ha donat l'editor es pot resumir en una paraula: velocitat. És un editor molt ràpid, carrega en pots segons i l'autocompletat de codi, a més d'estar a un nivell molt bo, és també molt ràpida. Sols per això ja trob una justificació més que raonable per donar-li una oportunitat.

La gestió de projectes és un tant limitada, únicament ens presenta un sistema de fitxers, però és suficient per la majoria de projectes.

El suport de Python és molt bo i és extensible mitjançant plugins. En té un per Django que ens deixa executar el servidor de desenvolupament de Django. Tanmateix tot això és un poc anecdòtic si ho comparam amb la senzillesa i usabilitat d'aquest editor. Per ara (i duc una hora llarga amb ell) m'està agradant molt.

Destacar la bona integració que té amb la shell de Python, basant-se en pyCrust, la integració amb pylint i un parell d'utilitats que ens fan la vida més fàcil: un testejador d'expressions regulars i un gestor de trossos de codi (snippets).

Una altra de les coses que m'ha agradat és el suport que té per a l'edició de documents en RestructuredText, permetent-nos tenir una previsualització en temps reals de com queda el document.

He trobat a faltar un major suport per l'execució de test unitaris (sols té integrats els doctests) i la integració plena d'un depurador. Es recolza en un depurador molt bo, en Winpdb, però en aquesta versió pareix que s'ha deixat un tant desactualitzat. Supòs que obrint un ticket s'arreglarà aviat.

És un editor senzill, útil per que vol un editor gràfic i no pot o vol fer servir IDEs com Netbeans o Eclipse.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Anunci Python i Django workshow


Escrit per Aaloy a 20 de November , 2009 a les 8:24 p.m.

Actualització

Hem arribat als 20. La resta haureu d'esperar a la propera!

Tothom que s'ha apuntat fins al meu darrer comentari, el 24 entra dins aquest primer workshow. Aniré posant per aquí i pel Twitter les novetat que hi hagi.

Em sap greu pels que els agradaria venir i han quedat fora, però compreneu que la sala és petita, 20 persones són moltes i el format que hem pensat (amb voluntaris ajudant als nouvinguts) no dóna per massa més.

Si la cosa agrada us promet que no serà la darrera! :)

No sé quin nom posar-li, en Paco proposa al twitter "programming python & django before beers" o "def bit():" però a les hores que acabarem (més de les 18:00) ja us dic jo que al Parc Bit no hi ha res obert per anar fer unes cerveses. M'ha agradat molt el Creant Bits, començant amb Python i Django i ja veurem com acabarem...

Així doncs faig l'anunci oficial:

Creant Bits amb Python i Django
Divendres 4 de desembre
Hora: 16:00 a 20:15 (o més)
Lloc: Sala de formació del Parc Bit
Organitza: APSL
Col·labora: Incubit, que ens deixa la sala.

En quant als continguts, crec que els hauríem d'afinar entre tots. Per començar jo us proposaria el següent:

  • Introducció a Python (1.5h)
  • Pausa (15 min)
  • Introducció a Django (1.5 h)
  • Pausa (15 min)
  • Posant una aplicació Django a producció (45 min)

La idea seria que la gent li fa ganes saber què és això de Django i Python pugui fer-se'n una idea. Es donarien quatre pinzellades de Python, amb exemples que tothom pugui executar al seu portàtil.

Per la part Django: mostrar com començar, que és, veure com es pot crear una plantilla, l'organització del codi i veure alguns exemples d'aplicacions, etc. Ha de servir com a presa de contacte i que tothom pugui fer-se una idea de les possibilitats que té aquest bastiment.

Com que al final el que volem tots és posar-lo en producció, s'explicaria com n'és de bo de fer posar-ho a un entorn Apache.

Però com us dic sols és una idea. El temps és limitat, però si estau interessats en donar-li una altra orientació estic obert a qualsevol possibilitats.

Requisits

  • Tothom amb el seu portàtil, millor amb Linux. Si algú vol dur el seu fix i pantalla serà benvingut, però avís que hi ha una passejada i no sé com està la sala d'endolls.
  • Python 2.5 ó 2.6 instal·lat, el 3.x no us funcionarà amb Django.
  • Un editor amb ressaltat Python: vim, gvim, notepad++, Netbeans, Eclipse, ...
  • La darrera versió estable de Django insta·lada. La 1.1.1 en el moment d'escriure això.
  • Navegador Firefox amb el Firebug instal·lat
  • iPython instal·lat

Inscripció

Lliure, però teniu en compte que la sala té una capacitat d'unes 20 persones, així que, per favor, deixau un comentari per a que pugui controlar quants serem i saber si hem arribat al límit de la sala. Al comentari podeu posar el tipus d'orientació que us agradaria.

Quan serà la propera?

Dependrà de l'èxit d'aquesta i de que Parc Bit/Incubit tengui lliure la sala i ens la cedesqui (des d'aquí moltes gràcies des de ja!). M'agradaria que això acabàs com a grup d'usuaris/empreses més o manco estable, però això ho hem de decidir entre tots.

Altres

Miraré d'informar-me com està la sala en el tema de connexions d'Internet. Si hi ha connectivitat fins i tot podríem intentar fer un projecte en grup, si no aquesta potser la propera vegada.

M'agradaria poder dir que hi haurà catering, però seria mentida :)


Traducciones/Translations by apertium

33 comentaris, 0 trackbacks (URL) , Tags: Python Django


Planes estàtiques amb Flatpages


Escrit per Aaloy a 19 de November , 2009 a les 6:15 p.m.

Fa estona que volia posar contingut no directament relacionat amb el Blog a Trespams, ja sabeu, quí soc, sobre el blog i coses d'aquests, però no ho arribava a fer. Finalment m'he decidit i aprofitaré per explicar com podem fer aquest tipus de coses amb Django.

El primer de tot és centrar el problema. L'objectiu és tenir lligat a la nostra aplicació (el blog en aquest cas) un conjunt de planes que no estan relacionades amb l'aplicació o aplicacions, que no han de ser editades o ho han de ser molt poc.

El cas típic són les planes de condicions legals, les planes de "sobre mi" dels blogs. Agafau la idea, no?

Com que és un cas molt comú Django té una aplicació al contrib pensada per fer precisament això. Ens proporciona un formulari on podem posar el títol, el contingut en format html i indicar si volem la plantilla que s'utilitzarà, si permetem comentaris o no i si l'usuari ha d'estar registrat al sistema per veure la plana. Aquesta aplicació se'n diu Flatpages.

La documentació de Django està força ben explicada.

  1. Afegim 'django.contrib.sites' a INSTALLED_APPS del nostre setting.py si no hi és ja. Si no hi era convé fer un syncdb. Crearem una entrada al Sites amb la nostra url i també posarem el SITE_ID del settings.py el valor de l'id d'aquest registre. Si sols en teniu un serà 1.

  2. Afegim 'django.contrib.flatpages' als INSTALLED_APPS.

  3. Afegim 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' a MIDDLEWARE_CLASSES del settings.

  4. Executam python manage.py syncdb per a generar les taules de Flatpages.

  5. Cream el directori flatpages dins el nostre directori de templates i cream l'arxiu d'default.html. Aquesta serà la plantilla per defecte que s'utilitzarà. Hem de personalitzar-la com volguem. A aquesta plantilla flatpages li passa la variable flatpage, a partir de la qual podem obenir el títol i el cos del text amb {{flapage.title}} i {{flatpage.content}}. Podeu fer una ullada al codi de trespams del svn per tenir-ho més clar.

Ara ja sols és cosa d'anar afegint registres amb les urls i continguts que volguem. Django en no trobar una plana abans de mollar un 404 el que farà és anar a flatpages, si la url correspon a una que hagem definit generarà la plana a partir de la plantilla i els continguts del registre corresponent.

Per acabar, dir que no fa falta posar el filtre safe a les variables. Per defecte se suposa que l'html és segur, ja que no està pensat per a que un usuari extern a l'aplicació l'editi.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Comparant Django i Drupal


Escrit per Aaloy a 16 de November , 2009 a les 5:56 p.m.

Pareix que alguns desenvolupadors de PHP s'estan plantejant anar cap a Python i Django com a una via per a fugir de les complexitats i problemes del PHP.

Des d'aquest blog vull encoratjar-los a al manco provar-ho i després decidir si convé o no fer l'esforç. Personalment pens que sí, pero tot dependrà de l'experiència que tengui cada un i de com vulgui enfocar els projectes.

Per ajudar en la decisió trob que és força interessant visitar dos enllaços:

El primer és una apunt i el segon és la discusió a un fil de la llista d'usuaris de Django. No us perdeu els comentaris, on es discuteix si Django i Drupal són comparables o no.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Python Essential Reference - 4th Edition


Escrit per Aaloy a 02 de November , 2009 a les 11:14 a.m.

Python Essential Reference, en la seva quarta edició és un llibre dens, com es pot esperar d'un manual de referència, així que no és el millor llibre per a introduir-se en la programació amb Python, en canvi, si ja heu seguit algun tutorial, o algun altre llibre introductori, trobareu que Python Essential Reference toca aspectes pocs coneguts del llenguatge.

El llibre està dividit en tres parts, la primera, més de 200 planes, tracta la sintaxi del llenguatge en profunditat, la segona part, la referència en sí, va desgranant les principals llibreries que hom pot trobar de sèrie en una distribució de Python i la tercera ens ensenya com extendre Python amb C i com es comunica Python amb altres llenguatges (C, .Net o Java).

No es pot dir que ell llibre tengui caràcter pedagògic, però els exemples són als punts justs on ajuden a fer-se una bona idea del que se'ns explica. Com que l'autor David M. Beazley també contribueix a la documentació de Python trobarem que hi ha força semblances entre la documentació oficial i aquesta referència, tot i això, la manera d'explicar algunes coses i l'organització de la documentació és diferent i fa que se n'aprofiti més la lectura.

És un llibre per tenir aprop quan un programa en Python i per rellegir de tant en tant, a l'era Internet aquets tipus de manuals en paper són cada cop menys necessaris, però tot i això útils per gent com jo que prefereix llegir amb comoditat i/o en qualsevol lloc o moment.

Fitxa tècnica:

Python Essential Reference 4th Editon
David. M. Beazley
Ed. Addison Wesley
ISBN: 978-0-672-32978-4

Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Llibres i revistes Python


Mantenibilitat en llenguatges tipats i no tipats


Escrit per Aaloy a 01 de November , 2009 a les 12:38 p.m.

Quan surt aquesta discusió sovint, al manco al meu àmbit, la discusió es redueix a comparar Java i Python. Recentment en Paco ha obert la capsa dels trons al twitter amb una afirmació "Para escribir código mantenible, mejor un L.P. fuertemente tipado. Si es compilado, mejor".

Damunt el tipat o no em remetré al post de Ned Batchelder i a un comentari d'aquest post, amb al qual hi estic d'acord en un 90%, que traduït lliurement i sense limitar-ho a java diria:

  1. Python maximitza la productivitat dels bons programadors

  2. Els llenguatges tipats (Java a l'original) minimitzen el mal que els programadors mediocres poden fer al sistema.

Tant de bo la segona afirmació fos certa. La gent pot ser molt creativa i cap tipat del món no pot evitar que es faci un borrat complet contra la base de dades, que s'equivoqui amb l'algorisme del càlcul de l'IVA, les possibilitats són infinites. La referència obligada de que el tipat fort no és cap garantia de mantenibilitat la tenim al post How to Write Unmaintainable Code o passar-se per The daily WTF. El primer cas és fins i tot més sagnant, ja que la gran majoria d'exemples i tècniques es refereixen a llenguatges fortament tipats com el C, C++ i Java. Usos creatius del define, noms de variables que no tenen res a veure amb el que fa la variable, noms lleugerament semblants que fan coses totalment distintes,...

No vull fer sang damunt el tipat fort, la majoria de les coses que expliquen al How to Write es poden extrapolar a qualsevol llenguatge de programació. Però la realitat és el tipat fort no és cap garantia de mantenibilitat.

La mantenibiliat ens la donen les convencions del codi, la documentació, la inspecció periòdica o sistemàtica del nostre codi (o del codi d'altres), el llegir el code complete i aplicar els consells i pràctiques també ajuda força. Tenir test unitaris, programes que ens gestionin l'adherència als estàndarts i la complexitat del codi (n'hi ha per Java, Python, C++, ...), gestió acurada del errors, tests de regressió, etc. a l'hora de la mantenibilitat poden fer molt més que el simple tipat.

Potser perquè molts de programes que he fet al llarg del temps s'han mantingut en producció durant anys sóc un tant fanàtic de la mantenibilitat, preferesc codi matenible a codi guais que al cap d'un mes ningú, ni el seu creador, sabrà perquè ho va fer d'aquella manera. I aquest tipus de mantenibilidad no la dóna el tipat fort o el compilador, la mantenibilitat ens la dóna la gent i les regles que ens imposem a l'hora de crear el codi.

Com la seguretat la mantenibilitat no és quelcom que es pugui posar una vegada el programa està llest. La mantenibilitat ha de formar part del procés de desenvolupament. Sovint he refet o he fet refer codi perquè era mal de seguir, per massa acoblament, perquè la quantitat de ifs feia difícil saber per on anava el codi, i això m'ha passat tant en Java, Pascal o FORTRAN com en Python.

Llavors, pensant en que els programes es fan per a ser mantenibles tant en llenguatges tipats com en els no tipats, la pregunta que ens faríem doncs és perquè triar un llenguatge o un altra i aquí ja entraríem en un altre tipus de discusió.

Si pensam sols en termes de mantenibilitat i suposant que programam com toca potser estarem d'acord que hauríem de triar un llenguatge que fos bo de llegir, de seguir, expressiu i d'alt nivell. El millor codi és aquell que no s'ha d'escriure. Per mi pocs llenguatges compleixen això tant bé com Python, és un llenguatge molt clar de llegir, amb una corba d'aprenentatge molt suau i amb una dèria cap a la llegibilitat i la mantenibilitat que està incorporada al llenguatge dins la pròpia sintaxi (la identació) i a l'intèrpret mateix (provau import this) o llegiu The Zend of Python i les convencions de codi són públiques i definides des del 2001, la qual cosa no vol dir que no s'hagin canviat al llarg del temps.

En conclusió: el codi mantenible no ho dóna un compilador sinó el programador i la gestió que se dugui del projecte. No podem confiar en que el tipat fort ens farà la feina i ens alliberarà de la feina de crear tests unitaris, de comprovar que el codi està d'acord als estàndards o de que fa el que ha de fer.

Java, C, C++ són grans llenguatges no perquè tenguin tipat fort, sinó pel que els programadors hem fet amb ells. De la mateixa manera Python, PHP, Perl, Ruby són grans llenguatges no per la seva absència de tipat, sinó per codi que ens permeten escriure.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Informàtica Python Java


Curs de ASCII art en Python - I


Escrit per Aaloy a 25 de October , 2009 a les 4:25 p.m.

Avui iniciam el primer curs de ASCII art en Python. Per començar un repte: el pare de n'ETE en ASCII art, generat en Python. Seria així:

1
print(chr(79))

I ja ho tenim, Don ETE!

Eps! Algú ha vist les meves pastilles?


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Conyes marineres


Risc o seny


Escrit per Aaloy a 25 de October , 2009 a les 8:56 a.m.

Llegint l'article de Ned Batchelder, "The Scalability of programming languages" me n'adono que es troba en una tesitura com la que jo potser me trobaré d'aquí poc. Ell, un dels creadors de Tablo, comprat fa dos anys i mig per HP, es veu que es troba sota pressions per deixar de desenvolupar l'aplicació en Python per fer-ho en un dels llenguatges "corporatius" d'HP.

Per mi Django i Python representen un avantatge competitiu, ja que permeten una posada en producció, des de la concepció de la idea fins a posar l'aplicació al servidor, molt més curta, un temps de reacció als canvis i incidències difícilment superable, i sobretot, una capacitat de manteniment que fa que es pugui tocar una aplicació feta fa anys sense problemes.

El més curiós,però, és veure com les novetats, tot allò que pot representar una ruptura amb "la manera de fer les coses" presenta un problema fins i tot en les empreses que han fet de la innovació una de les seves raons de ser.

En una era on la tecnologia representa un avantatge competitiu important, limitar-se a allò que fa tot hom vol dir que no s'aprofita aquest avantatge. Això no vol dir tirar-se de cap cap a una nova tecnologia tan aviat com apareix, però una vegada avaluada, si la nova tecnologia representa una millora respecte al que tenim, el risc de ser dels primers en fer-la nostra es pot veure compensat per l'avantatge que ens donarà. Fins i tot si es demostra que la tecnologia no era allò que esperàvem, l'esforç necessari per a implantar-la i el canvi mental que suposa, farà que el nostre equip estigui més obert a les novetats i que llavors sigui molt més senzill anar introduint millores als nostres processos.

És un poc també com la situació en la que una empresa té l'opció de crear un nou programa per a comercialitzar els seus productes o comprar-ne un de ja fet. Desenvolupar-lo implica més risc i cost, però ens dóna un control total damunt el programa i podem fer-hi les modificacions que facin falta per adaptar-lo als nostres processos de negoci. Comprar-ho fet vol dir que qualsevol amb el capital suficient pot fer el mateix que nosaltres comprant el mateix programa que nosaltres. El que es dóna a més, és que nosaltres podem haver pagat per unes modificacions al programa que han costat temps i esforç de desenvolupar, i la nostra competència les tendrà des del primer dia a cost zero. Adéu a l'avantatge competitiu!

En aquests moments la tecnologia web i els llenguatges que li donen suport estan en un moment d'evolució important: llenguatges d'script per desenvolupar webs, pas cap a models de base de dades no relacionals, utilització de bastiments javascripts purs, serveis REST... Tot això està molt lluny dels llenguatges i tecnologies "corporatives", però són les tecnologies que formen la base de les noves aplicacions d'Internet, de la innovació.

Les empreses hauran d'elegir si prefereixen quedar-se en un sector madur i anar fent el mateix que tothom, o començar a apostar per la innovació i jugar-se-la de tant en tant amb projectes i tecnologies no tant consolidades però amb molta projecció.

Els riscs hi són, però ja se sap, qui vol peixos ...


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Consultes dinàmiques a Django


Escrit per Aaloy a 15 de October , 2009 a les 9 p.m.

Estic mirant de fer un poc més fàcil la integració de jqGrid amb Django, integrar la paginació ha estat força senzill ja que jqGrid i Django fan servir un conjunt de variables semblant: plana actual, total de registres i llista de registres.

jqGrid passa aquests paràmetres per GET quan feim un camvi de plana, per tant podem aprofitar l'objecte paginator de Django per fer la paginació com si fos una aplicació HTML clàssica, sols que en lloc d'HTML retornarem JSON (o XML).

La cosa es complica un poc més quan es tracta de cercar. jqGrid fa servir quatre paràmetres: _search, que es posa a true quan s'ha activat la cerca, searchField que conté el camp pel qual es cerca. SearchField retorna el contingut de la variable index de colModels si existeix o bé el nom. Llavors tenim també searchOper que ens informa del tipus de cerca que es vol fer. Per exemple, passa eq per indicar la coincidència exacta, ew per indicar acaba amb i cn correspon a la sentència IN d'SQL. Per acabar searchString conté el valor del que estam cercant.

Així doncs es tracta de montar un filtre de manera dinàmica amb Django a partir dels valors que ens envia el jqGrid. El primer que hem de fer és fer la taula d'equivalències que mapejarà cada operació de jqGrid amb una operació equivalent de Django.

Per exemple:

1
2
3
4
django_equivalences = {
              'eq': '%s__iexact',
              'lt': '%s__lt',
              'le': '%s__lte'}

Això éns permet convertir fàcilment una expressió d'igualtat per a un camp amb l'equivalent Django

1
2
field = self.request.GET.get('searchField')
op = request.GET.get('searchOper') % field

Amb això ens trobam amb dos problemes: què feim amb les condicions del tipus "no és igual a" o "no comença amb" i què feim amb les expressions "in". I si m'apurau amb un problema més, com passam aquestes condicions que constriuim (o construirem) dinàmicament a Django. Perquè Django espera construccions del tipus Entry.objects.filter(id__gt=4) i nosaltres el que farem és construir-les.

Si ens fixam en la documentació veurem que el problema dels inclosos i dels exclosos està resolt per filter i excludes. Amb la mateixa construcció i generant la consulta amb excludes en lloc de filter ja ho tenim. Sols necessitam doncs que el mapeig a més de la plantilla ens retorni el tipus a que correspon filter o bé excludes.

1
2
3
4
django_equivalences = {
              'eq': ('filter', '%s__iexact'),
              'ne': ('exclude','%s__iexact'),
              'lt': ('filter', '%s__lt')}

Suposant que la nostra classe es diu Entry podríem fer quelcom semblant a això:

1
2
3
4
5
6
7
searchField = request.GET.get('searchField')
op = self.request.GET.get('searchOper')
value = request.GET.get('searchString')
tipo, filtro = django_equivalences[op]
query = {filtro%searchField:value}
record_list = Entry.objects.filter(**query) if tipo == 'filter' \
    else  Entry.objects.exclude(**query)

Fitxem-nos com hem fet la construcció dinàmica. En lloc de crear el codi el que feim és crear un diccionary query que té com a clau l'operació de filtratge damunt el camp que jqGrid ens passa i com a valor, el que volem cercar.

Ens queda encar un petit detall, què passa amb l'in, a Django mapejaria com icontains i espera una llista de valors com a paràmetre. Està clar que ho podríem tractar com un cas particular, però anem-ho a fer divertit, el que farem serà afegir una funció com a valor del mapeig que ens dirà el que hem de fer amb el valor, llevat de in i ni (includes i not includes) no s'ha de fer res, millor dit la funció ha de retornar el mateix valor que li passam (us sona la funció identitat?) i en cas contrari convertirem el valor que ens passi a una llista de valors mitjançant el mètode split(',').

Així doncs el nostre mapeix seria:

 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
identity = lambda x:x
django_equivalences = {
              'eq': ('filter', '%s__iexact', identity),
              'ne': ('exclude','%s__iexact',identity),
              'lt': ('filter', '%s__lt',identity),
              'le': ('filter', '%s__lte',identity),
              'gt': ('filter', '%s__gt',identity),
              'ge': ('filter', '%s__gte',identity),
              'bw': ('filter', '%s__istartswith',identity),
              'bn': ('exclude','%s__istartswith',identity),
              'in': ('filter', '%s__in',lambda x: x.split(',')),
              'ni': ('exclude','%s__in',lambda x: x.split(',')),
              'ew': ('filter', '%s__iendswith',identity),
              'en': ('exclude','%s__iendswith', identity),
              'cn': ('filter', '%s__icontains', identity),
              'nc': ('exclude','%s__icontains', identity)
              }
field = request.GET.get('searchField')
op = self.request.GET.get('searchOper')
value = request.GET.get('searchString')
try:
    tipo, filtro, f = django_equivalences[op]
except KeyError:
    tipo, filtro, f = django_equivalences['eq']    
filtro = str(filtro % field)
query = {filtro:f(value)}
record_list = Entry.objects.filter(**query) if tipo == 'filter' \
    else  Entry.objects.exclude(**query)

Com veis un exercici interessant perquè hi ha un poc de tot: funció lambda, us dels diccionaris, ús de les funcions com a objectes i pas de paràmetres a una funció mitjançant un diccionari.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django


Prova de concepte: APSLHOTELS


Escrit per Aaloy a 11 de October , 2009 a les 10:15 a.m.

Link to Prova de concepte: APSLHOTELS

Ahir al vespre vàrem obrir al públic la nostra prova de concepte del que pot ser una plana de reserves d'hotels: http://hotelestest.apsl.net/. I quan dic prova de concepte en refereixo a poder mostrar el que es pot fer amb la tecnologia Python+Django.

A la nostra comunitat un tant per cent elevat de la informàtica gira al voltant del negoci turístic. Quan vas a parlar amb algú del sector i li dius que programaràs la seva aplicació amb Python i Django sovint ho troben un poc estrany, estan acostumats al PHP i com a molt al Java o .Net, no veuen clar que es pugui fer.

Aquesta prova de concepte vol mostrar el senzill que és i algunes de les possibilitats que té la tecnologia actualment. Per fer la demostració volíem que la plana es connectàs a un XML/SOAP d'un tercer ja que d'aquesta manera es cobreix l'ús més habitual: fer una aplicació web on el motor i la capa de presentació estan separades per un servei XML. Dels proveïdors amb que contactàrem vàrem tenir molt bona acollida i suport de la gent de Valadis/Versys, ens varen donar moltes facilitats des del començament i el seu XML és molt senzill de mapejar. Està clar que les idees que es presenten es poden desenvolupar amb gairebé qualsevol proveïdor XML d'hotels, però vàrem tenir la sort de poder topar amb aquesta gent i que ens donàs accés al seu sistema de proves sense cap problema. Des d'aquí moltes gràcies.

Així doncs, tenim una aplicació B2C que permet reserva hotels i que connecta al motor XML de test d'un proveïdor extern. Com a bon entorn de test he de dir que es troba en contínua evolució i que les dades que es mostren són sols un subconjunt molt limitat de les que es tendrien en un entorn de producció. No us fixeu massa en les dades, sinó en el bessó del que es mostra. Per cert, podeu provar tot el que volgeu, tanmateix no es fa cap tipus de pagament ni control a la tarja que poseu.

Feta aquesta introducció anem a veure un poc la bèstia:

El desenvolupament

Com he dit l'aplicació està desenvolupada fent servir Python+Django, pel control de versions s'ha fet servir Subversion i pel control del projecte s'ha fet servir Trac.

Per crear l'aplicació hem mapejat l'XML a objectes Python amb la llibreria lxml, una llibreria que envolcalla les llibreries C libxml2 i libxslt. La velocitat del C i l'expressivitat de Python.

Per la depuració i programació hem fet servir: django-extensions, debug_toolbar, ipdb i ipython. La primera llibreria ens proporciona tot un conjunt de funcions còmodes per l'administració de l'aplicació, debug_toolbar ens diu quina plantilla es fa servir en cada pantalla, les seves herències, les sql que es generen, etc. ipdb és un depurador de línia de comandes, com el pdb però amb autocompletat i ressaltat de sintaxi. ipython és una consola Python, Django l'aprofita si està instal·lada, proporciona autocompletat, resaltat de sintaxis i un gran nombre de comandes extra.

Els editors més habituals han estat Netbeans, Eclipse, Vim, Kate i Notepad++ (per Windows). L'editor importa poc, tots estan configurats per fer feina amb UTF-8 i els tabuladors configurats a 4 espais. Amb això en tenim prou per poder fer servir en qualsevol moment l'editor que més ens agradi. Particularment vaig d'un a l'altra segons la màquina que faig servir.

Per les planes de contingut estàtic hem fet servir django-page-cms, la idea és mostrar com un gestor de continguts es pot integrar dins l'aplicació.

Els css i js es comprimeixen abans de servir-se gràcies a django-compress. Això vol dir que podem fer cachés mesos. Quan el css o el js canvii sols hem de tornar a generar els arixus comprimits i la llibreria els posa un nou nom.

La configuració de sistemes

L'aplicació s'executa en un chroot propi dins un servidor propi que duu moltes més aplicacions.

vendor_id   : GenuineIntel
cpu family  : 6
model       : 15
model name  : Intel(R) Pentium(R) Dual  CPU  E2180  @ 2.00GHz
stepping    : 13
cpu MHz     : 1994.999
cache size  : 1024 KB

top - 08:52:55 up 729 days, 13:56,  0 users,  load average: 0.07, 0.02, 0.00
Tasks: 126 total,   1 running, 125 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1015232k total,   984036k used,    31196k free,   154472k buffers
Swap:  2097144k total,     2948k used,  2094196k free,    90536k cached

L'entorn d'execució s'independitza de la màquina amb aquest chroot i a més es va crear un entorn virtual separat per l'aplicació amb virtualenv, això permet tenir un control total damunt les llibreries i el Python Path.

Seguint les millors pràctiques, hem separat els dominis que serveixen el contingut estàtic del contingut dinàmic. Això és prou senzill amb Django i sols has de recordar escriure les url amb {{MEDIA_URL}}.

Donat que és una màquina amb altres aplicacions volíem que el consum de memòria fos mínim. Per això hem optat per que el servidor http no fos un Apache sinó nginx que tenim configurat tant per servir el contingut estàtic com per a fer de proxy invers cap al motor WSGI que executa l'aplicació Django.

Pel motor WSGI triarem CherryPy (d'aquí no res posarem Tornado). Amb això tenim una relació de consum de recursos de màquina i rendiment molt òptima i una escalabilitat horitzontal fabulosa. Posant-hi un balancejador podem anar copiant i aferrant chroots i dins cada chroot podem tenir tantes instàncies de l'aplicació con suporti la màquina.

És veritat que no calia complicar-se tant per a una prova de concepte, però la idea no és tan sols mostrar el que es pot fer en programació, sinó com la tecnologia i el frameworks s'adapten a les necessitats actuals i futures de rendiment. És a dir, que s'escala cap amunt i cap avall.

L'aplicació

A la primera plana hi trobam el cercador, el típic cercador afegiria. Aquí utilitzam jquery-ui pels widgets de calendari. Hi ha la demostració de l'autocompletat als apartats de destinos i nombre de hotel.

El errors es mostren a la plana de manera poc intrusiva. Per això s'utilitzen plugins de jquery i les capacitats de serialització json de Python i Django, junt amb la validació de formularis de Django. És molt més senzill del que sona.

També es pot veure la utilització que es fa del CMS en les planes de Aviso Legal per exemple i també hi podem trobar dos tipus d'ofertes.

Aquestes es mantenen dins la part d'administració. No són gaire sofisticades, però serveixen per mostrar el dinamisme que es pot aconseguir i el concepete d'url semàntica.

Picant damunt una oferta per exemple, anirem a la plana de resultats. Recordem que anam contra un entorn de proves i que les dades són les que són. La idea és poder mostrar com les dades de l'hotel es carreguen dinàmicament mitjançant una cridada AJAX i com l'aplicació manté en sessió les dades de la selecció.

Hem posat també un filtre js. Pitjant damunt el filtre de categoria desapareixen els hotels amb aquesta categoria. Es pot utilitzar el mateix concepte per a filtrar els resultats per altres camps (preu, tipus d'habitació, ...)

A partir d'aquí ja anam a les pantalles que obtenen la informació de compra i a la de gràcies. Res destacable en aquest punt que no hagi sortit abans.

Pas a producció

Hi ha gent que ens ha demanat pel pas a producció d'aquesta aplicació. Recordau que és una prova de concepte, no hi haurà pas a producció, serveix per mostrar idees de programació, conceptes tecnològics i d'optimització.

El que si es pot fer és desenvolupar una solució a mida amb aquestes tecnologies, però serà sempre per a un tercer i si es fes hi hauria molta feina que en la prova de concepte s'ha obviada:

  • Continguts estàtics: hem posat lorem ipsum gairebé per tot.
  • Filtres
  • Paginació de resultats
  • Connexió amb una passarel·la de pagament
  • Generació del bono
  • Enviament del bono al client
  • Backoffice de control

L'aplicació tal com està i en les seves possibles evolucions té per objectiu que gent com nosaltres que ens dedicam al desenvolupament Python i Django poguem mostrar al món turístic el que es pot fer. Consideram l'aplicació com un Projecte Mascota: hi anam dedicant hores quan ens fa ganes.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Simple Web services


Escrit per Aaloy a 23 de September , 2009 a les 8:33 p.m.

L'altra dia ens van donar un "curset" d'introducció a TIBCO i ho pos entre cometes perquè curset potser no és la paraula adient, ja que més aviat va ser una presentació comercial. Tot i això i entre capada i capada d'avorriment vaig tenir l'ocasió de parlar amb el formador (o deformador) dels serveis dins les possibilitat que ofereix TIBCO. Deia que era molt senzill agrupar serveis i generar el WSDL corresponent per a ser consumit fàcilment per altres aplicacions.

No dubt que això sigui així però les meves reticències fonamental venen donades pel fet de que quan vols que el teu servei sigui consumit externament, el que has de fer és facilitar al màxim que la gent ho pugui fer.

El SOAP és un protocol que va néixer per ser simple i que ha acabat essent complexe fins al punt de fer-se immanejable, sobre tot gràcies a les extensions que es varen introduir per a facilitar-ne la creació i consum per llenguatges concrets. L'article de Peter Lacey del 2006, anomenat The S Stands for Simple en fa una discussió emprant el mètode socràtic que s'ho paga llegir.

Quan per raons de negoci (el cap ho ha exigit, per dir-ho més clar) hem tingut que fer els web services amb SOAP el que hem procurat sempre és controlar molt bé el resultat final del WSDL de manera que fos fàcilment consumible tant pel qui l'ha creat (Java en el nostre cas) com per altres llenguatges (Python per exemple). Aquest resultat difícilment s'obté si qui genera el SOAP suposa que és el mateix que l'ha de consumir i per tant no veu la complexitat des del punt de vista d'un tercer, sinó que genera el WSDL de manera que la transformació inversa li sigui favorable.

Per una altra banda afegir la capa SOAP i consumirla té un cost (el famós payload) en termes de capacitat de procés i ampla de banda. Si alguan cosa té és que entre namespaces, definicions i subdeficinions, un missatge que podria de ser de pocs bytes multiplica el seu pes per 100 o per 1000.

Per una altra banda, la complexitat del WSDL fa que sovint no basti el WSDL com a documentació (un dels objectius del SOAP) sinó que s'ha d'adjuntar una documentació addicional explicant cada missatge, quins són els paràmetres, etc. Així que argumentar que el WSDL s'autodocumenta és pecar un poc d'ingenuo i optimista.

El SOAP és bo si es mantén simple, el problema és que fer-ho simple i consumible fàcilment duu molta feina i se suposava que això ens ho hauria d'evitar.

Ara mateix l'alternativa és tornar a la simplicitat. Evitar la sobrecàrrega de feina de màquina i gent que suposa el SOAP i anar cap a protocols més senzills:

  • XML+HTTP. Amb una eina d'extracció de documentació podem fer la web de documentació i proves al mateix temps que escrivim el servei. Activant la compressió del gzip del servidor ens queda tot d'allò més compacte.

  • XML-RPC. Anam un poc més enllà. Podem consumir l'XML com si d'una llibreria es tractàs. Igual que abans la documentació dins el codi ens pot permetre estalviar molta feina. David Fisher ha fet un exemple molt instructiu amb Django d'aquest concepte.

  • Json-RPC o Json+HTTP. El Json s'ha convertit en un format d'intercanvi potent i senzill. Perquè no utilitzar-ho? Es pot consumir gairebé des de qualsevol llenguatge modern i la transformació a objectes nadius és trivial.

  • REST. Utilitzam les URL si l'HTTP per al nostre intercanvi d'informació. És el que mou la web. Se li ha donat un nom i un conjunt de criteris per a formalitzar el mecanismes d'accés als serveis.

El gran avantatge de tot això és que la generació del servei no requereix de llenguatges "empresarials", sinó que ho podem fer fins i tot amb qualsevol microframework (web.py, Tornado, ...) amb Django o amb qualsevol cosa que ens permeti respondre a una petició http i tractar-ne les capçaleres.

Per Django per exemple tenim el projecte Django-Piston que ens permet crear una API REST per als nostres projectes d'una manera molt poc intrusiva.

Fa una bona temporada que el SOAP i els WSDL que hem fet sols estan en manteniment. Si els tengués que fer ara i depengués de mi o serien serveis REST o bé XML purs i segurament tampoc estarien fets en Java.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django


Comenta, que serveix


Escrit per Aaloy a 21 de September , 2009 a les 9:02 p.m.

Quan un fa programes per aprovar l'assignatura sap que ha de posar comentaris perquè si no no és considera un codi professional, així que els posam per obligació, perquè hi han de ser, però potser sense adonar-nos de la seva utilitat real.

Els programes que feim per aprovar rarament són molt complexos, són exemples que es poden fer en poques setmanes i que rarament s'hauran de mantenir. La vida real és força diferent, els programes tenen un cicle de vida llarg, d'anys o dècades i s'han de fer millores correctives i adaptatives.

Aquell programa que ahir teníem molt clar al cap d'uns anys potser ja no ho tindrem tant i els comentaris que posàrem faran que tot ens resulti molt més fàcil d'entendre. I ja no diguem res si no som nosaltres el que hem de mantenir el codi!

Els bons comentaris ens haurien de dir el perquè del codi, què fa, què espera a l'entrada i què a la sortida. El codi ens dirà com ho fa. Això vol dir que el codi també ha de ser llegible, teclejar una mica més no costa tant i podem posar noms a les variables, a les classes, a les funcions, que tenguin significat.

Python per exemple té dues castes de comentaris, els que es fan servir per documentar el per què, i que sovint es presenten en forma de cometes triples i que es lliguen al doc (i a l'ajuda) de l'objecte i els comentaris fets amb la parrilla (#) que s'han de fer servir per comentar aspectes del codi que no necessitin. Java té quelcom semblant.

Però podem anar una mica més enllà. L'altra dia al twitter algu deia que li quedava fer la documentació de la pràctica. També molt habitual :) Posar bons comentaris ens pot estalviar una bona feina i no tan sols això, sinó que serà text de qualitat ja que s'ha fet amb menys presses i amb la ment més fresca en relació al que se està fent.

I encara una utilitat més: el pseudocodi. Particularment quan he de fer una cosa complexa començ per escriure'n les passes i les pos en forma de comentari. D'aquesta manera tenim més clar el que s'ha de fer i el temps s'aprofita, ja que aquest pseudocodis seran després els comentaris que ajudaran a seguir millor el codi.

No vull acabar sense citar una eina fantàstica per a la documentació: l'Sphinx us podeu adonar d ela bona feina que fa amb la documentació de Python o Django per exemple. Sphinx té una cosa molt bona, ens permet extreure els comentaris del nostre codi Python i afegir-los a la documentació, de manera que du el reaprofitament i la màxima DRY al món dels comentaris i la documentació. Per poc que us agradi el RestructuredText no deixeu de fer-li una ullada a aquesta eina, fins i tot si no programau en Python.

Per cert, en el seu dia vaig deixar un petit tutorial de RestructuredText a Bulma.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Django CherryPy vs Tornado


Escrit per Aaloy a 14 de September , 2009 a les 7:28 p.m.

Fa pocs dies s'ha alliberat un motor web anomenat Tornado part de la tecnologia que mou FriendFeed.

És un servidor no bloquejant que promet molt en quan a velocitat, pensat per moure FriendFeed i escalar la càrrega que faci falta.

Independentment de que es pugui desenvolupar amb ell tot una aplicació, sols la part de servidor http ja és prou interessant com per fer-li una ullada. En un post a la llista de Django Bret Taylor, un dels autors de Tornado, postejava com connectar el motor amb Django mitjançant WSGI.

Actualment una de les configuracions que més m'agraden per desplegar aplicacions és la combinació nginx+CherryPy WSGI+Django (amb balancejadors, memcached i tota la pesca gràcies a la bona feina de Bernat i Pere), així que estava força intrigat per veure com respondria Tornado.

La base estava feta: una mini-aplicació "hello world" que vaig escriure per comparar aquesta tecnologia/arquitectura amb diferents frameworks PHP, així que ha está un no res actualitzar la darrera versió de CherryPy WSGI i crear una nova aplicació semblant per Tornado.

El codi font és a:

I ara els resultats:

La màquina és un PPC 1 Gb de RAM, 2 CPU de 2 GHz

Amb Tornado WSGI:

ab -c 10 -t 60 http://localhost:8888/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Finished 25022 requests

Server Software:        TornadoServer/0.1
Server Hostname:        localhost
Server Port:            8888

Document Path:          /
Document Length:        266 bytes

Concurrency Level:      10
Time taken for tests:   60.019 seconds
Complete requests:      25022
Failed requests:        0
Write errors:           0
Total transferred:      9333206 bytes
HTML transferred:       6655852 bytes
Requests per second:    416.90 [#/sec] (mean)
Time per request:       23.987 [ms] (mean)
Time per request:       2.399 [ms] (mean, across all concurrent requests)
Transfer rate:          151.86 [Kbytes/sec] received

Connection Times (ms)
             min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       6
Processing:     3   24   1.9     24      68
Waiting:        0   24   1.9     23      68
Total:          7   24   1.9     24      68

Percentage of the requests served within a certain time (ms)
 50%     24
 66%     24
 75%     24
 80%     24
 90%     25
 95%     26
 98%     27
 99%     28
 100%     68 (longest request)

Usant CherrPy amb 3 threads (la millor configuració per la meva màquina segons les meves proves)

ab -c 10 -t 60 http://localhost:8088/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Finished 21500 requests

Server Software:        CherryPy/3.0.3
Server Hostname:        localhost
Server Port:            8088

Document Path:          /
Document Length:        266 bytes

Concurrency Level:      10
Time taken for tests:   60.001 seconds
Complete requests:      21500
Failed requests:        0
Write errors:           0
Total transferred:      8299000 bytes
HTML transferred:       5719000 bytes
Requests per second:    358.33 [#/sec] (mean)
Time per request:       27.907 [ms] (mean)
Time per request:       2.791 [ms] (mean, across all concurrent requests)
Transfer rate:          135.07 [Kbytes/sec] received

Connection Times (ms)
             min  mean[+/-sd] median   max
Connect:        0    0  20.5      0    2999
Processing:     3   28  10.7     26     413
Waiting:        3   26  10.3     24     412
Total:          3   28  23.1     26    3031

Percentage of the requests served within a certain time (ms)
 50%     26
 66%     28
 75%     30
 80%     31
 90%     34
 95%     38
 98%     43
 99%     48
 100%   3031 (longest request)

En resum:

416.90 req/s for Tornado WSGI
358.33 req/s for CherryPy

Tornado fa que una CPU es posi al 100% gairebé des del començament de l'execució de la comanda ab, CherryPy també posa la CPU al 100% però ho fa més tard i potser això explica la diferència, potser algún gurú de CherryPy em podrà dir una configuració millor que la que tinc.

58 peticions representen un 16% més de peticions que pot aguantar Tornado respecte de CherrPy, i si us hi fixau el codi que es necessita per executar-hi Django és pràcticament calcat gràcies al WSGI.

Seguesc fent proves, però per ara la cosa pinta molt bé.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Python Django


Decoradors a Python


Escrit per Aaloy a 30 de August , 2009 a les 7:22 p.m.

Què és un decorador

Un decorador és el nom d'un patró de disseny. Els decoradors alteren de manera dinàmica la funcionalitat d'una funció, mètode a classe sense tenir-ne que fer subclasses o canviar el codi font de la classe decorada. En el sentit de Python un decorador és quelcom més, inclou el patró de disseny, però van més allà, Bruce Eckel els assimila a les macros de Lisp.

Els decoradors i la seva manera d'utilitzar-se ens ajuden a fer el nostre codi més net, a autodocumentar-lo i a diferència d'altres llenguatges de programació no requereixen que ens aprenguem un altre llenguatge de programació (com passa amb les anotacions de Java per exemple). En la seva utilització podem atracar-nos a la programació orientada a aspectes (AOP) o utilitzar-los per a afegir sistemes de control a les nostres funcions, de log, caché, ... Les possibilitats són infinites. El decoradors formen part de Python des de la versió 2.4 i com diu Michele Simionato ens aporten el següent:

  • Redueixen el codi comú i repetitiu (l'anomenat codi boilerplate).
  • Afavoreixen la separació de responsabilitats del codi
  • Augmenten la legibilitat i la mantenibilitat
  • Els decorador són explícits.

Aquesta potència té un preu: en rendiment (que s'haurà d'avaluar per a cada aplicació) i en complexitat a l'hora de desenvolupar-los. Un decorador típic veurem que és molt bo d'escriure, però la cosa es complica un poc quan volem passar paràmetres o mantenir la signatura del mètode. Aquesta complexitat no és tant pel codi que s'ha d'escriure sinó perquè hem de recordar com s'ha d'escriure el decorador per a cada cas.

Afortunadament veurem que gent com Michele Simionato han desenvolupat paquets que ens simplifiquen molt la vida. Tot i això i abans de fer servir aquestes utilitats convé saber què són i desenvolupar-los sense ajuda. És un poc com aprendre's les taules de multiplicar i després ja utilitzar la calculadora.

Classificació dels decoradors

Podem dividir els decoradors en grups:

  • Segons els paràmetres que admeten:
    • No admeten paràmetres
    • Sí admeten paràmetres
  • Segons si preserven la signatura del mètode al que decoren:
    • Decoradors no que preserven la signatura
    • Decoradors que si la preserven

Els decoradors més senzill són aquells que no admeten paràmetres i no preserven la signatura

Un decorador que no fa res

Per començar crearem un decorador que el que farà es convertir qualsevol funció en un /dev/null, és a dir, no retornarà res i no farà res amb la funció.

1
2
3
4
5
6
7
8
def forat_negre(f):
    def none():
        pass
    return none

@forat_negre
def di_hola():
    return "hola"

Si executam di_hola() no tendrem cap resultat, millor dit tindrem None

La sintaxi @ del decorador de Python és el que s'anomena syntactic sugar, és a dir, una manera d'escriure les coses que ens simplifica la legibilitat, però fet i fet es podria escriure perfectament com

1
2
di_hola = forat_negre(di_hola)
di_hola()

i tendríem el mateix que fa el decorador. Recordem que les funcions són objectes i que es poden assignar i passar com a paràmetres a Python.

Tot i la senzillesa de l'exemple ens serveix per veure el següent:

Un decorador no és més que un envolcall cap a una funció i per tant ha de retornar una funció, més concretament un callable, per a entendre'ns, qualsevol cosa que posant-hi un doble parèntesi al costat () no peti.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def retorna_objecte(f):
   ....:     def obj():
   ....:         return object()
   ....:     return obj
   ....:

In [17]: def di_hola():
   ....:     return "Hola"
   ....:

In [18]: di_hola = retorna_objecte(di_hola)

In [19]: di_hola()
Out[19]: <object object at 0xf7f745e8>

Al nostre decorador forat_negre li hem passat una funcició sense paràmetres, però si li passam paràmetres ens trobarem una sorpreseta

1
2
3
4
5
6
7
8
@forat_negre
def suma(a,b):
    return a,b

suma(2,3)

TypeError Traceback (most recent call last)
TypeError: none() takes no arguments (2 given)

que per una altra banda és del tot normal, hem definit el forat_negre de tal manera que retorna una funció sense paràmetres, així que si li intentam passar els paràmetres que tenia la funció decorada senzillament es queixa i peta.

Anem a definir un poc millor el nostre decorador per a que no ens passi així i poder admetre el mateixos paràmetres que la funció decorada

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def forat_negre(f):
    "d'aquí no surt res"
    def none(*args, **kw_args):
        pass
    return none

@forat_negre
def suma(a,b):
    "suma dos parametres qualsevols si pot"
    return a+b

suma(2,2)

Ara ja no dona error. Així doncs una altra conclusió: a més de tornar una funció, hem de procurar que la definició de las funció que tornam admeti al manco els mateix nombre de paràmetres que la funció que volem decorar. Si no sabem quants són aquests ens curam en salut amb args i kw_args.

Fixem-nos que no hem mantingut la signatura de la funció i com a experiment intentau fer un help(suma). Tornarem damunt això un poc més endavant. Ara per ara ja sabem com crear decoradors simples a partir d'una funció.

Fent decoradors no intrusius

Si heu fet un help(suma) o un suma.__name__ potser un haureu sorprés en veure que le nom de la funció és none en lloc de l'esperada suma. Si pensau amb el que hem fet tampoc és d'extranyar, fet i fet hem substituït la funció original per una altra, recordem que el decorador f aplicat damunt la funció g és equivalent a fer g = f(g).

El que és aconsellable és que el decorador sigui capaç de mantenir la documentació i el nom de la funció que decora, ja que d'aquesta manera es simplifica l'ús de la funció i els autocompletadors de codi no es tornen bojos.

Això ho podem fer de dues maneres: la llarga i la curta

La manera llarga

1
2
3
4
5
6
7
def forat_negre(f):
    def none(*args, **kw_args):
        pass
    none.__doc__= f.__doc__
    none.__dict__= f.__dict__
    none.__name__= f.__name__
    return none

Amb les tres instruccions adicionals que hem posat tornar a recuperar les metadades de la funció original que passam al decorador. Si hara feim un help veurem que es fa damunt el nom de la funció correcta suma i que l'ajuda també és la seva.

Help on function suma in module __main__:

suma(*args, **kw_args)
    Suma dos parametres qualsevols si pot

Fixem-nos en la signatura de la funció no s'ha preservar. Abans admetia dos paràmetres i ara n'admet un nombre qualsevol. Per la majoria de casos això no té més importància, però al final de l'article veurem com es pot resoldre.

La manera curta

Com que el tema de reservar les metadades és força interessant i comú, al mòdul functools hi trobam la funció wraps que és en sí mateixa un decorador i que fa aquesta funció. D'aquesta manera el codi anterior quedaria:

1
2
3
4
5
6
7
from functools import wraps

def forat_negre(f):
    @wraps(f)
    def none(*args, **kw_args):
        pass
    return none

Fixau-vos que hem fet servir un decorador per crear un altre decorador. Insistirem en aquest tema més tard.

Un decorador amb arguments

El decorador que hem fet a l'apartat anterior era prou simple, feia ben poca cosa i no tenia paràmetres. Si volem fer decoradors hem de fer primer de tot que siguin útils, i també ens trobarem amb la necessitat de que aquests decoradors admetin paràmetres.

A Django, per exemple, podeu trobar que el decorador de cache admet paràmetres que ens permet dir-li durant quan de temps ha de cachejar els resultats, o el decorador vary_on_headers, que ens permet modificar el contingut de la resposta de les vistes afegint les capçaleres que indiquem.

Anem a veure com ho podem aconseguir nosaltres. També hi ha dues maneres de fer-ho, la clara i la complexa. La manera clara és la que recoman i utilitza una classe per a fer el decorador, la complexa requereix més esforça per a entendre què està fent el decorador, és més curta, però personalment preferesc un codi més legible.

De la mateixa manera els decoradors que hem fet com a funcions es poden crear com a classes, però en aquest cas, crec que la definició en forma de funcions és més bona de seguir, i ens permetrà distingir clarament entre els dos tipus de decoradors: el que no admeten paràmetres que es construeixen preferentment mitjançant funcions i els que admeten paràmetres, que es construeixen preferentment fent servir classes.

Per seguir amb el forat negre, ara el nostre exemple el que farà es mostrar el resultat o no segons li roti. Per això el que farem serà passar-li una funció com a paràmetre que en ser executada determinarà si s'ha de mostrar el resultat de la funció decorada o no

El mètode clar de fer decoradors amb arguments

 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
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import random

class forat_negre_sonat(object):
    "Un decorador amb fam"
    def __init__(self, mostrar):
        self.mostrar = mostrar

    def __call__(self, f):
        def none(*args, **kw_args):
            if self.mostrar():
                return f(*args, **kw_args)
            else:
                return "Nop"
        return none

@forat_negre_sonat(mostrar = lambda :random.choice((True, False)))
def suma(a, b):
    "Suma dos elements que li passam com a paràmetre"
    return a+b

if __name__=="__main__":
    print suma(2,3)
    print suma(5,6)
    print suma(9,5)

Fitxem-nos amb que hem fet:

  1. Hem creat una classe Python que al seu constructor (l'init) agafa el paràmetre o paràmetres que vulguem. És un constructor normal, així que admet paràmetres per defecte per exemple.

  2. Recordem que el decorador hem dit que ha de ser un objecte cridable (callable), a una classe, la cridabilitat la dóna el mètode call. Aquesta classe la definirem de manera que agafi la funció a decorar com a paràmetre. D'aquesta manera tenim accés tant als paràmetres del decorador, que hem passat al constructor, com a la funció decorada, que hem passat com a paràmetre al call.

Després d'això ja sols en queda encapsular la cridada com ho fèiem al cas anterior, retornant el decorador en lloc de la funció decorada.

A l'exemple el que he fet és mostrar que el paràmetre pot ser el que nosaltres vulguem, en concret he passat una funció anònima, creada amb lambda que és la que s'encarrega d'establir l'aleatoritat del resultat.

Si voleu podem fer aquest decorador una mica més complet, fent que admeti a més de funcions valors i que preservi el nom i documentació de la funció decorada.

 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
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import random

class forat_negre_sonat(object):
    "Un decorador amb fam"
    def __init__(self, mostrar=None):
        self.mostrar = mostrar

    def __call__(self, f):
        def none(*args, **kw_args):
            if callable(self.mostrar):
                opcion = self.mostrar()
            else:
                opcion = self.mostrar
            if opcion:
                return f(*args, **kw_args)
            else:
                return "Nop"
        none.__name__ = f.__name__
        none.__doc__ = f.__doc__
        return none

@forat_negre_sonat(mostrar = lambda :random.choice((True, False)))
def suma(a, b):
    "Suma dos elements que li passam com a paràmetre"
    return a+b

@forat_negre_sonat(mostrar=True)
def resta(a,b):
    return a-b

if __name__=="__main__":
    print "Exemple amb %s " % suma.__name__
    print suma(2,3)
    print suma(5,6)
    print suma(9,5)
    print "Exemple amb %s " % resta.__name__
    print resta(2,3)
    print resta(5,6)

El mètode enrevessat de fer decoradors amb arguments

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def forat_negre_dos(mostrar):
    def wrap(f):
        @wraps(f)
        def wrapped_function(*args, **kw_args):
            if callable(mostrar):
                opcion = mostrar()
            else:
                opcion = mostrar
            if opcion:
                return f(*args, **kw_args)
            else:
                return "Nop"
        return wrapped_function
    return wrap

Bé, enrevessat, el que es diu enrevessat no ho és, per una cosa tan simple no té massa història, però fixau-vos que és un poc més mal de seguir.

El primer que hem fet és definir la nostra funció, on hi hem posat els paràmetres que admet. Aquest funció retorna una altra funció que admet un argument, que és la funció decorada, que a la seva vegada admet un nombre indeterminat d'arguments (recordem que això ho estam forçant nosaltres).

Com que la segona funció, wrapped_function està definida dins wrap, té accés al paràmetre del decorador i pot actuar en conseqüència.

Encadenant decoradors

Els decoradors es poden encadenar, és a dir, una funció pot tener tans decoradors com faci falta i necessitem, sols limitats pel nostre sentit comú i la legibilitat del programa. Dos decoradors són habituals, tres no es veuen gaire, quatre o més són per pensar-s'ho.

Per a l'exemple manllevaré un dels decoradors més útils, el memoize, que ens permet cachejar una funció segons els seus paràmetres. Al Python Decorator Library hi ha una implementació del patró memoize prou senzilla de seguir amb el que ara sabem i a més ens servirà per completar la construcció de decoradors sense paràmetres fent servir una classe.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class memoized(object):
   """Decorator that caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned, and
   not re-evaluated.
   """
   def __init__(self, func):
      self.func = func
      self.cache = {}
   def __call__(self, *args):
      try:
         return self.cache[args]
      except KeyError:
         self.cache[args] = value = self.func(*args)
         return value
      except TypeError:
         # uncachable -- for instance, passing a list as an argument.
         # Better to not cache than to blow up entirely.
         return self.func(*args)
   def __repr__(self):
      """Return the function's docstring."""
      return self.func.__doc__

A diferència de la construcció amb paràmetres, al constructor de la classe memoized s'hi posa com a paràmetre la funció a decorar, i al mètode call hi van els paràmetres de la funció, en lloc de la funció a decorar com es feia a l'altre mètode.

Per què s'ha fet servir aquesta manera si l'altra és més senzilla? Dons perquè necesitam mantenir en memòria la caché i el que fa és mantenir-la en un diccionari dins de la mateixa classe. Si la caché fos externa (amb memcached per exemple), això s'hauria pogut fer perfectament en forma de funció.

A més definirem un decorador que ens servirar per indicar quan entram a la funció i comprovar el decorador memoized.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def log(f):
    "Registra l'execució de la funció"
    def wrap(*args):
        print "Excutant %s, args: %s" % \\
           (f.__name__, ",".join(str(x) for x in args))
        return f(*args)
    return wrap

@memoized
@log
def fibonacci(n):
    "Return the nth fibonacci number."
    if n in (0, 1):
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print fibonacci(12)

Provau d'executar aquest codi amb i sense la funció memoized. Amb els dos decoradors activus veureu que el cada decorador agafa com a entrada la funció ja decorada que surt del decorador que té més avall. Així el memoized agafa com a entrada la funció fibonacci ja decorada amb el log.

Podeu fer la prova amb un exemple més simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

def uppercase(f):
    "Dada una función f que devuelve un string lo pasa todo a mayúsculas"
    def wrap():
        return f().upper()
    return wrap

def make_bold(f):
    "Dada una función f que devuelve un string le añade los tags de bold"
    def wrap():
        return "<strong>%s</strong>" % f()
    return wrap

@make_bold
@uppercase
def say_hello():
    return "Hello world"

print say_hello()

Provau canviant l'ordre dels decoradors i veureu perfectament com es van aplicant els decoradors des de la funció per amunt. A l'exemple primer es converteix el "Hello word" a majúscules i després se li apliquen els tags de negreta.

La signatura pendent

Abans d'acabar ens queda un tema pendent: la signatura. Els decoradors que hem creat poden preservar el nom i la documentació de la funció que decoren, però no preserven la signatura, és a dir, el nombre de paràmetres que li passam.

Michele Simionato ha escrit un mòdul excel·lent anomenat decorator que extén la utilizació dels decoradors, mantén la signatura de la funció, el nom i la documentació, i a més ens dona la possibilitat de crear factories de decoradors. Una eina per a tenir sempre a mà. Amb aquest mòdul podríem escriure el codi de l'exemple anterior com:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from decorator import decorator

@decorator
def uppercase(f, *args):
    "Donada una funció f que retorna un string ho passa a majúscules"
    return f(*args).upper()

@decorator
def make_bold(f, *args):
    "Afegeix el tag strong a la sortida de la funció"
    return "<strong>%s</strong>" % f(*args)

@uppercase
@make_bold
def say_hello(nom):
    "Di hola, home!"
    return "Hello world %s" % nom

if __name__=="__main__":
    from inspect import getargspec
    print say_hello('World')
    print say_hello.func_name
    print say_hello.__doc__
    print getargspec(say_hello)

Si executau el codi podem veure que no ens ha fet falta recore a wraps o a reasignar nom, la pròpia llibreria de Simionato ho ha fet. A més, si ens fixam en la sortida de l'exemple:

<STRONG>HELLO WORLD WORLD</STRONG>
say_hello
Di hola, home!
ArgSpec(args=['nom'], varargs=None, keywords=None, defaults=None)

La primera línea correspon a la sortida de la funció que hem decorat. La segona és el nom d'aquesta funció. Ens surt el nom de la funció original i no el del decorador. La documentació també s'ha mantingut i per acabar, podem veure que la signatura de la funció és correcta, ens diu que té un argument obligatori anomenat nom.

Conclusió

Esper haver deixat un poc més clar el tema dels decoradors. Crear-los no és difícil, utilitzar-los és simple, sols hem de tenir clar què són i quan fer-los servir. Són una eina potent que ens permet fer el nostre codi més legible i cohesionat. Fora por i a disfrutar amb els decoradors.

Com tot en aquesta vida, usau-los amb coneixement i moderació.

Referències

Per escriure aquest article m'he basat en múltiples fonts, les més importants i útils han estat:


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Qooxdoo


Escrit per Aaloy a 20 de August , 2009 a les 10:31 p.m.

Qooxdoo és un bastiment per a la creació d'aplicacion web amb aparença d'escriptori, és a dir el que es coneix com a RIA (Rich Internet Applicatinons).

Aquests darrers dies he estat donant-li una ullada a aquest bastiment. Feia temps que li estava seguint la pista i finalment m'he decidit a avaluar-lo com toca, és adir, començant a fer-hi alguna cosa productiva.

Quan un va a la web de Qooxdoo el primer que pot reparar és que el bastiment compta amb un bon conjunt d'exemples que ens permeten veure les possibilitats del producte. L'aparença de Qooxdoo no és tan cuidada com la d'Extjs per exemple, però en el seu conjunt presenta tota les funcionalitats necessàries.

A l'hora d'avaluar el bastiment m'he fixat en dos punts que per mi són claus: la mantenibiliat de les aplicacions i les seves capacitats de connexió amb altres sistemes.

Donat que el cicle de vida de les aplicacions és molt més llarg que el temps de desenvolupament, tenir un bastiment que ens permeti crear aplicacions mantenibles és fonamental. El bastiment ens ha de permetre estructurar l'aplicació en mòduls i a més ens ha d'abstreure de les complexitats del javascript en el que fa a les colisions de noms.

Qooxdoo resol aquest tema molt bé. No és tan sols un bastiment gràfic, sinó que ha creat un vertader llenguatge orientat a objectes amb les possibilitats bàsiques que ens dóna el javascript. La gent que ve de Java es sentirà com a casa amb aquest model d'objectes: ens permet tenir variables i mètodes estàtics, crear fàcilment mètodes, parla de constructors, d'herència i fins i tot hi ha alguan cosa semblant als interfícies. Aquest model de programació es pot aplicar per modularitzar el codi, de manera que podem crear objectes que representin una finestra amb el seu contingut o bé fer un objecte per a representar un aspecte concret: un menú per exemple.

A l'hora de posar el sistema en producció Qooxdoo se n'encarrega d'esbrinar quins són els mòduls que es fan servir i empaquetar i comprimir els distints mòduls que hem creat en un únic arxiu (més de 500 K a les meves proves). Com es pot veure està força ben pensat, i és una de les coses que més m'han agradat, ja que amb una aplicació gran és molt fàcil perdre la pista del que es fa i acabar amb mega-arxius javascript que tenen un poc de tot. Qooxdoo ens ajuda a estructurar el nostre codi sense perder l'avantatge de poder fer el desplegament a producció amb un únic arxiu javascript.

La connexió amb altres sistemes també em feia passar un poc de pena. Hem connectat bastiments javascript fent servir json, xml i cridades ajax i quan més complexe és el bastiment més dificultats hi ha amb el json, ja que sempre ho esperen d'una determinada manera.

Qooxdoo permet connectar-se mitjançant Ajax i fent servir Json RPC. He creat una sèrie de serveis jrpc de prova amb Django i he mirat de connectar-los. M'ha costat una mica ja que no comptava amb el problema de la seguretat del javascript a IE i Firefox. No es permeten cridades a dominis diferents (crossdomain), però tampoc a ports diferents. Com que Django a desenvolupament ho tenia a localhost:8000 i el javascript ho tenia com a file:// no hi havia manera de fer la connexió per POST. La solució ha esta utilitzar nginx per a servir el javascript i a més per servir de proxy invers cap a Django, amb això ja he pogut fer la connexió sense problemes i tenir el millor dels dos mons, independitzant la capa de presentació de la capa del web service, creat amb el servidor Django i amb capacitat d'autodocumentació, de manera que el servidor Django és capaç de generar una plana on es publiquen els mètodes del json rpc i una petita utilitat per comprovar-ne el funcionament. Semblant al WSDL però millor i tot.

Encara és prest per saber si Qooxdoo complirà totes les espectatives que hi tenc. Falta comprovar com es porta amb widgets complexos: taules, llistes, mescla de contingut web html amb el javascript generat i veure com es pot tractar l'autenticació i la seguretat. Per ara pareix que generar interfícies d'usuari potents és prou senzill i que la comunicació amb altres sistemes és possible, i ara per ara li veig moltes més possibilitat que opcions com Dojo i Extjs, sobretot perquè li veig una filossofia que m'agrada: la de tenir un sistema pensat per al desenvolupador i per al qui ha de mantenir l'aplicació.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django qooxdoo


Django 1.1


Escrit per Aaloy a 29 de July , 2009 a les 8:35 p.m.

Des de fa poquetes hores ja tenim la versió 1.1 de Django. Les novetats es poden trobar a l'anunci oficial de Django 1.1.

Per mi el més interessant són els canvis a l'ORM (les agregacions són una característica llargament esperada) i les millores a l'Admin, que es pot personalitzar sense fer tant esforç com abans.

Enhorabona als "core developers" i a tots els que feim feina amb Django. Tenim cada dia un bastiment més productiu i potent, on tot el que s'afegeix no és per mor del marketing o perquè fa "cool", sinó perquè realment serveix per a la feina.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django


Escalabilitat, multiprocessador i GIL


Escrit per Aaloy a 16 de July , 2009 a les 8:34 p.m.

Una de les dèries que tenim com a informàtics (o que hauríem de tenir) és la d'aprofitar el millor possible els recursos que tenim a la nostra disposició.

Això es tradueix algunes vegades en discussions de que si un llenguatge és millor que un altre en l'aprofitament de la màquina, i quan toca a Python, el tema estrella és el GIL, el mecanisme intern que fa servir Python per poder ser multi-fil. GIL fa que les aplicacions que fan un us intensiu dels fils no siguin tan òptimes com podrien ser (potser sí són més segures i més bones de programar, però això és una altra història) i que puguem pensar no facin tan bon us dels múltiples processadors com es podria suposar. En aquests casos el millor potser és llegir un article molt aclaridor, en lloc de començar a pegar destralades.

Tot i això, pareix que hi ha una confusió entre el que representa l'escalabilitat d'una aplicació i el nombre de processadors d'una màquina. En principi pareix el mateix, però realment no ho és.

La vertadera escalabilitat no la dóna el fet de que l'aplicació aprofiti al 100% els 4 o 8 processadors que pugui tenir el nostre servidor, l'escalabilitat la tenim quan aquesta mateixa aplicació pot executar-se damunt 8 màquines amb els processadors que tenguin. És a dir, que davant un problema que necessiti més potència de màquina, la solució no sigui posar una màquina amb més processadors, sinó posar més màquines.

Aqui ja no parlar d'aplicacions que executin fils, parlam d'aplicacions que executen processos, de comunicacions entre processos, de particionat de la nostra aplicació de manera que pugui distribuir-se.

Una de les maneres més senzilles que he trobat d'obtenir això és mitjançant els sistemes de missatgeria i de les coes de feina. En aquest cas tenim un o varis servidors que actuen de gestors de les coes de feina i una sèrie de processos que envien tasques a la cua per a ser realitzades, processos que realitzen les tasques i processos que consumeixen els resultats. El GIL no és un problema, el problema és pensar en com s'ha de fer l'aplicació, en com distribuir les càrregues entre els servidors, en definitiva, el problema és d'enginyeria de programari pur i dur. Passam de pensar en dues dimensions a pensar en n-dimensions.

Si hi ha una cosa que té bona Python és la poca distància que hi ha entre voler fer una cosa i tenir la capacitat de fer-la. Així doncs fer programes que facin servir les cues és realment trivial: posam un servidor, el beanstalkc per exemple, les llibreries Python que facin de client i ja ho tenim llest. Separar l'aplicació real per a que vagi en capes i establir un sistema integrat de control, això ja és una altra cosa :)

Quan parlam d'aplicacions web un sistema de coes a més pot fer-se servir per a augmentar l'escalabilitat de la nostra aplicació i donar un millor temps de resposta als nostres usuaris.

Imaginem per exemple una situació típica: la nostra web (feta amb Django, per suposat) quan acaba el procés de compra envia un e-mail a l'usuari amb la factura del que acaba de comprar.

Si no ens hem complicat la vida, tendrem que dins la mateixa vista que guarda les dades de la compra hem posat una cridada a la funció que genera el pdf amb la factura i la cridada a la funció que l'envia.

Encara que la factura es generi força ràpid, generar i enviar el pdf pot ser un problema si tenim molta càrrega, consumeix cicles de CPU que estam llevant als visitants de la nostra web.

En aquest cas podem tenir una maquina o vàries destinades a la generació i enviament de les factures. La nostra aplicació web sols envia a la cua (a una altra màquina o màquines) la petició de que s'ha de fer la factura, i els processos que tenim escoltant a la cua de treballs generaran i enviaran la factura.

Com que l'enviament del treball a la cua de treballs és pràcticament instantani, l'usuari té la sensació de que la web ha respost molt ràpidament. Com que tenim màquines dedicades per a la generació de les factures aquestes també es generen força aviat i s'envien. Mentre hem deixat el servidor (o servidors web) descarregats per atendre més peticions. Hem escalat!

És un exemple típic, com veis l'escalabilitat no s'ha aconseguit posant més màquines que fessin de servidors d'aplicacions Django o posant més processadors, sinó separant les tasques en màquines especialitzades. Si parlam de parsejar XML la cosa és encara més divertida, a l'article [High-performance XML parsing in Python with lxml] (http://www.ibm.com/developerworks/xml/library/x-hiperfparse/) de la web d'IBM i gairebé a les acaballes també ens dóna la pista: l'estratègia de dividir i conquerir; una altra vegada més pensar en com feim les coses.

Aquest és un apunt damunt escalabilita, Python i Django, però perfectament podríeu substituir Python pel vostre llenguatge de capçalera i Django pel vostre bastiment web preferit, Python fa fàcil provar totes aquestes coses, però el realment important és adonar-se de que es poden fer.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Eclipse Galileo vs Netbeans Python (trunk) per Python i Django


Escrit per Aaloy a 05 de July , 2009 a les 11:42 a.m.

La publicació de la nova versió d'Eclipse, Galileo ha servir d'excusa per a tornar (al manco temporalment) a Eclipse com a entorn de desenvolupament per Python.

És la versió Galileo d'Eclipse millor que Netbeans? Doncs depèn, al cap i a la fi del que es tracta és de que l'IDE ens faci més productius, però a partir d'aquí ja és una qüestió de preferències personals.

Anem a veure les meves...

Facilitat d'instal·lació

La facilitat d'instal·lació és important perquè fa que puguis actualitzar de versió o posar un nou programador a fer feina en poc temps. La gent que fa feina amb PL em diu que necessita molt de temps per poder tenir l'entorn de desenvolupament llest, i com tots sabem temps són doblers.

Ambdós entorns són iguals de bons d'instal·lar. Ambdós tenen una arquitectura de plugins, així que la velocitat de posada en marxa depèn fonamentalment del nostre ampla de banda.

Si ens limitam a Python dir que la versió de Netbeans per Python ja duu el pluggin integrat i necessita davallar menys coses. De la mateixa manera no és necessiten pluggins addicionals al Netbeans per fer feina amb javascript, css o html, ja ve tot de sèrie.

Guanya Netbeans però de ben poc, ja que gràcies a projectes com Yoxo podem configurar-nos la nostra pròpia distribució d'Eclipse.

Portabilitat

Faig feina gairebé al 99% damunt Linux, però amb versions i386 i PPC. De tant en tant he de provar alguna cosa damunt Windows i per això vull que el meu entorn de desenvolupament pugui funcionar igual de bé en totes aquestes plataformes.

Aquí hi ha un guanyador clar: Netbeans. Funciona igual de bé a totes les plataformes. Eclipse dóna molts problemes al PPC i de fet aquesta plataforma no està soportada als repositoris oficials.

Suport per Python

La versió Python de Nebeans ja té el pluggin integrat. Eclipse necessita el pluggin PyDev. Ambdós pluggins són molt bons: permeten la utilització del virtualenv, tenen ressaltat de sintaxi, autocompletat, ajudes, plantilles, refactorització i creació de Unit Tests. Empat!

Suport per Django

Cap d'ells proporciona un suport específic per Django, encara que Netbeans ho té com a projecte. Tanmateix, però, el més important és que l'entorn permeti executar el python manage.py runserver --noreload en mode depuració.

Aquí Netbeans falla. El depurador està molt enfora del depurador d'Eclipse i Pydev, que et permet posar punts de ruptura on vulguis i té tot el que un necessita en depuració. Amb Netbeans no hi ha més remei que fer servir import pdb; pdb.set_trace() al codi i utilitzar el depurador de línia de comandes. És un punt de millora molt important per Netbeans, i si no us sentiu còmodes amb la depuració per línea de comandes llavors l'elecció clara és Eclipse.

Suport per CSS

Eclipse necessita el puggin d'Aptana per posar-se a nivell de Netbeans i al meu parer no ho aconsegueix del tot. Ambdós tenen ressaltat de sintaxis, autocompletat i ajudes, però Netbeans a més et pot presentar un exemple de com queden les coses. Útil en algunes ocasions per veure visualment que l'estil que estam modificant queda com volem o que és el que volem modificar. Netbeans guanya per poc.

Suport per Javascript

Una altra vegada més Eclipse necessita d'Aptana per posar-se a nivell de Netbeans. Netbeans més proporciona un mode de depuració de javascript basada amb Firebug que va força bé. Pareix que Eclipse també suporta quelcom semblant però no és tan inmediat com amb Netbeans. No és una opció molt important, però donat que estam parlant d'integració de les eines més habituals a l'IDE s'ha de tenir en compte. Netbeans guanya.

Control de temps i tasques

Ambdós entorns ens permeten gestionar llistes de tasques a fer i controlar el temps dedicat a cada tasca. Associar arxius a tasques i controlar automàticament quan hi estam fent feina i quant no. Això és fonamental pels qui factures a hores o senzillament volen dur un control dels projectes.

Nosaltres feim feina amb Trac i Eclipse gràcies al pluggin Mylyn permet connectar-se a un repositori Trac i gestionar-ne les tasques. Eclipse guanya!

Aprofitament de l'espai

Personalment m'agrada tenir una distribució on tengui a la vista l'arbre d'arxius, l'editor i la consola de tasques i missatges. Quan faig feina amb les dues pantalles de 20" puc posar l'IDE a una pantalla i el navegador a l'altra, però quan faig feina amb sols una pantalla l'aprofitament de l'espai de l'IDE fa que pugui, o no, col·locar-hot tot per a tenir una visualtizació més ràpida i ergonòmica.

Eclipse consumeix molt més espai que Netbeans. Es pot tunejar un poc, però la tendència és a desaprofitar pixels en menús, pipelles i demés. L'estructura de Netbeans per mí és molt més elegant i amb un aprofitament d'espai gairebé òptim. Com a IDE per al portàtil: Netbeans.

Integració amb SCM

La integració amb el sistemes de control de versions és prou bona a ambdós entorns. En la nova versió la integració del pluggin Subversive és excel·lent a Eclipse i l'assistent gràfic en el maneig de branques de Netbeans és simplement genial.

En les operacions del dia a dia Eclipse guanya. Té una opció que et permet veure els canvis entrant i sortint (això també ho fa Netbeans) però a diferència de Netbeans la interfície d'usuari està molt més orientada a la usabilitat i és trivial llevar arxius, marcar-los com a no integrables, veure'n les diferències abans d'integrar, etc.

La usabilitat de Netbeans és força dolenta, de fet és molt més còmode anar per línia de comandes. Guanyador i amb molt avantatge: Eclipse.

Revisió de canvis

Netbeans ens permet mostrar quins canvis hem fet a l'arxiu que estam editant i manté un control local de versions. Eclipse també manté aquest control local, però la usabilitat és inferior.

A l'hora de mostrar diferències Netbeans manté el ressaltat de sintaxi, Eclipse no. Per mi el més útil és el control local i aquí Netbeans és el millor.

Plantilles

Ambdós IDEs ens permeten crear i gestionar les nostres pròpies plantilles de codi i ho fan força bé. Empat!

Execució de tests unitaris

Ambdós sistemes ho permeten, però com passa amb el control de versions, el d'Eclipse és molt més usable. Guanyador per poc: Eclipse.

Refactorització

Els dos entorns suporten la refactorització de códi. Empat!

Eines d'edició

Ambdós entorns ens permeten fer cerques a tot el projecte, fer servir expressions regulars, substitucions, ... Els dos editors són força avançats i més que suficients per a les necessitats d'edició de codi. Empat!

Afegitons

Eclipse des del principi ha fet de la possibilitat d'extendre l'entorn mitjançant pluggins una virtut. Netbeans s'hi ha incorporat més tard i això es nota. La quantitat d'afegitons per Eclipse esborrona, per Netbeans cada cop n'hi ha més i millors però encara Eclipse n'és un clar guanyador.

L'aposta per Python i Django

Eclipse no té una orientació clara cap a Python, però gràcies a PyDev i a la facilitat que té Eclipse per a integrar pluggins s'ha convertit en un entorn molt potent per a la programació.

Netbeans en canvi té un build sols per Python, ple suport i al roadmap hi ha la intenció de suportar Django: creació de projectes, plantilles, etc. L'aposta per Python a ca'n Netbeans pareix molt més clara que a Eclipse, encara que en aquests moments i com a regla general, actualment Eclipse proporciona molta més funcionalitat i integració que Netbeans.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Enumerate: indexant les llistes en Python


Escrit per Aaloy a 21 de June , 2009 a les 11:21 a.m.

Quan feim feina amb llistes de tant en tant en sorgeix la necessitat de fer feina amb l'index de la llista en lloc (o a més) de l'element de la llista en sí.

Sovint ens podem trobar fent coses com

1
2
3
4
5
x = ['a','b','c']
i = 0
for item in x:
    i +=1
    print i, item

Davant aquest tipus de problemes, pensau que el més habitual és que el propi llenguatge ja ho tengui resolt, de la mateixa manera que els problemes més habituals de la programació web estan resolts per Django.

El nostre problema es redueix a fer servir enumerate. Aquesta funció agafa un iterador (una llista per exemple) i ens retorna un nou iterador, els elements del qual són una tupla composta pel comptador (l'índex) i l'element de l'iterador inicial que li passam com a paràmetre.

enumerate pot agafar com a paràmetre un nombre que serà el valor inicial de la seqüència (per defecte zero). La seva sintaxi és doncs enumerate(iterable, start=0)

Amb això podem escriure el codi anterior com

1
2
3
x = ['a','b','c']
for i, item in enumerate(x):
    print i, item

o sí volem comptar a partir d'1

1
2
3
x = ['a','b','c']
for i, item in enumerate(x, 1):
    print i, item

El nom a més és prou descriptiu de la funcionalitat que fa, amb la qual cosa el codi ens queda fins i tot millor documentat.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Django: Guia d'aprenentatge


Escrit per Aaloy a 07 de June , 2009 a les 4:49 p.m.

La pregunta a la que vull intentar respondre en aquest article, és la que fa força gent que vol canviar la manera en que fa aplicacions web i com que ha sentit parlar molt bé de Django, s'atraca a aquest bastiment amb l'esperança de poder millorar la seva productivitat, és a dir: què he de saber per començar a fer webs amb Django?

Primer de tot em de saber què és Django. Django és un bastiment, és a dir, tot un conjunt de llibreries que interactuen entre sí i que estan orientades a fer i mantenir llocs i aplicacions web.

Primera cosa, doncs, Django no és un llenguatge de programació. Per a fer servir Django es necesita conèixer el llenguatge de programació amb el que està creat i aquest llenguatge no és ni més ni manco que Python.

Segona cosa: Com a bastiment que és Django requereix que les coses es facin d'una determinada manera, és el que anomena modle MVT (Model Vista Template), però al mateix temps ens està dient que per sí sol Django no ens construirà la nostra aplicació. Hi haurem de fer feina, segurament aquesta feina serà més ràpida i productiva, però no hi ha solucions màgiques.

Prerequisits

  • Se suposa que sabeu programar en algun llenguatge d'alt nivell.
  • Se suposa que teniu coneixements d'HTML i CSS
  • Se suposa que teniu coneixements del que és una base de dades relacional i d'SQL
  • Se suposa que saber fer anar la línia de comandes

Si no compliu aquests quatre requisits convendria replantejar-se el tema. Segurament trobareu que Django no és l'eina màgica que havíeu suposat.

En aquests temps de crisi costa molt dir que no als clients i una vegada emmerdats anar cercant l'eina màgica que ens tregui del problema. Per molt bona que sigui l'eina sempre hi haurà una sèrie de requisits, si no els compliu, millor cercau una altra cosa (potser una altra feina?) o anau invertint en la vostra formació de base.

Supòs que tots els que llegiu això compliu de sobra aquests requisits i m'estic passant de prudent i tiquis-miquis, però tenc una anècdota molt recent: l'empresa per la que treball habitualment va demanar un pressupost a una empresa externa. A aquesta empresa (no diré noms, però no és de Mallorca) se li va demanar que el pressupòs contemplàs que el llenguatge de programació triat per fer l'aplicació fos Python+Django, Java J2EE (amb Hibernate, Spring i JSP) i que en tot cas podien presentar la proposta per PHP. La proposta Java feia servir CakePHP i Zend i cap referència a cap tecnologia Java. La proposta PHP cap referència a versió de PHP. Imaginau quina impressió dóna el pressupost! Per acabar-ho de rematar la primera cosa que deia és que s'havia de fer l'anàlisi de requeriments. Què s'ha pressupostat doncs? Entenc que amb la crisi tothom està arreplegant tot el que pot, però tot té un límit!

Així doncs, per a començar, suposarem que ja teniu un cert nivell informàtico-programador i que no és la primera vegada que sentiu parlar de l'HTML i heu picat planes web a mà. L'SQL potser inicialment no serà tant necessari, però si realment volem arribar a fer aplicacions web mantenibles és imprescindible saber-ne i conèixer al manco els fonaments de les bases de dades relacionals.

Bé, ja ho tenim clar, i si heu arribat fins aquí ara estareu esperant que us digui cap on heu de tirar, què és el que s'ha d'aprendre i amb quin ordre per fer que la corba d'aprenentatge sigui el més suau possible. Som-hi doncs!

El bàsic de Python

Python és un llenguatge de propòsit general, molt net i estructurat. Es poden fer tant aplicacions de consola, aplicacions web o aplicacions gràfiques, el que vulguem. El que propòs aquí es el mínim que convé saber per poder fer webs quan abans millor i anar aprenent el llenguatge i les seves llibreries associades així com ho necessitem.

Una guia molt senzilla és la que tenim a Python para todos de Raúl González Duque, i que ens servirà com a referència d'estudi i com no el tutorial de Python. Què hem de saber en aquesta etapa?

La consola

  • Utilitzar Python en mode consola: Python com a calculadora
  • Instal·lació i execució d'iPython

El llenguatge

Hem de perdre la por a fer feina amb una consola de comandaments. La consola ens permetrà experimentar ràpidament amb el llenguatge, ens n'adonarem del que representa fer feina amb un llenguatge interpretat i fer fer-ho am iPython a més ens permetrà veure algunes de les possibilitat d'aquesta consola (que no oblidem la utilitza Django si està instal·lada).

  • Creació del "Hola Món". Establim les regles de tabulació (tabulació a 4 espais, UTF-8 i format Unix pels arxius és la meva elecció.)
  • Tipus bàsics: experimentació amb el intèrpret i l'editor. Nombres, cadenes, boolenas, llistes, tuples i diccionaris.
  • Sentències de control: if, for, pass, break i continue
  • Dates a Python.
  • Creació de funcions bàsiques. Paràmetres per nom, valors per defecte.
  • Documentació i comentaris

Amb això ja tindrem les nocions bàsiques del llenguatge. Encara no hem entrat en tota la potència de Python però si hem programat en una altra llenguatge ja tindrem en ment un bon anàlisi comparatiu. Segurament ja estareu pensant com refer alguns programent en Python.

  • Orientació a objectes bàsica.
  • Les funcions són també objectes. La funció lambda
  • Els paquets.

Amb un poc de sort ja sabreu què és la orientació a objectes, si nó és un bon moment per aprendre'n. L'objectiu és adonar-nos de que tot en Python és un objecte i com s'estructura el codi en classes, mòduls i paquets.

  • Programació funcional: fonamental veure la comprensió de llistes.
  • Ordenació de llistes.
  • Creació Manipulació de fitxers, utilització de les llibreries os y sys
  • Les excepcions.

Amb això ja tindrem gairebé el 80% del que necessitarem per fer una aplicació de consola, però no oblidem que a més nosaltres volem fer aplicacions web i a més amb Django, així que afegirem un parell de coses més:

  • Expresions regulars bàsiques amb Python. L'eina Kodos.
  • El mòdul Sqlite3 i el plugin de Firefox per a gestionar les bases de dades Sqlite.
  • l'easy_install. Hem de saber què és i com utilitzar-lo per a instalar noves llibreries.
  • Batteries included. Convé saber què significa aquest concepte i veure, al manco llegir, quines són les principals llibreries que Python inclou i què fan.

Amb això ja ens estam orientant cap a la web i Django. Les expressions regulars les farem servir sovint a Django a l'hore de mapejar urls amb les funcions Python corresponent. El mòdul sqlite ens permetrà veure el que és la API d'accés a base de dades de Python, familiaritzar-nos amb sqlite i si no ho havíem fet ja, començar a instal·lar els primers plugins de Firefox. Perquè tothom té Firefox per a desenvolupar webs, no?

Django

Podem començar a fer aplicacions web una vegada coneguem i ens sentim còmodes amb el llenguatge. El temps dedicat a Python no és per res temps perdut, ben al contrari. Feis exercicis, creau scripts, llegiu codi, l'important és poder llegir Python i que les instruccions surtin de manera natural.

  • Instalau Django des del subversion
  • Feis el tutorial de Django.

No està pensat per ser un manual de millors pràctiques sinó per mostrar el principal del bastiment i tenir alguna cosa repetible, una base comú.

  • Mirau appfusedjango, el subprojecte project. Està fet per a crear nous projectes pel mètode de copiar i aferrar.

Els models

  • Creació de models: l'ORM de Django.
  • Utilització de la consola per a introduïr dades als models
  • Utilització del manager de Django per afegir dades.
  • Execució de consultes fent servir l'ORM

En les aplicacions web passarem força temps manipulant dades, bé des de la web o bé directament amb scripts i la consola. Convé que no hi hagi por.

Les plantilles

  • Creació de planes estàtiques

És un primer exercici. Consisteix en agafar un grapat de planes estàtiques i convertir-les en una aplicació Django utilitzant el direct_to_template. Això ens ha de permetre veure com, sense més complicacions podem fer planes a la manera tradicional (i inefectiva).

  • Les plantilles de Django: definició i concepte d'herència.
  • Filtres
  • Tags
  • Refer les planes estàtiques anteriors fent servir l'herència de plantilles.

Aquí començarem a veure les possibilitats que té Django respecte de la manera més tradicional de fer les coses. Veurem que fer webs amb blocs i pensant en diferències és molt productiu.

El view.py i url.py

  • Veure com les urls mapegen contra funcions Python
  • Utilització de les plantilles
  • Les expressions regulars i les urls.
  • HttpResponse, render_to_template , redirect

Ens n'hem d'adonar que no hi ha màgia, que tot és codi Python que envolcalla al protocol HTTP.

Formularis

  • Creació de formularis.
  • Renderització de formularis
  • Formularis lligats a dades
  • Validació i control d'errors.
  • Pujar arxius i imatges.
  • L'autenticació

Dedicau el temps que sigui necessari als formularis. De ben segur en fareu molts al llarg del cicle de vida d'una aplicació web. Django és molt elegant en la seva utilització.

Quan es veuen els formularis m'agrada passar també per l'autenticació, ja que en aquest punt ja es té tot el necessari i consider que l'aspecte de la seguretat s'ha de tractar tot d'una que es pot.

Reflexió

No sé ben bé com anomenar aquesta etapa, però convé que dediqueu alguns dies a fer una aplicació web des de zero. Pensau en alguna que es us agradaria fer i vegeu-ne la seva viabilitat. Reduïu-ne l'abast si és necessari, però intentau fer una aplicació web completa.

L'objectiu és adonar-nos del que sabem, del que no sabem i del que no sabíem que no sabíem.

Django i Javascript

  • Json i Django
  • Exemple de jQuery (o la llibreria que utilitzeu) i com lliga amb les urls y views de Django.

No sols d'HTML viu l'home i cada cop les planes web tenen més javascript. Django és agnòstic en el que fa a la llibreria a utilitzar, però sigui com sigui fa que sigui realment fàcil interactuar amb llibreries javascript com jQuery, extjs o Dojo.

Fent les webs escalables

  • Internacionalització
  • Els principals middlewars.
  • Sistemes de cachés
  • Context processors
  • Creació de tags pròpies

Aquí ens n'adonarem de les respostes que dóna Django als problemes reals quan la nostra web passa de ser una idea a estar en producció i té prou visites. Hem de veure a més com podem interactuar amb les peticions HTTP i amb la informació que passam a les plantilles.

Reutilització

  • Convé adonar-se de com Django permet reutilitzar codi, de l'estructuració del projecte en aplicacions i d'aquestes en mòduls PYthon.
  • Repassar els principals settings.
  • Repassar les aplicacions que ja venen incorporades al bastiment: e-mail, sessions, paginació, signals, sitemaps i sites.
  • Algunes aplicacions interessants: django-photologuer, django-page-cms, django-rosetta, django-command-extensions

Reflexió 2

Tornau a fer l'aplicació anterior incorporant javascript, internacionalització, cachés i tags fets ad-hoc. Mirau com queda el codi i com queda l'aplicació.

Arribats a aquest punt ja estau llets per anar agafant cada vegada més coneixements. No oblideu que aquests han de venir tant des de la part Python com de Django. Quan una cosa no vegeu com es pot fer amb Django cercau en el propil llenguatge. Si és un problema comú segur que està solucionat en el propi bastiment, i si no ho està vol dir que ja té solució a nivell de llenguatge de programació.

Això és tot, esper que us servesqui de guia i us animi a fer feina en aquest bastiment. Recordau, però que no hi ha bales de plata i que tot requereix esforç i dedicació.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Django i python: orientat a la feina


Escrit per Aaloy a 03 de June , 2009 a les 6:36 p.m.

Avui, mentre explicava a la gent de l'equip web un grapat d'optimitzacions que podem fer per fer que les nostres aplicacions siguin més ràpides i actualitzables, al mateix temps pensava que en la potència que ens està donant Python i Django gràcies a la seva orientació cap a fer les coses com s'han de fer.

La comparació amb Java, l'altre llenguatge que feim servir, no pot deixar d'estar present, i llevat d'excepcions (poques) he de dir que la combinació Python i Django en surt sempre afavorida davant de Java.

Django està molt orientat a la web, però no de qualsevol manera, està orientat a fer webs que s'han de mantenir i que han d'escalar. Per això veus que és gairebé trivial fer coses que en altres plataformes resulten força costoses: qui no recorda el que s'ha de fer per fer un redirect amb Struts, per exemple?, o la complexitat que té validar formularis tant amb Struts com amb Spring?.

Django està orientat a fer aplicacions pel món real, això vol dir: que surtin ràpides, puguin sofrir moltes modificacions al llarg del seu cicle de vida i que es pugin actualitzar també molt ràpidament.

El bastiment segueix de molt aprop la filosofia de Python, la de mantenir les coses legibles i simples. Hi ha d'haver sempre una manera obvia de fer les coses, i quan fas feina amb Django veus que els 90% dels problemes que et planteja una aplicació web tenen resposta directa. La resta poden dur un poc més de feina, que normalment vol dir fer herència d'alguna classe.

Una de les darreres aplicacions en les que he pogut comparar Java i Python ha estat una aplicació que agafa XML, el manipula i el posa dins una base de dades. L'aplicació original, feta amb Java+Spring+Hibernate, va dur setmanes de feina, mesos si contam el manteniment posterior. Fer el mateix amb Python (lxml+sqlalchemy) ha duit menys de 4 dies-home de feina.

Posar l'aplicació Java en producció també va ser un poc malson per mor de les dependències entre les llibreries. Posar-la en Python dins un virtualenv és pràcticament immediat.

Tampoc ha resistit la comparació el manteniment i la correcció d'errors. El temps que passa per localitzar l'error i fer l'actualització en Python és mínim, en Java multiplicant per 10 o per 20 aquest temps.

Ens queda un tema: el rendiment. Com moltes vegades he dit per aquí el rendiment basta que sigui "prou bo", és a dir, per l'aplicació que estam parlant partíem d'un temps de càrrega d'entre 4 hores en les primeres versions Java, a 30 minuts a les darreres versions amb multi-fil. Amb màquines amb 8 Gb de RAM i menjant-se la màquina tant amb memòria com amb processador.

La versió Python també es feta amb multi-fil, on el nombre de fils és parametritzable, amb pool de connexions i demés. Sols que escrit amb un 10% de les línies de codi. El temps? Doncs 6 minuts i mig. Consum de memòria: mínim, ús de la CPU: intensiu però sense deixar torrada la màquina.

Python està orientat a que les coses es facin i es mantenguin. Per algunes tasques concretes potser no serà la millor solució, però cada vegada estic més convençut que és el primer que s'ha de provar. Si no va prou bé, el prototip ens haurà servir per a conèixer millor l'abast del problema sense haver-hi invertit molt de temps, i sempre som a temps d'optimitzar alguna secció del codi amb C, de maneres per utilitzar codi C (o C++) o bé d'escriure codi intermig que es transformi en codi C compilable n'hi ha força. Al wiki de Cpython (una de les eines que ens permet fer això) n'hi ha un bon grapat.


Traducciones/Translations by apertium

5 comentaris, 0 trackbacks (URL) , Tags: Python Django


Django, imatges i Imagekit


Escrit per Aaloy a 23 de May , 2009 a les 4:46 p.m.

Django Imagekit és una llibreria creada per Justin Driscoll que ens permet crear miniatures i/o distints tamanys d'imatges a partir de la imatge original, i que s'integra molt bé amb Django. És una llibreria més senzilla que la de django-photologue ja que no té tota la funcionalitat per a crear gal·leries fotogràfiques.

El tutorial per a fer-la anar està força bé, però aprofitaré la benentesa per a fer cinc cèntims de com podem pujar una imatge al nostre site amb Django i presentar-la de nou.

El codi font de l'exemple complet és a appfusedjango.

El model

Per començar definim el model:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.db import models
from imagekit.models import ImageModel

from django.db import models
from imagekit.specs import ImageSpec

class Photo (ImageModel):
    image = models.ImageField(upload_to='photos')
    comments = models.TextField()
    num_views = models.PositiveIntegerField(editable = False, default = 0)

    class IKOptions:
        spec_module = 'sample.specs'
        cache_dir = 'photos/cache'
        image_field = 'image'
        save_count_as = 'num_views'

    def thumb(self):
        if self.image:
            return '<img src="%s">' % self.thumbnail.url
        else:
            return ""
    thumb.allow_tags = True
    thumb.short_description = 'Foto'

En aquest cas es tracta d'un model molt senzill, guardam la imatge image al directori photos i posarem dins la base de dades tant la referència del fitxer (això és important, dins la base de dades no és guarda la imatges sinó sols la metadada) i el comentari.

El camp num_views ens pots servir per anar guardar la quantitat de vegades que s'ha vist la imatge i és utilitzat si volem per la llibreria d'Imagekit.

La part interessant és a la classe IKOptions. Aquesta defineix quin tractament se li donarà a la imatge, on s'enmagatzemaran les miniatures i a quin camp es fa referència.

  • spec_module És el mòdul d'ImageKit que es farà servir i que defineix els tamanys possibles. D'aquí una estona el veurem amb detall. En el nostre cas el modul es diu specs.
  • cache_dir : guardarem els distints tamanys generats a photos/cache
  • image_field: Les metadaes son pel camp image del model.

El mètode thumb ens serviex per poder posar la miniatura dins el llistat de l'admin.

Els tamanys

Per definir els tamanys Imagekit distingeix entre el que és la visualització de la imatge i les manipulacions que s'hi fan. La imatge original necessita passar per uns filtres que Imagekit anomena processors. Una imatge pot generar-se aplicant un o més d'aquests filtres.

 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
from imagekit.specs import ImageSpec
from imagekit import processors

# define the thumnail processor
class ResizeThumb(processors.Resize):
    width = 100
    height = 75
    crop = True

class ResizeDisplay(processors.Resize):
    width = 600

class ResizeBig(processors.Resize):
    width = 800

# define your spec
class Thumbnail(ImageSpec):
    pre_cache = True
    processors = [ResizeThumb,]

class Display(ImageSpec):
    processors = [ResizeDisplay,]

class Big(ImageSpec):
    processors = [ResizeBig,]

A l'exemple sols faig servir un tipus de processor el de Resize per a redimensonar la imatge als tamanys que farem servir.

Pujam una imatge

Per pujar una imatge necessitam definir un formulari, el mètode que tractarà aquest formulari i les urls que farem servir, així com les plantilles que es mostraran.

Les urls

Aquesta és la part senzilla.

1
2
3
4
5
6
7
8
9
from django.conf.urls.defaults import *
from django.conf import settings 
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',    
     url(r'^$','sample.views.index', name="main-page"),
     url(r'^display/(?P<id>\d+)/$','sample.views.display', name="display-image"),
....

Definim una url per l'index que identificarem per nom main-page i que serà tractada al mètode index del mòdul views del nostre paquet sample.

I una altra ulr que ens permetrà visualitzar la imatge a partir del seu identificador. Anomenam a aquesta url display-image, original que és un...

El formulari

 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
# -*- coding: UTF-8 -*-

from django import forms
from PIL import Image

class AttachmentForm(forms.Form):
    """Form for the attachment sample. Added a simple validation
    to accept only png files checking for 'image/png' in the
    content_type of the file"""
    image = forms.FileField(help_text="add a png or jpg file")
    comments = forms.CharField(widget= forms.Textarea, help_text="describe the image")

    def clean(self):
        "Validate the entire form"
        cleaned = self.cleaned_data        
        try:
            file = cleaned['image']
        except Exception, e:
            # perhaps this is not a file
            raise forms.ValidationError("Not valid file: %s" % e)
        if not file.content_type.lower() in ["image/jpeg", "image/png", "image/jpg"]:            
            raise forms.ValidationError("Just jpg or png files please")
        im = Image.open(file)
        if not im.format in ['JPEG','PNG']:
            raise forms.ValidationError("Just jpg or png files please")
        return cleaned

El formulari com es pot veure és d'allò més normalet, definim un camp per la imatge i un camp per als comentaris.

La part "nova" està en la validació. No ens podem fiar del que ens diu la gent que puja, així que le que farem és comprovar que el mime type es correspon amb un format vàlid, i com que fins i tot això es pot manipular, farem una comprovació addicional amb PIL per a comprovar que la imatge és el que diu ser. Aquesta comprovació dependrà del vostre nivell de paranoia.

Tractant la imatge

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from models import Photo
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from forms import AttachmentForm

def index(request):
    "Obtains the attachment and saves it to the disk"
    if request.method == 'POST':
        form = AttachmentForm(request.POST, request.FILES)
        if form.is_valid():
            f = form.cleaned_data['image']
            foto = Photo()
            foto.image.save(f.name, f)
            foto.comments = form.cleaned_data['comments']
            foto.save()
            return HttpResponseRedirect('/')
    else:
        form = AttachmentForm()
    fotos = Photo.objects.all()
    return render_to_response('index.html', {'form':form, 'fotos': fotos})

Per a tractar arxius i imatges la part delicada és recordar que hem de passar la informació al formulari afegint el request.FILES i pensar a posar al formulari enctype="multipart/form-data"

A més d'això hem de guardar dues vegades: una per la imatge, que guardarà el contingut dins el sistema de fitxers, i una altra per la resta del model i les metadades de la imatge. Per això tenim un foto.image.save(f.nam, f) guarda la imatge al sistema de fitxers.

Mostrar una imatge és prou senzill

1
2
3
def display(request, id):
    foto = Photo.objects.get(pk=id)
    return render_to_response('image.html', {'foto': foto})

Mostrant les imatges

Al template hi passam objectes del tipus Photo, que recordem tenen tota la fontaneria del ImageKit.

Això vol dir que a més de la imatge original, puc fer servir els formats que he definit a specs: Thumbnail, Display, Big.

Per utilitzar-los en la nostra plana sols hi hem de fer referència:

1
2
3
<li><img src="{{foto.thumbnail.url}}" /></li>
<li><img src="{{foto.display.url}}" /></li>
<li><img src="{{foto.big.url}}" /></li>

com podem veure sols és cosa de fer referència al tamny definit i treure'n el que ens interessa, en el nostre cas la url per a mostrar la imatge.

Per darrera ImageKit se n'ha encarregat de fer les transformacions i guardar la imatge a la caché, de manera que la feina pesada de generació dels distints tamnays sols se fa un cop.

A partir d'aquí ens podem comlicar tant com voguem, per exemple:

  • Al mètode clean fer que no es puguin pujar imatges de més d'un tamany.
  • Reescriure el mètode save del model per a que no es guardi la imatge original sinó una altra imatges ja reduïda.
  • Escriure més processors per fer més manipulacions a les imatges.
  • etc. etc.

Però el és segur és que amb això que us he contat tingueu el 90% dels casos solucionats.

Nota: Oscar, esper que això et servesqui ;)


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


Django vs PHP frameworks


Escrit per Aaloy a 10 de May , 2009 a les 6:36 p.m.

Llegint llegint he anat a parar a una plana que compara els principals bastiments (frameworks) PHP entre sí per demostrar com n'és de ràpid el KumbiaPHP comparat amb els altres.

Com que el codi utilitzat per les proves està disponible, doncs he vist que era un simple hello world, així que he creat un projecte hello_world a appfusedjango per poder comparar amb Django.

Disclaimer: La velocitat d'execució no ho és tot. Segur segur, que fet en x ben optimitzat l'aplicació y és més ràpida per aquesta prova. Això no és per veure qui la té més llarga, sols intent comparar coses més o manco semblants per saber on estam. Disclaimer 2: Qui en sap d'aquest tipus de proves és la gent que es dedica més a sistemes, en Bernat o en Guillem, per exemple :)

Amb què he fet les proves?

  • La màquina es un PPC 64 de 2 MHz amb 1 Gb de RAM utilitzat Ubuntu 9.1 i Gnome com a Desktop. És a dir, no he fet servir un servidor i hi ha moltes coses executant-se.
  • Python 2.5.2 (r252:60911, Jul 31 2008, 17:33:15) [GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
  • Com que em feia peresa muntar i configurar l'Apache he fet servir un servidor fet amb Django, el CherryPy, concretament el mòdul WSGI que podeu trobar al Django-Cerise sense executar-ho amb mode daemon per tenir major facilitat de modificar la quantitat de threads.
  • La versió de Django és la trunk
  • Per a les proves finals he optimitzat l'aplicació llevant tot el codi de depuració i middlewares que no es feien servir com el del la compressió gzip.

L'aplicació

L'aplicació no té cachés (tot a dummy i sense per-site-cache) i he fet que la plana en generar-se passi per la vista per a que es faci tot el recorregut MVT.

Els resultats:

executam ab -c 10 -t 60 http://localhost:8088/ cada vegada:

  1. Aplicació amb codi extra, servidor amb 10 threads 277 req/s
  2. Aplicació amb codi extra, servidor amb 3 threads 285 req/s
  3. Aplicació amb codi extra, servidor amb 5 threads 285 req/s
  4. Aplicació optimitzada, servidor amb 5 threads 334 req/s
  5. Aplicació optimitzada, servidor amb 3 threads 331 req/s

KumbiaPHP, el més ràpid de la comparativa PHP treu 34 req/s, casualitat? He fet alguna cosa malament? Potser, però no sóc el primer, hi ha gent que també ha notat un fort augment del rendiment en passar de PHP a Django.

I una de les proves:

ab -c 10 -t 60 http://localhost:8088/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Finished 20009 requests

Server Software:        CherryPy/3.0.3
Server Hostname:        localhost
Server Port:            8088

Document Path:          /
Document Length:        266 bytes

Concurrency Level:      10
Time taken for tests:   60.003 seconds
Complete requests:      20009
Failed requests:        0
Write errors:           0
Total transferred:      7723474 bytes
HTML transferred:       5322394 bytes
Requests per second:    333.47 [#/sec] (mean)
Time per request:       29.988 [ms] (mean)
Time per request:       2.999 [ms] (mean, across all concurrent requests)
Transfer rate:          125.70 [Kbytes/sec] received

Connection Times (ms)
          min  mean[+/-sd] median   max
Connect:        0    2  82.1      0    3001
Processing:     4   27  41.7     24    2295
Waiting:        0   25  41.5     21    2291
Total:          5   30  92.1     24    3038

Percentage of the requests served within a certain time (ms)
  50%     24
  66%     28
  75%     30
  80%     32
  90%     37
  95%     43
  98%     50
  99%     59
 100%   3038 (longest request)
(development)aaloy@G5:/tmp/djangocerise-master/src$ ab -c 10 -t 60 http://localhost:8088/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Finished 19863 requests

Server Software:        CherryPy/3.0.3
Server Hostname:        localhost
Server Port:            8088

Document Path:          /
Document Length:        266 bytes

Concurrency Level:      10
Time taken for tests:   60.006 seconds
Complete requests:      19863
Failed requests:        0
Write errors:           0
Total transferred:      7667358 bytes
HTML transferred:       5283558 bytes
Requests per second:    331.02 [#/sec] (mean)
Time per request:       30.210 [ms] (mean)
Time per request:       3.021 [ms] (mean, across all concurrent requests)
Transfer rate:          124.78 [Kbytes/sec] received

Connection Times (ms)
          min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       3
Processing:    12   30   9.0     29     361
Waiting:       10   28   8.7     27     361
Total:         12   30   9.1     29     362

Percentage of the requests served within a certain time (ms)
  50%     29
  66%     31
  75%     33
  80%     34
  90%     37
  95%     40
  98%     44
  99%     47
 100%    362 (longest request)

Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


nose, per testejadors amb mala memòria


Escrit per Aaloy a 10 de May , 2009 a les 10:42 a.m.

Supòs que ja ningú dubta de la importància dels test unitaris a l'hora de programar. Els tests ens permeten provar que el que feim és correcte i repetir-ho tantes vegades com volguem i de manera controlada.

Els tests són una part important dels mecanismes de refactorització d'aplicacions, ja que ens asseguren que l'aplicació funciona de la mateixa manera abans i després de refactoritzar.

Els test, però, tenen un problema, fins ara els test unitaris s'han d'escriure d'una determinada manera, recordar les llibreries que has d'importar, com fer un testsuite. Per mi això significa anar a la documentació del pyUnit cada vegada o copiar un test anterior. És el que té dedicar-se a gestionar projectes, que no pots tenir al cap coses que sols fas servir de tant en tant, ja que sols dediques una quantitat mínima d'hores a programar.

Davant aquesta necessitat de fer tests sense tenir que preocupar-nos de la fontaneria intrínseca del pyUnit una de les millors opcions que hi ha és la llibreria nose. Vull dir, si sé que nose és perfecte per la feina (acudit fàcil).

Per fer un test típic basta recordar el següent:

  • la sintaxis dels assert de Python: assert condicio, missatge.
  • que gairebé qualsevol funció o classes que contengui test o Test separat amb quió baix és una funció testejable.
  • Que un test falla quan l'assert falla
  • Que fent un nosetest -v nom executarem els tests en mode verbose del paquet o funció que li indiquem

La documentació és molt més àmplia, indica quan capturar la sortida, com fer l'equivalent al setup i al teardown dels pyUnit, capturar excepcions, etc. Però el 80% de vegades aquestes quatre coses que indic seran més que suficients.

La llibreria nose ens permet escriure els tests de la manera que estam tots acostumats quan escrivim codi per provar una funció, en nivell de formalització i cerimònia comparat amb els unittests clàssics és mínima, i per tant afavoreix que poguem convertir el codi de proves informal a codi per a la realització de test unitaris amb tant sols reanomenar la classe o funció que hem escrit, i moltes vegades ni tan sols això, ja que, no sé vosaltres, però jo ja tenia la mania de anomenar test a les funcions de prova :)


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Custom management commands


Escrit per Aaloy a 09 de May , 2009 a les 1:03 a.m.

Si heu fet al manco el tutorial de Django haureu fet servir per exemple el típic

python manage.py runserver

per a iniciar el servidor. A partir de la versió 1.0 de Django podem fer servir una sintaxi semblant per crear comandaments lligats a la nostra aplicació i que s'executin també d'aquesta manera.

Podem crear comandaments per fer qualsevol cosa, de fet es que acaba fent és executar un script de Python. Així doncs, ens podríem demanar quina avantatja hi ha en fer-ho així, vàries: - No necessitam definir cap variable d'entorn per dir quin settings hem de fer servir, l'script manage.py ja se n'encarrega. - El comandament quedarà lligat a l'aplicació. Per tant podem crear comandaments que depenguin d'una aplicació concreta i que la complementin. - Podem accedir a l'ajuda del comandament si l'hem definida. Permet a l'usuari saber com ha de fer servir la comanda.

Podem crear tres tipus de comandament a mida, que trobam definits dins django.core.management.base

  • Comandaments que hereten de AppCommand i que poden prendre com a paràmetre una llista d'aplicacions Django.

  • Comandaments que hereten de LabelCommand que poden prendre com a argument qualsevol cadena de text.

  • Comandaments que hereten de NoArgsCommand i que no prenen paràmetres.

Si anau a la documentació oficial de Django veureu que el que hi ha és més aviat poc. Potser perquè quan ja saps com és fa pareix tan fàcil que potser no mereix l'esforç. Tot i això, crec que és una opció prou útil i que hauria de tenir més importància a la documentació.

Afortunadament hi ha al manco dos articles prou bons que ens ajuden amb exemples a treure partit d'aquesta funcionalitat. Si anam al codi font comprovarem que tot està força documentat, però la realitat és que encara que a partir de la versió 1 fer aquests tipus de coses s'ha convertit en trivial, en versions anteriors no ho era.

Aquest apunt té per objectius fer publicitat d'aquesta funcionalitat, podeu trobar més informació al codi font de Django i als següents apunts:

i exemples tant al codi font de Django com a django-extensions .

Fes-hi una ullada, després de tot, fer un hello world d'aquesta manera és prou senzill:

1
2
3
4
5
6
from django.core.management.base import NoArgsCommand

class Command(NoAgrsCommand):
    help ="prints hello world"
    def handle_app(self, app, **options):
        print "Hello world"

Per provar-ho creau un paquet python anomenat management dins la vostra aplicació Django, i dins aquest paquet un altre anomenat commands. Dins aquest hi posaríem els nostres scripts. Per exemple si la nostra aplicació es diu uep i volem crear un comandament que es cridi com python manage.py hello hauríem de crear una estructura com:

uep/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            ___init__.py
            hello.py

Els noms dels comandament poden col·lisionar, així que o bé tenim noms prou especials o bé convé prefixar-los amb el nom de la nostra aplicació per tal de tenir una oportunitat més.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Visor de logs amb Django


Escrit per Aaloy a 22 de April , 2009 a les 8:53 p.m.

L'altra dia em vaig trobar amb la necessitat de mostrar els logs de l'aplicació a la plana web que estava desenvolupant.

Per anar bé, els logs han de ser dinàmics, és a dir, la plana ha de refrescar-se segons apareguin noves línies de log (i això vol dir Ajax). La idea és tenir quelcom com el tail -n 100 f ... del Linux. Per la naturalesa de les peticions Http, no podem arribar a tenir la funcionalitat del tail, però podem atracar-nos-hi un poc. Aquest article explica com ho podem fer utilitzant Django, Python i jQuery.

El javascript

La part del navegador ha de refrescar-se automàticament i sols el tros que correspongui a la visualització dels logs. A cada refresc haurà de cridar al servidor, obtenir la informació del log i presentar-la.

Per aconseguir això utilitzarem Jquery i un afegitó anomenat Timers. Aquest plugin ens permet definir de manera una funció que s'executarà cada x milisegons fins que l'aturem o tanquem el navegador.

El temps de refresc dependrà de la nostra aplicació, i el truc està en donar-li un temps un poc menor que la velocitat a la que la nostra aplicació genera les línies de log. No fa falta mirar-s'hi gaire, sols és per donar una major sensació d'actualització en temps real i que es mostri la informació línia a línia. Si li donam un temps major farem menys peticions i la presentació del log potser es faci per blocs de línies.

També necessitarem fer una cridada Ajax al servidor per a que ens doni les línies de log. Això ho podem fer directament amb jQuery, amb la funció jQuery.ajax per exemple. Veurem que a més del text del log necessitam la darrera posició llegida, així que farem servir el getJSon, que ens permetrà obtenir la informació de manera més estructurada.

El servidor

Per simular el tail farem un poc de trampa. La primera vegada que es faci la petició ens situarem al final del fitxer de log i la segona ja enviarem la informació. És a dir, mostram al informació a partir de la segona vegada que es crida la funció. Com que el temps es suposa que és petit no té massa importància i simplifica molt la programació. Hem de tenir en compte que l'obtenció de log ha de ser ràpida.

Per a posicionar-nos farem servir la funció seek i tell ens donarà la posició resultant una vegada llegit el fitxer. Aquesta posició és la que anirem passant a cada petició, de manera que quan tornem a llegir el log, ho farem a partir de la darrera posició llegida. A l'exemple a més he fet servir la funció reverse per tal d'ordenar la llista de línies llegides de més nova a més antiga i simular l'scroll.

 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
from django.http import HttpResponse
from django.utils import simplejson
def _tail(f, actual = 0):
    """
    Returns a tuple with the actual position on the file
    and the last read lines in html format.
    """
    text =""
    if actual == 0:
        f.seek(0, 2)
    else:
        f.seek(actual)
        log = f.readlines()
        if log:
            log.reverse()
            text = "<br/>".join(log)
    return f.tell(), text

def tail(request):
    "Tail simulation"
    f = open('sample.log', 'rU')
    pos = int(request.GET['pos'])
    pos, msg =  _tail(f, actual= pos )
    f.close()
    data = {'msg': msg,
            'pos' : pos
           }    
    return HttpResponse(simplejson.dumps(data))

Fixem-nos que el que tornam és una estructura json, creada a partir d'un diccionar Python mitjançant el simplejson.

A més consumim la posició del fitxer des de la que hem de començar a llegir, que vindrà donada pel paràmetre get. No he posat validacions ni tractament d'excepcions, ho deix com a exercici per al lector (això sempre queda bé dir-ho quan un no té massa ganes de fer feina :-P )

Amb el que ara veim del codi de servidor, el codi javascript ja és entenidor:

 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
 <script type="text/javascript">
    $(function() {
        var pos = 0;
        var demos = $("div.wrapper div.demos");
        var active = false;
        $('#log').ajaxStart(function(){$('#loading').show()});
        $('#log').ajaxStop(function(){$('#loading').hide()});
        $('.controlled-interval', demos).
            find('.start').css("cursor", 
                              "pointer").click(function() {
            if (!active) {
              active = !active;
              $('#log').everyTime(1000, 'controlled',
                               function() {
                          $.getJSON(
                            "/log/tail/?pos="+pos,
                            function(data) {
                              if (data.msg !=''){
                                $("#log").prepend(data.msg+'<br/>');
                              }
                             pos = data.pos;
                            }
                           )
               });
              }
        }).end().find('.stop').css("cursor", 
                    "pointer").click(function() {
            if (active) {
                active = !active;
                $(this).parents("div").
                                     find('#log').stopTime('controlled');
            }
        });
    });
    </script>

Qui fa la feina és $('#log').everyTime(1000, 'controlled', function() qui es que manté el timer. Cada vegada que passa un segon (1000 ms) es crida al la funció del servidor amb la posició que acabam de llegir. El primer cop no tenim aquesta posició i la inicialitzam a zero, d'aquí que necessitem dues cridades a la funció per obtenir el primer log.

Notem com és de simple manipular el json a Javascritp. data té el text que hem d'escriure i la darrera posició de l'arxiu. Si el text està en blanc simplement ens limitam a actualitzar la posició; si hi ha contingut el posarem dins del div amb identificador log que tenim definit dins la plana.

S'hi pot fer molta més: posar-hi més disseny, podríem fer que es retornàs ja informació la primera vegada que es crida, tenir un reset per a que cuan continua el log sols venguin les darreres línies i no tot el bloc, però la idea és la mateixa:

  • fer servir un timer
  • fer servir ajax enviant la darrera posició on hem arribat
  • fer servir seek i tell per posicionar-nos dins l'arxiu

Com és habitual l'exemple ho podeu davallar d'appfusedjango, o directament un svn co http://code.google.com/p/appfusedjango/source/browse/#svn/trunk/tail

Hi trobareu també una petita utilitat anomenada generate_log.py executant-la en una consola escriu línies de text que poden ser llegides per l'exemple.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Carregant imatges


Escrit per Aaloy a 19 de April , 2009 a les 11:35 p.m.

Normalment quan treballam amb imatges amb Django ho feim des del punt de vista de disseny, és a dir, posam les imatges al directori on s'han de servir i per avall. Potser a vegades hem fer servir quelcom com el Django Photologue per a tenir les fotografies més estructurades dins la nostra aplicació.

Què passa però, si el que volem és carregar les fotografies des d'un script a la nostra base de dades, o quan ens trobam que la informació no ve pels canals habituals d'una imatge que es puja mitjançant un formulari.

Aquest és el cas que tractarem en aquest apunt. Tenim una imatge que ens arriba codificada en base64 i el que volem és afegir-la amb un petit comentari a la nostra aplicació.

El model que farem servir és tan senzill com això:

1
2
3
4
5
from django.db import models

class Photo (models.Model):
    image = models.ImageField(upload_to='photos')
    comments = models.TextField()

La imatge ens ha arribat com a una cadena de text. El contingut ho podeu trobar al subversion del projecte appfusedjango.

El primer que hem de fer es decodificar la imatge, després crearem una instància de la classe Photo, li assignarem el contingut, el comentari i guardarem. Fins aquí supòs que tothom d'acord.

El problema, però, és que ImageField suposa que li passarem una objecte del tipus File i nosaltres el que tenim és una cadena de text. Necessitarem per tant simular aquest tipus de dada de manera que puguem tractar amb informació que no vengui directament d'un fitxer de text. Ni més ni manco el ContentFile. Aquesta classe que podem trobar a django.core.files.base el que fa és precisament això.

Una altra de les coses que hem de fer és guardar la imatge i assignar-li el nom. Per fer això haurem de crear el nostre objecte Photo, guardar la imatge i després guardar-ho tot a la base de dades.

Anem a veure com quedaria:

>>>import base64
>>>from django.core.files.base import ContentFile
>>>from sample.models import Photo
>>># ja tenim tot el necessari
>>>raw_data = base64.b64decode(open('test/encoded_logo.b64').read())
>>># acabam de llegir les dades, podrien arribar directament
>>>foto = Photo()
>>>foto.image.save('the-image-name.gif', ContentFile(raw_data))
>>>foto.comments = "Django & Python rules!"
>>>foto.save()

El pas del 'image.save' és necessari perquè Django no guarda les imatges per defecte dins la base de dades sinó al sistema de fitxer (de fet podem guardar-ho allà on ens vengui de gust gràcies a les possibilitats del custom storage) així que necessitam definir tant el nom de la imatge com les dades. A la definició del camp ImageField ja li hem dit que voliem deixar les imatges a la carpeta photos que estarà dins el directori que tinguem definit com el nostre MEDIA_ROOT.

El projecte que he pujat serveix per demostrar la idea. Per provar-ho basta fer un

$ python manage.py shell

des del directori del projecte imatges per a convèncer-vos que la cosa funciona veient com es van creant les imatges dins la carpeta media/photos/ del projecte.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Nou ressaltat de sintaxis pel blog


Escrit per Aaloy a 11 de April , 2009 a les 5:24 p.m.

Fins ara Trespams tenia un ressaltat de sintaxi basat en Javascript, el que feia que no resultàs molt natural escriure apunts amb molt de codi font, ja que significava tenir que posar codi html dins el markdown per tal de marcar aquell tros de codi com a ressaltable pel Javascript.

Com a part de les modificacions he canviat això pel ressaltat basat en Pygments.

Aquest plugin permet fer servir tota la potència de Pygments per al ressaltat, podem resaltar HTML, plantilles Django, Javascript, Java, gairebé qualsevol cosa, i a més fent servir una sintaxi molt més natural, basta afegir un SheBang típic de Unix per a indicar el llenguatge.

La implementació està pensada per a no generar el codi HTML des de Markdown cada vegada, sinó que es guarden dues versions, una amb el codi original i l'altra amb el codi convertit a HTML i ja renderitzat amb els plugins. Això fa que el guardar l'apunt sigui un poc més lent (ha de convertir, reindexar i guardar) però la plana es pot mostrar molt més ràpidament. Optimització prematura? Potser sí, però tanmateix és d'aquelles coses que consideraria una millor pràctica.

El canvi fa que el codi antic no aparegui ressaltat, a poc a poc ho aniré canviant així com vagi revisant els apunts. El que no fa (crec) és trencar res, manté el codi dins un <pre> però no ho ressalta.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Django regroup


Escrit per Aaloy a 10 de April , 2009 a les 7:36 p.m.

He fet quatre cosetes al programari del blog, per tal de mirar de passar al màxim la validació del w3c, encara tenc problemes amb els pre que em genera el markdown dins paràgrefs i que no passen la validació, però al manco ara la plana es veu prou bé amb Konqueror.

Fent neteja m'he trobat amb la necessitat de refer la jerarquia de l'històric d'apunts. No era gens neta i com que ara el blog té un menú prou potent, he decidit posar molta cosa que estava al lateral com a opció del desplegable.

Per fer la jerarquia d'articles el que es ja el primer de tot és obtenir la llista d'arxius. Això ho feim cridant un tag creat ad-hoc pel blog

 {% get_yearly_archive as archive_list %}

que el que fa és executar

Entry.objects.current_active().dates('pub_date', 'month', order='DESC')

i posar-ho a una variable que pot ser usada a la plantilla.

Amb aquest tag obtenim la llista de mesos en els que hi hem fet apunts. Un element d'aquest llista és per exemple:

 datetime.datetime(2008, 2, 1, 0, 0)

Com podem veure és un tipus datetime de python i com a tal podem obtenir-ne fàcilment l'any objecte.year o el mes objecte.month.

El que volem, com dic, és fer una jerarquia, és a dir, tenir una cosa com:

  • 2009
  • Abril 2009
  • Març 2009
  • 2008
  • Desembre 2008

Això normalment duria força feina, però és aquí quan hom veu que Django està pensat per als reptes de la vida real, ja ve amb un template tag predefinit que ens permet fer agrupacions, el regroup

Donada una llista ordenada regroup ens crea una estructura de dades que contindrà els elements pels quals volem fer el grup i dins aquests els element de la llista que tenen aquest element.

En el nostre cas hem posat l'historial dins una variable anomenada archive_list, que recordem és una llista de datetime. Farem

{% regroup archive_list by year as historial_list %}

Amb això hem creat la nostra estructura de dades. Fixem-nos que l'exemple que duu la documentació de Django diu que regroup té com a paràmetre una llista de diccionaris. És part de la gràcia del tipat feble, pel que fa al funcionament de la funció l'estructura datetime es comporta com espera l'iterador groupby que és el que fa servir internament.

Podem simular-ho perfectament a la consola de iPython

1
2
3
4
5
>In [43]: p = itertools.groupby (x, lambda z: z.year)
>In [44]: for i in p:
>   ....:     print i[0]
>   ....:     for j in i[1]:
>   ....:         print j

Ja tenim l'estructura de dades, així que ara sols quedar recorre i presentar la informació tal com la volem. Per això cal tenir en compte que la nostra estructura defineix la variable grouper que fa referència al valor del camp pel qual estam fent l'agrupació

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{% for year in historial_list %}
   <li><span class="dir">{{year.grouper }}</span>
       <ul>
         {% for d in year.list %}
            <li><a href='{% setting "BLOG_ROOT" %}
               {{ year.grouper }}/{{d|date:"m" }}/' class='menu_content'>
                {{ d|date:"M Y" }}</a>
            </li>
         {% endfor %}
        </ul>
     </li>                        
 {% endfor %}

Com és habitual, és més llarg d'explicar que de fer.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Comentaris funcionant (esper)!


Escrit per Aaloy a 04 de April , 2009 a les 12:26 a.m.

Link to Comentaris funcionant (esper)!

Gràcies a Hugo me n'he assabentat que la darrera actualització del blog havia fet malbé el sistema de comentaris.

He pujat una nova versió sense el codi de depuració que feia que els comentaris no es poguessin processar ja que hi havia un punt de ruptura :(

Bé, res greu, coses de les actualitzacions a les males hores del vespre. En Pau també m'ha passat una incidència amb la visualizació del calendari amb Firefox 3.5. Pau, d'això se'n diu viure al límit!

Encara he de netejar molts divs innecessaris de l'anterior disseny. Vull jugar també amb algunes APIs de llocs socials i integrar-les, una de les primeres segurament serà la de Delicius.

Com que a mi també m'agrada viure perillosament estic aprofitant l'edició del blog per provar les versions de desenvolupament de Netbeans per Python. Estic tirant de les construccions del Hudson i provant-ho damunt el PowerPC.

No puc comparar-ho amb l'Eclipse perquè tanmateix damunt el PowerPC com ja he contat l'Eclipse no va gens bé, així que ja no hi ha comparació possible.

Res, que si algú s'anima a posar un comentari donaré per tancat el bug!


Traducciones/Translations by apertium

8 comentaris, 0 trackbacks (URL) , Tags: Python Django


Python més ràpid


Escrit per Aaloy a 28 de March , 2009 a les 11:42 a.m.

En Miquel ahir em va fer un enllàc vist pel Menéame. Reseguint la cadena d'enllaços arribam al projecte unladen-swallow.

El projecte és força interessant, i si segons diuen tenen per objectiu augmentar la velocitat d'execució de Python fins a cinc vegades vol dir que li haurem de tenir esment. Tot i que el projecte no arribi a bon port, de bon segur que les contribucions que pot fer a la base de codi de Python seran important. Recordem que Google té en plantilla a Guido Van Rossum el "pare" i dictador benèvol de per vida de Python.

La cosa està, però en no caure en el parany de dir que com que s'està treballant en la velocitat d'execució de Python aquest és un intèrpret lent. En quant a velocitats d'execució el que ha de primar sempre és el fet de si el compilador o intèrpret és prou ràpid per la feina. Si sols miram la velocitat segurament encara acabaríem fent les coses en Assembler, però la cosa està en que normalment el punt crític en quant a l'execució no està tant en el compilador o intèrpret, sinó en l'algorisme que feim servir.

Escriure un bon algorisme està directament relacionat amb la capacitat del programador, per suposat, però el llenguatge també hi intervé. Un llenguatge mal d'escriure, mal de depurar fa molt més difícil programar algorismes òptims, ja que el programador no sols s'ha de concentrar en el què vol fer, sinó que també ha de pensar en com ho ha de fer amb les eines que té.

Un dels grans avantatges de Python és que ens permet concentrar-nos en el que volem fer més que en el com. El llenguatge flueix de manera natural, la relació entre línies de codi i funcionalitat és molt baixa, de manera que aconseguim molt amb molt poques línies, però a més, aquestes tenen una expressivitat que fa fàcil saber que estam fent.

Per una altra banda Python també ens ofereix una gran quantitat d'eines per enllaçar amb programes i llibreries de C, o eines com Cython que fan que fer extensions en C per Python sigui molt semblant a programar en Python mateix. La programació numèrica és un altre dels camps on Python brilla, permet tenir tota l'expressivitat del llenguatge a l'hora de dir el que s'ha de fer i delegar la feina a les llibreries específiques de C.

Esper que el projecte tengui molt d'èxit i que aviat en puguem veure els fruits, però pensau que escriure codi lent es pot fer en qualsevol llenguatge de programació.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Trucs de l'administrador de Django


Escrit per Aaloy a 19 de March , 2009 a les 11:50 p.m.

Encara que a la documentació de l'administrador ho diu, sovint estam tan acostumats a fer feina amb camps que ens oblidam que al list_display de l'admin de Django hi podem fer servir qualsevol atribut o mètode susceptible de ser cridat (entre d'altres opcions).

Això obre tot un món de possibilitats a l'hora de presentar informació en forma tabular dins l'admin, podem crear enllaços cap a altres seccions, mostrar imatges, el que se'ns pugui ocorre i que tengui algun tipus de lligam amb el model.

Un exemple ben senzill, suposem que tenim un model que té una imatge associada, el que voldríem és que a la informació ens aparegui aquesta imatge, llavors faríem:

Al model afegim

1
2
3
4
5
6
7
def get_foto(self):
  if self.foto:
     return '<img src="%s" width="64">' % self.foto.url
  else:
     return ""
get_foto.allow_tags = True
get_foto.short_description = 'Foto'

i a l'admin.py afegirem a la configuració de l'administrador del nostre model

1
list_display = ('get_foto', ...)

Podem complicar-ho tant com vulguem, un exemple ho tenim al Django Snippet d'Admin list Thumbnail, que complica la funció per a crear les miniatures de les imatges que es presentaran i disminuir així el pes de la plana.

El que és important d'aquest exemple és veure l'ús que es fa de allow_tags, si el mètode té aquesta propietat Django no escaparà l'html que li passem, donant-nos joc a presentar tot tipus de codi html, baix la nostra responsabilitat, clar.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Fent net


Escrit per Aaloy a 19 de March , 2009 a les 6:52 p.m.

Una de les primeres coses que estic fent al Blog és la part de neteja. El codi és heretat de Blogmaker i hi ha molta cosa que amb les noves versions de Django es pot fer d'una altra manera o senzillament que es pot fer quan abans no es podia.

Ara la part d'administració de Django es pot personalitzar bastant, no és per tirar coets i normalment si s'han de fer coses complexes preferesc crear un backoffice ad-hoc, però pel que és redactar un apunt ja va prou bé.

Això m'ha permès eliminar força codi relacionat amb la part de presentació dels apunts i l'edició. Com que estic fent servir Markdown per a l'edició dels apunts he posat un editor anomenat Markitup, que permet tenir un entorn més amigable per a l'edició de Markdown i personalitzar les accions.

Per posar el codi dins l'administrador ha estat senzill. Amb la versió 1.0 Django va separar la part de vissualtizació i edició del model, amb la qual cosa podem definir com s'editarà el nostre model mitjançant un objecte que hereda de admin.ModelAdmin. Un dels aspectes més interessants és que podem afegir arxius Javascript i css, de manera que afegir el javascript necessari per fer servir l'editor de Markitup enlloc del simple text area ha estat cods de fer

       class Media:
           "Javascript configuration for Entry model in Admin"
            js = [(settings.BLOG_MEDIA_PREFIX + 'js/jquery.js'),
                 (settings.BLOG_MEDIA_PREFIX + 'js/entry_change_form.js'),
                 (settings.BLOG_MEDIA_PREFIX + 'js/markitup/jquery.markitup.pack.js'),
                 (settings.BLOG_MEDIA_PREFIX + 'js/markitup/sets/markdown/set.js'),
                 (settings.BLOG_MEDIA_PREFIX + 'js/editor.js')
                ]
            css = { 'screen': (
                      (settings.BLOG_MEDIA_PREFIX +
                            "js/markitup/skins/markitup/style.css"),
                      (settings.BLOG_MEDIA_PREFIX + 
                             "js/markitup/sets/markdown/style.css") )
                 }
   

A fer dissabte s'ha dit!


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Nova versió del blog de Trespams


Escrit per Aaloy a 18 de March , 2009 a les 10:56 p.m.

Avui hem posat en producció (beta total) una nova versió del programari que fa que aquest blog pugui llegir-se. Això potser ho haureu notat els que feis servir l'Akregator, ja que els fils RSS us apareixeran duplicats (al manco a mi me passa), em sap greu.

Les novetats són sobretot en la part tècnica: nou cercador, més javascript en la part d'administració i l'execució baix un entorn virtualenv que permetrà fer-ne actualitzacions de manera separada de la resta d'aplicacions Django del servidor.

Aniré escrivint damunt les millores així com tengui temps. Aquest apunt és sols per avisar que Trespams està en beta i que si peta miserablement i m'ho voleu fer saber us estaré agraït.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Llibreries per a la manipulació de Dates


Escrit per Aaloy a 13 de March , 2009 a les 5:44 p.m.

Aquests dies m'ha tornat a pegar per a fer feina al Blog. En Bernat m'ha creat un virtualenv al servidor i ho aprofit per anar fent-hi quatre coses.

L'aspecte gràfic no canviarà per ara, si ho fa serà per convertir-se en més minimalista encara, el que sí canviarà és la part interna i les eines d'administració.

Fent feina amb l'administració m'he topat amb la necessitat de manipular dates amb Javascript. La llibreria Javascript que he posat per a gestionar taules Datatables(per l´unic motiu que tenia ganes de provar-la) no gestiona bé formats que no siguin el de Date de javascript, molt anglosaxó, ell.

Per arreglar-ho vaig tirar d'una llibreria de Javascript que ens permet manipular dates i formats de la manera que vulguem, i això m'ha fet recordar la conveniència de deixar per escrit un grapat de llibreries de manipulació de dates que extenen les que ja tenen els meus llenguatges de programació preferits:

  • Datejs Magnífica llibreria per a javascript. La versió "estable" es un poc antiga però fa el que ha de fer i també hi ha la del subversion, que encara no he provat, però que serà el que faré quan acabi aquest apunt. Un petit emperò, no sé que fa, però me deixa el Firebug torrat i ben torrat, necessita gairebé cinc minuts per tornar en sí.

  • python-dateutil s'està convertint en una referència obligada per a la manipulación de dates en Python, i això que Python de per sí ja ho fa fàcil.

  • python-egenix-mxdatetime un vell conegut per als qui hem tractat amb bases de dates amb Python. És la llibreria recomanada per la seva potència i rapidesa. L'anterior potser té la novetat i la senzillesa però la llibreria d'Egenix té la solvència que dóna l'edat.

  • Joda Time l'equivalent Java de les llibreries anteriors. Forma part de les llibreries que sempre posa a qualsevol projecte Java.

Abans de reinventar la roda (o el calendari) no oblideu fer una ullada a aquestes llibreries.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Java


Validació de formularis amb Ajax, jQuery i Django


Escrit per Aaloy a 01 de March , 2009 a les 2:37 p.m.

Validació Ajax amb Django

A un dels projectes que estam fent el client va demanar una funcionalitat que requeria que s'enviàs un formulari al servidor i se'n fessin les validacions, però que la plana no es tornàs a recarregar. Un treball per Ajax, se'ns dubte. El problema era que tal com funciona el mecanisme habitual de processament de formularis amb Django, es requereix que es torni a recarregar la plana.

Una opció podria ser la de fer tota la validació del formulari amb Javascript, però llavors tendríem el perill de que l'usuari desactivàs el Javascript al navegador i pogués enviar dades directament sense validar. Podría fer-se tot per duplicat, és a dir, fer la validació amb Javascript i també al servidor, però això va en contra de la norma DRY (Don't repeat yourself).

Així doncs el que volem és el següent:

  • Enviar el formulari per ajax, però que sigui també possible fer un POST normal.

  • El servidor ha de fer les validacions i la plana presentar el missatge d'error si hi ha problemes.

Primer sense javascript

El primer que hem de fer és veure que tot funciona abans de posar-hi el javascript, així que crearem la nostra aplicació, crearem el formulari i hi posarem les valicacions que vulguem.

Farem un formulari molt senzill, el típic formulari de contatcte, però ho aprofitarem per introduïr-hi una opció nova a Django 1.0: la personalització dels missatges de validació.


from django import forms

class ContactForm(forms.Form):
    "Defines the login for as in the Django sample"
    subject = forms.CharField(label="subject: *", max_length=100)
    message = forms.CharField(label="Request",
        widget=forms.Textarea(attrs={'rows':'10', 'cols':'80'}))
    email = forms.EmailField(label = "Your email *", max_length=120,
        error_messages = {'required': u"No e-mail, no message"})
    cc_myself = forms.BooleanField(required=False)

    def clean_message(self):
        "We wan't to verify that it containts some words"
        msg = self.cleaned_data['message'].strip()
        if len(msg.split(None)) >5:
            raise forms.ValidationError(u"Really? This is quite short for a message")
        return msg

La manera més ràpida de mostrar el formulari és quelcom com

    <table>
    <form action='.' method="POST">
    {{form}}
    <tr><td span="2"><input type="submit" value="send" name="enviar" /></td></tr>
    </form>
    </table>

Però té un problema, que no controlam on es posen els missatges d'error i nosaltres volem controlar-ho. En aquest cas, i per simplicitat, posarem tots els missatges d'error a la part superior de la pantalla. Així que hem de rescriure la plantilla per a que ho contempli.

El nostre formulari queda doncs com

    {% if form.errors %}
        {{ form.errors }}
    {% endif %}
    <table>
    <form action='.' method="POST" >
    <tr><td>{{form.subject.label}}<td>{{form.subject}}</td></tr>
    <tr><td>{{form.message.label}}<td>{{form.message}}</td></tr>
    <tr><td>{{form.email.label}}<td>{{form.email}}</td></tr>
    <tr><td>{{form.cc_myself.label}}<td>{{form.cc_myself}}</td></tr>
    <tr><td span="2"><input type="submit" value="send" name="enviar" /></td></tr>
    </form>
    </table>

És a dir, ara tenim exactament el que teníem abans (no us hi fixeu amb la maquetació), però ara si hi ha errors es presentaran a la part superior del formulari.

Concentrem-nos ara en la manera de presentar els errors. Com que la nostra idea és que es puguin mostrar els errors que venguin per Ajax, el que farem és no presentar-los així, sinó que els posarem dins un div i farem que aquest es presenti o no en funció de si hi ha errors.

    {% block errors %}
    <div class="errores" {% if not form.errors %} style="display: none" {% endif %} >
      <p id="error_msg">
          {{form.errors}}
      </p>
    </div>
    {% endblock errors %}

Això a efectes pràctics és el mateix que el cas anterior, ja que com que si no hi ha errors from.errors no mostrarà res. El que sí hi ha ja és tota l'estructura que ens servirà per mostrar els errors dins l'arbre DOM de la plana web.

I ara i afegim l'Ajax

Necessitam tres coses: enviar el formulari per ajax, que si hi ha errors de validació els puguem presentar i que si tot va bé es faci la redirecció cap a la plana que toqui.

Hi ha alguns projectes que volen fer aquest tipus de coses de manera més o manco genèrica, però ara per ara no n'hi ha cap que em convenci, així que millor ho feim a mà. Si més no us deixo alguns enllaços:

El que sí faré es manllevar codi d'aquests projectes per als nostres propòsits, especialment del Django Ajax Validation.

En concret, adaptam el codi que ens permet obtenir els errors i passar-los a una estructura json:


class LazyEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Promise):
            return force_unicode(obj)
        return obj

def validate(request, form, new_url=''):
    if form.is_valid():
        data = {
            'valid': True,
            'url': new_url
        }
    else:
        if request.POST.getlist('fields'):
            fields = request.POST.getlist('fields') + ['__all__']
            errors = dict([(key, val) for key, val in form.errors.iteritems() if key in fields])
        else:
            errors = form.errors
        final_errors = {}
        for key, val in errors.iteritems():
            if key == '__all__':
                final_errors['__all__'] = val
            if not isinstance(form.fields[key], forms.FileField):
                html_id = form.fields[key].widget.attrs.get('id') or form[key].auto_id
                html_id = form.fields[key].widget.id_for_label(html_id)
                final_errors[html_id] = val
        data = {
            'valid': False,
            'url': new_url,
            'errors': final_errors,
        }
    json_serializer = LazyEncoder()
    return HttpResponse(json_serializer.encode(data), mimetype='application/json')

El validate és una funció prou genèrica, ens tornará una variable, valid que ens indicarà si hi ha errors de validació o no, la url on s'ha de redireccionar i la matriu d'errors.

Django ja posa al request una variable per indicar-nos si la petició s'ha fet per HttpRequest o no, request.is_ajax() això ens permetrà distingir com s'ha enviat el post i fer la validació d'una manera o altra


def index(request):
    redirect_url = '/thanks'
    if request.method == 'POST':
        contact_form = ContactForm(request.POST)
        if request.is_ajax():
            return validate(contact_form, redirect_url)
        else:
            if contact_form.is_valid():
            # do whatever you need as everything is valid
                return HttpResponseRedirect(redirect_url)
    else:
        contact_form = ContactForm()
    return render_to_response('index.html', {'form': contact_form})

El codi del view.py és d'allò més senzillet, sols feim que si la petició ve per post, tornam una cosa o altra en funció de si aquesta és una petició Ajax o si és una petició normal.

Fins ara no hem posat gens de javascript i tot segueix funcionant amb normalitat.

És temps de posar-ho les llibreries javascript. Ho farem amb jQuery, que és la que tenc més per mà, supòs que amb altres llibreries no hi ha d'haver cap problema, i a més faré server un plugin força bo per al tractament de formularis amb jQuery, el jQuery Form Plugin.

Com presentar els missatges, si s'han de presentar tots, els efectques que volguem, ja es cosa nostra. El json el que ens torna és l'identificador del camp i una matriu amb tots els errors que hi hagi. El procés que facem ja és cosa nostra.

    $(document).ready(function(){
            form_options = {
              timeout: 3000,
              dataType: 'json',
              type: 'POST',
              beforeSubmit: function(formData, jqForm, options) {
                  // you can add additional validation here and return
                  // false if it's not valid
                  jQuery('#boton').toggle();
              },
              success: function(responseJson, statusText) {
                  if (responseJson.valid) {
                     // redirect to hte new url
                     document.location.href = responseJson.url;
                  } else {
                      // create the error structure
                      var msg = ""
                      for (key in  responseJson.errors) {
                        camp = key.split('_')[1];
                        msg = msg + camp+":"+responseJson.errors[key][0]+"<br/>";
                      }
                      $('#error_msg').html(msg+'<br/>');
                      $('.errores').show(100);
                      jQuery('#boton').toggle();
                  }
              }
            };
            $('#testform').ajaxForm(form_options);
    });

A l'exemple el que he fet és montar un missatge amb el primer error de cada camp sols a efectes demostratius, podeu fer el que us vengui millor: validar abans d'enviar, mostrar efectes als camps dels errors, mostrar un diàleg, sols estau limitats per la vostra imaginiació i el Javascript.

El codi complet de l'exemple l'he pujat al projecte appfusedjango


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Llibres a febrer de 2009


Escrit per Aaloy a 26 de February , 2009 a les 9:19 p.m.

Ahir em vàren arribar dos llibres nous:

  • jQuery in Action de Bear Bibeault i Yehuda Katz, editorial Manning, ISBN 978-1933988351
  • Django 1.0 Template Development de Scott Newman, editorial Packt Publishing, ISBN 978-1-847195-70-8

Pels títols ja sabeu de què van, així que com sempre us dic un poc el que esper de cada un i les primeres impresions:

JQuery in Action

De l'estil d'aquesta sèrie de Manning: bona edició i continguts interessants explicats correctament. Estic encara als primers capítols de la primera lectura ràpida i m'està agradant força. Al JQuery l'estic fent servir cada cop més i necessitava un llibre com aquest per poder-ne tenir una idea completa. Hi ha molta documentació del JQuery, però està prou dispersa com per a que un llibre com aquest tengui sentit.

Django 1.0 Template Development

Aquest ho vaig comprar perquè m'interessava veure com s'introduïa el tema del desenvolupament Django orientat a dissenyadors més que a programadors. Com sabeu sóc de la opinió que el dissenyador/maquetador ha de formar part activa dins un equip de programació web, amb el mateix nivell d'implicació que un analista/programador. Això vol dir fer anar un subversion, maquetar amb un editor de text com Eclipse o Netbeans i com no entendre com es poden fer servir les plantilles Django de la mateixa manera que es coneixen les possibilitats del CSS. La conclusió que en tenc per ara és: a poc a poc i amb molts exemples. Me pareix una bona aproximació i esper tenir l'oportunitat de posar-ho en pràctica. El llibre pot ser un bon material de referència per a propers cursos.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Llibres i revistes Python Django


Django a l'empresa: liquidacions de despeses


Escrit per Aaloy a 22 de February , 2009 a les 6:57 p.m.

Aquesta setmana hem posat a preproducció una aplicació interna per a la gestió de les despeses feta amb Python, Django i Postgres. La idea és poder saber quines són les despeses més comuns per tal de fer-ne un seguiment i si s'escau poder fer una negociació amb els preoveïdors habituals.

Per això era necessari que les liquidacions de despeses que fins ara es feien amb un excel (quin mal fan els excels a les empreses!) es facin ara mitjançant una aplicació web. D'aquesta manera a la primera etapa l'usuari té exactament el que tenia abans, a saber, una fulla de paper que el seu cap pot signar i després presentar al caixer; però a més l'empresa començarà a tenir dades que es podran analitzar.

L'autentificació dels usuaris es fa contra l'Active Directory de l'empresa, en les darreres versions de Django és trivial canviar de sistema d'autentificació i han aparegut diferents snippets que ens permeten canviar el sistema d'autentificació estàndard per un altre. En el meu cas l'snipet d'autentificació per l'Active Directory ens ha anat força bé. De la mateixa manera seria trivial fer l'autentificació contra un LDAP o contra un altre sistema és força senzilla i la documentació de Django és molt aclaridora.

En aquesta aplicació a més de l'autenticació hem fet molta feina amb jQuery, hi ha moltes dades amb autocompletat (per les companyies aèries i pels aeroports per exemple), validacions, quadres de diàleg modals, etc. Es tractava de fer una aplicació el més àgil possible, però sense deixar de ser una plana web, per tal de no assustar massa als nostres usuaris. És una cosa en la que s'ha d'anar alerta, m'agrada molt l'article de Jefff Atwood Avoiding The Uncanny Valley of User Interface que explica el perquè es pot produïr un rebuig de l'usuari quan les coses volen ser massa paregudes al que ja coneix però sense arribar a ser-ho. En el nostre cas els usuaris conèixen les aplicacions d'escriptori i les interfícies de Forms i qui més qui manco sap fer anar un navegador web.

Podríem haver fet una aplicació RIA amb Extjs per exemple, però per molts dels usuaris que faran servir l'aplicació veurien que és una aplicació d'escriptori sense notar que s'executa al navegador. Per ells això implica complexitat, que algú en faci la formació individualitzada com es fa a les altres aplicacions. Massa cosa per el que ha d'esser una aplicació que té per objectiu imprimir un paper. Així doncs vàrem preferir que l'aplicació es semblàs a una plana web, amb formularis i enllaços, on el que s'imprimeix també és una plana web. Potser encara així algú trobarà l'aplicació complexe, però crec que ja serà per por i reticència al canvi més que per mor de l'aplicació en sí.

L'aplicació ha estat desenvolupada per un equip relativament nou en la programació Python i Django i tot i això s'ha pogut entregar en el temps previst (que ja considerava aquest fet per una altra banda). Això no fa més que refermar-me en el convencimient de que a més de la potència del llenguatge hem de considerar també la corba d'aprenentatge a l'hora de triar amb què farem el desenvolupament.

En aquest projecte i en els temps de crisi en que estam, encara em resulta sorprenent que hi hagi algú que es qüestioni si la base de dades en lloc de ser Postgres no hauria de ser Oracle. En aquests casos m'agradaria poder amollar allò de "It's about costs stupid!" i dedar-me tan ample. El que més em sobta és que s'amolla com si tu fossis el que has de justificar la decisió, quan realment hauria de ser a l'inrevessa. Per què fer una cosa damunt Oracle quan es pot fer amb Postgres (o Mysql)? Amb preocupa aquest tipus d'inèrcia informàtica, de curtor, que no s'atura a pensar en els costs actuals i futurs, tant és si hi ha al mateix moment algú d'Oracle demanant pel nombre de llicències. Sorprenent!

Però bé, és d'aquestes coses que acabes assumint com a comportament normal i que potser ni amb tota la pedagogia del món s'arribarà a res. Tenc l'esperança que aquesta crisi que vivim al manco servesqui per a que la gent es plantegi un poc la relació costs beneficis. Sols que s'aturi a pensar-ho un moment de ben segur que ja no caldrà el justificar el perquè s'ha anat cap a una solució de programari lliure. La part ètica? Què voleu que us digui, si la part econòmica ja costa (i estam parlant d'empreses!) imaginau-vos que pot arribar a costar que s'entengui el que representa el programari lliure en la vessant social i ètica.

I és una llàstima, perquè hi ha moltes empreses sortint de l'armari, que publiquen el codi que fan servir internament en forma de projectes lliures, de llibreries. El darrer exemple que se m'acud ve del Washington Times que ha obert un blog amb un bon conjunt de projectes Django o també un post damunt robots controlats amb Python.

Projectes com els que treim darrerament venen a mostrar que la tan cercada reutilització, els mètodes per lluitar contra la crisi del software, potser són molt més aprop del que ens pensam. El codi lliure permet la reutilització a nivells mai somiats, Python a més ho fa més fàcil, Django ho fa divertit per la web.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Caçant bubotes


Escrit per Aaloy a 14 de February , 2009 a les 1:43 p.m.

Poc a poc hem anat evolucionant la nostra arquitectura d'aplicacions des de aplicacions monolítiques fetes amb J2EE cap a aplicacions on la capa de serveis està en Java (per ara!) i aquests es consumeixen amb Python, utilitzant Django com a bastiment web.

Per a consumir serveis SOAP feim servir una llibreria anomenada ZSI que ens permet generar les classes Python a partir el WSDL dels serveis.

ZSI és un projecte que avança molt a poc a poc, veus que no està mort, que funcina, però la versió 2.1 fa estona que està en alfa. Tot i això, la versió 2.0 funciona prou bé com per poder consumir els serveis sense problemes.

L'altra dia però, ens vàrem trobar amb un problema d'aquests que classific com a "bubota". Atacàvem al servei que tenim de Transfers i tot anava com a la seda, atacàvem al d'excursions i el mateix. Atacàvem als dos dins la mateixa aplicació i l'espai de noms es feia un embolic, de manera que ens posava l'espai de noms del primer servei que s'executava.

Ho va descobrir en Juanjo i jo li deia que no podia ser fins que vaig fer un petit test de manera independent de l'aplicació que estam fent i ho vaig tocar amb les mans. Hi havia un problema i ara el problema era poder identificar a on, al webservice (poc probable), a la capa de servei que cosumia el SOAP (probable), al mapeig del ZSI (menys probable) o al ZSI en si (encara menys probable).

Quan un es troba amb un problema així, el primer és anar de més probabilitat a menys i anar descartant coses. La idea a més és aprofitar que hom està fent la depuració per a refactoritzar el codi, de manera que la feina de depuració serveixi a més per deixar un codi més clar i per tant més fàcil de depurar (recordau la primera regla de la refactorització: l'aplicació ha de funcionar exactament com funcionava abans, no introduïm nova funcionalitat).

Així doncs va començar un procés de refactorització de la primera capa, la que consumia el servei mapejat amb ZSI. Vaig organitzar millor el codi en paquets, vaig eliminar importacions innecesàries i vaig aillar totalment la part de transfers de la part d'excursions. Els tests però treien sempre el mateix, error quan atacàvem als dos serveis a l'hora.

La següent passa va ser anar a veure el codi generat pel ZSI (amb wsdl2py). Els generador de codi van molt bé, però sovint són massa genèrics. Així que també vaig fer-ne la refactorització. Eliminar totes les importacións amb arterisc, canviar nom comuns als dos paquets de transfers i excursions, i passar la part de defnició de tipus Soap a un arxiu independent, de manera que sols importàssim els realment necessaris. Tampoc! Els tests seguian donant el mateix... Bé al manco la refactorització era conseqüent...

Ara ja sols quedava la passa de la depuració línia a línea, fins i tot anant al codi del ZSI. Per això vaig començar amb un depurador senzillet comp el pdb (amb la seva versió d'ipdb) i vaig poder identificar a quin punt hi havia el problema. Pareixia estar en les cridades que feia el codi generat cap el ZSI. Per a facilitar la depuració en aquest punt ja vaig passar a un depurador gràfic, el winpdb, ja que permet veure d'una ullada totes les variables locals i globals.

Entrant al ZSI vaig descobrir la bubota. El ZSI cacheja els tipus d'objectes generats, però de tal manera que ho fa de manera global a l'aplicació, per tant, si dos objectes tenenen el mateix nom (com era el nostre cas), retorna el primer nom generat amb el seus espai de noms (cagada del ZSI al meu entendre). Potser és poc comú el que feim, però crec que és un cas típic d'optimització prematura. Es cachegen els tipus generats potser sense importar-hi.

La solució doncs va ser llevar les cachés, fes que generàs el tipus cada cop. Esperava pot ser una pèrdua de rendiment, però no és així, pareix que fins i tot posar i treure de la caché és comparable al temps d'execució a la genereació del tipus. He d'afinar un poc més amb aquesta apreciació, però el que està clar és que per al consum que es fa del servei, la diferència de temps no és significativa a l'ordre dels milisegons, i del tot menyspreable si ho comparam amb el *lag * produït per les línees de comunicació. Amb la caché activada 1 segon, i sense 1 segon igual.

La bubota està caçada, els fantasmes desapareixen quan es fa la llum i amb tot el procés he après molt millor com funciona el ZSI per dintre.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Entorns de treball virtuals per Python


Escrit per Aaloy a 12 de February , 2009 a les 6:23 p.m.

Si he fet feina amb Python amb projectes diferents us haureu trobat amb algun d'aquest problemes:

  • La llibreria que heu devallat del svn per a un projecte té canvis, les necessitau pel projecte actual però són incompatibles amb el projecte anterior.

  • Necessitau provar l'aplicació que teniu amb les mateixes llibreries i versió que hi ha a producció. Podeu tirar de svn per la versió de producció, però les dependències de les llibreries han canviat.

  • Teniu tants paquets i llibreries instal·lats al site-packages que les utilitats d'autocompletat es tornen bojes per mostrar-vos les ajudes.

Per a resoldre tots aquets problemes hi d'altres semblants podem fer ús de dues utilitats com són el virtualenv i el virtualenvwrapper, creades per Ian Bicking i Doug Hellmann respectivament. Veurem en aquest article com fer-les servir.

El primer que hem de fer és instal·lar-les. Ho podem fer amb easy_install virtualenv i easy_install virtualenvwrapper. La primera utilitat és la que realment farà la feina, l'altra és un script que ens facilita el maneig i la gestió dels distints entorns.

Suposaré que ens trobam en la situació de que volem crear un entorn net, on crearem la nostra aplicació i que tendrà totes les llibreries i dependències necessàries per a executar-la. Per començar el primer que farem serà configurar el nostre entorn de treball

$mkdir ~/.virtualenvs

Ara editarem el nostre arxibu .basrch i hi afegim

   export WORKON_HOME=$HOME/.virtualenvs
   source /usr/bin/virtualenvwrapper_bashrc
   

La localització del virtualenvwrapper_bashrc pot canviar segons la distribució, adaptau-la segons convengui.

Després d'editar el .bashrc feim un source ~/.bashrc per a activar els canvis i ja podem crear el nostre primer entorn virtual.

Crearem un entorn anomenat estable que tindrà la versió estable de Django i poca cosa més. Prèviament ens haurem baixat dita versió i l'haurem descomprimida a un directori temporal.

$ mkvirtualenv --no-site-packages estable
$ workon estable

Això ens haurà creat un entorn de Python amb les llibreries per defecte. És el que li hem dit amb --no-site-packages. Si no li haguéssim posat aquesta opció, ho hagués creat, però amb tot el que tenim al site-packages actual, si ens interessa bé, però si no, com és el cas, millor pensar en crear els entorns virtuals fent servir aquesta opció.

Ara podem anar al directori on hem descomprimit Django i fer un python setup.py install. Si us hi fixau veureu que Django ara no s'instal·la al directory site-packages del sistema, sinó al del entorn virtuals. Això és així perquè hem activat l'entorn amb la comanda workon. Aquesta comanda ens permet veure els entorns que tenim definits i a més, posant-hi el nom del entorn, canviar d'entorn virtual, a partir del canvi, les comandes d'instal·lació via setup.py o easy_install es faran a l'entorn virtual actiu.

Si voleu tornar a l'entorn de treball del sistema podeu desactivar l'entorn virtual amb un deactivate.

Tenim un petit problema però, necessitam el PIL i és una tudada d'espai tenir-ho que instal·lar des de zero. Per això podem fer dues coses, o bé crear un enllàç simbòlic dins l'entorn virtual, en el nostre cas dins $WORKON_HOME/stable/lib/python2.5/site-packages/ o bé fer ús de la comanda que ens proporciona el virtualenvwrapper anomenada add2virtualenv. Així si executam

$workon estable
$add2virtualenv /usr/lib/python2.5/site-packages/PIL

Hauríem afegit al nostre entorn virtual el paquet PIL del sistema. Ho podeu anar fent amb paquets que necessiteu, però teniu en compte que l'objectiu de l'entorn virtual és tenir un sistema net i no acabar amb tots els paquets que hi ha al sistema llincats.

Podem crear tants entorns virtuals com necessitem i vulguem, fins i tot un per cada aplicació. La comanda workon ens permetrà visualitzar-los i canviar entre ells amb tota facilitat.

Si optau per crear un entorn virtual per aplicació segurament trobareu molt útil saber que a directori bin del nostre entorn virtual podem crear dos scripts que d'existir s'executaran associats a l'entorn virtual, es tracta de postactivate que s'executa després d'activar l'entorn i predeactivate que s'executa abans de la desactivació.

En algunes aplicacions el meu postactivate és tan senzill com un canvi de directori cap a la carpeta de feina de l'aplicació i una actualització (via svn up) del codi.

Anem ara al tema de l'autocompletat. Com ja he comentat darrerament estic fent servir Netbeans per Python com a entorn de desenvolupament. Netbeans ens permet definir quins entorns d'execució farem servir (i gràcies a Xus vaig saber del cromo que deia que podíem fer-ho servir per a indicar a Netbeans que fes servir aquest entorn.

Una vegada creat el nou intèrpret de Python dins el Netbeans segurament encara haureu de pensar en fer una cosa més, assignar-ho al nostre projecte ja que en cas contrari tindria l'entorn antic. Així doncs anau a les propietats del projecte i assignau com a Python Platform l'entorn virtual que acabau d'afegir.

Podeu crear tants entorns d'execució de Python com siguin necessaris, cada un amb el seu entorn virtual associat i fer que sigui l'entorn d'execució per defecte del projecte. Això farà que l'autocompletat sols us mostri les opcions que es poden agafar d'aquestes llibreries, resolent un dels emperons més grans que particularment li trobava a l'autocompletat de Netbeans. Amb això i amb la recent descoberta de que depurar Javascript des de Netbeans és possible i més àgil que amb firebug (encara que ho faci servir, deu ser cosa del refresc del navegador), encara fa que em decante més per Netbeans com a entorn de desenvolupament per defecte per Python amb el permís de Vim, clar.

Us aconsello jugar amb els virtualenvs, fer proves, mirar quins paquets i utilitats feis servir més, si els vostres aplicatius s'executen bé en un entorn net (com possiblement serà l'entorn de producció), podeu crear-ne tants com vulgeu. Un entorn típic per a desenvolupar amb Django té un tamany de 35M (du -h $WORKON_HOME/estable/), que no és res pels tamanys dels discs durs actuals.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Balanç del 2008


Escrit per Aaloy a 31 de December , 2008 a les 11:32 a.m.

Aquest serà el darrer apunt d'aquest any, i com a tal toca fer balanç del que ha significat aquest 2008 que aviat s'acaba.

Programació

A l'aspecte de programació ha estat un bon any: Django ha tret la versió 1 i Python l'esperada versió 3. Hem vist com han aparegut força projectes basats en Django que marcan la línia cap a l'esperada reutilització de codi.

Ha estat l'any on personalment he fet molta més programació amb Django que en Java. Els tipus de projectes que han sortit feien que fos molt més ràpid i optim fer-ho d'aquesta manera. Alguns dels llocs web han tingut força visites i hem pogut comprovar el bé que se comporta Django.

L'aparició de Netbeans per Python també crec que és una notícia prou important. Fins ara pareixia que Python estava apartat dels plans de Sun (no així dels de Google com s'ha vist amb l'AppEngine) i pel que apunta pareix que a mitjà plaç i amb permís d'Eclipse i Aptana podria ser l'editor de referència per Python.

Gestió de projectes

He seguit amb discussions filosòfiques damunt el que és un projecte i el que no és. Per mi un projecte per definició ha de tenir una data d'acabament, però ja he perdut l'esperança de que algunes coses canviïn.

Scrum com a metodologia cada cop m'agrada més. Estic molt còmode amb la idea de que les persones són el més important de l'equip i de que el cap de projecte ha de ser un facilitador. Scrum s'adapta molt bé a aquesta manera de pensar i de fer.

De la família

Llums i ombres enguany. Llum pel naixement del meu segon nebot, ombres per la pèrdua de gent propera, en un cas molt jove que va omplir la família de consternació, i un petit ensurt amb el Pare que afortunadament no ha anat a majors.

Els meus menuts creixent dia a dia, enguany ja saben colcar en bicicleta (amb 3 sessions en tingueren prou per aprendre'n!) i trobar la IP del seu ordinador Linux.

Dels amics

Donar-vos les gràcies per ser-hi, per aguantar-me els acudits dolents i pels bons moments que hem passat junts. La vida no seria digne de ser dita vida sense els amics.

De la feina

La feia principal rara, moltes baixes, buids de poder i una fusió que pareix que no s'arriba a concretar. Personalment he demanat una reducció de jornada que esper es concreti al 2009. No m'agraden les situacions d'impàs i mentre tot es defineix prefereixo dedicar més temps a la família.

De la feina "pla B" bé, molt bé. Projectes molt interessants que esper es concretin molt aviat. El 2008 ha estat un any de transició, d'agafar embranzida per l'any següent i crec que s'ha aconseguit.

Gràcies a tots i totes per seguir aquest blog i pels comentaris que anau deixant. Esper que el 2008 hagi estàs profitós per les vostres vides. Ens llegim al 2009!


Traducciones/Translations by apertium

7 comentaris, 0 trackbacks (URL) , Tags: General Python Django


Django no és PHP ni JSP


Escrit per Aaloy a 27 de December , 2008 a les 1:38 p.m.

Alguna de la gent que s'atraca a Django per desenvolupar web ve de PHP o del món Java (amb JSP) i intenta aplicar les mateixes tècniques que havia fet servir abans, trocejant les planes i fent servir includes, desaprofitant la potència de les plantilles de Django.

Includes per la maquetació

Els includes existeixen en Django, però no tenen la mateixa importància que en PHP a l'hora de fer la maquetació.

Les plantilles a Django es poden heretar. Això és un concepte nou per la gent que ve de PHP o JSP, però és la manera de muntar les planes web. El que feim és definir la nostra plantilla base (o plantilles) i programarem per diferències. És a dir, quan hem creat la plantilla, la plana següent la maquetarem pensant "és com la plantilla base però amb aquesta secció i aquest altra diferent".

Cada una de les diferències la podem posar dins un bloc. Així les plantilles hereten els continguts i el blocs de les plantilles pare i podem sobreescriure els continguts dels blocs.

Això vol dir que normalment no trocejarem la nostra plana en capçalera, menú, contingut i peu, en quatre arxius diferents, sinó que estarà tot dins el mateix arxiu i el que farem serà definir distints blocs segons convengui o no sobrescriure'ls en les plantilles filles.

No hem de passar pena de posar massa blocs, de fet millor trocejar massa que massa poc. I encara així, sempre som a temps d'editar la plantilla base per anar afegint els blocs que necessitem.

Includes per a carregar llibreries

JSP té el concepte de taglibs per a realitzar funcions com el formateig (fmt), estructures de control (core), etc.

A Django la majoria d'aquestes estructures estan ja dins el llenguatge de plantilles, definint els tags i els filtres. Els primers realitzen accions complexes i contenen les estructures de control de fluxe, els segons ens permeten modificar el contingut final que es mostrarà a l'usuari.

Com al Java amb el JSP Django ens permet definir llibreries de tags i filtres que es poden utilitzar en la generació de la plantilla amb un simple {% load "nom-de-la-llibreria" %}.

Les plantilles de Django no permeten incloure codi Python i el que es pot fer és limitat. La idea és que la plantilla tracti sols amb la capa de presentació i tot el que requereixi tractar amb regles de negoci o funcionalitat complexa es delegui a la capa Python.

Els includes de Django

Estan pensats per evitar tenir que repetir codi no per a la maquetació dels continguts. Pensem en el cas que tenim funcionalitat semblant però que ha d'anar a plantilles amb unes estructures molt diferents. Llavors el que feim és extreure aquell troç de funcionalitat i posar-ho a un arxiu separat per tal que es pugui reaprofitar el codi.

És més, si la repetició encapsula funcionalitat i aquesta es pot reaprofitar en altres aplicacions l'adequat seria crear un tag que generàs l'HTML. Django proporciona fins i tot decoradors que ens permeten crear molt fàcilment plantilles html que agafin paràmetres, o crear tags que generin HTML tal i com nosaltres el volem.

En definitiva, abans de posar-vos a maquetar convé fer una ullada a la documentació de Django, començant per Django template for designers que ens introduirà en els principals conceptes, després pegar una bona llegida als tags i filtres i una vegada dominem Python anar cap a la creació de les nostres pròpies llibreries de tags.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python Django


Cursos de Python i Django


Escrit per Aaloy a 23 de December , 2008 a les 6:03 p.m.

Els que em coneixeu segurament haureu notat que m'agrada ensenyar de la mateixa manera que m'agrada aprendre. Ensenyar permet compartir experiències i coneixements amb un nivell de comunicació que és difícil d'assolir en un llibre o un article, i poder ensenyar allò que a més t'agrada ho fa tot molt més divertit.

Com en moltes coses, per mi la màxima és fer les coses com m'agradaria que algú les fes si fossin per mi. Això sovint fa que m'ho prengui de vegades massa seriosament, sóc exigent amb els altres i per tant també ho sóc amb mi.

Per a que un curs sigui productiu la gent que hi assisteix hi ha d'anar mínimanent motivada. Una manera de tenir aquesta motivació és que la matèria que s'hi imparteix es vaig a utilitzar en un futur proper o ja s'estigui utilitzant.

Python i Django fa que sigui molt ràpid poder tenir un entorn de proves i començar a fer coses. La corba d'entrada és molt suau i es poden començar a fer aplicacions molt ràpidament, per tant, augmenten les possibilitats de que la gent ho pugui fer servir en el seu dia a dia, fins i tot si no s'hi dedica al 100%. Tasques d'administració de sistemes, scripts d'importació, generació de codi, etc. són molt ràpides de fer en Python i per tant es pot veure un resultat immediat del que s'ha après. La realimentació que s'aconsegueix, la gratificació de l'ego, fa que la gent pugui veure una utilitat en allò que fa.

A mi els formadors que m'agraden són aquells que a més de dominar la matèria (no ho doneu per suposat, no és la primera vegada que vaig a un curs on que l'impartia sols sabia el que deien les presentacions que duia preparades) hi fan feina. Sobretot en la part tècnica això es nota molt. Qui fa feina dia a dia amb una tecnologia a més de tenir-ne els coneixements també us podrà donar consells, millors pràctiques, trucs de la professió que no estan als manuals. Llavors és cosa de cada un aplicar-los o no, però el que està clar que a més d'una simple explicació de coneixements hi ha la part de transmissió d'experiència.

Per això m'agrada que els formadors no es dediquin professionalment a la formació, sinó que la seva feina principal impliqui fer feina amb aquella tecnologia i obviament m'aplic el mateix criteri. M'agrada ensenyar, però em trob molt més còmode amb mi mateix ensenyant allò que faig servir, en el meu cas Python, Django o gestió de projectes informàtics.

Ensenyar implica també preparar-se les classes. Per mi aquesta preparació no significa llegir-se la matèria (que també) sinó a més preparar exemples, la línia argumental de la classes i un conjunt de "plans bé". És a dir, tu prepares el que se suposa que serà el contingut de la classe d'aquell dia, però pot ser que s'avanci més ràpid del que s'havia pensat, o que sorgeixi més interès per un tema concret o que trobis que és millor fer un exemple més senzill per acabar de lligar el que s'havia explicat. Poder anticipar-se a tot això i tenir-ho previst també fa que s'avanci molt més en la formació. A mi que el formador se quedin pensant en el que ha de donar a continuació no m'agrada gens.

És difícil trobar gent bona per donar formació, de la mateixa manera que és difícil trobar bons programadors. Particularment aspir a que la gent acabi amb el convenciment de que després dels cursos en sap més que abans de començar, que la formació se li faci curta i es quedi amb ganes de saber-ne més.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django


Dos apunts d'avui


Escrit per Aaloy a 16 de December , 2008 a les 8:48 p.m.

Avui he llegit pels RSS dos apunts que m'han agradat:How Are You Staffing Your Startup? i Outsourcing Killed By Django And Ruby On Rails

El primer apunt reflexiona sobre el paper dels administradors de sistemes als equips de desenvolupament, a les startups. La veritat és que no hi puc estar més d'acord, especialment quan la feina té a veure amb la web.

Entenc la web com una feina d'equip, on es necessita, a més de la idea, els desenvolupadors que la programin, els creatius i maquetadors i la la gent de sistemes per a que ho posi en producció i tengui cura de l'estabilitat del sistema.

Pens que a part de l'èxit d'una aplicació hi té molt a veure l'entorn on es desenvolupa, tenir un entorn de preproducció adequat, tenir un entorn de producció escalable. Que la gent de sistemes pugui dedicar temps a monitoritzar l'aplicació, a millorar les eines que té a cercar o crear-ne de noves.

L'altra apunt fa referència a com Django i Rails poden canviar la mentalitat d'outsourcing de les empreses. Ambdós bastiments fan que el cost de les aplicacions sigui menor i que la quantitat de codi repetitiu que s'escrigui sia molt menor que en les aplicacions escrites en Java (o .Net donat el cas).

Potser l'article no està redactat de manera molt afortunada (ja veureu els comentaris), però crec que la idea és vàlida. Django i Rails permeten fer més coses amb menys gent, però a més fa que la gent millor encara destaqui més i sigui més productiva, ja que l'hem alliberada de gran part de les tasques repetitives que no aporten valor i que precisament són les que tenen més sentit externalitzar.

Això no vol dir que la gent de la India els països de l'Est o Sudamèrica no sigui creativa, sinó que senzillament no fa falta treure les coses fora quan a casa t'ho poden fer igual o millor a pràcticament el mateix cost i temps.

Dugem les coses un poc més enfora. Suposem un projecte gran, 100.000 línies de codi Java pel cap baix. Aquests tipus de projectes són els que normalment se durien cap a consultores de fora. Amb Django se pot convertir en un projecte de 20.000 línies tranquilament i llavors els projectes que es treien fora perquè no hi havia mans per al desenvolupament es poden posar en mans d'empreses d'entre 5-10 desenvolupadors amb garanties de que el projecte s'acabi i s'entregui. En poques paraules, es pot passar de les macro-consultores a les PYMEs altament especialitzades i localitzades fins i tot al territori de qui demana l'aplicació. En la meva opinió això és bo, ja que per una part abaratint els costs fa possible que es puguin fer altres projectes, però a més perquè s'està afavorint un treball de qualitat, no basat en la força bruta sinó en la capacitat de l'equip.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Gestió de projectes


És temps de Python


Escrit per Aaloy a 08 de December , 2008 a les 1:55 a.m.

En Paul Graham a un conegut assaig deia que si un volia bons programadors tenia que anar cercant a gent que fes feina amb Python. Era per allà el 2004 i supòs que la gent de Google li va fer cas, un poc exagerats per això, posant en plantilla a Guido Van Rossum. A l'assaig es fa referència a que algú amb prou interès per aprendre un llenguatge minoritari com el Python hauria de tenir passió per la programació i per tant téndria mols números per ser un bon programador.

Potser ara Graham es decantaria per coses com Scala o Erlang, però tanmateix el que importa realment és que la gent que s'ha apropat a Python fins ara ho fa cercant alguna cosa millor, un llenguatge més manejable i que li permeti expressar en codi les seves idees.

Bastiments com Django, Pylons o Turbogears han donat una nova magnitud al llenguatge, posicionant-lo com un competidor de primera línia en el desenvolupament web.

La pròpia experiència indica que Python i un framework web com Django permet una productivitat un ordre de magnitud superior a la que s'obté en Java i una corba d'aprenentatge molt menor i més suau.

Aquests darrers mesos hem posat en producció dues web noves i estam a punt de llançar una web per a la gestió de congressos i events que ataca a un web service consumit en Python, tot això a partir d'un equip mínim de programació i en el darrer cas fins i tot a partir de gent que mai havia programat en Python. La programació és molt ràpida i l'escalabilitat i els temps de resposta de les webs tan bo o millor que les webs que tenim en Java. Dels continguts, què dir-vos? Programació no hi té res a veure ;)


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Python 3.0 ja és aquí


Escrit per Aaloy a 04 de December , 2008 a les 9:11 p.m.

Després d'anys de feina tenim la nova versió de Python aquí. La llista de novetats és força nombrosa i implica a més que es romp la compatibilitat cap enrera.

Tranquils no passa res! Python 3.0 representa el primer intent de rentada de cara a Python per a convertir-lo en el que serà un llenguatge fins i tot més potent que l'anterior, sense perdre la riquesa i expressivitat actual de Python, però solucionant d'una vegada problemes com el del tractament uniforme del tipus de dades sencer, l'unicode i tenir difernts maneres de fer el mateix per obtenir resultats semblants (range i xrange per exemple).

Als sistemes operatius seriosos poden conviure vàries versions de Python sense dificultats. Ara mateix a la meva màquina tenc el 2.4 i el 2.5 essent aquest darrer la versió per defecte. A poc a poc les distribucions l'aniran incorporant, primer com a opció i d'aquí un parell d'anys com a versió per defecte, però llavors ja no serà el 3.0, sinó el 3.x segurament.

L'important del llançament de la versió 3.0 és que a partir d'ara començarem a veure millores en el rendiment de Python 3, pel que pareix la versió 3.0 és més lenta que la 2.5, i a poc a poc s'aniran adaptant les llibreries de tercers.

Això vol dir que no fa falta frissar massa a portar totes les nostres aplicacions al 3.0. Primer convé esperar a que les nostres distribucions preferides posin la 2.6 com a versió per defecte i després anar adaptant-nos poc a poc i començar a agafar conciència dels canvis de la 3.0 per a fer que quan donem el pas gairebé tot ho faci l'script automàtic de conversió.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python


Formulari compost a Django


Escrit per Aaloy a 23 de November , 2008 a les 7:48 p.m.

  • Get the Englist translation of this post at: Gencat

  • Obtén la traducción al castellano de este post en: Gencat

Django té un sistema de tractament de formularis molt bo. Ens permet per una banda la reutilització de codi, ja que separa el que és la definició del formulari i la seva validació de la part de presentació.

La validació de la informació és d'allò més interessant, ja que assegura que si un camp del formulari l'hem definit com a sencer, el valor de la variable que obtindrem serà un sencer, o en cas contrari hi haurà un error de validació.

Per tot això, la idea és utilitzar el formularis de Django sempre que sigui possible i reutilitzar tot el que tinguem.

El problema es presenta quan en una plana ens n'adonam que necessitam dos formularis, no hi ha cap exemple de com fer-ho a la documentació. Veurem que una vegada solucionat el problema és tant obvi i simple que efectivament no importa documentar-ho, encara que estaria bé :)

Per a aquest exemple he posat el codi al repositori d'appfusedjango

Anem per feina

La idea és ben senzilla, crearem dos formularis i els farem servir a la nostra vista. Per a facilitar-no les coses, podem posar els dos formularis dins una llista de manera que per muntar la plana sols haurem de recorre la llista de formularis que li passam.

A l'exemple he creat dos formularis LoginForm i ContactForm, que es generaran a two_forms.html

        def two_forms(request):
           if request.method == 'POST':
               contact_form = ContactForm(request.POST)
               login_form = LoginForm(request.POST)        
               if  login_form.is_valid() and contact_form.is_valid():
                   # do whatever you need as everything is valid
                   return HttpResponseRedirect('/thanks/')
           else:
               contact_form = ContactForm()
               login_form = LoginForm()
           forms = [contact_form, login_form]
           return render_to_response('two_forms.html', {'forms': forms})
     

Com podem veure el funcionament és el mateix que en el cas d'un formulari normal, sols que hem crear-ne dos (o els que vulguem) i anar cridant al mètode is_valid per fer les validacions.

Això funciona perquè cada formulari agafa del post sols les dades que coincideixen amb els camps que té definits i perquè a l'hora de muntar la plana Django mira si el formulari té errors (la validació es fa tant cridant a is_valid com accedint als errors), d'altra manera i donat que Python fa servir l'avaluació curta de l'and, no tindríem els errors del segon formulari si el primer ja no validàs. Això explica el perquè hi ha aquesta doble via de llançar la validació a Django.

I això ens duu de manera natural a la següent qüestió, què passa si als formularis hi ha camps amb al mateix nom?

Doncs que Django no serà capaç d'esbrinar a quin camp correspon cada cosa in ens trobarem amb un petit problema.

Però tranquils, això està controlat, basta afegir un prefix que sigui únic per a cada formulari. Aquest prefix s'afegirà al nom dels camps que es generen per a l'HTML i d'aquesta manera en recuperar la informació Django sabrà quin camp correspon a cada formulari.

Hem de tenir cura d'assignar el prefixe tant a la part del GET com a la part de POST de la funció.

I això és tot, fàcil i net!

Observacions finals

A l'exemple de tres formularis amb el mateix nom he deixat codi de depuració, el import ipdb; ipdb.set_trace(), ho podeu llevar, levar sols la i del ipdb o bé instal·lar el ipdb, un depurador un poc més potent que el pdb de tota la vida i amb ressaltat de sintaxis.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Netbeans IDE 6.5 edició Python


Escrit per Aaloy a 22 de November , 2008 a les 11:51 a.m.

Fa no res ha sortit l'edició Early Accés de Netbeans per Python.

L'IDE integra un editor amb autocompletat de codi, depuració integrada, integració de subversion i tot el que un normalment pot esperar d'un entorn avançat de programació.

En la documentació he vist que hi ha una branca per Django, però encara no he trobat com accedir-hi. Tanmateix no té molta importància ara per ara, ja que si és un bon editor per Python ho serà per editar projectes Django.

En l'execució damunt el PPC amb la màquina virtual IBM JDK6, funciona acceptablement bé, però he tingut que variar alguns dels paràmetres d'inici per a que l'entorn no s'arrossegàs tant. Res de nou, per Ecipse també ho vaig fer, i la impressió general és que llevat els casos en que a la màquina virtual li pega per consumir el 99% de les dues CPUs, l'entorn es comporta molt bé.

El paràmetres de tunejat:

    -J-Xmx356m 
    -J-Xverify:none 
    -J-XX:CompileThreshold=100 
    -J-XX:PermSize=64m 
    -J-XX:MaxPermSize=96m 
    -J-Djava.net.preferIPv4Stack=true
    

Aspectes destacats

  • Entorn de feina: net, molt net. Aprofita molt l'espai de treball i es deixa un gran espai per a l'editor.

  • El completat de codi és del milloret que he vist. A més han aconseguit extreure la documentació i presentar-la de manera poc intrussiva i elegant.

  • Possibilitat de crear plantilles. És una de les coses que faig amb Vim i que molts editors tenen, però que no acaben de funcionar tan bé com les de Vim. Netbeans ho fa. He portat algunes de les plantilles de Vim sense problemes.

  • Navegació entre objectes pitjant amb la tecla Ctrl damunt l'objecte. És un de les coses que més m'agraden d'Eclipse per Java i Netbeans també ho ha incorporat a Python.

  • Neteja de imports innecessaris. Ens deixa el codi més net.

  • Tecles ràpides per gairebé totes les opcions. Per mi és important que allò que faig habitualment permeti ser executat sense tenir que llevar les mans del teclat.

  • Possibilitat de crear les nostres pròpies plantilles.

  • L'eina de gestió de diferències entre fitxers manté el l'acoloriment de sintaxi. Una gran millora respecte a tot el que hi ha. És una eina fantàstica.

Punts a millorar

  • L'autocompletat té un preu: l'inici del programa és lent ja que parseja tot el que troba. Si teniu l'opció del la finestra de tasques activada la cosa pot arribar a ser insuportable. Afortunadament si posam que sols ens mostri les tasques de l'editor actiu la cosa millora. Tot i així crec que falta algun tipus de configuració per poder definir per projecte les llibreries que s'utilitzaran i les que no per agilitar-ne el parseig i evitar que algunes vegades a l'autocompletat surtin cents d'opcions que no tenen res a veure amb el projecte.

  • La integració amb subversion és bona, però la interfície de selecció del que s'ha de pujar i del que no és molt farragosa, ja que s'ha de seleccionar element a element amb un desplegable.

  • Pareix haver hi un error quan es selecciona una plantilla, ja que sols te deixa triar les de Python i Others. Es posa tot allà dintre i ja està, però sorprèn.

  • Refactorització. La que té Eric4 amb Rope o la de pyDev mateix permeten més opcions.

Hi ha un bon grapat de coses que vull explorar com són les macros, la inserció de codi i mirar més a fons el comportament de les plantilles, crec que se n'hi pot treure molt de suc.

Com a conclusió dir que m'ha agradat molt aquest entorn. Potser estic molt condicionat a que ha funcionat força bé en el PPC, però en la meva opinió és un entorn fantàstic per al desenvolupament de Python en general i de projectes Django en particular.

El faré servir una temporada, sols hi duc dos dies, a veure si tot confirma ser tant bo com sembla i puc trobar com instal·lar la branca per Django. Això sí, el vim seguirà essent un dels meus principals entorns de desenvolupament: quan un ha de fer modificacions de codi ràpidament o treballar per ssh no hi ha res millor. Sempre serà o bé una eina principal o complementària. Sempre és bo tenir dos editors de capçalera.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python Django


El django-admin no és per fer aplicacions d'usuari final


Escrit per Aaloy a 15 de November , 2008 a les 10:24 a.m.

Quan la gent dóna les seves primeres passes amb Django sovint queda enlluernada pel django-admin, una aplicació Django que ens permet definir un gestor de les nostres aplicacions, de manera que podem gestionar els usuaris, donar-hi permisos, gestionar les bases de dades, etc.

Hi ha que dir que l'aplicació està molt ben feta i es pot configurar moltíssim: indicar quins camps s'han de visualitzar, per quins camps es cercaran, fins i tot posar-hi javascript o modificar-ne l'aparença per a que la nostra aplicació faci el que nosaltres volem.

Aquesta facilitat però, té un perill, la gent té tendència a pensar que Django serveix per fer aplicacions web amb l'admin, que la seva aplicació ha de ser l'admin, així que recordem-ho:

El django-admin no és per fer aplicacions d'usuari final

Django admin fa molta màgia per dintre i aquesta màgia es basa en convencions que s'apliquen als models i a la definició del ModelAdmin, i aquestes convencions fan que per una part pugem tenir un gestor per a la nostra aplicació en quatre potades, però per altra, que si volem fer alguna cosa que surti del que està definit i establert ens durà molta feina.

La millor manera d'encarar-ho és tenir molt clar per a què serveix l'admin i per a què no:

  • Manteniment de les nostres taules de configuració i mestres? sí. Normalment ens anirà fantàstic per això i a meś ho podem tenir molt ràpidament. És ideal en les primeres etapes del desenvolupament, ja que es pot donar a l'usuari per a que carregui dades de prova i ja veu com pot funcionar l'aplicació.

  • Com a gestor web de la nostra base de dades? També anirà molt bé. Podem mapejar les taules i els camps que volem administrar, fer cerques, filtrar, editar i esborrar.

  • Com a eina d'usuari final per a que pugui configurar l'aplicació? Sí, si aquesta sols implica operacions senzilles, per exemple l'edició d'una plana web, afegir registres a una taula, etc. Podem definir quines taules pot tocar cada usuari i definir-ne perfils.

  • Com a eina d'usuari final on l'aplicació té molta lògica de negoci o bé un fluxe complex. NO. Millor començar un gestor pel nostre compte. Fer formularis CRUD amb Django és molt senzill i encara que pareix que dupliquem el que hi ha al django-admin, aquesta feina extra es veurà compensada a l'hora de definir els fluxs més complexos, ja que tindrem tota la potència de Python i Django al nostre abast i no estarem limitats a les convencions i funcionament del Django admin.

Les coses funcionen molt millor quan s'utilitzen per allò que estan pensades.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Rapid GUI Programming with Python and Qt


Escrit per Aaloy a 10 de November , 2008 a les 12:30 a.m.

El divendres vaig rebre un nou llibre:

Rapid GUI Programming with Python and Qt
Editorial Prentice Hall
Autor: Mark Summerfield
ISBN: 978-0-13-235418-9

Es tracta d'un manual de programació per al desenvolupament d'aplicacions amb Python i les Qt.

Me l'he estat fullejant i la veritat és que sols amb la qualitat de l'edició el llibre promet.

El primers capítols duen una petita introducció a Python, que segurament servirà a la gent que s'atraqui al món del la programació d'aplicacions d'escriptori portables, cercant quelcom més senzill de fer anar que la programació en C++.

L'estructura s'orienta per temes, on es van construint aplicacions de diferents nivells de complexitat i es complementa el tema amb tot un seguit d'exercicis per a que el lector faci feina pel seu compte.

Els que ens dedicam fonamentalment a la programació web, sovint tenim tendència a pensar que l'escriptori clàssic no té sentit, i això no és cert. Ara per ara encara hi ha força aplicacions i entorns on una aplicació d'escriptori pot solucionar millor els problemes al nostre client que una aplicació web. Poder oferir una solució feta amb Python amb una llibreria com Qt4 (no oblidem tampoc les WxPython) sens dubte ens serà de profit per ambdues parts.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Llibres i revistes Python


Senyals a Django


Escrit per Aaloy a 03 de November , 2008 a les 8:35 p.m.

El mecanisme de senyalització de Django ens permet desacoblar les nostres aplicacions, ja que permet notificar d'accions que es produeixen a tot una sèrie de receptors o subscriptors d'aquestes senyals.

Podem distingir dos tipus de senyals a Django:

  • Les senyals que venen predefinides a bastiment i que podem trobar a la documentacio de Django. En aquest cas Django se n'encarrega de llançar l'avís de que una acció concreta s'ha produït i nosaltres com a programadors podem generar codi per tal de subscriure'ns a aquest tipus de missatges i disposar-ho tot per a que s'executi el codi que ha de respondre a aquestes accions.

  • Les senyals definides per l'usuari. Es un cas més general que l'anterior. Django ens permet definir les nostres pròpies senyals i enviar-les. En aquest cas doncs, podem definir tant la senyal com el tractament que se'n faci.

Escoltant senyals

Per a escoltar una senyal necessitam fer dues coses:

  1. Definir la funció que rebrà la senyal amb el codi que s'ha d'executar.

  2. Connectar la funció amb la senyal, és a dir subscriure's a la senyal.

Les funcions que haurem de definir han de ser de la forma:

 

def nom_funcio (sender, kwargs): "Aqui va la documentacio" # aqui el codi pass

Hem de tenir en compte que el nombre d'arguments que pot transmetre una senyal per definició pot canviar en qualsevol moment. Els arguments es passen per nom, i la nostra funció ha d'estar preparada per gestionar aquest possible canvi d'arguments.

Per a connectar la senyal amb la nostra funció importarem la senyal a la que ens vulguem subscriure i ens hi connectarem amb el mètode connect de la pròpia senyal.

Per exemple per connectar-nos a la senyal que s'emet quan una petició http ha acabat bastaria fer:

 

    from django.core.signals import request_finished
    request_finished.connect(nom_funcio)

Les senyals tenen un paràmetre per defecte, el sender, que ens permet subscriure'ns sols a aquelles senyals que provenguin d'instàncies d'una classe determinada.

Definint les nostres senyals

Per a definir les nostres senyals hem fer dos passos:

  1. Definir la senyal i els arguments que passarà

  2. Allà on ens interessi enviar la senyal amb els arguments que hem definit

Pel primer pas basta importar django.dispatch i crear una instància de Signal amb els paràmetres que hem definit.

 

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

Enviar la senyal és encara més senzill, basta cridar al mètode send amb els paràmetres que hem definit, sense oblidar-nos d'establir la variable sender amb l'objecte que ha enviat la senyal.

Suposem que volem que el sistema ens avisi automàticament cada cop que algú esborra un usuari de la base de dades.

Per a mostrar-ho farem ús del senyal pre_delete que es troba a django.db.models.signals. Per al registre crearem una taula a la BD que guardarà l'usuari que se va esborrar i el seu nom.

La senal pre_delete s'envia quan s'entra dins el mètode delete del model, com a arguments té el sender que és la classe que envia la senyal i instance que recull l'objecte que s'eliminarà de la base de dades.

Nosaltres ens volem registrar sols a les senyals que impliquin eliminar usuaris de l'aplicació, així que sols ens subscriurem a les senyals que provenguin de django.contrib.auth.models.User

El nostre model seria

 

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Autor: aaloy

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import pre_delete

class Eliminat(models.Model):
user = models.CharField(max_length=30)
created = models.DateTimeField(editable=False, auto_now_add=True, blank=False,
    db_index = True)

def __unicode__(self):
    return self.user 

def registra(sender, **kwargs):
    instancia = kwargs['instance']
    usuari = Eliminat(user=instancia.username)
    usuari.save()

pre_delete.connect(registra, sender=User)

Per a veure com podem utilitzar les nostres pròpies senyals, crearem una senyal que registrarla la IP i el nom de la vista.

Per això crearem una nova classe, per exemple LogIp, que mantindrà el registre i connectarem el senyal amb la funció registre_ip

Hem de tenir en compte que es un exemple acadèmic, té poc sentit fe aquest tipus de coses, és a dir, definir una senyal dins el mateix model i enviant-la a la vista. Té molt més sentit que el senyal s'envii en resposta a una acció dins el propi model o a una acció particular de la vista i que una aplicació externa s'hi subscrigui.

 

class LogIp (models.Model):
    "Logs the ips and calling functions"
    name = models.CharField(max_length= 200)
    ip = models.IPAddressField()
    created = models.DateTimeField(editable=False, auto_now_add=True, blank=False,
        db_index = True)

in_view = django.dispatch.Signal(providing_args=['ip', 'name'])

def registra_ip(sender, **kwargs):
    ip = kwargs['ip']
    name = kwargs['name']
    log = LogIp(name= name, ip=ip)
    log.save()

in_view.connect(registra_ip)

in_view és la nostra senyal, com a paràmetres hem posat ip i name, això vol dir que la funció que es registri com a escoltadora els pot fer servir i ha de ser capaç de admetre'ls. Per una altra banda també ens diu que allà on enviem la senyal amb el send haurem de proporcionar també aquests paràmetres.

Si llançam el senyal a la vista quedaria com

 

def list(request):
    "Lists the deleted users"
    deleted = Eliminat.objects.all()
    in_view.send(sender=chivato.views.list, ip=request.META['REMOTE_ADDR'], 
    name=request.get_full_path())
    return render_to_response('list.html', {'deleted':deleted})

Amb això cada cop que algú des de el seu navegador accedeixi a la funció quedarà registrada la url d'accés i la seva ip.

Tot el codi font amb un projecte complet llest per funcionar, sols heu de canviar las rutes al properties.py a appfuse o directament

svn checkout http://appfusedjango.googlecode.com/svn/trunk/signals


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


CMS i Django


Escrit per Aaloy a 02 de October , 2008 a les 9:09 p.m.

Llegit un apunt d'Okkum m'ha fet pensar en el joc que ens poden donar els CMS en el desenvolupament web i en els perills que també comporten.

De CMS n'hi ha en gairebé qualsevol llenguatge i bastiment que tengui una mínima orientació cap a la web, ja que al cap i a la fi es tracta de facilitar que l'usuari que ha de crear els continguts de la web ho tengui fàcil per fer-ho, i que les limitacions que imposa el CMS facin que tota la web tengui una estructura comú (afegiu-hi aquí la paraula corporativa).

Per PHP i Java n'hi ha per avorrir de CMS, per Python també n'hi ha un bon grapat, però si me'n tengués que quedar un per les possibilitats que té i la funcionalitat que aporta se'ns dubte em quedaria amb Plone.

De totes maneres, l'apunt no vol ser una comparativa de CMS, sinó reflexionar damunt les seves utilitats i els seus perills. Una web que és bàsicament contingut té en un CMS l'aplicació ideal. Si ho volem fer amb Django actualment hi ha dues possibilitats que destaquen:

  • Django cms alliberat recentment i que és un complet sistema de publicació de continguts.

  • Django page cms una aplicació per Django, és a dir pensada per a integrar-se amb un projecte web.

Cada un d'aquests projectes representa una manera d'entendre els CMS. El primer imposa que el CMS sigui l'aplicació principal i que en tot cas les funcionalitats que es vulguin afegir s'hi facin a partir del que el CMS ofereix, amb més o manco passibilitats d'integració. Pot anar molt bé si la única cosa que hem de fer és posar un formulari, però pot ser un maldecap si volem construir-hi la nostra aplicació web.

El Django page cms, per contra, suposa que serà una aplicació més a l'ecosistema del projecte, i per tant està pensant per integrar-se en qualsevol projecte. En contra té menys funcionalitat de CMS, menys cosa feta que l'anterior.

L'elecció d'un camí o un altra dependrà de l'aplicació i del client. Si hi ha o hi pot haver funcionalitat més enllà de la gestió de continguts és millor anar cap a una solució integrable que cap a una solució de CMS pura. Si els nostres usuaris no saben el que volen ni cap a on ha d'evolucionar la web doncs el sistema de component també és recomanable. Per webs de contingut que s'han de gestionar i mantenir ràpidament, el CMS pur és ideal.

I si el vostre negoci depèn del CMS, doncs no ho dubteu gaire: Plone, a anys llum de qualsevol CMS obert o tancat, i és també Python.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Llista de llenguatges de programació que conec


Escrit per Aaloy a 09 de September , 2008 a les 8:40 p.m.

En Corey Goldberg escriu al seu blog damunt la llista de llenguatges de programació que coneix, amb la qual cosa ha iniciat un memme? ;)

La cosa està en llistar els llenguatges de programació en el que un se n'ha desfet en un moment o altre de la seva vida. És a dir, no val dir que un coneix el llenguatge Petito si no ha passat de "hello world".

En el meu cas i per ordre cronològic

  • GW-BASIC
  • Turbo Basic/Quick Basic
  • Assembler 8086
  • Shell
  • FORTRAN
  • Pascal
  • Icon
  • Matlab
  • Modula 2
  • Object Pascal (Delphi)
  • SQL
  • Python
  • Forté
  • C
  • VbScript
  • Java
  • Javascript

Ja no es podríen classificar com d'haver fet res seriós:

  • Fast (un llenguatge que generava .com especialitzat en fer jocs).
  • Lisp
  • C++
  • Groovy
  • Ruby
  • C#

El Deplhi és un dels llenguatges que més he disfrutat de programar, però em vaig cansar de les tonteries de Borland i vaig trobar Python, quina gran troballa! Des d'aleshores no l'he deixat mai al Python, encara que altres llenguatges també s'han anant fent el seu lloc, el Python sempre ha estat en primera línia, encara que fos per generar codi per altres llenguatges.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Informàtica Python


Avaluació pererosa a Python: un exemple amb Django


Escrit per Aaloy a 04 de September , 2008 a les 3:16 p.m.

Aprofitant que tenc vacances (eps! una setmana i ja s'acaben) aprofitaré per comentar un codi que serveix tant per entendre un poc més Python com per fer feina amb Django. La idea és del blog themorge.org


class PersonForm(ModelForm): "Simple form for the Person model" class Meta: model = Person

def edit(request,id=None): """Edits the Person model""" formulario = PersonForm(request.POST or None, instance = id and Person.objects.get(id = id) ) if request.method == 'POST': if formulario.is_valid(): persona = formulario.save() return HttpResponseRedirect('/fitxa/guardada/%s/'%persona.id) return render_to_response('edit.html', {'formulario': formulario})

Això representa un formulari d'edició amb Django (versió trunk). Tenim definit als models de l'aplicació una classe Person i el que volem és crear un formulari d'edició a partir d'aquest model.

A la classe PersonForm podem veure com hem creat el formulari a partir de la definició del model. Podem personalitzar el formulari afegint-hi validacions específiques, canviar la manera que s'edita etc, però si volem una cosa bruta i ràpida amb quatre línies en sortim (sí, la documentació sí és necessària).

Definim ara la part d'edició en sí, en el meu cas l'he anomenada edit, i fa referència a una plantilla edit.html, que bàsicament conté

       <form action="." method="post">
           <table>
               {{formulario}}
           </table>
           <button name="submit" value="submit"  type="submit">Send</button>
           <button name="reset" value="reset" type="reset">Reset</button>
       </form>
   

L'embolcall de l'html ho deixo com a exercici pel lector.

Anem a veure què passa quan a través de la url anam a parar a l'edit. Tenim dos casos possibles inicialment, que vulguem afegir un mou registre o que en volguem modificar-ne un ja existent. En el primer cas se'ns ha de mostrar un formulari en blanc, i en el segon un formulari amb les dades omplertes del l'objecte Persona agafat de la base de dades per l'ORM de Django.

Els urls que jo he definit són

     (r'^edit/(?P\d+)/$','edit'),
     (r'^add/$','edit'),

Com veim hi ha dues urls que apunten al mètode edit, la primera agafa l'id com a paràmetre, per la qual coses les urls serien de la forma /edit/10/ i la segona no agafa cap paràmetre i per tant queda com /add/. De fet podríem no haver fet la distinció entre edit i add, però si la feim el codi html ens quedarà molt més bo de llegir, més semàntic.

Estudiem el cas add. Quan entrem al mètode edit, com que no li passam l'id agafarà el valor per defecte, a saber None ja que així ho me definit al mètode, i ara comença la part interessant...

Definim un objecte de tipus PersonForm, la inicialització de la classe agafa dos paràmetres, el primer són els valors inicials del formulari i el segon és la instància de la classe que volem modificar.

Quan seleccionam editar un registre o afegir-ne un de nou, el que feim és un GET i quan guardam el registre hem definit al nostre codi HTML que el que farem és un POST.

Així doncs, ara hem fet un GET. L'operador or en Python funciona de la següent manera: x or y primer avalua x, si x és vertader retorna x i si no avalua y i retorna el resultat. Fixem-nos que deim que és una avaluació pererosa perquè si x dóna vertader, y mai s'arribarà a avaluar. Com que hem fet un GET la primera expressió és False i n'avaluarà la segona, amb la qual cosa obtenim un preciós None com a resultat de la inicialització de valors.

Anem ara a la segon part, la que ens defineix la instància, en aquest cas tenim un and. L'and funciona de la següent manera: x and y primer avalua x si x és fals llavors retorna el seu valor i si no s'avalua y i es retorna el seu valor. En el cas doncs de que x sigui fals ja no s'avaluarà y.

En el nostre cas hem passat el valor id=None llavors ja no itentarà ni crear una instància de l'objecte, senzillament passarà None com a valor de la instància, és a dir, res, i això indicarà a Django que es tracta d'un registre nou.

Per tant, quan hem entrat via add que que obtenim és un formulari buid. Com que hem entrat per GET l'if no s'avaluarà i Django ens presentarà una plana html amb un formulari buid.

Suposem ara que feim un /edit/2/, en aquest cas també haurem passat per GET però el valor de l'id serà 2.

formulario llavors tendrà None com a valors inicials per la mateixa raó que abans, però ara el primer terme de l'and és vertader i se n'avalua el segon, això fa que obtinguem com a valor per la instància l'objecte Person que té id igual a 2. En aquest cas tendrem un formulari ple amb els valors del registre i Django ens presentarà un formulari amb les dades de l'objecte com a valors i lligarà la instància al formulari (això significa que podrem fer formulario.save() per guardar el registre).

Molt bé, ja tenim un formulari, ple o buid, tant fa, ara el que feim és afegir o modificar dades i pitjam damunt l'opció de guardar. Això farà un post i cridarà a la mateixa url (el punt a l'action així ens ho garanteix), això significa que per a la inserció l'id serà None i per afegir l'id serà un número, 2 en el nostre cas.

Ara entram per POST, la qual cosa fa que la primera part de l'or sigui vertadera, amb la qual cosa el formulari tindrà com a valors inicials el que hagem posat, perquè això? dons per si no hem passat la validació del formulari, si anam per POST i el formulari no passa la validació, anirem novament a la mateixa plantilla, però ara tindrem com a valors del formulari el que hem passat, és a dir, si no passam la validació tornarem a tenir un formulari ple amb les dades que hem escrit.

És important fixar-se que les dades inicials a diferència de les dades que passem per la instància no necessàriament han de ser vàlides, no passen pels mecanismes de validació de Django, i això és el que ens permet recuperar tot el que hem escrit encara que estigui malament segons la validació.

Seguim, si hem entrat per edit l'id serà None i per tant ja no es crea cap objecte Person, si hem entrat per add l'id tindrà valor i s'avaluarà el segon terme de l'and amb la qual cosa haurem recuperat el registre de tipus Person que té id igual al nombre passat i l'haurem lligat al formulari.

Com que és un POST i passam la validació el que se fa és guardar el registre (si estava inicialitzat farà un update, sinó farà un insert a la base de dades i ens retornarà la instància de la classe Person que s'ha guardat.

Ara, que ja s'ha guardat sols ens queda fer un redirect de manera que evitem problemes amb dobles pitjades i refrescs varis. En el meu cas vaig a una url que em presentarà la fitxa que tot just acabam de crear/modificar.

Esper que aquest exemple us hagi agradat tant com a mi quan ho vaig veure. És tot un exemple de l'expressivitat de Python i del ben pensat que està Django. Però el més important de tot d'aquesta història és que sempre es pot aprendre alguna cosa i que per aprendre és important dedicar temps a llegir codi dels altres.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django


Django 1.0


Escrit per Aaloy a 04 de September , 2008 a les 8:48 a.m.

La versió 1.0 de Django fa sis hores més o menys que ha sortit del forn, es pot veure ja el comunicat oficial a la plana web del projecte.

Els números són impressionants, de de la darrera versió estable s'han fet 4.000 commits, s'han resolt 2.000 errors i s'han afegit o llevat prop de 350.000 línies de codi. I el que trob un factor diferenciador de Django: s'han afegit 40.000 línies noves de documentació, pocs bastiments de codi obert dediquen tants esforços a la documentació.

Se m'ha acudit passar-li el sloccount a trunk del projecte

SLOC DirectorySLOC-by-Language (Sorted)
46235 django python=46235
24683 tests python=24683
341 docs python=341
80 examples python=80
56 top_dir python=56
16 scripts sh=16
0 extras (none)
0 patches (none)

Totals grouped by language (dominant language first):

python: 71395 (99.98%)
sh: 16 (0.02%)

Total Physical Source Lines of Code (SLOC) = 71,411

Development Effort Estimate, Person-Years (Person-Months) = 17.68 (212.16) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC1.05))

Schedule Estimate, Years (Months) = 1.60 (19.15)

(Basic COCOMO model, Months = 2.5 * (person-months0.38)) Estimated Average Number of Developers (Effort/Schedule) = 11.08

Total Estimated Cost to Develop = $ 2,388,334

Si a més contam css, javascript i l'html amb una altra eina

 ./cloc-1.04.pl  --exclude-dir=.cvs,.svn --no3 .

    http://cloc.sourceforge.net v 1.04  T=11.0 s (80.8 files/s, 10177.7 lines/s)
    -------------------------------------------------------------------------------
    Language                     files          blank        comment           code
    -------------------------------------------------------------------------------
    Python                         739          15302           8184          83136
    HTML                           100            364             15           1725
    Javascript                      15            107            269           1547
    CSS                             15             83             82            505
    XML                             10             49              8            431
    make                             1             12              4             54
    SQL                              8              1              9             40
    Bourne Shell                     1              4              7             17
    -------------------------------------------------------------------------------
    SUM:                           889          15922           8578          87455
    -------------------------------------------------------------------------------

Hem d'agraïr la feina feta als desenvolupadors principals de Django i a la comunitat que s'ha format al voltant per tot l'esforç i la bona feina feta.

Django s'ha convertit per mi en un factor diferenciador en el que fa a productivitat i velocitat de desenvolupament, cada nova millora ha estat sempre orientada fer les coses més naturals per al programador, més pythoniques, molt en la línia del llenguatge de programació en el qual està fet Django, el Python.

Ara sols resta anar adaptant el codi per a incorporar les millores i les noves maneres de fer feina. Els canvis dels darrers mesos han estat tan grans i tan ràpids que sovint costava anar seguint el fil, però el resultat és un bastiment com per a sentir-se orgullosos.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django


AppfuseDjango actualitzat a la versió de Django


Escrit per Aaloy a 16 de August , 2008 a les 11 a.m.

Avui he actualitzat AppfuseDjango per a que funcioni amb la darrera versió de Django.

Darrerament hi ha molts canvis a Django, ja atracant-se a la versió 1.0, i alguns d'ells són incompatibles amb les versions anteriors.

La idea de l'aplicació és que servesqui de punt de partida per altres aplicacions i com a codi de mostra per a la gent que està començant amb Django. El tutorial de Django està força bé per començar, però després, quan has de fer una aplicació amb manteniments, internacionalització, etc. es queda un poco curt. No hi ha res a dir, l'objectiu del tutorial és ensenyar i el d'una aplicació com AppfuseDjango l'objectiu és ser un punt de partida.

De fet ja fa estona que no faig servir la utilitat de Django per a crear projectes i aplicacions, el que faig és fer un export d'AppfuseDjango i començar a partir d'allà.

Pels qui us demaneu què s'hi pot trobar:

  • Un manteniment CRUD: llistat, alta, baixa i modificació d'un registre.
  • Exemple de paginació
  • Mostra com internacionalitzar una aplicació.
  • Un exemples de com fer anar l'Ajax i json amb extjs i jqgrid

Però el més important crec que no és tant el codi que es mostra sinó que es vegi l'organització d'un projecte.

Veureu que s'ha tret la configuració del settings.py cap a un properties.py, que no trobareu al codi, sinó que hi ha un properties.py.template. Això es fa per deixar l'aplicació preparada per l'entorn de producció i per a ser utilitzada per molts desenvolupadors. Fent-ho així, poden tenir distintes configuracions per a cada un dels desenvolupadors i com que són pròpies de cada un estan fora del control de versions de l'aplicació. Tanmateix sols és cosa de copiar el properties.py.template i modificar-ho per a adaptar-ho a les nostres necessitats (directori, caché, etc.).

Una altra cosa que he anat posant són algunes optimitzacions i hacks útils trobases pels fòrums o de collita pròpia. Entre elles una que ens permet tenir una caché de plana que depèn de l'idioma de visualització, és a dir, es genera la clau de la caché amb l'idioma de l'usuari.

Com a darrera incorporació, un mètode molt elegant d'editar i inserir registres que fa ús de l'avaluació pererosa en Python, trobat a themorgue.org, el codi ha quedat molt net, potser una mica menys entenidor que abans, però l'estalvi en línies de codi ho compensa.

Els plans futurs crear branques amb distints tipus de projectes per assolir l'objectiu de tenir el punt de partida bàsic per a cada tipus d'aplicació: html pura, html amb ajax, RIA, etc. i així sols tenir que baixar l'estrictament necessari en cada cas.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Unit tests


Escrit per Aaloy a 25 de July , 2008 a les 6:10 p.m.

Supòs que no he de convèncer ningú de les bondats dels tests unitaris a l'hora de desenvolupar.

Encara que no arribem als extrems que proposa la gent que fa "test driven development" tenir tests ajuda a l'hora de provar les aplicacions:

  • Queda constància del que s'ha provat
  • Les proves són repetibles
  • Es poden anar afegint noves proves quan es detecten noves situacions i/o errors.
  • Ens ajuden a la refactorització, ja que ens ajuden a comprovar que la refectorització no ha variat el funcionament del programa.

El clàssic en test unitàris per Python és PyUnit que segueix una aproximació diguem-ne ortodoxa, és a dir, basada en crear una classe que extén TestCase i a partir d'aquí anar agrupar els tests en el que s'anomena una TestSuite.

Una altra aproximació ben diferent és la de doctest, aquí el que tenim és que s'escriu el codi de testeig i la serva sortida dins de la mateixa aplicació, no hi ha API però sovint pot resultar un tant confús.

Finalment tentim un nouvingut (relativament, que ja té uns anyets) el py.test, que ve avalat per la gent de PyPy i que va ser creat per a testejar el desenvolupament de l'intèrpret de Python fet en Python.

En Grig Gheorghiu's té un conjunt de tres articles comparant unittest, doctest i py.test, encara que són un poc antics, en temps Internet, crec que ajuden molt a veure les principals mancances i avantatges de cada un.

Per suposat, hi ha molts més a sistemes de testeig, podeu trobar-ne una recopilació a Python Testing Tools Taxonomy

He estat gairebé una setmana desenvolupant i fent tests amb py.test, i he de dir que m'ha agradat, és molt més còmode de fer anar que cap dels altres. Basta que creis un mètode que comenci amb test_ o acabi en _test i ja ho tenim llest per ser provat, que passi el test o no sols es cosa de que hi hagi un asssert que retorni vertader si ha passat un testo o fals si no l'ha pasat.

Una de les característiques que m'han agradat més és la possibilitat de desactiva ràpidament un test amb disabled = True on el valor es pot substituir per una condició, amb la qual cosa podríem fer que un test s'executàs o no en funció de l'avaluació d'una condició, per exemple, si detectam que estam a l'entorn de producció no borrar la base de dades!

I també he trobat molt còmode l'opció de poder establir condicions d'inici i acabament per tot un mòdul, això m'ha permés per exemple, obrir una sessió i mantenir la connexió i el identificador de sessió per a tots els tests, tancant la sessió sols quan tots els tests ja han acabat.

La part que m'ha xocat és la manera de testejar les excepcion, ho fan amb

 py.test.raises(Exception, func, *args, **kwargs)
 py.test.raises(Exception, "func(*args, **kwargs)")

És a dir, s'ha d'importar el mòdul py i cridar a py.test.raises, després posarem l'excepció que volem tractar, la funció que s'ha de testejar i que genera aquesta expepció i seguidament els paràmetres que té la funció.

Crec que això ha estat la única cosa més complicada d'aprendre. La resta és tot ben natural.

Me falta provar la part de distribució de tests, però per ara crec que va camí de convertir-se en la meva eina de testeig Python de referència, a l'espera, això sí, de veure com ho puc integrar amb Hudson.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


A voltes amb els trackbacks


Escrit per Aaloy a 22 de July , 2008 a les 8:01 p.m.

Benvolguts amics, coneguts i saludats,

Aprofitant la calor, la benentesa, i la moguda de Django estic aprofitant per retocar el sistema de trackbacks del blog.

Estic abusant un poc de la confiança i fent proves a altres blogs a més del meu, per veure si hi ha problemes.

Si veis alguna cosa rara, trackbacks que venen de trespams amb poc sentit o antics, perdonau-me, estic abusant de la vostra confiança i amabilitat. Procuraré no fer massa destrossa, i no cal dir-ho, esborrau el que convingui.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Benchmark: mako i lmxl


Escrit per Aaloy a 13 de July , 2008 a les 9:20 p.m.

Estam plantejant fer una remodelació de la web B2B que tenim, ara està feta en Java i aprofitant la remodelació la meva intenció ( si me deixen clar), és aprofitar que la remodelació és força important per a reescriure el codi en Python i Django. Si fos un canvi petit no m'ho plantejaria, però és un canvi que pot dur mesos de feina i en aquest cas refer-ho amb Python suposa no allargar els temps de desenvolupament, encara que la feina global en termes de funcionalitat sigui més gran.

Però tampoc és cosa de tirar-se a la piscina, i abans de res convé saber amb què ens trobarem. Un dels aspectes més crítics és que hem de consumir un servei web en format xml sobre http. Cap problema, urllib per exemple és capaç de fer tot això i molt més, la cosa està en poder generar els xml i consumir-los en molt poc temps.

Aquest cap de setmana m'he entretingut fent un petit benchmark [1], volia comprovar una hipòtesis: fer servir un llenguatge de plantilles per generar les peticions xml i un parsejador per a consumir l'xml que m'arribarà. L'altra opció es generar les peticions directament amb la llibreria de parseig.

La primera opció té com avantatges que s'ha d'escriure menys codi i que queda força documentada la petició en sí. La segona te l'avantatge de que que sols fas servir una llibreria. Així doncs el que ha de dir la darrera paraula és el factor rapidesa. Serà més ràpida la llibreria de plantilles o la de l'xml?

Primer presentarem els candidats: per una part mako, un llenguatge de plantilles que surt molt ben parat a les comparatives de velocitat i de l'altra lxml una interfície Python damunt unes de les llibreries per tractar xml més ràpides del mercat libxml2 i libxslt.

Per fer les proves tirarem de les dades que representen els cotxes que he tingut:

    VEHICLES =  {'seat': {'id': 1, 'color':'blanc', 'preu':150000},
      'ax'  : {'id': 2, 'color':'blanc', 'preu':1100000},
      '505' : {'id': 3, 'color':'blanc', 'preu': 500},
      'saxo': {'id': 4, 'color':'blau',  'preu':1500000}
  }

El que farem és generar un xml que representi aquesta estructura de dades. Com que tant amb mako com lxml la cosa va força ràpida, farem 10.000 repeticions.

El codi per mako és

    from mako.template import Template
    template = """
     <vehicles>
    % for marca, desc in vehicles.items():
         <vehicle id = "${desc['id']}">
             <marca>${marca} </marca>
             <color>${desc['color']} </color>
             <preu>${desc['preu']} </preu>
         </vehicle>
    % endfor
     </vehicles>
    """
    plantilla = Template(template)
    for i in xrange(1, REPETICIONS):
        xml =  plantilla.render(vehicles=VEHICLES)

El que feim és compilar la plantilla un pic, per simular les condicions reals, ja que en una aplicació en producció és el que faríem, i renderitxar l'xml amb les dades fins al nombre de repeticions desitjat.

Per lxml la cosa és semblant, sols que el codi (si no tenim en compte la definició de la plantilla) és una mica més llarg

    from lxml import etree
    for i in xrange(1, REPETICIONS):
        root=etree.Element( 'vehicles' )
        for marca_vehiculo, desc in VEHICLES.items():
            vehicle = etree.SubElement(root, 'vehicle', id = str(desc['id']) )
            marca = etree.SubElement(vehicle, "marca" )
            marca.text = marca_vehiculo
            color = etree.SubElement(vehicle, "color" )
            color.text = desc['color']
            preu = etree.SubElement(vehicle, 'preu')
            preu.text = "%.0f" % desc['preu']
            xml2 = etree.tostring(root, pretty_print=False)

També en aquest cas procuram que hi hagi igualtat de condicions, i hem posat el pretty_print a False de manera que l'xml generat no estarà ben tabulat i no quedarà tan elegant com el el cas de mako, però també és el que faríem en un entorn de producció.

I ja està, ara sols falta executar-ho i mostrar-ne els resultats:

Repeticionsmakolxml
1000,0761 s0.0277 s
1.0000,2897 s0.2823 s
10.0002.3076 s2.8906 s

D'això en treim dues conclusions:

  • Que ambdues aproximacions són molt ràpides en la generació de l'xml.
  • Que mako s'aprofita molt bé de la precompilació de les plantilles. En el benchmark he considerat que es compilava un pic i després s'executaven les repeticions. Si no consideram el temps de compilació
Repeticionsmakolxml
1000,0201 s0.0277 s
1.0000,2079 s0.2750 s
10.0001,9888 s2.9513 s

És a dir, que si feim sempre feina amb el mateix tipus de peticions, l'opció de fer servir mako per a generar les peticions xml, compilant les plantilles a l'inici de l'aplicació és té un rendiment molt millor que generant l'xml cada cop amb l'lxml.

Per cert, les proves estan fetes amb Ubuntu Linux corrent en un PowerPC de doble processador de 2 GHz, és a dir, res de l'altre mon.

[1] Sí, ja ho sé, no m'hauria d'endur feina a casa


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python


Protocol Buffer


Escrit per Aaloy a 08 de July , 2008 a les 10 p.m.

Protocol Buffer és una llibreria d'intercanvi de dades que Google ha alliberat i que segons promet és de 20 a 100 vegades més ràpida que l'XML (i ja no parlem de SOAP) per a l'intercanvi de dades entre aplicacions.

Lo de la velocitat s'haurà de comprovar, però el que sí pareix clar és que és prou senzilla per ser abastable, la documentació és bona, i ja va dirigida als tres principals llenguatges de programació (C++, Java i Python).

Com que en faig servir dos habitualment, doncs es cosa de fer-hi una ullada i algunes proves d'stress a veure si realment és tant ràpida com diuen.

La cosa no sé si s'imposarà com a format d'intercanvi entre llocs d'Internet, però el que sí tenc clar és que si és més ràpid pot ser ideal com a format per a tecnologies SOA dins de la mateixa empresa. Una reducció d'un factor 10 ja seria prou bona, amb l'avantatge de que es podrien crear (com ara, per cert) serveis en un llenguatge i consumir-los en un altre, però sense tenir que pagar el preu que suposa en termes de rendiment fer-ho en format WSDL.

Es prest, per donar-ne una opinió amb més fonament, però pel que he vist de la documentació i els exemples, crec que aquest format i jo ens durem bé :)


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Java


Sigues dinàmic


Escrit per Aaloy a 29 de June , 2008 a les 3:53 p.m.

L'altra dia vaig fer un petit prototip per veure amb quines dificultats em trovaba a l'hora de connectar amb Python contra l'LDAP de l'empresa (un Notes) i contra l'Active Directory. Aquesta funcionalitat ja la tenia desenvolupada en Java, però com que l'aplicació que estava plantejant es faria amb Python, vaig començar a mirar els tempes més problemàtics: l'autentificació i com imprimir els pdfs.

La connexió amb l'LDAP i la funcionalitat que volia, per tal d'obtenir tota la informació del l'usuari que es connectava no va ser gens problemàtica, en total 76 línies de codi davant les 350 llargues de Java, o el que és el mateix en Python vaig haver d'escriure un 80% menys de línies per a tenir la mateixa funcionalitat. D'això no poden concloure que sempre els programes en Python seran un 80% més curts, però és una evidència més del que parlam quan deim que s'escriu molt menys codi i que és més ràpid fer-ho.

El perquè en aquest cas concret, dóna peu a aquest apunt, presentarem la manera de tractar amb Python dues situacions bastant comuns quan ens hem de connectar a altres sistemes o fer servir llibreries de tercers: la transformació de diccionaris en objectes i el com tractar el cas en que tenim múltiples paràmetres opcionals.

Diccionaris a objectes

La situació és la següent: tenim un diccionari que volem passar com a paràmetre i fer servir les seves claus com si fossis propietats de la classe, de tal manera que si una clau no existeix ens dóni el valor per defecte.

Aquesta situació me la vaig trobar connectant a l'LDAP. La llibreria de Python en interroga l'LDAP torna un diccionari i volia que aquest diccionari formàs part de la classe Usuari que havia de contenir tota la informació de l'usuari que s'estava connectant a l'aplicació.

Suposem doncs que el diccionari que ens retornen és:

dades = {'nom': 'Antoni Aloy',
         'telefon': '555 55 55 55',
         'localitat': 'Binissalem',
         'email': 'aaloy@example.com',
         'blog': 'http://trespams.com'
    }

El que volem és posar tota aquesta informació dins una objecte de tipus Usuari de tal manera que sigui fàcilment manipulable i entendible. És a dir, que puguem fer usuari.telefon,

       class Usuari:
           "Exemple de com transformar les claus d'un diccionari en propietats"
           def init(self,ldap_prop = dict()):
               self.__propietats = ldap_prop

    def __getattr__(self, name):
        "Obtenim l'el valor de la propietat del diccionari"
        try:
            return self.__propietats[name]
        except:
            return "No assignada"

    def __str__(self):
        "Representació textual de l'usuari"
        return "%s - %s" % (self.nom, self.email)

    def propietats(self):
        "Retorna la llista de propietats"
        return self.__propietats.keys()

Ho podríem fer servir amb el codi següent:

    if __name__ == "__main__":
        u = Usuari( ldap_prop = dades)
        print "Nom %s" % u.nom
        print "Telefon %s " % u.telefon
        print "Provincia %s " % u.provincia
       print u

El nostre cas era prou senzill, si volem quelcom més complexe podem anar a a una recepte de Michael Foord, on podem veure com s'extén l'objecte diccionari per a fer el mateix que hem fet en el nostre exemple i a més permetre la utilització de paràmetres normals.

Parametrització

Tenim una classe amb una gran quantitat de paràmetres que es poden modificar. Per defecte tots aquests paràmetres tenen un valor per defecte. Volem que l'usuari pugui actualitzar els valors i obtenir-los. A més hi pot haver paràmetres que són sols de lectura.

Aquesta situació me la vaig trobar instanciant classes de Reportlab. A l'hora d'utilitzar la llibreria ens trobam en aquesta situació: tenim una gran quantitat d'atributs que podem assignar, però la major part del temps els valors per defecte ja ens estan bé. Vegem com ha resolt la situació la gent de Reportlab:

    class BaseDocTemplate:
        """...."""
        _initArgs = {   'pagesize':defaultPageSize,
                        'pageTemplates':[],
                        'showBoundary':0,
                        'leftMargin':inch,
                        'rightMargin':inch,
                        'topMargin':inch,
                        'bottomMargin':inch,
                        'allowSplitting':1,
                        'title':None,
                        'author':None,
                        'subject':None,
                        'keywords':[],
                        'invariant':None,
                        'pageCompression':None,
                        '_pageBreakQuick':1,
                        'rotation':0,
                        '_debug':0}
        _invalidInitArgs = ()
    
        def __init__(self, filename, **kw):
            """create a document template bound to a filename (see class 
                documentation for keyword arguments)"""
            self.filename = filename
    
            for k in self._initArgs.keys():
                if not kw.has_key(k):
                    v = self._initArgs[k]
                else:
                    if k in self._invalidInitArgs:
                        raise ValueError, "Invalid argument %s" % k
                    v = kw[k]
                setattr(self,k,v)
    
            p = self.pageTemplates
            self.pageTemplates = []
            self.addPageTemplates(p)
            ...

A l'hora de crear la classe BaseDocTemplate els atributs es defineixen dins un diccionari _initArgs, a la inicialització de la classe l'únic paràmetre obligatori és filename, però perfectament podem fer

myTemplate = BaseDocTemplate(filename="test.pdf", showBoundary=1, author="aaloy")

A l'init el que fa es repassar-se tots els atributs que hem definit al diccionari, si els paràmetres que s'han passat no coincideixen amb la clau del diccionari es crea un nou atribut a l'objecte amb el valor que té al diccionari (el valor per defecte). En canvi si hi és, verifica primer que no sigui un paràmetre de sols lectura, comprovant-ho a _invalidInitArgs i en cas que no ho sigui crea l'atribut amb el valor que li passam com a paràmetre en lloc del valor per defecte que té definit al diccionari.

D'aquesta manera ens permet utilitzar i assignar valor molt fàcilment i sols inicialitzar allò que necessitam.

La quantitat de codi que ens estalvien aquests deus receptes és proporcional al nombre d'atributs que tengui la nostra classe si la fessin en un llenguatge de programació no dinàmic.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python


La meva experiència amb Django


Escrit per Aaloy a 22 de June , 2008 a les 12:03 p.m.

En Maties Bonet ens va escriure un e-mail a mi i a Guillem demanant-nos per la nostra experiència en l'ús de Django.

Amic Maties, bona cosa has demanat! Pepara't perquè aquest apunt pot ser llarg :)

La meva relació seriosa amb Django ja té més de dos anys, és difícil estimar la data, però si en tingués que donar una seria la del 3 d'agost del 2006, data del primer commit del major projecte que hem fet amb Django fins ara,amb actualment més de 12.000 línies de codi

Aquest projecte inicialment estava desenvolupat en PHP, però necessitàvem més funcionalitat i essent les nostres filies més tendents cap al Python que cap a altra cosa, començarem a investigar els bastiments que començavent a surgir. Cercàvem quelcom que permetés una bona escalabilitat, facilitat de desenvolpament i una separació molt clara entre codi i capa de presentació. A més una de les restriccions inicials era que el servidor que tenia que dur tot això no havia de ser massa potent, un host virtual baratet hauria de ser suficient per començar.

Amb Juan avaluarem un grapat d'opcions. Anàvem mirant frameworks i en discutíem els avantatges i inconvenients. Férem una ullada a Pylons, a TurboGears i alguns altres, fent algun prototip i discutint-ne els resultats.

TurboGears estava força bé, i Pylons representava gairebé l'estat de l'art en quant a integració de componentes, però Django tenia una avantatja per a nosaltres fonamental: la seva integració entre els distints components (integració que a més no compromet a res), una documentació fantàstica i el ser un bastiment que s'havia fet servir en projectes grans, en projectes que estaven funcionant. Es podria dir que no era sols una idea sinó que el funcionament del framework era una realitat.

Així doncs ens decidirem per Django. Mesos després Guido Van Rossum com el framework que li agradava més. Un any i mig després veuríem Django com un bastiment suportat per Google. Això vol dir que tenim bon ull per les tecnologies? Segurament, però vist en perspectiva l'elecció de qualsevol dels altres dos bastiments també ens hauria anat força bé.

Django, però té aquell quelcom que et fa sentir content i satisfet amb la programació que fas. És entenidor i abastable i quan t'enfrontes a un problema de la vida real trobes que hi ha una solució en Django, per una raó molt senzilla: el bastiment es va crear per resoldre problemes de la vida real, com una resposta a la necessitat que tenien els seus creadors de tenir un bastiment que els proporcionàs una gran rapidesa en el desenvolupament i al mateix temps una gran escalabilitat.

Una vegada vàrem veure la potència del bastiment, junt amb la potència de Python i ho compararem amb el que teníem i feiem en Java, el pas següent va ser lògic: utilitzar el que sabíem en la feina diària per a l'empresa per la qual treballàvem, una multinacional del turisme. Sense deixar Java del tot, ara podíem incorporar una nova eina que ens permetria poder desenvolupar webs a la velocitat que li agradava al negoci.

Posar Django i Python a una empresa molt tradicional no és senzill, "aquest tipus de la web sempre fent coses rares!" Però l'evidència s'imposa i amb un poc de ma ampla del nostre director d'informàtica anterior començarem a fer els nostres primers projectes amb Django per a l'empresa. És una tasca que avui en dia encara costa. Quant més consultors externs té l'empresa més difícil és aquesta tasca. Aquests suposats experts no tenen idea de què és Django i de les seves possibilitats, i tampoc els convé, ja que després de la consultoria hi sol haver un desenvolupament i com que el desenvolupament és més ràpid i ells cobren per hores, doncs que no convé gaire. Però això és una altra història i també serà un altra apunt.

Actualment doncs, feim/faig servir Django i Python en quatre grans tipus de projectes:

  • projectes on no s'ataca a la base de dades "legacy" de l'empresa, sinó que s'han desenvolupat des de zero.
  • projectes on hi ha una gran part de continguts i a més lògica de negoci senzilla.
  • projectes que consumeixen web services en SOAP que estan a la seva vegada desenvolupats en Java.
  • el nostres projectes particulars, com aquest blog.

Els resultats són molt bons, un exemple: fa dues setmanes ens telefonaren del dimecres per a un projecte crític, s'havia de crear una web completa per a un client que havia d'estar llesta el divendres al matí de la mateixa setmana. El dimecres horabaixa en tendríem més detalls.

L'horabaixa ens defineixen un poc millor el projecte: bàsicament contingut que se'ns aniria passant en format word i que segurament aniria sofrint modificacions damunt la marxa.

Tot just penjar el telèfon ens posarem en marxa. La gent de sistemes creà el repositori subversion pel projecte (no importa la pressa que tenguis, sempre, sempre un control de versions) i inicià la configuració del nou domini.

Una hora després ja teníem el domini intern funcionant i la primera versió del codi dins el subversions. Havíem reaprofitat la funcionalitat que teníem per a la gestió de continguts i ara sols era cosa de crear el disseny, passar-ho a plantilles i posar el contingut. El que ens feia més por era no tenir els DNS replicats per l'hora d'entrega.

El dijous al matí el disseny ja estava llest i es comença a maquetar i pujar continguts. Es crearen algunes plantilles per a fer que les opcions de menú canviessin dinàmicament i s'anava pujant tot al servidor de producció mitjançant un update del subversions. El temps de pujada d'una nova versió era aproximadament de 30 segons, versió que ja pujava testejada gràcies a que amb Django pots anar provant l'aplicació amb el seu servidor integrat (i amb recàrrega automàtica).

Ens sobraren un parell d'hores del temps limit. Total del projecte 35 hores-home. Entregable: aplicació amb subdomini propi, tipus portal de continguts, multi-idioma, amb menús desplegables, i la maquetació a partir de documents word de l'equivalent a una trentena de planes web amb una mescla de text, fotografies i descàrrega d'arxius. Rendiment de l'aplicació: generació d'una plana web en 1,2 segons sense caché.

Si ho haguéssim tingut que fer en Java encara estaríem muntant el CMS o pujant els continguts. Amb les plantilles de Django i la possibilitat d'herència que tenen, poguérem crear el nostre lloc web amb molt poc temps i passar els continguts a HTML de manera molt més ràpida que l'equivalent a crear el disseny en un CMS clàssic de PHP o Java (ja no en parlem de fer el mateix amb Java i sense CMS).

El millor de tot és que encara que no sabíem que se'ns demanaria estàvem raonablement segurs de que si no era res molt complexe es podria fer, ja que el bastiment no ens fermava (com sovint fan els CMS més habituals) sinó tot el contrari.

La meva experiència amb Django? Fantàstica i demostrable. Fins al punt que quan veig el que puc fer en aquest entorn em fa molta peresa tornar cap a Java, els temps d'espera tot i que desenvolupam en local resulten molests, les posades en producció s'eternitzen. I això que gràcies al nostres administradors de sistemes ho tenim tot que va com una seda a l'entorn J2EE i les posades en producció són ràpides, però quan ho compares amb un pocs segons tot resulta lent.

Consider Django com un avantatge competitiu: ens permet fer desenvolpaments molt ràpidament i a més sabem que escalaran bé. Com que no estam lligats a cap bastiment de javascript concret podem incorporar el que necessitem segons el projecte: jQuery, extjs, res... i la separació que es fa en capes de l'aplicació també es pot fer a l'hora de desenvolupar i permet treballar en paral·lel a la gent de sistemes, disseny i programació.

I així doncs, quan voleu que posem Django a la vostra empresa?


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python Django


A la pregunta de Servo ....


Escrit per Aaloy a 15 de June , 2008 a les 7:20 p.m.

Al comentari del darrer apunt de Servo es demanava el perquè el Set hauria de ser més ràpid si el primer algorisme és d'ordre N.

El codi dels sets es molt semblant al dels diccionaris, però està una mica més optimitzat, a l'igual que els diccionaris està implementat com una taula hash, però a diferència d'aquests els conjunts sols guarden el parell clau/hash en lloc de la tripleta clau/valor/valor del diccionari.

Com que els sets guarden els parells clau/hash, totes les operacions binàries, com la intersecció s'executen sense cap cridada al mètode _ _hash__ dels elements individuals. Això és molt més ràpid que el codi equivalent que fa servir diccionaris.

També resulta que quan feim un bucle damunt un set Python (CPython) directament recorre la taula hash en lloc de fer ús d'un iterador (que seria un poc més lent).

La pregunta ha servit per fer un poc de recerca, de fet la meva resposta és la traducció d'una resposta molt completa l'he trobada a Python in Science .

Però no ens conformem amb això, fem un "show me the code" a la plana citada anteriorment hi ha un exemple que ens anirà bé per començar. Es tracta de trobar la intersecció de dos conjunts, però ho podem adaptar per fer les cerques al diccionari:


import random
import time
REPETICIONS = 1000

## Generam els valors
seta = set([random.randint(0,100000) for n in xrange(10000)])
setb = set([random.randint(0,100000) for n in xrange(10000)])

print "Cerques a conjunts"

t0 = time.clock()
    for i in xrange(REPETICIONS):
    total = seta.intersection(setb)
print "Intersections - Time: %s seconds"%(time.clock()-t0)

t0 = time.clock()
for i in xrange(REPETICIONS):
    total2 = []
    for element in seta:
        if element in setb:
            total2.append(element)
print "Fent el bucle Time: %s seconds"%(time.clock()-t0)

## I ara el nostre cas

print "Cerques amb diccionaris"

## Generam els valors
dicta = {}
dictb = {}
for i in xrange(10000):
    dicta[random.randint(0,100000)]=i
    dictb[random.randint(0,100000)]=i

t0 = time.clock()
for x in xrange(REPETICIONS):
    total2 = []
    for valor in dicta.keys():
         if dictb.get(valor):
            total2.append((valor, dicta[valor]))

print "Bucle - Time: %s seconds"%(time.clock()-t0)

t0 = time.clock()
for x in xrange(REPETICIONS):
    total2 = [ (repe,dicta[repe]) for repe in   set(dicta).intersection(set(dictb))]

print "Set intersection - Time: %s seconds"%(time.clock()-t0)

I aquí el resultat


Cerques a conjunts
Intersections - Time: 0.94 seconds
Fent el bucle Time: 6.11 seconds
Cerques amb diccionaris
Bucle - Time: 11.63 seconds
Set intersection - Time: 6.97 seconds

En el primer cas estam davant un algorisme d'ordre N² davant un algorisme NlogN sin o record malament de la intersecció de conjunts.

El nostre cas és una mica menys clar en el que fa a l'ordre de l'algorisme, però amb el codi es pot veure com el resultat final s'obté és un 60% més ràpid.

Tot i això fixem-nos amb que per a obtenir el resultat en segons he tingut que fer 1.000 iteracions i que hem fet servir un conjunt de 10.000 elements, sols per a que quedi clar de que potser ambdós algorismes són prou ràpids per a la nostra tasca diària. Sols hem de tenir clar que quan cerquem maneres d'optimitzar el nostre codi, mirar si feim aquests tipus d'algorismes moltes vegades


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Trobar elements repetits


Escrit per Aaloy a 15 de June , 2008 a les 12:37 p.m.

Tenim el següent problema: "tenim dos diccionaris amb dades i volem trobar els elements d'una diccionari que estan també dins l'altra"

Suposem, per exemple que tenim:

x = {'a':1,'b':2,'r':3}
y = {'a':1,'r':3, 'c':14}

La opció més directe pareix ser la de recorre els elements de la primera llista i veure si hi són a la segona, una cosa com

for valor in x.keys():
    if y[valor]:
        print valor, y[valor]
a 1
r 3

o bé una altra opció més curta:

for repe in set(x).intersection(set(y)):
     print repe, x[repe]

o si m'apurau

[ (repe,x[repe]) for repe in set(x).intersection(set(y))]
[('a', 1), ('r', 3)]

Ara quan algú us demani que és això de que Python ve amb les piles incloses ja teniu un exemple més per a mostrar.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python


Millores al blog


Escrit per Aaloy a 08 de June , 2008 a les 11:30 a.m.

De tant en tant faig feina al blog, no per escriure-hi sinó per afegir-hi noves funcionalitats. Encara hi ha moltes coses que m'agradaria posar-hi i millorar, però a poc a poc esper anar arribant-hi.

A la darrera actualització he fet algunes millores que recomanaven al Google Webmaster Tools com la d'afegir descripcions úniques per apunt. Django a les seves plantilles té el filtre truncatewords_html que m'ha anat fantàstic per això.

Una de les millores que volia fer també era la d'amagar un poc tota la llista de mesos que hi ha. Aquest blog té apunts des del 2004 i la llista començava a ser molt llarga. Ara veureu que sols apareixen els anys (sempre que tingueu el javascript activat clar) i que en pitjar damunt ells es despleguen els mesos. Fer això ha estat entretingut perquè ha implicat jugar amb dos tags més de Django, el for per obtenir si estava a la primera posició del bucle o a la darrera, per tal de poder tancar els divs, i el tag ifchanged que ens permet saber si una variable (en el meu cas l'any) ha canviat o no respecte al seu valor anterior.

Amb aquestes eines ha estat possible muntar l'estructura necessària per a utilitzar un el Animated Collapsible DIV, una petita utilitat per jquery que permet ocultar i mostrar divs a voluntat, agrupant-los per categories si convé.

Ara la plana principal al meu entendre queda un poc més neta i amb prou espai a la columna lateral esquerra per anar afegint més coses.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django


Capa de negoci a Django


Escrit per Aaloy a 28 de May , 2008 a les 11:55 p.m.

Del post anterior em quedava el tema de tractar el tema de la capa de negoci en els llenguatges dinàmics. Com en el cas anterior faré servir Django com a exemple i deix al lector la feina d'extrapolar cap el seu llenguatge dinàmic+bastiment preferit.

En Domingo diu que els llenguatges dinàmics mostren molta de la seva potència a la capa de presentació, que és allà on en treuen més profit. Això és veritat però es queda curt. És a dir, quan un usuari demana un canvi, el més habitual és que aquest s'acabi reflexant en la capa de presentació,però això no vol dir que no se canvii la capa de negoci. El fet però és que anar des de la capa de persistència, passant per negoci i mostrar el resultat al navegador, té un cicle de temps més curt en el llenguatge dinàmic, interpretat, que en el compilat, ja que sol ser molt més curt fer els canvis (gearing factor i totes aquestes herbes) i necessitea un temps més curt de desplegament, però no ens enganem, és tot el procés que és curt, si no afecta a capa de presentació el que passarà és que el desenvolupament serà encara més ràpid.

Sovint amb les aplicacions del tipus crea el teu blog en 10 minuts en Django, Rail o qualsevol altra, es dóna una idea equivocada del que se pot fer, donant la imatge de que aquestes aplicacions van molt bé per fer webs que depenen d'un conjunt de taules senzilles i que tenen una lògica de negoci poc complexa.

Bé, supòs que podríem demanar-li a Ricardo, que és un exemple que tenim ben aprop,de com s'ho fa per calcular el karma de Meneame o per saber si una notícia ha de sortir en portada o quan ha de deixar de ser-hi. Tot això es lògica de negoci, està feta en un llenguatge interpretat, el PHP.

En el cas de Django no oblidem que el que tenim per davall és Python i que l'ORM que ens proporciona Django ho podem fer servir o no, substituir-ho per un altre, fer les cridades directament a BD o senzillament, utilitzar-ho com a part del nostre model de negoci i posar-i les regles que ens convenguin.

Segons la nostra aplicació podem anar afegint custom managers que ens permetran definir regles damunt les dades que volem obtenir, mètodes de classe per definir les funcionalitats que vagin damunt tots els elements de la taula, i missatges que ens ajudin a manipular la informació i les seves relacions.

Això ho podem fer directament des de la classe de l'ORM, el model, o bé, recordem que tenim Python a la nostra disposició, crear un nou paquet, crear una classes o un conjunt de classes i utilitzar el model per aplicar les regles de negoci que vulguem quan aquestes s'hagin de convertir en registres de la base de dades o la seva informació hagi de venir de la base de dades. Per cert, i abans que algún ho demani, sí hi ha transaccions!

Això vol dir que podem fer servir els llenguatges dinàmics per a modelar la nostra capa de negoci, doncs sí, ho podem fer. Això vol dir que la nostra aplicació ha d'estar escrita en un llenguatge dinàmic sempre? No, depèn de l'aplicació. Potser per una aplicació concreta amb un requeriments concrets tenir un contenidor EJB amb transaccions distribuïdes serà el que necessitam, el que vull dir és que no podem descartar fer l'aplicació en un llenguatge dinàmic sols perquè ens hem quedat amb la idea de que sols va bé perquè el seu ORM te crea molt fàcilment les taules i els CRUDs.

De fet la part d'administració de Django va molt bé com a eina administrativa, però no és una eina d'usuari final. Com a desenvolupador te va molt bé perquè pots fer cerques ràpidament, començar a omplir les dades del manteniments mestres i anar directament a les butzes de les dades, però no hem de confondre això ni amb l'aplicació final i amb que és tot el que se pot fer amb els llenguatges dinàmics.

I encara hi ha un punt que no hem tractat, el tema de la integració dels llenguatges dinàmics amb els llenguatges compilats (jpython, jruby, groovy, etc). Segons la nostra aplicació i els nostres usuaris, tenir un llenguatge interpretat, fins i tot creat per al domini de la nostra aplicació pot ser una opció molt interessant. Permeteu-me una batalleta: fa un grapat d'anys vaig desenvolupar el programa de gestió d'excursions de l'empresa on feia feina (Delphi + IBObjects + Firebird), una aplicació client servidor clàssica amb un grapat de procediments amagatzemats quan feia falta. La cosa és que hi havia excursions que tenien ofertes que el proveïdor donava i que s'havien de cotitzar, ofertes del tipus: "si es reserva una excursió per dos adults i un nin, el nin tendrà un 25% de descompte damunt la tarifa" o "el tercer adult es gratis i els nins no paguen" etc. Això ho podria haver desenvolupat amb un bon conjunt de camps de la base de dades i tenir previstes les condicions, però la solució hagués estat visualment atractiva però mala de programar i molt propensa a errors. Per contra vaig optar per a modelar-ho con si fos una fórmula de una fulla de càlcul, a la que els meus usuaris estaven acostumant on es podia jugar amb el preu per data, amb sumes, restes, if i tant per cents i parèntesi, un mini intèrpret pensat per a tractar amb fórmules matemàtiques. Les condicions així posades eren flexibles i amb unes possibilitats pràcticament il·limitades respecte al que haurien estat si ho hagués fet amb una "interfície amigable". En aquest cas, integrar un intèrpret a l'aplicació va ser la millor solució.

Com no me cansaré de repetir, no es tracta de dinàmics/interpretats respecte d'estàtics/compilats. Es tracta de perdre la por i els perjudicis i poder triar en cada cas aquella tecnologia que millor s'adapti al problema a resoldre de manera que aquest es pugui resoldre amb el menor cost actual i futur per al nostre client, en alguns casos aquest llenguatge serà C, C++, C#, Java o el que sigui, però en altres, en molts altres un llenguatge dinàmic serà la millor opció per als nostres clients.


Traducciones/Translations by apertium

5 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Django


D'errors i línies de codi


Escrit per Aaloy a 26 de May , 2008 a les 9:30 p.m.

En Domingo al seu blog fa una referència al meu apunt damunt llenguatges dinàmics i un bon grapat de bones reflexions.

Això de no creure's el que dic és una postura molt sana, sobretot perquè en la redacció d'un apunt me puc deixar detall i dades que són interessants. Una postura crítica ajuda a reflexionar i a completar les frases que d'altra banda s'haurien deixat com a dogmes de fe. Jo sóc del mateix tarannà, hi ha coses que me crec i coses que a poc que vegi indicis de contradiccions, doncs cerc més informació o deman explicacions. Això, he de dir també, m'ha causat força problemes en el món empresarial, on sovint el "no pensis" és una qualitat que ajuda a progressar.

Bé, però no me'n vull anar per les bardisses, o sí, ja que hi sóm, aprofitaré per dir perquè no faig servir el Twitter: no hi ha espai abastament :)

Afirmació: El nombre de línies de codi que escriu un programador al dia és una constant. Això és un efecte estadístic. Fa temps es parlava de que un programador escriu cinc línies de codi sense errors al dia. Està clar que és una dada estadística i fins i tot controvertida, ja que que és molt complexa definir què és i que no és una línia de codi i de que les línies de codi no s'han de fer servir per mesurar el rendiment. Tot i això podem trobar referències capítol 5 de Software estimation de McConnell on a la Taula 5.1 torbam la relació entre les línies d'un projecte i les línies de codi per programador i any. Això no vol dir que tots els programadors escriguin les mateixes línies de codi (de fet la productivitat entre programador pot arribar a un factor 10 -Peopleware-, però sí que en mitja i per a una organització i projecte, el nombre de línies de codi que s'escriu en promig és constant. A Sofware Mesurement and Estimation diu "Studies have shown that a proficient programmer can programm aproximately the same number of debugged lines of code per day regarless of the language". D'aquí que gent com QSM o David Consulting Group facin comparatives entre llenguatges per a comparar l'expressivitat de cada llenguatge. Per exemple C té un gearing factor de 128 i Java de 53. Això vol dir que una funció que en C necessita 128 línies de codi en Java en necessitarà en promig 53. Si un programador experimentat escriu, essent optimistes 10 línies de codi depurat al dia, llavors necessitarà 13 dies en C i 6 en Java (nombres redons).

D'aquí llavors que els llenguatges dinàmics, al necessitar menys línies de codi per fer el mateix poden completar els projectes en un temps més curt. Com que el temps significa doblers, implica que els projectes surten més barats.

Afirmació: el número de errores directamente proporcional al número de líneas que se escriben Aquí també entram en temes estadístics. De fet es parla de nombre d'errors d'un programa per mils de línies de codi. De la mateixa manera que en el cas de les línies de codi que escriu un programador, hi ha moltes diferències, però Casper Jones a "Program Quality and programmer Poductiviy (1997)" va recopilar algunes dades, que també cita en McConnell. També Reifer en va fer estudis, per exemple per la part Web estudià 65 projectes, i trobà que el ratio és de 6 errors per KESLOC (KESLOC Kilo (Thousand) Equivalent Source Lines of Code). Aquest nombre depèn força del tipus del projecte i de l'organització, però és estadísticament significatiu. Per tant, si donam com a bo el que els llenguatges dinàmics necessiten menys línies de codi per expressar el mateix que els llenguatges compilats tradicionals (Java, C, C+, ...) tendrem que el nombre d'errors en valor absolut també serà menor.

He trobat també una comparativa entre el nombre d'erros per Java i C++ a un paper de Geoffrey Phipps anomenat Comparing Observed Bug and Productiviy Rates for Java and C++, me l'he d'acabar de llegir, però conclou que amb un 95% de confiança C++ té 9,7 vegades més defectes per KLOC que Java i que a més els defectes en Java són més fàcils de corregir. D'aquí a fer una extrapolació cap a la banda dels llenguatges dinàmics hi ha sols un pas, sols falta que algú s'animi a fer l'estudi.

En Domingo també demana l'opinió damunt els llenguatges dinàmics per la capa de negoci, però això crec que dóna per un apunt per ell sol, així que ho deixaré per la propera vegada, però promet, amenaç, amb tractar-ho.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Informàtica Python Django


Llenguatges dinàmics


Escrit per Aaloy a 25 de May , 2008 a les 11:18 a.m.

Link to Llenguatges dinàmics

Al blog de Ricardo he esta llegint l'apunt anomenat "Lenguajes dinámicos, programadores y FUD" així com els seus comentaris. Tot i que hi he deixat allà un comentari amb la meva opinió, crec que com a programador en llenguatges dinàmics i tradicionals he de dir la meva.

El primer de tot que voldria fer és evitar el FUD damunt els llenguatges dinàmics, per una raó molt senzilla: la realitat és molt cruel i estam cansats de veure i de llegir damunt projectes web realitzats en Python, Ruby o PHP que estan triomfant i que no desapareixen o exploten per mor de no tenir un llenguatge compilat al darrera. Per tant, la primera cosa que hem de tenir clara és que la teoria pot estar molt bé, però la realitat ens està dient dia a dia que és possible escriure i mantenir programes en llenguatges dinàmics. A un li poden agradar més o menys aquests tipus de llenguatges, però el que no es pot fer és negar la realitat.

També m'agradaria focalitzar l'àmbit de discussió: estam parlant de programes web. Per programes d'escriptori no tenc dades abastament, potser perquè els llenguatges dinàmics brillen en el desenvolupament web més que en la part d'escriptori, llevat de que considerem el Visual Basic com a llenguatge dinàmic, clar.

La cosa està en que amb les màquines actuals els llenguatges dinàmics son ja prou ràpids com per ser competitius amb els llenguates compilats. Això no vol dir que siguin més ràpids, sinó que són prou ràpids per fer la feina i que la diferència de velocitat que hi hagi no sigui rellevant. Que PHP ens serveixi una plana en 1 segon i en Java la serveixi en 0,98 segons, doncs no és rellevant ja que per l'usuari de l'aplicació no significarà cap diferència en l'espera.

Això vol dir que ens hem de passar ja a programar en un llenguatge dinàmic i deixar els compilats? No. Vol dir que és necessari que a més d'un llenguatge tradicional convé que tinguem al caixò de les eines un llenguatge dinàmic. D'aquesta manera quan ens demanin una aplicació podrem sospesar els pros i els contres i fer-la en el llenguatge que s'adapti millor al projecte. Fins i tot si el nostre projecte es farà en un llenguatge tradicional, tenir un llenguatge dinàmic se pot integrar al projecte en forma de rutines de generació de codi, scripts de testeig, etc.

Dit això, i considerant l'àmbit de les aplicacions web, anem a veure avantatges i inconvenients d'elegir un llenguatge dinàmic per al nostre projecte, com que el que més conec és el Python, em permetreu la llibertat de donar els exemples i les referències en aquest llenguatge, in en Java en el cas de llenguatge compilat, en el ben entès que segur que hi ha el mateix tipus de solucions en Ruby, PHP o el vostre llenguatge dinàmic preferit i en C, C++ o .Net, etc.

Cicle de desenvolupament Agafem una aplicació web típica Java desplegada a un servidor Tomcat i la mateixa aplicació feta en Python. El cicle al primer cas és: escriure codi, compilar, corregir els errors de sintaxi, desplegar-ho al servidor, provar i iniciar novament tot el cicle, bé per escriure nou codi o bé per corregir els errors. A l'aplicació Python+Django tendríem: escriure codi, provar i tornar a escriure el nou codi. L'augment de productivitat la tenim no per el fet de no tenir que corregir errors, sinó pel fet de no tenir temps d'espera en el desplegament i per haver d'escriure menys línies de codi.

Errors de sintaxi. Aquí algun haurà pensat, "s'ha deixat els errors de sintaxi", efectivament, ho he fet perquè vull dedicar-lis el seu propi apartat. El compilador caça els errors de sintaxi i ens n'informa, però això vol dir que hem d'executar el compilador, és veritat que això els IDEs com Eclipse ho fan automàticament, però tot i això s'ha de fer. Realment podem fer el mateix amb eines com Pylint o Pychecker i a més el primer a més és capaç de treure tot un conjunt d'estadístiques damunt la qualitat del codi, talment com ho fan eines Java com PMD. El pylint per exemple és el que fa servir PyDev per a indicar els errors de sintaxi i donar avisos damunt el codi. Està clar que aquests errors potser no seran tan acurats como els dels compiladors, però són prou bons com per fer la feina, amb l'avantatge, però que ho podem llançar quan nosaltres vulguem i que no s'ha de passar necessàriament pel compilador per a executar el programa.

Per una altra banda, que el compilador o pylint ens caci els errors de sintaxi ens pot donar una falsa sensació de seguretat, el errors de sintaxis poden fer petar l'aplicació, és veritat, però els errors més greus solen ser els errors en la lògica de negoci de l'aplicació. Quan aquests se detecten el que s'ha de fer és corregir-los el més aviat possible, i en aplicacions web entram en

La fase de desplegament El temps que necessitam per posar una aplicació en producció seria anecdòtic si les aplicacions no tinguessin errors. Necessitar 30 minuts o 10 minuts per una aplicació que es posi en producció i que estigui mesos o anys sense modificar-se no és significatiu. Però, si l'aplicació sofrirà canvis o els errors que es detectin s'han de resoldre el més aviat possible, una diferència de 30 minuts en el desplegament pot marcar la diferència. En el cas d'un error en la lògica de l'aplicació que afecti a un .jar, significa en el millor dels casos compilar l'aplicació o el jar corresponent, substitur-ho i reiniciar el servidor d'aplicacions o l'aplicació, cosa que pot dur entre uns 30 segons y un bon grapat de minuts, depèn del que hi hagi desplegat. El el cas d'un llenguatge dinàmic sovint basta actualitzar els arixus afectats (svn update per exemple) i fer un reload del servidor Apache que tenguem. Cosa d'un parell de segons tot plegat. Està clar que podem minimitzar el primer cas amb servidors redundants i configuracions d'alta disponibilitat, però a un cost major de complexitat.

L'orientació a la tasca Les aplicacions web tracten fonamentalment amb text i produeixen text. Agafam dades de formularis, les tractam, obtenim dades de les bases de dades i les manipulam per a presentar-les a l'usuari en una sortida HTML, etc. Tractar cadenes és part fonamental de les aplicacions, i això és una cosa que els llenguatges compilats fan en general força malament des del punt de vista del nombre de línies de codi que s'han de picar. Els llenguatges dinàmics són força bons tractant informació textual. Posaré un exemple que ens pot servir per il·lustrar aquest fet: les plantilles. En Java tenim tres llenguatges de plantilles principalment: JSP-EL (ja ho sé està agafat pels pels però acceptau-me'l), Freemaker i el venerable Velocity i en general són força infumables. Comparem-ho amb el que hi ha per Python i veurem la diferència (Django, Web String, Cheetath, Mako, Jinja, ...). No crec que sigui anecdòtic, la diferència és que és força senzill manipular text en Python i per tant és força senzill crear-te el teu propi llenguatge de plantilles.

Productivitat El nombre de línies de codi que escriu un programador al dia és una constant. Això vol dir que si volem tenir més productivitat s'ha d'anar cap a llenguatges que ens permetin escriure més funcionalitat en menys codi. Donat el boom que viuen actualment els llenguatges dinàmics encara no hi ha molta literatura al respecte in encara no hi ha ni Ruby, ni PHP ni Python al QSM, però sí que hi ha forces comparatives, una de les més interessants és la d'Stephen Ferg que senyala que programar en Python és de 5 a 10 vegades més productiu que fer-ho en Java.

Diversió i motivació Els llenguatges dinàmics són sexy. Permeten al programador un alt grau de realimentació. Pot provar les seves idees molt ràpidament i això el motiva molt més que tenir que esperar a que el compilador acabi per poder fer les proves.

Pel demés, els llenguatges dinàmics també necessiten unit tests (sols que es poden escriure més ràpidament i amb menys línies), una gestió acurada dels projectes, planificació del que volem fer i refactorització del codi.

La conclusió de tot això és que deixem de banda FUDs que no duen a res i a l'hora d'avaluar un projecte considerem també si és apte per a ser fet en un llenguatge dinàmic, si ho és endavant, el nostre cul no perilla per dita elecció, ja que tant Python, com Ruby com PHP seran prou capaços de fer la feina.


Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django


vim ide per python


Escrit per Aaloy a 17 de May , 2008 a les 9:22 a.m.

Aquesta setmana i gràcies a l'entrada del blog de sontek he retornat al vi com a editor principal per a la programació en Python.

Periòdicament estic canviant entre vim, kate o Eclipse amb PyDev, segons la màquina en que faig feina i el que estic fent, però pas gran part del temps fent feina amb la consola i trobar la configuració que permet tenir el millor de els entorns gràfics a vim m'ha sorprès gratament.

Amb la configuració i el conjunt de plugins que ha seleccionat sontek tenim un editor que permet tabs, autocompletat, plantilles, integració amb subversion i tota la potència d'edició de vim. El tema del depurador integrat està un poc més verd, però tampoc l'he trobat massa a faltar.

A un dels comentaris hi havia la recomanació per a la instal·lació a més de NERDTree un navegador de fitxers integrat al vim que facilita molt la vida, i també ho vaig posar. També he adaptat les plantilles que venen per Django per a que agafi la sintaxi del trunk i he afegit algun retoc més com que posi la codificació als fitxers i coses així, les plantilles són tan bones de modificar que me pareix que puc acabar amb un bon repositori de codi :)

Tot i que no poseu la configuració crec que és interessant fer-hi una ullada a com ha organitzat Sontek (John M. Anderson) el seu fitxer de configuració per veure un exemple del que es pot fer amb vim i de com es pot incloure codi Python dins l'arxiu de configuració. Una de les maneres d'aprendre a programar és llegir codi d'altres (bon codi si és possible) i en l'arxiu de configuració de vim podem dir el mateix: llegint configuracions d'altra gent podem arribar a personalitzar l'editor fins a límits insospitats.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Python Django


Primeres modificacions al blog nou


Escrit per Aaloy a 15 de April , 2008 a les 9:11 p.m.

Link to Primeres modificacions al blog nou

Avui he fet les primeres modificacions al Blog. Ja us deia que això de posar en producció les coses et força a trobar i corregir els errors més ràpid.

M'he trobat que els RSS no funcionaven. La idea era mantenir la compatibilitat amb els RSS de Wordpress, de tal manera que els vells subscriptors no notassin el canvi, però me vaig deixar una s i no anaven. El canvi ha esta molt senzill, però ha sigut cosa d'esperar a arribar a casa per fer les modificacions.

De pas he aprofitat per arreglar quatre etiquetes que no estaven ben posades i posar una validació als comentaris de manera que "peti" en posar un comentari buid. La part de comentaris és potser el que menys m'agrada, ja que encara fa servir les llibreries de oldforms de Django i jo ja estic molt acostumat a les noves. El canvi de oldforms a newforms serà de les primeres coses que vull fer. El que em frena un poc és la part de control de l'spam, però miraré si puc fer servir algun component per a connectar amb l'Akismet i fer-ne el backoffice per a controlar-ho.

De les coses que més m'agraden del nou programa és la possibilitat de fer servir el Markdown per a escriure els posts. Al Wordpress segurament se deu poder fer alguna cosa per l'estil, però no m'hi he volgut barallar mai. Vaig posar també javascript per a colorejar codi Python, així que ara veureu que els articles que tenguin codi quedaran un poc millor presentats.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Powered by Django


Escrit per Aaloy a 14 de April , 2008 a les 9:24 p.m.

Link to Powered by Django

Un dels propòsits del 2008 pareix que ja s'ha complert. Gràcies a Bernat avui hem canviat el blog vei en wordpress que quedarà a blog.trespams.com, més que res per si alguna cosa anàs malament mentre es fa el canvi.

El nou blog està en fase beta, però seguint els principis del la programació àgil, he preferit posar-ho en producció i anar polint els detalls que queden. Com podeu veure pel titol el blog és Django powered. Corre damunt un servidor dedicat que tenim per APSL dins un entorn chroot que en Bernat ha montat.

El codi font del blog està disponible al repositori de google code, com a una branca amb suport unicode del blogmaker, així que tothom és benvingut a col·laborar-hi i a posar-hi tickets pels errors que segurament hi trobareu.

Al codi font també hi ha l'importador de Wordpress cap al blog, de manera que veureu que els articles de l'antic lloc, comentaris i trackback estan inclosos en el nou. La importació no ha estat massa complexa i s'ha fet a partir de l'exportación en format RSS que fa el Wordpress. Tot i això hi ha petites millores a fer, com les de tractar millor els paràgrafs. Alguns articles encara no han passat per la modificació i les lletres es veuen molt atapides, fruit de la combinació de l'importado i de la fulla d'estils que he fet servir per a contruir el lloc, el multiflex.

Encara queden cosetes a polir, veig que m'he deixat el peu del "powered by django" per exemple, la part d'agraïments, etc. etc. que aniré posant durant els propers dies i setmanes. El d'avui és una beta, gairebé alfa, però com us dic, crec que l'important és perdre la por i posar-ho en producció i anar-ho millorant dia a dia.


Traducciones/Translations by apertium

6 comentaris, 0 trackbacks (URL) , Tags: Python Django


Ja tenim el compte d’Appengine


Escrit per Aaloy a 11 de April , 2008 a les 11:37 p.m.

Doncs això, fa poques hores he rebut el missatge que diu que ja puc fer coses amb l'Appengine de Google, en morenosan en va dir ahir que havia rebut també el seu, així que pareix que estan obrint el grifó bastant de pressa.

Ara es cosa d'anar pensant què es pot fer. De totes maneres no es tant l'aplicació en sí, com poder provar l'entorn i començar a veure les seves possibilitats, com s'hi fa feina, quines diferències hi ha entre poder fer l'ORM de Google i el de Django, veure les limitacions que ens imposa...

Temps al temps!


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python Django


Bons temps per Python


Escrit per Aaloy a 09 de April , 2008 a les 12:03 a.m.

La blogosfera en va plena Google ha llançat el seu appengine , un servei que permet hostejar aplicacions de fins a 500 Mb d'espai i 5 milions de visites mensuals que Google ha llançat i que té com a llenguatge vehicular el Python.

Per a accedir-hi un s'ha d'apuntar a la llista d'espera, ja que pareix que els comptes en fase beta s'han esgotat, i tot i les limitacions del sistema en el que fa referència als accessos als sistemes de fitxers, limitacions de la part de base de dades, que no es puguin llançar subprocessos i coses per l'estil, obre la possibilitat a tot un ventall d'aplicacions web.

On la notícia ha impactat més és a la comunitat Python: un llançament espectacular de Google, amb Guido pel mig, am Python com a protagonista i amb Django com a estrella convidada, ja que Django, encara que la versió "estable", ve de sèrie en el sistema.

Si encara no ho heu fet és un bon moment per aprendre Python i Django (ueps, tal volta seria un bon moment per publicitar-ne cursos :-P ) ja que un dels emperòs més grans que hi havia és que no se disposava d'un servidor a preus assequibles on poder fer anar les aplicacions. Ara amb el llançament de Google, les possibilitats de fer desenvolupaments amb Python i Django es multipliquen, limitats a les possibilitats de l'entorn que proporciona Google, sí, però permetran en breu començar a fer aplicacions web i hostetjar-les a un preu inmillorable.

Amb això esper a més que els hostingaires de sempre es posin les piles i donin a preus raonables allotjament per Django, proporcionant a més el servei que ara Google no ofereix: el de tenir una base de dades relacional pròpia al darrera.

I és que un dels grans problemes de l'oferta de Google és que no ets ben bé l'amo de la teva base de dades i algunes coses que permet fer l'ORM de Django molt fàcilment a l'ORM substitutiu de Google no es poden fer.

És clar que no totes les aplicacions necessiten d'una base de dades relacional al darrera, així que l'appengine de Google és per una part un bon banc de proves per veure com va això de la programació web amb Python i Django i per una altra una manera ràpida i econòmica de posar en producció projectes web que d'altra manera tendrien un cost prohibitiu pel programador mig.


Traducciones/Translations by apertium

2 comentaris, 3 trackbacks (URL) , Tags: Informàtica Python Django


Propietats a Python


Escrit per Aaloy a 21 de March , 2008 a les 1:41 p.m.

En Corey Goldberg al seu blog a un recull interessant d'enllaços de lectura obligada per aquella gent que està fent la transició de Java o C# cap a Python.

Un dels més interessant és l'article de Phillip J. Eby anomenat Python is not Java on recull les diferències fonamentals que hi ha entre programar en Java o programar en Python. Una de les afirmacions més xocants és potser aquesta: Getters and setters are evil. Evil, evil, I say! Python objects are not Java beans. Do not write getters and setters, és a dir "Els getter i setters són el dimoni. El Dimoni, dic. Els objects de Python no són Java beans. No escriguis getters i setters."

La frase pot semblar molt bèstia, i de fet ho és, ja que és una afirmació que s'ha de matitzar molt, com de fet ho fa en Ryan Tomayko al l'apunt Getters/Setters/Fuxors on s'explica molt bé quan fer servir aquest tipus de construccions, però en definitiva el que ens hem de quedar és amb la idea de que normalment Python no necessita mètodes accessors i que l'ús normal d'aquest és el de marcar un atribut com de sols lectura.

Com sempre hi ha "la manera de Python" de fer les coses, i sol ser la manera més senzilla de fer-les.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Informàtica Python


Integració de Hudson i Trac


Escrit per Aaloy a 15 de March , 2008 a les 11:52 a.m.

A un altre post ja vaig parlar de Hudson, un sistema d'integració contínua, relativament nou però que permet fer el que un necessita d'aquestes característiques de manera fàcil i amb una interfície molt cuidada.

Hudson es pot integrar amb Trac, de manera que al Timeline del projecte trac poguem veure com han anat les integracions sense tenir que anar al Hudson. D'aquesta manera la gent que fa el seguiment del projecte pot veure a més dels commits al subversions, modificacions al wiki i tickets, com han anat les integracions i quan s'han fet.

La integració dels dos aplicatius és força senzilla:

  • Instal·lam el python-feedparser si no ho tenim ja al nostre servidor.
  • Anam a http://trac-hacks.org/wiki/HudsonTracPlugin i descarregam el plugin.
  • Descomprimim el plugin i amb permisos d'administrador executam python setup.py install això ens crearà el paquet egg i configurarà el plugin a nivell global dins el trac.
  • Editam l'arxiu trac.ini del nostre projecte trac que volguem enllaçar amb Hudson i modificam la llista de components, en el meu cas la cosa queda com
[components]
iniadmin.iniadmin.iniadminplugin = enabled
webadmin.* = enabled
HudsonTrac.* = enabled
  • Cream també una entra a anomenada hudson allà on posarem tant la url del rss del nombre projecte hudson que volguem controlar com el de la vista del projecte, de tal manera que s'hi crei un enllaç dins Trac
[hudson] display_subprojects = false feed_url = http://servidorhudson/hudson/view/Java/rssAll main_page = http://servidorhudson/hudson/view/Java/
  • Canvia el servidorhudson pel vostre servidor. Aquí el que he fet és enllaçar directament contra la vista de projectes anomenada Java que he creat. Podria enllaçar a un projecte concret o a una vista relacionada amb el projecte que es gestiona amb el Trac.

Si s'han seguit aquestes passes i una vegada refrescada la plana del Trac, ens apareixerà una nova secció anomenada Hudson a la part de navegació del Trac que enllaça a la url que hem posat a main_page i al timeline apareixerà una opció que ens permetrà visualitzar les integracions, en forma de control check.

Pels que feu servir Trac de manera habitual, és interessant instal·lar-se també el plugin IniAdminPlugin, que ens crea un menú de configuració per web del trac.ini del nostre projecte.

És interessant a tot això adonar-se de que gràcies als formats oberts, en aquest cas al RSS que publica Hudson i que pot consumir Trac, dues aplicacions fetes en tecnologies totalment diferents es poden integrar.

Al Hudson passa una cosa semblant, està pensat per tractar amb format oberts, típicament sortides XML en format UnitTest, la qual cosa permet afegir-hi projectetes de Python, Java o SoapUI.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Java


Winpdb


Escrit per Aaloy a 27 de January , 2008 a les 11:15 a.m.

Winpdb és un depurador en mode consola i gràfic per aplicacions Python.. Fa un parell de setmanes que han alliberat una nova versió. En la seva part gràfica, el programa presenta una interfície clara i permet la depuració remota de les aplicacions.

Es pot utilitzar per depurar scripts de Python molt fàcilment sols executant el programa passant-hi com a paràmetre l'script a depurar, tal com se fa amb el depurador estàndard de Python.

La vertadera potència, però està en la facilitat en que es poden depurar aplicacions remotes o aplicacions com les que es poden fer en Django per exemple. En a quest cas afegint aquesa línia al que vulguem depurar

import rpdb2 
rpdb2.start_embedded_debugger_interactive_password('clau de seguretat')

ens permetrà connectar-hi el depurador.

Winpdb fa temps que roda, però és la primera vegada que puc fer anar la depuració remota sense problemes. Una eina més a afegir a la capça de programació.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


Què torbes a corregir un error de producció?


Escrit per Aaloy a 18 de November , 2007 a les 4:34 p.m.

Trobar un error al teu codi és un emprenyo. Poder començar la depuració en 30 segons, trobar l'error 2 minuts, pujar-ho al subversion i actualitzar la versió de producció de manera que als 5 minuts d'haver detectat l'error estigui corregit no té preu.

Això és el gran avantatge dels llenguatges d'script, que el temps que passa des de que trobes un error a poder-ho corregir és molt curt (llevat d'excepcions amb errors difícils de trobar i depurar, clar). Curt perquè normalment posar en marxa l'entorn de desenvolupament no duu més que uns pocs segons i ja pots començar a depurar.

Si tot està ben organitzat el codi estarà a un repositori subvesion i l'entorn de producció no serà sinó un client de subversion, de manera que fer una actualització una vegada trobat un error que no afecti a la base de dades, és bàsicament

  • svn ci
  • ssh al servidor
  • svn update

I en alguns casos recarregar l'Apache. Encara desenvolupament i sistemes siguin equips separats, davant un error crític el temps de resposta pot ser tan curt com 5 minuts.

L'experiència amb Java és que ja directament és necessiten els 5 minuts sols per posar en marxa l'entorn, 5 o 10 minuts més per depurar i si hi ha sort i sols era un error de jsp 2 mintus més actualizar i forçar la compilació del jsp per a que el proper usuari no vegi en enlentiment en la pàgina. Normalment més del doble per a corregir el mateix tipus d'error.

Si la correcció de l'error implica canviar codi que no sigui jsp o html les diferències són encara més grans i sovint pot implicar tenir que reiniciar el servidor, la qual cosa ens obligarà a tenir sempre dos servidors balancejats si no volem tenir als usuaris aturats durant 5 minuts. L'Apache es recarrega en segons, i tot i que sempre és bo tenir-ho tot duplicat i balancejat, la necessitat no es tan forta com en el cas anterior.

Personalment m'agrada molt Java i les llibreries que s'han desenvolupat en aquest llenguatge, però si el nostre negoci depèn del ràpid que puguem actualitzar la nostra aplicació web hi ha entorns i llenguatges amb més avantatges que Java o .Net.


Traducciones/Translations by apertium

4 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Java


Ordre a la llista!


Escrit per Aaloy a 23 de September , 2007 a les 7:18 p.m.

Una de les accions més repetitives que sovint feim quan passam cap a la capa de presentació és la d'ordenar els elements que volem que es presentin. Potser algú dirà que els elements ja poden venir ordenats de la consulta a la base de dades, però què passa si en lloc d'atacar a una base de dades directament obtenim el que s'ha de mostrar d'un servei web? o ja hem fet un tractament de les dades i ara les volem ordenar per un altra camp. Poder fer ordenacions de manera senzilla i ràpida ens soluciona molts maldecaps.

Anem a veure com Python ens permet fer ordenacions de llistes de pràcticament qualsevol cosa. Python fa servir el mètode sort per a ordenar una llista. L'exemple més senzill seria l'ordenació d'una llista d'enters

  $ llista = [2,4,5,6,7,6,7]
  $ llista.sort()
  $ llista
  [2, 4, 5, 6, 6, 7, 7]

Sí, és tan senzill com pareix, i fins i tot podem fer

  $ llista.reverse()
  $ llista
  [7, 7, 6, 6, 5, 4, 2]

Però clar, una llista a Python pot contenir qualsevol cosa, no sols sencers, anem a veure què passa si passam una llista de parells (tuples)

  $ llista = [(1,5),(1,2),(2,2),(3,5),(4,8),(1,9)]
  $ llista.sort()
  $ llista
  [(1, 2), (1, 5), (1, 9), (2, 2), (3, 5), (4, 8)]

Suposem però que jo el que vull és que s'ordeni pel segon element de la tupla, aquí si un prové d'altres llenguatges de programació ja se pot esperar tenir que escriure un bon munt de codi, però no en Python, l'ordenació es fa per una clau i podem definir quina és aquesta clau passant-li al mètode sort una funció construida de manera que prengui un sol element i ens retorni la clau a comparar.

Per fer el que volem farem servir el mòdul operator, i dins aquest la funció itemgetter, aquesta funció ens retorna una altra funció que aplicada damunt una llista o tupla ens donarà l'element especificat que haguem definit, així per exemple

  $ segon=itemgetter(1)
  $ segon(llista)
  (2, 2)
  $ llista
  [(1, 2), (2, 2), (1, 5), (3, 5), (4, 8), (1, 9)]
  $ segon(llista)
  (2, 2)
  $ segon((1,5))
  5

Farem servir aquesta funció per a obtenir la clau per la qual volem ordenar la nostra llista, així

  $ llista.sort(key=segon)
  $ llista
  [(1, 2), (2, 2), (1, 5), (3, 5), (4, 8), (1, 9)]

Pensem en les implicacions que té això quan volem omplir un select d'html, encara que la llista ens hagi arribada ordenada per codi, podem fàcilment canviar l'ordenació al texte sols amb aquesta instrucció. Un altra paràmetre que ens serà de molta utilitat a l'hora de fer ordenacions és el cmp, aquest ens permet passar una funció que donats dos arguments haurà de retornar un nombre positiu per indicar que el primer és major que el segon, zero per indicar que els elements són igual o negatiu per indicar que el segon és major que el primer.

Per exemple, suposem que el que volem fer és ordenar la nostra llista segons el que sumen els seus components.

   $ t=llista[:] #Farem primer una còpia de la llista original
   $ def ordena(x,y):
         p1 = x[0]+x[1]
         p2 = y[0]+y[1]
     return p1-p2
   $ t.sort(cmp=ordena)
   $ t
   [(1, 2), (2, 2), (1, 5), (3, 5), (1, 9), (4, 8)]
   $ llista
   [(1, 2), (2, 2), (1, 5), (3, 5), (4, 8), (1, 9)]

O també poden fer un codi més florit i escriure

  $ t = llista[:]
  $ t
  [(1, 2), (2, 2), (1, 5), (3, 5), (4, 8), (1, 9)]
  $ t.sort(lambda x,y: x[0]+x[1]-y[0]-y[1])
  $ t
  [(1, 2), (2, 2), (1, 5), (3, 5), (1, 9), (4, 8)]

Si algú ha tingut la paciència d'arribar fins aquí, un momentet, que ara ve el més interessant. Què passa quan en lloc de llistes de nombres tenim llistes d'objectes? Doncs res, podem fer servir el paràmetre cmp o el paràmetre key segons ens vagi millor per fer l'ordenació. Anem a veure tres maneres de fer el mateix. Primer definirem la nostra llista d'objectes

   class Persona:
       def __init__(self, nom, edat):
           self.nom = nom
           self.edat = edat
       def __cmp__(self, altri):
           return cmp(self.edat,altri.edat)

Aquí el que he defint és una funció cmp dins la classe, que és la que faríem servir per defecte a l'hora d'ordenar una llista d'objectes d'aquest tipus, així:

    $ agenda = [Persona('Benjamí', 31), Persona('Pau', 22),  
      Persona('Juan',34), Persona('Ricardo',38), 
      Persona('Guillem',28), 
      Persona('Bernat',58)]
    $ agenda.sort()
    $ for amic in agenda:
    $   print "%25s \t %i" % (amic.nom, amic.edat)
            Pau            22
            Guillem        28
            Benjamí        31
            Juan           34
            Ricardo        38
            Bernat        58

Ara suposem però que volem ordenar la llista per nom. Una opció seria refer el mètode cmp, però tenim altres opcions. La primera és encriure una nova funció de comparació:

  $ def compara_nom(amic1, amic2):
  $    return cmp(amic1.nom,amic2.nom)
  $ agenda.sort(compara_nom)
  $ for amic in agenda:
  $     print "%25s \t %i" % (amic.nom, amic.edat)
                  Benjamí        31
                   Bernat        58
                  Guillem        28
                     Juan        34
                      Pau        22
                  Ricardo        38

O bé, si ens agrada més l'opció lambda

 $ agenda.sort(lambda amic1, amic2: cmp(amic1.nom,amic2.nom))

Però encara tenim una altra maner, deixant que Python faci la feina per nosaltres, hem d'indicar la clau d'ordenació i fer que les eines de comparació del llenguatge facin la seva via. El problema però està en com dir-li quina clau fer servir, això s'aconsegueix amb attrgetter de la llibreria operator.

 $ agenda.sort(key=attrgetter('nom'))
 $ for amic in agenda:
 $     print "%25s \t %i" % (amic.nom, amic.edat)

Fixau-vos el senzill que seria poder fer una ordenació per qualsevol camp de la classe.

Referències:


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Python


Creant objectes a la manera de Python.


Escrit per Aaloy a 09 de September , 2007 a les 10:33 p.m.

He estat mirant coses damunt patrons de disseny aplicables al model d'interfície gruixuda d'usuari, aplicables a la programació amb wxPython, arribant a tres patrons:
  • El patró MVC (Model view controller) de sobres conegut per la gent que es dedica al desenvolupament web fonamentalment.
  • El patró MVP (Model view presenter) , que intenta fer que la part d'interacció sigui més testejable y que Flower ha considerat que s'havia de xapar en dos.
  • El patró Presentació Model, que independitza la capa de presentació de la vista, en un intent de separar el que és la part de comportament i estat de la vista en un model que és part de la presentació però que no es específic d'una implementació d'interfície concreta.
Llegint el codi he arribat una manera de construir objectes a partir de llistes que no havia fet servir fins ara. Això s'explica millor amb un exemple: class Agenda: ... def __init__(self, nom, llinatge, telefon, amic=True): ... self.nom = nom ... self.llinatge = llinatge ... self.telefon = telefon ... self.amic = amic ... La manera habitual de crear un objecte de tipus Agenda seria per exemple amic = Agenda('toni','aloy','971xxxxxxx') Suposem ara que tenim la informació dins una llista o una tupla, bastant habitual si per exemple hem importat les dades d'un arxiu de text o des de una base de dades, aleshores podem tenir la informació com un_amic =('Pau','Rul·lan','971xxxxxxx') La creació de l'objecte és molt ràpida d'escriure amic2 = Agenda(*un_amic) amic2.nom 'Pau' És a dir, s'han substituït els paràmetres de construcció de l'objecte pels valors de la llista. Aquesta substitució és posicional, és a dir, el primer valor correspon al primer paràmetre, el segon al segon, etc. La sintaxi és conseqüent amb la manera d'anomenar llistes de paràmetres en la construcció de funcions, per exemple: def prova (x, *y): ... print x ... for item in y: ... print item ... prova(2,3,4,5,6,7) 2 3 4 5 6 7 prova (2,7,10) 2 7 10 Però clar, si això funciona d'aquesta manera i estam parlant de Python i haurà una manera obvia de fer el mateix si en lloc d'una llista tenim un diccionari, és a dir, ara tenim: un_amic ={'nom':'Benjamí','llinatge':'Villoslada','telefon':'971xxxxxxx'} Les claus del diccionari coincideixen amb els noms dels paràmetres, i hauríem de poder fer amic3 = Agenda(**un_amic) Això és Python, i per tant amic3.nom 'Benjam\xc3\xad'

Traducciones/Translations by apertium

3 comentaris, 0 trackbacks (URL) , Tags: Python


Trac i clearsilver


Escrit per Aaloy a 20 de August , 2007 a les 9:07 p.m.

El clearsilver és una llibreria de plantilles que abans feia servir el Trac i que s'està substituint a favor de Genshi, una altra llibreria que segons la gent de Trac els causa molt menys problemes.

La cosa està, però, en que no es lleven les dependències fins a la versió 0.11 no s'eliminen les dependències de clearsilver i fins i tot en aquesta versió es mantindran les estructures necessàries per a que els plugins segueixin funcionant.

He pogut comprovar de primera mà el que es diu a la web de Trac, "clearsilver sucks", ja que amb Ubuntu i la versió 2.5 de Python no fa sino donar problemes, que sospit han acabat per tomar el servidor Apache 2 que tenim al webhosting virtual. En un primer moment he pensat que se'ns hauria tornat a oblidar el pagament, però no, tots els serveis eren vius llevat de l'Apache i als logs he vist un bon munt d'errors del tipus

   /usr/lib/python2.5/site-packages/trac/web/clearsilver.py:128: RuntimeWarning:

Python C API version mismatch for module neo_cgi: This Python has API version 1013, module neo_cgi has version 1012.

No ha costat molt trobar referències tant al Trac com a Ubuntu d'aquest error i de com solucionar-ho. Aquesta és la recepta que m'ha servit a mi:
  • Davallam el codi font: wget http://www.clearsilver.net/downloads/clearsilver-0.10.4.tar.gz
  • Modificam els arxius config i config.in, cercam les línees que diuen python_version i i afegint la versió 2.5 al davant.
  • Descomprimim el fitxer tar xvzf clearsilver-0.10.4.tar
  • Davallam paquets adicionals però aquesta vegada per apt-get
    • sudo apt-get install zlib1g-dev
    • sudo apt-get install autoconf
    • sudo apt-get install python-dev
  • Feim el configure: ./configure --disable-wdb --disable-compression --disable-perl --with-python=/usr/bin/python2.5
  • I per acabar el sudo make install
  • Com que la instal·lació no acaba d'anar fina convé copiar la llibreria generada sudo cp -i neo_cgi.so /usr/lib/python2.5/site-packages/neo_cgi.so per acabar de substituir la referència que hi havia de la llibreria anterior o fer un ellaç simbòlic cap a la nova instal·lació de clearsilver: sudo ln -s ./clearsilver-0.10.4-py2.5-linux-i686.egg/neo_cgi.so neo_cgi.so
Reiniciam l'apache i ja no hauríem de tenir més errors d'aquest tipus.

Referències:


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Gestió de projectes Informàtica Python


Creant un tag per Django


Escrit per Aaloy a 14 de July , 2007 a les 9:33 p.m.

Una de les coses que fa Django potent com a bastiment és la seva extensibilitat, és força senzill extendre el llenguatge de plantilles amb nous filtres i nous tags.

Avui tenia la necessitat de generar un nombre aleatori, semblant a l'exemple d'Ajax d'un post anterior, pero aquesta vegada volia que no fos necessari cridar a cap vista.

Això seria molt senzill de fer passant el valor com a paràmetre, però pot servir com un exemple més de com crear els nostres propis tags.

Crear un tag en Django requereix de dues passes: crear la funció de compilació i crear la classe que renderitzarà el trag mostrant el text que volem.

Una vegada fet això basta registrar el tag i per això el mateix Django ja ens facilita un decorador.

La randomCode és la que agafa el tag i els paràmetres del tag i en far el parseig. En aquest punt hem de caçar els errors de sintaxi del tag i informar a l'usuari de com es fa server.

La classe randomCodeNode agafa el valor que li passa la funció anterior i fa les operacions necessàries per a tonar-nos una cadena de text presentable en una plana web.

L'esquelet ens pot servir com a base de tags molt més complexos, per exemple tags que incloguin automàticament llibreries javascript , o que facin més senzilla la seva utilització, ...

Per fer servir aquest tag n'haurem de carregar l'arxiu dins la nostra plantilla, com en el cas del filtres això se fa amb un {% load arx %} on arx és el nom de l'arxiu que conté el tag que acabam de crear.

   from django import template
   import string
   import random
   CODE_CHARS = string.ascii_uppercase+string.digitsregister = template.Library()

@register.tag(name="randomCode")

def randomCode(parser, token): try: # split_contents() knows not to split quoted strings. tag_name, num = token.split_contents() except ValueError: raise template.TemplateSyntaxError, "%r tag requires an integer as a single argument" % token.contents.split()[0] if not (num[0] == num[-1] and num[0] in ('"', "'")): raise template.TemplateSyntaxError, "%r tag's argument should be in quotes and be a positive integer" % tag_name try: value = int(num[1:-1]) if value <0: raise template.TemplateSyntaxError, "%r tag's argument must be a positive integer" except: raise template.TemplateSyntaxError, "%r tag's argument must be a positive integer" % tag_name return randomCodeNode(value)

class randomCodeNode(template.Node): def init(self, num): self.num = num def render(self, context): try: codi = random.sample(CODE_CHARS,self.num) except ValueError, e: raise template.TemplateSyntaxError, "tag's argument must be a lower than %i" % len(CODE_CHARS) s = '' return s.join(codi)

El Wordpress no és gaire bo amb el codi, podeu trobar-ho millor colorejat a Djangosnippets


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python Django


Depurar una aplicació Django


Escrit per Aaloy a 23 de January , 2007 a les 1:27 a.m.

De tant en tant faig algun apunt per tenir una referència posterior, a manera de documentació per tal de no oblidar-me de com se fa una cosa, d'una referència o procediment. Avui és un d'aquests apunts.

Com he comentat en altres apunts darrerament estic fent molta cosa amb Python i Django com a bastiment MVT web. En un món ideal el meu G5 funcionaria de conya amb l'Eclipse i tendria un depurador de Python integrat a l'IDE, però com que no tot pot ser tan meravellós com la combinació de Python i Django, doncs vaig tenir que cercar alternatives. Una prou bona és el Kdevelop, així que al G5 per programar utilitz les següents eines:

  • Kdevelop com a Editor principal. Ben bé es podria fer amb Kate, però m'agrada la funcionalitat extra que aporta.
  • Vim, no pot faltar mai!
  • svn per interactuar amb el repositori subversion. També feia server kdesvn però al final m'és més còmode la línea de comandaments.
  • kdiff3. Que la comparació visual està molt bé, tú!
  • Firefox amb les extensions de webdeveloper tools i firebug
  • Eclipse. Quan la gestió dels canvis se me complica i puc mantenir-ho estable el temps suficient per fer els canvis.

El problema d'això és si no es fa servir l'Eclipse no disposam de depurador integrat a l'IDE. [1]. Això per mi és un problema, menor, sí, perquè Python és prou net i elegant i la informació de depuració de Django prou extensa com per anyorar-ho poc, però què voleu, m'agrada al manco tenir l'opció de poder seguir la traça d'un programa sense tenir que tirar de logs i prints. [2].

Doncs mirant un poc pels fòrums de Django i un poc per Google, he arribat a la següent recepta:

  1. Executam el servidor de Django amb l'opció de noreload: python manage.py --noreload runserver
  2. Al fitxer el codi del qual volem depurar feim from settings import DEBUG if DEBUG: import pdb
  3. L'important aquí és l'import, l'altre codi sols és per assegurar-nos que si ens deixam el codi de depuració en producció "petarà".

  4. Per acabar al lloc on voguem posar el punt de ruptura escriurem: pdb.set_trace()

Això fa que en arribar a al punt hom hem establer la traça, la consola del servidor de Django ens mostri el símbol de depuració.

-> user = request.user
(Pdb)

A partir d'aquí ja hem entrat en mode de depuració i basta llegir-se un poc la documentació del depurador integrat de Python per anar tirant. Recordem que no estam sols davant d'un simple depurador, sinó que tenim tota la potència de Python al depurador mateix. Un tutorial força senzill per començar és Debugging in Python.

Una vegada acabeu de depurar hem de recordar llevar o comentar el pbd.set_trace(). Possiblement hi haurà maneres més potents de fer això, com la depuració remota, però aquesta és prou senzilla de fer anar. [1] Eric en duu un de depurador integrat, però darrerament no hi ha manera de posar-hi accents a l'editor i no es cosa sols del G5, així que l'he descartat de moment com a IDE.

[2] He vist gent no fer servir el depurador integrat de l'Eclipse programant en Java, però me'n reservaré l'opinió.


Traducciones/Translations by apertium

2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django


wxPython in Action


Escrit per Aaloy a 23 de May , 2006 a les 12:59 a.m.

Per la llista de Python, concretament a pun apunt del Dr. Dobb's Python-URL! m'he assabentat de l'existència del llibre wxPython in Action i de la possibilitat de fullejar un dels seus capítols a la web pythontrheads.

Això són bones notícies, ja que vol dir que a partir d'ara tindrem més documentació d'aquesta excel·lent llibreria gràfica. Pels qui no la coneguin wxPython és un embolcall (binding) per Python de les llibreries wxWidgets, abans conegudes com wxWindows. Aquestes llibreries són portables, potents i no gaire pesades. L'emperò és que la seva gestió d'events no és tan neta com el mètode signal/slot de les Qt.

Com a avantatge tenim els nombrosos exemples que hi ha per fer gairebé de tot el que se'ns acudi en termes d'interfície gràfica i dissenyadors d'interfícies d'usuari com les wxGlade, o un IDE com el Boa Constructor .

Les wxPython són una opció a tenir en compte si ens plantejam fer una interfície d'usuari que sigui lleugera i portable, i m'atreviré a afegir dues característiques més: mantenible gràcies a la claretat i senzillesa de Python i divertida de programar.


Traducciones/Translations by apertium

1 comentari, 0 trackbacks (URL) , Tags: Informàtica Python


Un Hibernate per Python.


Escrit per Aaloy a 23 de May , 2005 a les 11:16 p.m.

Fa estona que tenc ganes de fer una aplicació de gestió amb Python, així com a prova de concepte i per passar-m'ho bé programant.

Una de les coses que més m'aturen és el tema de la persistència. Ja sabeu, al final les aplicacions de gestió han de volcar les seves dades a una base de dades i això fa que es tengui que escriure i mantenir força codi SQL.

Darrerament he estat llegint molt damunt Java i hi estic començant a fer feina professionalment. Quan estàvem montant l'arquitectura que faríem servir per les aplicacions també va sorgir el problema de la persistència. Llavors una de les opcions que més m'agradaren per la seva potència i simplicitat va ser l'Hibernate.. Aquest bastiment de persistència m'agrada molt i fa que una de les tasques més propenses a errors i més tocacollons com és el d'escriure i mantenir codi SQL estigui molt més controlada.

Per Python estava cercant quelcom semblant i no l'acabava de trobar. Avui se m'ha acudit cercar servidors web per Python i he anat a topar amb SunkWeb. Pel que he estat llegint fins ara pot ser la resposta, cap dels bastiments de persistència que havia vist per Python tenen la bona pinta que té aquest. Si és tan potent com pareix aviat podré tenir alguna cosa feta.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python


The Daily Python-URL


Escrit per Aaloy a 30 de August , 2004 a les 2:50 p.m.

Pels qui encara no el conegueu i us agrada el llenguatge Python trobareu força interessant aquesta pàgina .

En ella es fa un resum de les notícies i novetats més interessants relacionades amb el món de Python. Molt recomanable si no us voleu perdre res.


Traducciones/Translations by apertium

0 comentaris, 0 trackbacks (URL) , Tags: Python