miércoles, 28 de marzo de 2012

while (fracaso) { try{do_something} catch(e){ continue; } }


Después de varios "fracasos" puedo decir con seguridad que la parte difícil de programar no es tener una idea y desarrollarla, no porque sea fácil, sino porque entre entre mas dificultad presente el problema mas nos entusiasmamos y a pesar de todo lo disfrutamos (al menos yo).

Pero a la hora de "vender" el producto y lo pongo entre comillas ya que no lo digo en el sentido de intercambiarlo por dinero sino hacer que las personas lo usen, recomienden... todo va mal. Por poner ejemplos:

ConceptMap: una aplicación para crear mapas conceptuales que desde su lanzamiento ha tenido la increíble estadística de 2 personas afiliadas y 1 mapa conceptual creado.

jsaw5: un juego que permite crear rompecabezas de hasta 100 fichas usando cualquier imagen y compartirlos con amigos usando una url corta, que empezó muy bien pero a medida que pasan los días veo como las estadísticas en analytics van cayendo.

Y eso que estas aplicaciones son GRATIS, ahora no me imagino lo difícil que debe de ser atraer usuarios que paguen por usar tu programa.

No es solo la falta de experiencia en marketing y esas cosas, sino que la tarea en sí resulta molesta a diferencia de programar que disfruto aun cuando me saca canas verdes.

Tal vez no tengo talento para esto, tal vez nunca pueda llegar a crear una aplicación "popular", pero por el momento seguiré intentando.

Alguien mas se siente de esta forma, algún consejo.

jueves, 8 de marzo de 2012

jsaw5: Crea rompecabezas personalizados


Ya llevo tiempo sin escribir, y es que en estos días han pasado muchas. Ahora que tengo tiempo libre (ya que estoy desempleado) me puse en la tarea de crear un juego en javascript y HTML5: jsaw5.

Actualización: el demo fue movido a dropbox

En jsaw5 podrán crear, resolver y compartir rompecabezas personalizados con amigos en twitter y facebook. Espero que lo visiten y den sus opiniones, aun debe tener varios errores pero si esperaba a que fuera "perfecto" 

En el proceso de creación agregue algunas mejoras a canvas-event-js las cuales espero hacer disponibles en cuanto pueda.

miércoles, 28 de diciembre de 2011

Visor de imágenes para xkcd

Una de mis tiras cómicas favoritas es xkcd, y desde ya hace tiempo quería descargarme todas las tiras para tenerlas en el disco duro. Si han leido xkcd seguro sabran de los textos adicionales que Randall Munroe pone en el atributo title de las imágenes, este texto es complementario a la tira y en muchas ocasiones mas gracioso que la tira misma.

Así que si descargaba las tiras tenia que hacerlo con todo y titulo. Tuve un tiempo libre esta navidad y me puse a recolectar información de como hacerlo, lo primero era descargar las imágenes y guardar el texto del tooltip en los metadatos de la imagen, para esto me fue de mucha ayuda este articulo donde se explica como hacerlo usando la libreria PIL.

Lo siguiente era crear un visor de imágenes que mostrara los tooltip, algo complicado y mas teniendo en cuenta que esta es la segunda interfaz gráfica "seria" que hago, pero encontré muy buenos recursos como este libro online sobre tkinter con muy buenos ejemplos, ademas de un script que implementa un tooltip que me cayo de perlas.

Luego de varias horas de pelearme acomodando los widget (con pack y grid), he aquí el visor de imágenes junto con todas las tiras hasta la fecha (1-995).
Si bajan el código fuente necesitaran instalar las siguientes librerías:
  • PIL (en Windows y Linux)
  • ImageTk (solo en Linux: python-imaging-tk)
  • Tkinter (solo en Linux)
Si tienen alguna sugerencia no duden en dejarla en los comentarios, siempre es bueno saber en que mejorar.

lunes, 26 de diciembre de 2011

