Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Smiljan, un Mini Generador de Planetas

Ha­ce po­co, cam­bian­do un pla­ne­ta de un ser­ver a otro, se pin­chó to­do. Los pos­ts vie­jos eran nue­vo­s, fee­ds que no te­nían un post ha­ce 2 años se po­nían siem­pre pri­me­ra­s... un de­sas­tre.

Po­dría ha­ber vuel­to al ser­ver vie­jo, y em­pe­za­do a de­bu­guear por­qué raw­dog ha­cía eso, o cam­biar a pla­ne­t, o bus­car otro pro­gra­ma, o usar un agre­ga­dor on­li­ne.

En vez de ha­cer eso, me pu­se a pen­sar... ya es­cri­bí va­rios lec­to­res de fee­d­s... Fee­dpar­ser es­tá sien­do man­te­ni­do ac­ti­va­men­te... raw­dog y pla­net es­tán aban­do­na­dos (pa­re­ce)... es di­fí­cil im­ple­men­tar el pla­ne­ta mí­ni­mo?

Bue­no, no, la ver­dad que no. Ti­po que me lle­vó 4 ho­ras y no fué muy di­fí­ci­l.

Un mo­ti­vo por el cual ha­cer es­to fué más fá­cil que pa­ra los que hi­cie­ron raw­dog o pla­ne­t, es que no me pu­se a ha­cer un ge­ne­ra­dor de si­tios es­tá­ti­co­s, por­que ya ten­go uno así que to­do lo que es­te pro­gra­ma (se lla­ma Smi­l­jan) ha­ce es:

  • Pa­r­­sea una lis­­ta de fee­­ds y la gua­r­­da en una ba­­se de da­­tos si ha­­ce fa­l­­ta.

  • Des­­ca­r­­ga esos fee­­ds (res­­pe­­tan­­do etag y mo­­­di­­fie­­d-­­si­n­­ce)

  • Pa­r­­sea esos fee­­ds bus­­can­­do po­s­­ts (lo ha­­ce fee­­dpa­r­se­­r)

  • Ca­r­­ga los po­s­­ts (un su­­b­­co­­­jun­­to de esos da­­to­­s) en la ba­­se de da­­to­­s.

  • Usa esas en­­tra­­das pa­­ra ge­­ne­­rar en­­tra­­da pa­­ra Niko­­­la

  • Usa Niko­­­la pa­­ra ge­­ne­­rar y su­­bir el si­­tio

Así que acá es­tá el re­sul­ta­do fi­na­l: http://­pla­ne­ta.­p­y­tho­n.or­g.ar que to­da­vía ne­ce­si­ta te­mas y otras co­sas, pe­ro an­da.

Im­ple­men­té Smi­l­jan co­mo 3 ta­reas de doi­t, lo que lo in­te­gra muy fa­cil­men­te con Niko­la (si pro­bas­te Niko­la: po­nés "from smi­l­jan im­port *" en tu do­do­.­p­y, y un ar­chi­vo fee­ds con los fee­ds en for­ma­to raw­dog y lis­to) y voi­lá, co­rrer es­to ha­ce un pla­ne­t:

doit load_feeds update_feeds generate_posts render_site deploy

Acá es­tá el có­di­go de smi­l­jan.­py en es­ta­do "ha­ck chan­cho que an­da". Buen pro­ve­cho!

# -*- coding: utf-8 -*-
import codecs
import datetime
import glob
import os
import sys

from doit.tools import timeout
import feedparser
import peewee


class Feed(peewee.Model):
    name = peewee.CharField()
    url = peewee.CharField(max_length = 200)
    last_status = peewee.CharField()
    etag = peewee.CharField(max_length = 200)
    last_modified = peewee.DateTimeField()

class Entry(peewee.Model):
    date = peewee.DateTimeField()
    feed = peewee.ForeignKeyField(Feed)
    content = peewee.TextField(max_length = 20000)
    link = peewee.CharField(max_length = 200)
    title = peewee.CharField(max_length = 200)
    guid = peewee.CharField(max_length = 200)

Feed.create_table(fail_silently=True)
Entry.create_table(fail_silently=True)

def task_load_feeds():
    feeds = []
    feed = name = None
    for line in open('feeds'):
        line = line.strip()
        if line.startswith('feed'):
            feed = line.split(' ')[2]
        if line.startswith('define_name'):
            name = ' '.join(line.split(' ')[1:])
        if feed and name:
            feeds.append([feed, name])
            feed = name = None

    def add_feed(name, url):
        f = Feed.create(
            name=name,
            url=url,
            etag='caca',
            last_modified=datetime.datetime(1970,1,1),
            )
        f.save()

    def update_feed_url(feed, url):
        feed.url = url
        feed.save()

    for feed, name in feeds:
        f = Feed.select().where(name=name)
        if not list(f):
            yield {
                'name': name,
                'actions': ((add_feed,(name, feed)),),
                'file_dep': ['feeds'],
                }
        elif list(f)[0].url != feed:
            yield {
                'name': 'updating:'+name,
                'actions': ((update_feed_url,(list(f)[0], feed)),),
                }


