Magic Mistery Forum

../images/magicmisteryforum/ralsina.jpg
Autor:Roberto Alsina
Bio:Gurú de PyQt, rst, y miembro de Canonical.
Webpage:http://lateral.netmanagers.com.ar/
Twitter:@ralsina
../images/magicmisteryforum/magic_mistery_forum.png

Hace un tiempo se juntaron muchos programadores Python en un mismo lugar, a tratar de divertirse programando. Yo estaba ahí, aunque no programé mucho, tuve una idea. De esa idea hay un video, que no pienso linkear porque soy gordo, estaba cansado y parezco Bellini el mentalista.

La idea era acerca de como se podría hacer una aplicación de foros sencilla. Muy sencilla. Demasiado sencilla. [1]

Partamos de definir exactamente qué es un foro.

  1. Sitio web
  2. Los usuarios pueden entrar con nombres distintivos.
  3. Se pueden crear "hilos" de discusión.
  4. En dichos hilos, los usuarios pueden responder a los comentarios de otros.
  5. Los comentarios pueden verse instantáneamente en el sitio.
  6. Algunos usuarios son moderadores, y pueden borrar spam, o comentarios agresivos, o contrarios a reglas de convivencia del foro.
  7. Los usuarios tienen avatares, imágenes que los identifican.
  8. Es posible suscribirse a un hilo o tema, y recibir notificaciones cuando alguien comenta en el mismo.
  9. Podés postear imágenes, videos etc.

Tal vez, si nos ponemos demandantes podríamos pedir integración con redes sociales, o la posibilidad de votar mensajes o temas interesantes.

Bueno, vamos a hacer eso. Todo eso. Pero lo vamos a hacer de la mejor manera posible: haciendo trampa.

Para ello, vamos a utilizar los servicios de Disqus un proveedor de sistemas de comentarios para sitios web (por ejemplo blogs) [2].

Resulta que Disqus ofrece un bonito API y hay un módulo python para el mismo.

¿Cómo funciona? Poniendo un poquito de javascript en tu HTML. Ese javascript tiene un identificador de sitio y uno de discusión. Con eso, Disqus reconoce "ok, esta es la discusión ABC1 del sitio tal", busca en su base de datos, genera un gran pedazo de HTML, lo injerta en tu página web, y listo, comentarios en tu página.

Ahora bien, nadie dice que la página que contiene los comentarios tenga que tener algo interesante aparte de los comentarios, ¿no?

Por otro lado, es posible obtener una lista de "todas las discusiones activas del sitio".

Entonces podemos usar esa lista de discusiones para generar la "lista de temas" del foro.

Y cada tema podría ser un link a una página básicamente vacía, con el suficiente javascript como para que salgan los comentarios...

Empecemos con lo primero, una lista de temas generada a partir de una cuenta de Disqus.

Cualquier framework, miniframework, o microframework sirve. Yo voy a hacerlo con bottle pero solamente porque ya sé como funciona. Podés usar Flask, Django, web2py, pyramid, o hacer un script cgi... es muy básico.

import time
# Necesito un framework web
import bottle
# Necesito la API de Disqus
import disqusapi as disqus

# Configuracion
shortname = 'magicmisteryforum'
url = "http://foro.netmanagers.com.ar"
# Esto lo obtenes vos en la página de disqus. Cada persona que quiera
# hostear esto necesita una distinta.
api = disqus.DisqusAPI(open("key").read().strip())

# Que vemos cuando el usuario entra en "/"?
@bottle.route('/', method='GET')
def index():
    # Parametro opcional "msg", para errores
    msg = bottle.request.GET.get('msg', '')

    # Tomamos la lista de threads de Disqus
    threads = api.threads.list(forum=shortname, limit=25)

    # Le pasamos esas dos cosas a un template
    return bottle.template('main.tpl', threads=threads,
        shortname=shortname, msg=msg, url=url)

¿Y qué es ese template? HTML medio feo, sabrán disculpar:

<h1>Magical Mistery Forum</h1>
<!-- Mostramos el error -->
%if msg:
    <div class="error">
    {{msg}}
    </div>
%end
</hr>
<div>
<!-- Un widget de disqus que muestra cosas interesantes -->
<script type="text/javascript" src="http://bit.ly/nsOAAA">
</script><a href="http://disqus.com/">Powered by Disqus</a>
</div>

