Hemen zaude: Hasiera Blogak Periferiatik, erral python

Periferiatik, erral

python

Erantzunen RSSa daukagu orain

Blog produktu gisa Quills erabiltzeak, Plone eta bere inguruan sortutako zope3keria guztiak bezala, lan errepikakorrak ekiditea ekarri digu. Orain era erraz baten, nahi dugun edozeren RSSak ditugu. Hori ez dela zaila esango du batek baino gehiagok eta arrazoia du. Baina RSS horiek, RSS, ATOM, RDF edo dena delakoaren plantila behin eta berriz kopi-pasteatu gabe lortzen ditugu.

DISCLAIMER: gaurkoa astuna izan daiteke programatzailea ez bazara. Zoperi buruz ezer ez badakizu, agian zerbait txineraren antzekoa dela usteko duzu. Python ez badakizu, ikasi egin behar zenuke, ikusi ze erraza den Python idaztea.

Denaren erroan Zoperen Component Architechture delakoa dago. Honen oinarria esaldi bakarrean labur daiteke: idatzi osagai txikiak baina bere funtzioa oso definituta dutenak, horrela berrerabilgarriak izango baitira. Teoria erraza da, eta praktika ere bai, eta adibide batekin azaltzen saiatuko naiz.

Guk badakigu nolakoa izan behar duen RSS edo ATOM formatua duen jario batek. Estandarrak dira eta Interneten aurki ditzakegu euren espezifikazioak. Badakigu zein eremu diren derrigorrezkoak eta zein hautazkoak. Hori jakinda kontratu bat sinatu dezakegu: zuk izenburu bat, webgunearen URLa eta jarioan erakutsi beharreko elementuak ematen badizkidazu, nik horrekin RSS edo ATOM jario bat emango dizut. Honek tranpa bat ere badu barnean, jarioan erakutsi beharreko elementuak ere eman behar baitizkidazu. Jarri diezaiogun beste balditza bat kontratuari: elementu horietako bakoitzak, izan ditzala bere izenburua, deskribapena, testu osoa, URLa, egilea, data eta etiketak edo kategoriak emango dituzten metodo batzuk. Kontratu hori onartzen dugunez, idatz dezagun kontratu hori programazio lengoaia baten (Pythonen gure kasuan), interfaze batzuen bidez:

# Hau interfaces.py fitxategian gordeko dugu
from zope.interface import Interface

class IFeed(Interface):
    """ Hau izango da gure jarioaren kontratua """

    def getBaseURL():
        """ Webgunearen URLa emango du metodo honek """

    def title():
        """ Gure jarioaren izenburua emango du metodo honek """

    def getFeedEntries():
        """ Metodo honek jarioan erakutsi beharreko elementuak emango dizkigu """


class IFeedEntry(Interface):
    """ Hau izango da jarioan erakutsiko diren elementuek bete beharko duten kontratua """

    def title():
        """ Izenburua """
   
    def description():
        """ Deskribapena """

    def body():
        """ Testu osoa """

    def getURL():
        """ Elementuaren URLa """

    def author():
        """ Egilea """

    def date():
        """ Data """

    def tags():
        """ Etiketa edo kategoriak. Zerrenda bat itzuliko du honek """

Kontratu hori izanda RSS edo ATOM plantilla idaztea zuen esku uzten dut, baina espezifikazioari begiratzea besterik ez dago hori egiteko.

Kontratu hori baldin badaukagu, bete besterik ez dugu egin behar. Gure jarioa eta jarioan erakutsi beharreko elementuak zein diren jakin beharko dugu. Gure kasuan, jarioan erakutsi beharreko elementuak erantzunak dira, baina hementxe topatzen dugu lehen arazoa: erantzunak errepresentatzeko Ploneren objektuek ez dute IFeedEntry kontratua betetzen.
Orduan kontratua betearazi beharko diegu euren kodea aldatuz. Baina horrek arazo nabarmen bat azaleratuko digu: zer gertatuko da beste arrazoiren bategatik erantzunek beste kontratu bat bete behar badute? erantzun objektuei behin eta berriz kodea gehitzen ibili behar gara? Erantzuna ezezkoa da. Gure erantzunak kontratua betetzeko disfrazatu egin beharko ditugu, eraldatu edo adaptatu egin beharko ditugu.

