A Hard Day's Knight (Nightside, #11)
![]() |
Review:Ok-ish but obviously part of a series I am not tempted to read. God for a 99 cent special. |
![]() |
Review:Ok-ish but obviously part of a series I am not tempted to read. God for a 99 cent special. |
La verdad, no hay gran diferencia en la API:
¡Hasta porté el tema default a Jinja! Y no, tampoco es muy distinto del lado de los templates:
Pero bueno, sobre gustos no hay nada escrito, dijo la vieja lamiendo sapos. Otros motores de template son probablemente fáciles de usar mientras tengan semánticas parecidas.
Branch para review acá en github y sí, cualquiera que esté leyendo esto y sepa de Jinja2 o Python es bienvenido a hacer un review.
Como diría él mismo: tenerlo al lado todos los días es ¡GENIAL! e ¡INCREÍBLE!
Feliz cumple, tatito.
Si tiene algo lindo, lo hizo él. Si tiene algo feo o roto, lo hice yo.
Lo hice básicamente para ver si era difícil portar temas de wordpress a Nikola. Y es posible, pero consiste en leer php y reescribir como templates de Mako.
Si bien no es un port perfecto ni mucho menos, es un punto de partida para alguien que realmente le interese.
Así se ve:
Y acá está el archivo. Para usarlo, expandirlo en la carpeta themes, configurar el dodo.py para qu THEME sea "blogtxt", rebuild y listo.
Este tema es LGPL (ver license.txt incluído). Buen provecho!
Hace poco, cambiando un planeta de un server a otro, se pinchó todo. Los posts viejos eran nuevos, feeds que no tenían un post hace 2 años se ponían siempre primeras... un desastre.
Podría haber vuelto al server viejo, y empezado a debuguear porqué rawdog hacía eso, o cambiar a planet, o buscar otro programa, o usar un agregador online.
En vez de hacer eso, me puse a pensar... ya escribí varios lectores de feeds... Feedparser está siendo mantenido activamente... rawdog y planet están abandonados (parece)... es difícil implementar el planeta mínimo?
Bueno, no, la verdad que no. Tipo que me llevó 4 horas y no fué muy difícil.
Un motivo por el cual hacer esto fué más fácil que para los que hicieron rawdog o planet, es que no me puse a hacer un generador de sitios estáticos, porque ya tengo uno así que todo lo que este programa (se llama Smiljan) hace es:
Parsea una lista de feeds y la guarda en una base de datos si hace falta.
Descarga esos feeds (respetando etag y modified-since)
Parsea esos feeds buscando posts (lo hace feedparser)
Carga los posts (un subcojunto de esos datos) en la base de datos.
Usa esas entradas para generar entrada para Nikola
Usa Nikola para generar y subir el sitio
Así que acá está el resultado final: http://planeta.python.org.ar que todavía necesita temas y otras cosas, pero anda.
Implementé Smiljan como 3 tareas de doit, lo que lo integra muy facilmente con Nikola (si probaste Nikola: ponés "from smiljan import *" en tu dodo.py, y un archivo feeds con los feeds en formato rawdog y listo) y voilá, correr esto hace un planet:
doit load_feeds update_feeds generate_posts render_site deploy
Acá está el código de smiljan.py en estado "hack chancho que anda". Buen provecho!
# -*- 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,))],
}