Actualización: canvas-event-js 0.2

Después de meses sin actualizar, he aquí algunas cosas nuevas que pueden encontrar en canvas-event:
Ideas que tengo pero no he implementado:
  • Drag-n-Drop live para evitar llamar drag en cada objeto creado, ya que demasiados callback pueden poner lenta las aplicaciones.
  • moveUp, moveDown, toFront, toBack: para mover objetos entre capas (como en PowerPoint). 
Todo esto lo pueden encontrar en el repositorio de github a donde he movido el proyecto, donde ademas pueden dejar sus sugerencias y reportes de errores, su ayuda es muy importante y siempre bien recibida.

P.D.: Has creado alguna aplicación usando canvas-event, deja el link en los comentarios (simple curiosidad *¬*)

viernes, 23 de diciembre de 2011

Creando un blog con django - parte 3

En la parte anterior dejamos la base de datos lista, es momento de crear las plantillas para visualizar estos datos.
Lo primero que haremos es configurar el directorio donde estaran guardas las plantillas. Creamos el directorio templates en la raíz del proyecto y editamos el archivo settings.py.
.....
import os

TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__) ,"templates"),
)
....
Así el script puede encontrar el directorio templates aun cuando cambiemos de ubicación la carpeta del proyecto. Ahora editamos el archivo urls.py para incluir la url a la pagina.
....
urlpatterns = patterns('',
    (r'^$', 'blog.views.index'),
    ....
)
Con esto le decimos a django que toda petición hecha a la raíz del blog sera manejada por la función index ubicada en el archivo views, la cual luce así:
from django.shortcuts import render_to_response

def index(request):
  return render_to_response("index.html")
Al visitar la raíz del sitio, django llamara la función index pasando como argumento un objeto request que contiene información sobre la solicitud (datos POST y GET entre otros). La función index realizar algún proceso y retornar un objeto response.

Si iniciamos el servidor de desarrollo y entramos a la dirección localhost:8000 veremos un mensaje de error como el de la imagen ya que la plantilla index.html aun no existe, así que manos a la obra.
<!DOCTYPE HTML>
<html lang="es-ES">
<head>
 <title>Mi Blog</title>
</head>
<body>
<!-- hasta aquí siempre es igual -->
<h1>Hola Django</h1>
</body>
No muy útil, pero por el momento es todo lo que necesitamos. Imagine que creamos otras plantillas para post individuales, contacto, faq, quejas... debemos repetir la cabecera en cada una de ellas.

El sistema de plantillas de django permite crear plantillas base con bloques que luego pueden ser reemplazados por las plantillas que heredan de ésta; para tal efecto creamos la plantilla base.html:
<!DOCTYPE HTML>
<html lang="es-ES">
<head>
 <title>Mi Blog</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
Y editamos la plantilla index.html para que extienda base.html:
{% extends "base.html" %}
{% block content %}
<h1>Hola Django</h1>
{% endblock %}
Ok, teniendo una idea general del sistema de plantillas, seguimos con la pagina inicial del blog empezando por editar el archivo views.py.
from django.shorcuts import render_to_response
from blog.models import Post

def index(request):
    # recuperamos 5 primeras entradas
    posts = Post.objects.filter()[:5]
    return render_to_response("index.html", locals())