Zope munduan, adapter edo eraldatzaileak, objektu bat hartu eta kontratu bat betetzeko aldaketak egiten dituzten osagaiak dira. Konbentzioz, klasearen context aldagaian gordetzen da objektua, eta bere jatorrizko metodoak erabiltzen dira kontratua betetzeko behar diren metodoak sortzeko. Ikus dezagun adibide bat erantzunekin:

# Hau syndication.py fitxategian gordeko dugu
from zope.interface import implements
from interfaces import IFeedEntry

class DiscussionFeedEntry:
    """ Objektu arrunt bat sortuko dugu, IFeedEntry interfazea edo kontratua
        betetzen duena, hori esateko da hurrengo implements sententzia """
    implements(IFeedEntry)
   
    def __init__(self, context):
        """ Hauxe da metodo hasieratzailea, adapterra sortzerakoan
            metodo honi deitzen zaio objektua context aldagaian pasatuz
            eta konbentzioz context aldagaian gordetzen dugu """
        self.context = context

    def title(self):
        return self.context.pretty_title_or_id()
   
    def description(self):
        return self.context.Description()

    def body(self):
        return self.context.CookedBody()

    def getURL(self):
        return self.context.absolute_url()

    def author(self):
        return self.context.Creator()

    def date(self):
        return self.context.created()

    def tags(self):
        # Erantzunek ez dute tagik edo etiketarik gure kasuan
        return []

Egin dugu gure erantzunek kontratua betetzeko lana. Kontratu hori bete dugula esan behar diogu Zoperi orain, hori ZCML deritzon XMLren dialekto baten egiten da, honela:

<adapter
     for="Products.CMFDefault.interfaces.IDiscussionItem"
     provides=".interfaces.IFeedEntry"
     factory=".syndication.DiscussionFeedEntry"
     />

Konfigurazio fitxategi horrekin, IDiscussionItem kontratua betetzen duten elementu guztiei (hori da erantzun elementuek betetzen duten kontratua), IFeedEntry kontratua betearazten dien eraldatzailea erregistratzen dugu, DiscussionFeedEntry klaseari esker.

Ez zarete galdu, ezta? Gauza bera egin beharko dugu IFeed kontratuarekin, kasu honetan blogeko erantzunen RSSa erakutsi nahi dugunez, blogarentzako eraldatzailea idatzi beharko dugu. Honela:

# hau ere syndication.py fitxategian idatziko dugu
from zope.interface import implements
from interfaces import IFeed, IFeedEntry

class WeblogRepliesFeed:
    def __init__(self, context):
        """ Lehen bezala, metodo hasieratzailea """
        self.context = context

    def getBaseURL(self):
        return self.context.absolute_url()

    def title(self):
        return '%s - %s' % (self.context.Title(), 'Erantzunak')

    def getFeedEntries(self):
        """ Katalogoari eskatuko dizkiogu blog honetako erantzun guztiak
            eta ondoren IFeedEntry bihurtuta bidaliko ditugu, hori baita
            IFeed kontratuak eskatzen diguna """
        weblog_path = '/'.join(self.context.getPhysicalPath())
        brains= self.context.portal_catalog(portal_type='DiscussionItem',
                                            path=weblog_path,
                                            sort_on='created',
                                            sort_order='reverse')
        # Katalogoak brain izeneko objektu arin batzuk itzultzen ditu
        # horietatik abiatuz jatorrizko objektuak, DiscussionItem-ak
        # lortu behar ditugu, eta horiek ondoren IFeedEntry bihurtu
        objects = [brain.getObject() for brain in brains]
        return [IFeedEntry(obj) for obj in objects]