def task_update_feeds():
    def update_feed(feed):
        modified = feed.last_modified.timetuple()
        etag = feed.etag
        parsed = feedparser.parse(feed.url,
            etag=etag,
            modified=modified
        )
        try:
            feed.last_status = str(parsed.status)
        except:  # Probably a timeout
            # TODO: log failure
            return
        if parsed.feed.get('title'):
            print parsed.feed.title
        else:
            print feed.url
        feed.etag = parsed.get('etag', 'caca')
        modified = tuple(parsed.get('date_parsed', (1970,1,1)))[:6]
        print "==========>", modified
        modified = datetime.datetime(*modified)
        feed.last_modified = modified
        feed.save()
        # No point in adding items from missinfg feeds
        if parsed.status > 400:
            # TODO log failure
            return
        for entry_data in parsed.entries:
            print "========================================="
            date = entry_data.get('updated_parsed', None)
            if date is None:
                date = entry_data.get('published_parsed', None)
            if date is None:
                print "Can't parse date from:"
                print entry_data
                return False
            date = datetime.datetime(*(date[:6]))
            title = "%s: %s" %(feed.name, entry_data.get('title', 'Sin título'))
            content = entry_data.get('description',
                    entry_data.get('summary', 'Sin contenido'))
            guid = entry_data.get('guid', entry_data.link)
            link = entry_data.link
            print repr([date, title])
            entry = Entry.get_or_create(
                date = date,
                title = title,
                content = content,
                guid=guid,
                feed=feed,
                link=link,
            )
            entry.save()
    for feed in Feed.select():
        yield {
            'name': feed.name.encode('utf8'),
            'actions': [(update_feed,(feed,))],
            'uptodate': [timeout(datetime.timedelta(minutes=20))],
            }

def task_generate_posts():

    def generate_post(entry):
        meta_path = os.path.join('posts',str(entry.id)+'.meta')
        post_path = os.path.join('posts',str(entry.id)+'.txt')
        with codecs.open(meta_path, 'wb+', 'utf8') as fd:
            fd.write(u'%s\n' % entry.title.replace('\n', ' '))
            fd.write(u'%s\n' % entry.id)
            fd.write(u'%s\n' % entry.date.strftime('%Y/%m/%d %H:%M'))
            fd.write(u'\n')
            fd.write(u'%s\n' % entry.link)
        with codecs.open(post_path, 'wb+', 'utf8') as fd:
            fd.write(u'.. raw:: html\n\n')
            content = entry.content
            if not content:
                content = u'Sin contenido'
            for line in content.splitlines():
                fd.write(u'    %s\n' % line)

    for entry in Entry.select().order_by(('date', 'desc')):
        yield {
            'name': entry.id,
            'actions': [(generate_post, (entry,))],
            }

Nikola 2.1.1 + GitHub

Por otro la­do, si pro­bas­te 2.1 y no te fun­cio­nó, pro­bá 2.1.1, por­que me ol­vi­dé de agre­gar un par de ar­chi­vos en uno de los te­mas en 2.1

Alimento para la Culpa

Spa­nis­h-on­ly pos­t, so­rr­y!


Ha­ce co­mo dos años es­cri­bí un pe­da­zo de un li­bro. De vez en cuan­do lo mi­ro con ca­ri­ño y pien­so que es­ta­ría bue­no ter­mi­nar­lo, o re­de­fi­nir­lo y ce­rrar­lo, y co­sas así. Has­ta hi­ce un plan que nun­ca pu­de po­ner en prác­ti­ca por­que la vi­da te lle­va a ha­cer co­sas dis­tin­ta­s.

Re­sul­ta que es­tán usan­do mi hu­mil­de ca­cho de li­bro co­mo ma­te­rial de es­tu­dio en la ma­te­ria IWI-131 Pro­gra­ma­ción de com­pu­ta­do­res - 1er Se­mes­tre 2012 de la Uni­ver­si­dad Téc­ni­ca Fe­de­ri­co San­ta Ma­ría en Chi­le.

Por un la­do, me po­ne con­ten­to. Por otro la­do me po­ne ner­vio­so que un li­bro que ci­ta es­to sea ma­te­rial de es­tu­dio pa­ra chi­cos de 18:

Has­ta que cum­ple vein­ti­cin­co, to­do hom­bre pien­sa ca­da tan­to que da­das las cir­cuns­tan­cias co­rrec­tas po­dría ser el más jo­di­do del mun­do. Si me mu­da­ra a un mo­nas­te­rio de ar­tes mar­cia­les en Chi­na y es­tu­dia­ra du­ro por diez año­s. Si mi fa­mi­lia fue­ra ma­sa­cra­da por tra­fi­can­tes co­lom­bia­nos y ju­ra­ra ven­gan­za. Si tu­vie­ra una en­fer­me­dad fa­ta­l, me que­da­ra un año de vi­da y lo de­di­ca­ra a aca­bar con el cri­men. Si tan só­lo aban­do­na­ra to­do y de­di­ca­ra mi vi­da a ser jo­di­do.

—Neal Ste­phen­son (S­now Cras­h)

Por otro la­do má­s, me da ga­nas de ter­mi­nar­lo. Por el úl­ti­mo la­do (con lo cual lo que ten­go es una sen­sación cua­dran­gu­la­r), pien­so: que ra­ro se­ría que te den un li­bro en la fa­cu que es­tá to­do in­com­ple­to. Es co­mo ha­cer­se fan de The Event y nun­ca en­te­rar­se que era el fa­mo­so Even­t, co­mo ha­ber vis­to Twin Peaks y nun­ca sa­car­se de en­ci­ma la du­das de qué era to­da esa bi­za­rrea­da. Co­mo se­guir to­da­vía es­pe­ran­do que Mel Brooks ha­ga la se­gun­da par­te de la his­to­ria del mun­do pa­ra po­der en­ten­der "Jews in Spa­ce".

Aho­ra po­dría no ter­mi­nar­lo co­mo de­ci­sión ar­tís­ti­ca!

Y por su­pues­to:


Contents © 2000-2023 Roberto Alsina