Cada vez que visitemos a la raíz del blog se consultaran los primeros 5 post y se pasaran a la plantilla index.
{# plantilla index.html #}
{% extends "base.html" %}

{% block content %}
{% for post in posts %}
    <h1><a href="/{{ post.id }}">{{ post.title }}</a></h1>
<p>{{ post.pub_date}}</p>
{{ post.body }}
    {# esto no es muy eficiente* #}
    {% for tag in post.tags.filter %}
        <a href="/tags/{{ tag.tag }}">{{ tag.tag }}</a>
    {% endfor %}
{% endfor %}
{% endblock %}
* Extraer las etiquetas de esta forma no es eficiente ya que al llamar a filter (5 veces) se hace una nueva consulta a la base de datos, algunas soluciones se pueden encontrar aquí.

Con esto la pagina principal esta lista. Sigamos con la pagina individual, veremos que al trabajar con django se sigue mas o menos el mismo patrón:
# archivo urls.py
urlpatterns = patterns('',
    (r'^$', 'blog.views.index'),
    # expresión regular que captura el numero después de post/
    (r'^post/(?P<id>\d+)$', 'blog.views.single'),
)
# Archivo views.py
...
# la funcion recibe el numero capturado
def single(request, id=0):
    # recuperar post según id
    post = Post.objects.get(id=int(id))

    return render_to_response("single.html", locals())
{# plantilla single.html #}
{% block content %}
    <h1><a href="/{{ post.id }}">{{ post.title }}</a></h1>
    <p>{{ post.pub_date}}</p>
    {{ post.body }}
    {# esto no es muy eficiente* #}
    {% for tag in post.tags.filter %}
        <a href="/tags/{{ tag.tag }}">{{ tag.tag }}</a>
    {% endfor %}
{% endblock %}
Notan algo raro? el código de single.html e index.html luce muy similar; podemos eliminar el código repetido moviéndolo a otra plantilla (article.html) que luego incluimos en estas.
{# plantilla article.html #}
</div>
    <h1><a href="/{{ post.id }}">{{ post.title }}</a></h1>
    <p>{{ post.pub_date}}</p>
    {{ post.body }}
    {# esto no es muy eficiente* #}
    {% for tag in post.tags.filter %}
        <a href="/tags/{{ tag.tag }}">{{ tag.tag }}</a>
    {% endfor %}
</div>
Ahora editamos las plantillas index.html y single.html para que utilicen article.html.
{# plantilla inde.html #}
{% extends 'base.html' %}

{% block main_content %}
    {% for post in posts %}
        {# pasamos la variable post como argumento #}
        {% include 'include/article.html' with post=post %}
    {% endfor %}

{% endblock %}
{# plantilla single.html #}
{% extends 'base.html' %}

{% block main_content %}
      {% include 'include/article.html' %}
{% endblock %}
Con esto damos por terminado esta parte. No se olviden de comentar.

P.D.: Procurare tener la próxima parte mas rápido.

viernes, 9 de diciembre de 2011

Actualización mget

Debido al rediseño de megaupload mget ha dejado de funcionar, el problema se debe específicamente a que el link de descarga ya no se identifica con el id downloadlink sino con la clase download_regular_usual, ademas el tiempo de espera ahora es de 60 segundos.

Por el momento no tengo como arreglar la versión para windows pero la de linux ya esta lista, solo hay que reemplazar el archivo antiguo y dar permisos de ejecución.

sábado, 26 de noviembre de 2011

Creando un blog con django - parte 2


Llego el momento de crear los modelos, pero antes analicemos los datos que necesitamos guardar. Nuestro blog tendrá post, comentarios y tags, los que a su vez se componen de:
  • Post: fecha, titulo, contenido y etiquetas.
  • Etiqueta: nombre.
  • Comentario: autor, fecha, contenido.
Ahora bien, una etiqueta puede estar relacionada con varios post, así como un post tener varias etiqueta, por lo que tienen una relación many to many. Los comentarios por otro lado solo pertenecen a un post por lo que necesitan una clave foránea que lo relacione con el respectivo post.

Teniendo claro el modelo de la base de datos, es hora de escribir el código necesario para crear las tablas, afortunadamente django nos facilita la tarea. Editamos el archivo models.py ubicado en la carpeta de la aplicación:
# 1
from django.db import models

# 2
class Tag(models.Model):
    # 3
    tag = models.CharField(max_length=100, unique=True, db_index=True)
    # 4
    def __unicode__(self):
        return self.tag
Veamos que hicimos:
  1. Importamos el modulo models.
  2. Creamos una subclase de models.Model, 
  3. Cada columna de la bd es representada por un atributo, en este caso tag es de tipo caracter (CharField), existen otros tipos de campo según el valor almacenar. Los argumentos describen las propiedades del campo como la cantidad de caracteres que puede contener (max_length), que no se permiten valores repetidos (unique), ademas de hacer que se indexe (db_index) ya que es comun hacer búsquedas como: buscar todos los post con tag django.
  4. el método __unicode__ permite dar un nombre mas significativo a las instancias, este es utilizarlo en el sitio administrativo.
Sabiendo esto es fácil crear las demás tablas:
# post
class Post(models.Model):
    # la relación many to many entre Post y Tag
    tags = models.ManyToManyField(Tag, blank=True)
    title = models.CharField(max_length=200)
    body = models.TextField()
    # fecha de publicación, se agrega fecha automáticamente
    pub_date = models.DateField(auto_now_add=True)

    def __unicode__(self):
        return self.title

class Comment(models.Model):
    # Cada comentario pertenece a un post
    post = models.ForeignKey(Post)
    # nombre es opcional (blank) y por defecto es Anonimus (default)
    name = models.CharField(max_length=100, blank=True, default="Anonimus")
    # el cuerpo del comentario
    body = models.TextField(verbose_name="comment")
    # fecha de publicación, se agrega fecha automáticamente
    date = models.DateField(auto_now_add=True)

    def __unicode__(self):
        return "Por " + self.name + " en " + self.post.title
La clave foránea y relación many to many se crea en una simple linea de código. Ahora sincronizamos nuevamente y entramos al interprete de python para introducir información en la base de datos:
$ python manage.py syncdb
$ python manage.py shell
# importamos los modelos
from blog.models import Tag, Post

# aun no hay tags así que creamos una
django = Tag(tag="django")
django.save()

# creamos un post
new_post = Post(title="mi primer post", body="El primer post que realizo")
new_post.save()
# agregamos un tag y volvemos a guardar
new_post.tags.add(django)
new_post.save()
Como vemos, para introducir un registro en la base de datos creamos una instancia de la clase adecuada y seguidamente llamamos el método save, ¿pero como recuperamos un registro? existen tres métodos para consultar la base de datos:
  • get: retorna un solo registro, lanza una excepción si la consulta retorna cero o mas de un registro.
  • filter: retorna un array con los registros que coinciden la consulta o un array vacío.
  • exclude: similar a filter pero retorna los registros que no cumplen con la consulta.
Estos métodos se acceden a través del atributo objects que poseen las subclases de models.Model.
# recuperar todos los post
Post.objects.filter()

# recuperar los primeros 10
Post.objects.filter()[:10]
Para refinar la búsqueda usamos argumentos que corresponden con los campos de la tabla:
# post segun titulo
Post.objects.filter(title="mi primer post")
Los campos que son claves foraneas (Post.tags, Comment.post) son algo especial: podemos seguir la relación escribiendo el nombre de la columna seguido de dos guiones bajos (_), pudiendo acceder el modelo al que apunta dicha relación. veamos un ejemplo:
# post con tag django
Post.objects.filter(tags__tag="django")
Al usar tags__ ya no estamos mas en Post sino en Tag, ahora podemos consultar los campos de esta tabla (tag). otro ejemplo:
# comentarios de posts con tag django
Comment.objects.filter(post__tags__tag="django")
Seguimos la relación a Post (post__) y consultamos la columna tags__ lo que nos lleva a Tag donde finalmente consultamos el atributo tag o_O.

Con esto tenemos lista la base de datos, en la próxima parte veremos como crear plantillas para visualizar estos datos.

Muchas gracias por su atención, no olviden dejar sus sugerencias, inquietudes y amenazas en los comentarios, hasta la próxima.
 
© 2009 NovatoZ. All Rights Reserved | Powered by Blogger
Design by psdvibe | Bloggerized By LawnyDesignz