<!-- formulario para crear nuevo hilo -->
<div>
<form method="POST" action="/new">
<span class="loud">New Thread Title:</b>
<input type=text name=title>
<input type="submit" value="Create">
<div class="error">When you create a thread, it may take a minute
to appear on the list.</br> All threads and posts will be deleted
every now and then, this is a test site only.
</div>
</div>
<!-- lista de todos los hilos existentes -->
% for t in threads:
    %if not t['identifiers'] or t['identifiers'][0] != t['title']: continue
    <div class="large">
    <a href="/thread/{{t['id']}}">{{t['title']}}</a>
    [ <a href="/thread/{{t['id']}}#disqus_thread"
    data-disqus-identifier="{{t['id']}}">#</a>]
    <div class="small">
      <small>{{t['likes']}} likes -- {{t['dislikes']}}
      dislikes -- {{t['createdAt']}}</small>
    </div>
    </div>
% end
<hr>
<!-- Comentarios -->
<script type="text/javascript">
    var disqus_shortname = '{{shortname}}';

    /* * * DON'T EDIT BELOW THIS LINE * * */
    (function () {
        var s = document.createElement('script'); s.async = true;
        s.type = 'text/javascript';
        s.src = 'http://' + disqus_shortname + '.disqus.com/count.js';
        (document.getElementsByTagName('HEAD')[0] ||
        document.getElementsByTagName('BODY')[0]).appendChild(s);
    }());
</script>

¿Y dónde te lleva lo de "crear nuevo hilo"?

# Se accede en la URL /new
@bottle.route('/new', method='POST')
def new():
    # El usuario manda el titulo del hilo
    title = bottle.request.forms.get('title', None)
    if not title:
        bottle.redirect('/?msg=Missing%20Thread%20Name')
        return
    # Creamos el hilo en disqus
    thread = api.threads.create(forum=shortname, title = title, identifier = title)
    print "THREAD", thread
    thread_id = thread.__dict__['response']['id']

    # Esto estaría buenísimo pero no anda porque hay una demora
    # en la creación del hilo
    #api.posts.create(thread=thread_id, message="Post about %s here!"%title)
    #bottle.redirect('/thread/%s'%thread_id)

    # Y te mandamos a la lista de hilos de nuevo, donde este aparecerá luego
    bottle.redirect('/')

¿Y cómo se puede ver un hilo?

# El hilo esta en /thread/id
@bottle.route('/thread/:id')
def thread(id):
    t = api.threads.details(thread=id)
    return bottle.template('thread.tpl',
        shortname=shortname,
        id=id,
        thread=t.__dict__['response'])


# Codigo standard que no es muy interesante
@bottle.route('/static/:path#.+#')
def server_static(path):
    return bottle.static_file(path, root='./static')

app = bottle.app()
app.catchall = False
bottle.run(host='0.0.0.0', port=80, app=app)

Faltaría ver el template que se usa para ver un hilo:

<!-- una manera de volver a la lista de threads -->
<div>
<a href="javascript:history.back(1)">Back to Thread List</a>
</div>
<!-- titulos -->
<h1>Magical Mistery Forum</h1>
<h2>{{thread['title']}}</h2>
<hr>
<!-- comentarios -->
<div id="disqus_thread"></div>
<script type="text/javascript">
    var disqus_shortname = '{{shortname}}';
    var disqus_identifier = '{{id}}';
    var disqus_url = '{{url}}/thread/{{id}}';
    (function() {
        var dsq = document.createElement('script');
        dsq.type = 'text/javascript';
        dsq.async = true;
        dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
        (document.getElementsByTagName('head')[0] ||
            document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();
</script>
<noscript>Please enable JavaScript to view
the <a href="http://disqus.com/?ref_noscript"> comments powered by
Disqus.</a></noscript><a href="http://disqus.com" class="dsq-brlink">
blog comments powered by <span class="logo-disqus">Disqus</span></a>

Y... no hay más aplicación. Eso es todo. Funciona? Sí!

http://content.screencast.com/users/ralsina/folders/Jing/media/b1b1a83a-1d32-4373-b46c-f26d83c7fde1/2011-09-13_2149.png

Hasta ahora... un hack. Pero pensémoslo un poco más filosóficamente. Si bien no digo que esta chanchada es un foro maravilloso que reemplaza a phpBB o engendros similares, muestra como con un poco de maña uno puede tomar componentes provistos y usarlos para generar cosas distintas. Python, usado de esta manera, regresa un poco a sus orígenes como "glue language", el lenguaje usado para pegar cosas heterogéneas y convertirlas en algo distinto.

Y resulta que hacer "una aplicación web" usando un framework no es súper difícil [3]. Y hacer un deployment de una tampoco lo es. Y te da un campo para jugar. Y a veces, simplemente uno hace estas cosas porque... es divertido.

El código completo de Magic Mistery Forum está en http://magicforum.googlecode.com

[1]Lamentablemente nunca llegué al extremo de simplicidad que pensé en ese momento. Tal vez algún día...
[2]Son, por lo que oí, la aplicación Django mas grande del mundo.
[3]En serio, el lado Python de la aplicación "se entiende" si sabés el lenguaje. ¿No?

Help PET: Donate

blog comments powered by Disqus

Último cambio: Thu Sep 22 06:38:27 2011.  -  Esta revista está bajo una licencia Creative Commons.