Azken lerro horretan dago trukoa, IFeedEntry(object) sententziarekin, Zoperen Component Architechture delakoa jartzen da martxan, object elementuaren interfazeetako bat, eta IFeedEntry interfazearen arteko bihurketa egiten duen adapter edo eraldatzaile bat bilatzeko. Gure kasuan, gorago definitutako DiscussionFeedEntry aurkituko du eta horrela IFeedEntry kontratua beteko dugu.

Geratzen den gauza bakarra, IFeed kontratua betetzen dutenentzat bista bat definitzea da, RSS eta ATOM plantilla eta IFeed interfazea lotzeko.

Beste adibide bat ikusteko, guk darabilgun Quills produktuaren syndication.py eta syndication.zcml moduluak ikus ditzakezu, basesyndication eta fatsyndication osagai txikiak nola erabiltzen dituzten ikusteko. Osagai txiki horiei esker da orain hain erraza edozeri RSS edo ATOM jarioa jartzea, plantilla ikutu beharrik gabe.

Inor heldu al da honaino? Opari bat emango diot hona heltzen den lehenari :)

He puesto un huevo, he puesto dos, he puesto tres...

No, no, que no me he vuelto loco todavía. He comentado varias veces por aquí lo de los huevos de Python, y el buildout y la tienda de quesos. Cada vez que hablo con Nando al respecto, Aitzol nos mira raro, pero ya le verá la potencia ya :)

La cosa es que estoy trabajando en un proyecto con el nuevo y reluciente Plone 3 y trabajando también con el nuevo LinguaPlone la solución para contenido multilingüe de Plone, hasta que Ramón y compañía terminen plone.multilingual.

Nada más instalar LinguaPlone, crear contenido en un idioma, traducirlo y probar el cambio de idioma entre ambos contenidos, detecte un bug crítico que impedía hacer la redirección entre ambos contenidos correctamente.

Tanto en el Plone básico como en LinguaPlone, los autores del producto tienen la manía de mostrar por defecto el cambio de idioma con banderitas (con todos los rollos políticos que pueda crear eso) o como alternativa un menú desplegable con los idiomas presentes en la web que funciona con redirecciones hechas con JavaScript.

Tanto lo de las banderitas como lo de la redirección con JavaScript es una solución que no nos gusta nada. Creemos que los textos de cambios de idioma deben ir en enlaces de texto plano, y además con el nombre del idioma en versión original. Creemos que es mejor que alguien que quiera ver una web en español lea el texto Español y el que quiera ver la web en euskara Euskara, y no siempre Spanish o Basque.

Como tanto el tema del JavaScript como el del texto clicable en el idioma original no era un tema nuevo. Acudí rápidamente a las diferentes plantillas de Plone y LinguaPlone para modificarlas para que hicieran lo que debían.

Empecé a buscar en las plantillas y no había nada por allí. Un rápido vistazo a mi buildout me mostró que las características de cambio de idioma en Plone están en el paquete plone.app.i18n y su padre plone.i18n. Así que acudí rápidamente a plone.app.i18n para llevarme la sorpresa de que todo eran viewlets de Zope3, con sus correspondientes clases y archivos zcml.

