Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre planet

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,))],
            }

Contents © 2000-2024 Roberto Alsina