Bueno, es una novedad lo de los viewlets y el zcml y todo eso, pero gracias a los paquetes plone.theme y plone.browserlayer se pueden registrar fácilmente viewlets específicos para tu producto que sólo se aplican cuando tu paquete está instalado (sin tener que hacer engorrosos overrides.zcml. Para los que quieran saber más, en una frase, ambos paquetes introducen un traversal hook para añadir un interfaz más al request, de forma que al registrar tus propios viewlets como los de plone.app.i18n pero para un interfaz propio, se utilizan los tuyos al ser más específicos que los del producto original.

La cosa es que empecé a hacer dichas modificaciones en el producto que dará forma y colores al proyecto en cuestión, pero me di cuenta de que será una cosa que utilizaremos muchas veces en diferentes proyectos. Así que rápidamente creé otro paquete con los archivos necesarios para hacer esa personalización: un paquete pequeño y fácilmente reutilizable, que es de lo que se trata cuando escribimos componentes Zope3.

Afortunadamente, el comando paster acudió a mi rescate para crear la estructura básica de un paquete y crearlo en forma de huevo, para poder gestionar sus dependencias y poder utilizarlo en el buildout del proyecto:

$ sudo easy_install -U ZopeSkel

Con este comando instalo el comando paster y un montón de plantillas diferentes para generar diferentes tipos de paquetes para Plone: paquetes con temas, buildouts para Plone 3 y 2.5.x, simples proyectos plone, proyectos con tipos de objeto archetypes, ... Por ejemplo,

$ paster create -t plone

Con ejecutar eso y responder a las preguntas que nos hace el propio comando, tenemos preparada la estructura de un nuevo paquete. Sólo hace falta escribir las clases necesarias, registrar el perfil de Generic Setup y reescribir las plantillas para que tengan los enlaces tal y como queremos y quitar el JavaScript innecesario.

Una vez preparado el paquete, podemos utilizar el script release_egg.sh que se puede encontrar en el servidor SVN de Zope y crear una nueva versión de nuestro huevo python. Así mismo, lo podemos subir a un servidor de nuestra intranet, al estilo Cheeseshop y configurar nuestro buildout para que también revise ese servidor en busca de los huevos que necesite. Para terminar, le tenemos que decir a nuestro buildout, o mejor aún, al paquete que estamos escribiendo para contener todo lo necesario en nuestro proyecto, que tiene como dependencia el paquete que contiene la personalización de los menús de cambio de idioma. Buildout se las arreglará para descargar, instalar y cargar en Zope nuestro paquete.

De ahora en adelante, cada vez que queramos que el cambio de idioma sea simple, añadiremos como dependencia a nuestro producto este paquete, y lo instalaremos. Nada más. Divide y vencerás.

¿Se entiende? ;)

Mikel Larreategi 2007/11/12

Beste lau Plone prest

Iazko azaroan argitaratu genituen bi Plone webgune berri.: Goiena.net eta Unibertsitatea.net.

Aurtengo azaroa ere ez da atzean geratuko, eta argitaratu berri ditugu beste lau Plone: Karkara.com - Orio eta Aiako orainkaria, Zingizango.com - Legazpiko Euskara Elkartea, Amorebieta-Etxanoko Udala eta IMH - Elgoibarko Makina Herramintaren Institutua, azken hau Tolon eta Kororen lan itzelarekin batera.

Martin Aspeli with a copy of his book !!

Hurrengoak? Ba ikusiko dugu, baina azken urte honetan asko ikasi dugu. Gure Plone sektak beste partaide bat ere badu, Lur, argazkian nirekin eta Ploneren 3. bertsioaren bultzatzaile handienetakoa izan den Martin Aspelirekin ageri dena.

Hurrengo pausuak Plone 3 erabiltzea, produktuak Python arrautza bihurtzea eta Buildout erabiltzea izango dira. Badarabilt zerbait eskuartean.

Honetan aritu nahi duzu? Ez ahaztu CodeSyntaxek zu bezalako hackerrak behar ditu.

Mikel Larreategi 2007/11/10

Buildout reloaded

Hace unos meses escribí sobre buildout. Desde entonces ha habido muchos avances en ese mundo, incluyendo un excelente tutorial de Martin Aspeli sobre el tema.

  • ¿Cuántas veces has subido una versión de un producto al servidor, has arrancado zope y ha explotado?
  • ¿Cuántas veces se te ha olvidado subir ese producto que sólo cambias de pascuas a ramos?
  • ¿No sería mejor, que en vez de documentar la instalación y guardar esa documentación (con los problemas que tienes luego de tener que actualizar tanto la instalación como la documentación), la propia documentación sirviera como guión para instalar nuestra aplicación?

¿Qué es buildout?

Es una forma declarativa de declarar (valga la rebuznancia) qué es lo que va a tener tu aplicación (en nuestro caso la aplicación será una instancia de Plone):

  • Plone 2.5.3 y por lo tanto su correspondiente Zope.
  • Que tenga instalados unos productos que estoy desarrollando yo en mi svn privado
  • Que tenga instalados unos productos desde un svn público

Eso es muy bueno, ¿cómo lo hago?

Primero creamos una carpeta llamada 'mibuildout', en el que ponemos el archivo bootstrap.py. Posteriormente creamos en esa misma carpeta un archivo de configuración llamado 'buildout.cfg'. El contenido del archivo 'buildout.cfg' será el siguiente:

[buildout]
parts =
plone
zope2
otherproducts
svnproducts
prepproducts
instance

find-links =
http://dist.plone.org
http://effbot.org/downloads
eggs =
itools &gt;=0.9 &lt;0.10
develop =

[plone]
recipe = plone.recipe.plone25install
url = http://plone.googlecode.com/files/Plone-2.5.3-final.tar.gz

[zope2]
recipe = plone.recipe.zope2install
url = http://www.zope.org/Products/Zope/2.9.8/Zope-2.9.8-final.tgz

[otherproducts]
recipe = plone.recipe.distros
urls =
http://www.codesyntax.com/bitakora/Bitakora-0.1.10.tgz
http://hathawaymix.org/Software/CookieCrumbler/CookieCrumbler-1.2.tar.gz
http://download.ikaaro.org/localizer/Localizer-1.1.0.tar.gz
http://download.ikaaro.org/ihotfix/iHotfix-0.7.0.tar.gz
http://iungo.org/products/Epoz/releases/Epoz-2.0.2.tar.gz

nested-packages =

version-suffix-packages =
Localizer-1.1.0.tar.gz
iHotfix-0.7.0.tar.gz

[svnproducts]
recipe = plone.recipe.bundlecheckout
url = http://svn.plone.org/svn/collective/Quills/bundles/1.5-zope29

[preproducts]
recipe = plone.recipe.command
command = cd ${otherproducts:location}/TextIndexNG2 &amp;&amp; ${buildout:executable} setup setup.py install

[instance]
recipe = plone.recipe.zope2instance
zope2-location = ${zope2:location}
http-address = localhost:8080
user = admin:admin
eggs =
${buildout:eggs}
products =
${plone:location}
${otherproducts:location}
${svnproducts:location}

Ahora solo nos queda invocar el buildout:

$ python2.4 bootstrap.py
$ ./bin/buildout -v

Ya tenemos en marcha una nueva y reluciente instancia, con Plone, Bitakora, Quills y todo lo que hemos puesto en el archivo de configuración.

Pero... ¿cómo demonios?

Vayamos por partes, como dijo Jack el Destripador. Un buildout se compone de diferentes partes que se indican en la variable 'parts'. Por cada parte buildout creará una carpeta del mismo nombre dentro de la carpeta 'parts'.

En la variable eggs, le indicamos unos huevos o librerías Python que queremos que instale. buildout es inteligente, e irá al PyPI a por ellos. En este caso le hemos dicho que se descargue la librería itools, pero una versión entre la 0.9 y la 0.10 (es un requisito de Localizer).

Tras la configuración general del buildout, sólo nos queda indicarle qué hacer en cada parte, por lo que tenemos una sección de configuración por cada parte. Todas las partes tienen una variable llamada 'recipe' que indica que receta se debe utilizar para instalar dicha parte. Cada receta es diferente, e intentaré explicar lo que hace cada una de las utilizadas aquí

plone.recipe.plone25install

Como su propio nombre indica (¿esto es autosignificante Nando?), se descarga Plone 2.5.3 desde la URL que se indica en url y lo descomprime.

plone.recipe.zope2install

Descarga Zope desde la URL indicada en url, lo compila e instala

plone.recipe.distros

Descarga los archivos indicados desde las URL indicadas y los descomprime. Además si alguno de los paquetes contiene otros archivos (se me ocurre por ejemplo el Localizer Metapackage), los extrae al sitio adecuado (hay que indicarle cuales utilizando la variable 'nested-packages'). Si además, cuando descomprimes los archivos, las carpetas que se crean tienen un sufijo indicando la version (al estilo Localizer-1.0.1), si se lo indicas en 'version-suffix-packages', buildout se encarga de ajustar el nombre.

plone.recipe.bundlecheckout

Descarga los archivos de un svn a partir de la url

plone.recipe.command

Ejecuta un commando arbitrario

plone.recipe.zope2instance

Instala una instancia zope2, utilizando el zope instalado en la parte zope2 y añadiéndole como path de los productos que tienen que cargar todos los que le añadimos en la variable products.

¿Y ahora qué?

Ahora que sabemos cómo utilizar un buildout, sólo nos falta crear nuestro buildout.cfg, en el que documentamos todas las versiones de los productos que vamos a instalar, y guardarlo en un svn, para poder llevar el registro de cambios que le hagamos (nuevas versiones, ...) y poder replicar fácilmente la configuración de la máquina de desarrollo en la máquina de producción.

Mi próximo proyecto Plone lo gestionaré con un buildout como este (lo que también facilitará la tarea de poder utilizar nuevas librerías para plone escritas en forma de huevos.

Preguntas, dudas, comentarios, ...

Estoy abierto a cualquier tipo de pregunta o consulta sobre buildout, ¡déjame un comentario!

Actualización: Acabo de hacer la prueba y también funciona en Windows !!!

Mikel Larreategi 2007/10/04

San Andresak ondo pasatzeko bi aurkezpen

San Andres bezpera izan zen atzo eta gaur San Andres eguna. Jaieguna Eibarren.

Atzo eta gaur partehartze zuzena izan dudan bi webgune aurkeztu dira. Unibertsitatea.net eta Goiena.net

Biak Plone edukiak kudeatzeko sistemarekin eginak Zope plataformaren gainean. Ez dira izan lanean egin ditugun lehenengo Ploneak (aurretik publikoak dira Dantzan.com eta Mondragon Unibertsitatea ), baina hauek izan dira nolabait Ploneren tripak ikasteko baliogarri izan zaizkidan biak.

Ploneren trukoak ikasi, eduki mota berriak sortu, produktuak sortu, DTML baztertu eta ZPT idatzi, eta garrantzitsua izan den beste alderdi bat ere bai: kode guztia fitxategi sisteman idatzi eta SVN zerbitzari batekin sinkronizatuta eduki. Zope3 eta CMF/Plone munduan lan egiteko era zein den ikasteko ere balio izan didate proiektu hauek. Eta benetan esan behar dut ondo daudela nahiz eta bere gauzak izan.

Proiektu eta lan bakoitza bere horretan aztertu behar da eta dituen ezaugarriei erreparatu erabili beharreko plataforma zein den erabakitzeko. Ploneren aldeko apustua egin dugu eduki kudeatzaile sistemak behar dituzten proiektuetarako eta tresna oso ahaltsua da benetan milaka produktu ezberdin dituena.

Laster hasiko naiz proiektu hauetan ikasitakoetaz idazten. Ikasi ditudanak norbaiti baliogarri bazaizkio erabil ditzan.

Mikel Larreategi 2006/11/30

Mikel Larreategi

Mikel Larreategi Arana

(Eibar, 1981). Informatikaria ikasketa eta ofizioz, CodeSyntaxen egiten dut lan Eibar.ORG bezalako webguneak egiten. Ikastolan nengoela xakean jokatzen hasi nintzen eta horretan jarraitzen dut, eta 2006tik Eibarko Klub Deportiboaren presidentea naiz. Xakeak eta Klub Deportiboak, horretxek jaten dit nire denbora.

Goiburuko irudia Barcelona da. Orain dela urte batzuk bisitatu nuen hiria eta asko gustatu zitzaidan. Irudia nondik hartu nuen ez dut gogoan (lehen jarrita nuen baina aldatu egin nuen eta berriz itzuli eta galdu egin dut kreditua. Baina haren CC-BY-NC lizentziak dioena jarraituz kreditua ematen diot. 

Azken erantzunak
halaxe da bai... Oier A., 2012/01/20
Arrazoia! e-gor, 2011/11/17
Autogestioa Txopi, 2011/10/17
Bizikletak Herbeheretan Trebi., 2011/09/03