Migrando datos desde PHP a Rails

January 22nd, 2010 | by | programación

Jan
22

Por esas cosas de la vida me encontré todo el día de hoy migrando datos viejos de un sistema hecho en PHP a uno hecho en Rails. Las cosas venían bastante simples definiendo modelos de ActiveRecord para las tablas de la base de datos vieja y reinsertando con modelos apuntando a las tablas nuevas. Pero … siempre hay un pero.

Resulta que el hermoso sistema anterior para evitar tener muchas tablas (o vaya a saber por qué) tenía en una parte un tabla donde cada field era un gran TEXT que contenía un array de PHP serializado.

class UserTextField < ActiveRecord::Base
  set_table_name "usertextfield"
  set_primary_key "userid"
 
  def folders
    field3
  end
end
 
$> user = UserTextField.find(3)
$> user.folders # => 'a:2:{i:0;s:4:"bkps";i:1;s:6:"listas";'

Justo cuando estaba por ponerme a parsear texto me encontré con php-serialize que permite serializar y deserializar estos string en cómodos tipos nativos de Ruby.

El código final queda entonces algo como :

class UserTextField < ActiveRecord::Base
  set_table_name "usertextfield"
  set_primary_key "userid"
 
  def folders
    PHP.unserialize(field3)
  end
end
 
$> user = UserTextField.find(3)
$> user.folders # => ["bkps", "listas"]

Y la migración de datos pudo continuar sin problemas :) .

2 Comments »

Regalando cosas por Bluetooth

August 12th, 2009 | by | gnu+linux, programación

Aug
12

El año pasado Movistar había lanzado una campaña muy pedorra en el subte, en donde unos carteles en el piso te invitaban a prender tu Bluetooth y te enviaban un file. Lo que te enviaban era una simple imagen, con tanto texto que en mi celular era casi ilegible y no tenía consigna alguna.

Sin embargo esto sirvió para que me encaprichara y quisiera armar algo similar para la oficina, orientado a que un cliente que viene a una reunión se pueda llevar un regalo, que en este caso es un juego J2ME.

Hacerlo realmente es una boludez. El real problema, que no voy a tratar acá, es tener una placa Bluetooth soportada por Linux (creo que esta solución aplica Windows, pero no lo probé), lo que puede resultar complicado. Yo en mi anterior laptop fallé en cada intento. Hoy en día en mi MacBook anda todo out-of-the-box por suerte así que pude jugar un poco.

El protocolo que se utiliza para intercambiar cosas es Object Exchange (OBEX) y tenemos una excelente biblioteca llamada OpenObex. A nosotros nos interesa particularmente ObexFTP que nos da el File Transfer sobre Obex.

El primer problema que tuve es que en Jaunty no está el binding de ruby, por lo que me tuve que bajar el source y diff de Karmic y crear mis libobexftp-ruby_0.22-1_i386.deb y sus dependencias.

Salvando eso, con los ejemplos del wiki sale muy fácil. La biblioteca nos permite descubrir devices, abrir channels y enviar archivos en pocas líneas. Acá un en ruby ejemplo :

#!/usr/bin/env ruby
 
require 'obexftp'
 
puts "Scanning BT..."
intfs = Obexftp.discover(Obexftp::BLUETOOTH)
dev = intfs.first # Es un array, podríamos iterar sobre todas las encontradas
 
channel = Obexftp.browsebt(dev, Obexftp::PUSH)
 
cli = Obexftp::Client.new(Obexftp::BLUETOOTH)
puts cli.connectpush(dev, channel)
puts cli.put_file('ver.jpg')
puts cli.disconnect

Hacer lo mismo en Python, Perl o cualqueir otro lenguaje soportado es igual de simple.

Obviamente es muy minimalista: agarra el primer device encontrado, abre un channel para hacer un PUSH (si el device no soporta PUSH retorna -1 según creo), luego abre la conexión y le envía el archivo.

Desde el celular vemos un mensaje de que se está abriendo una conexión y luego el detalle de lo que se quiere enviar, nombre del archivo, tipo de archivo, tamaño, etc. Podemos aceptarlo o rechazarlo. De aceptarlo se descarga pero no se guarda ni se instala, es un paso extra que debemos decidir si lo hacemos o no.

Un problema que encontramos para enviar juegos es que algunos celulares están bloqueados para esa función (para así vendértelos por el portal WAP oficial de tu carrier). Ya lo pudimos probar con varios celulares Nokia, Motorola y Samsung y funciona razonablemente bien.

Sobre este ejemplo nosotros tenés un poco más de trabajo, ya que guardamos los device ID y el contenido enviado, así cuando volvés te damos un contenido diferente :) . Si además no podés recibir el juego, te pasamos una imágen simpática :P .

5 Comments »

Sitemaps vía crawling

July 15th, 2009 | by | programación

Jul
15

Hoy me pidieron agregar un Sitemap para uno de los trabajos que hicimos para el gobierno y me encontré con que los plugins que uso para esta tarea no me cerraban de forma cómoda. El problema es que este sitio tiene, además del contenido dinámico, muchas páginas estáticas que no puedo referenciar desde un modelo, por lo que debía forzarlas y era bastante molesto.

Buscando encontré una solución práctica para este caso (donde hay pocas páginas, menos de 1k) que usa un crawler para recorrer todo el sitio y obtener las URLs a agregar al sitemap. El script que presentan me sirvió, aunque tuve que hacerle algunos cambios menores.

El primer problema que tenía era que me agregaba páginas que no deben ir en un sitemap (ni ser indexadas) como las de login, recuperar clave, form de registración, etc. Por lo que tuve que modificar ligeramente el código para no seguir los enlaces que estuvieran marcados con rel="nofollow" y para eso modifiqué en el método extract_and_call_urls la última línea como sigue :

links.each{ |link|
   extract_and_call_urls(link.href) unless
      !can_follow?(link) || ignore_url?(link.href) || 
      @visited_pages.include?(link.href) 
}

Y definiendo el nuevo método :

 def can_follow?(link)
   return false if link.nil? ||
   (link.attributes["rel"] && link.attributes["rel"].include?("nofollow"))
 
   true
 end

Entonces, cuando el crawler encuentra un enlace que el developer marcó que no debe seguirse en una indexación (esto es principalmente para los crawlers de los search engines) se ignora y no se agrega al sitemap.

El otro cambio menor fue que tenía algunas URLs con el path completo y por default siempre me agregaba al inicio el domain name, por lo que me quedaban URLs inválidas, por lo que hice la siguiente modificación :

# Antes
xml.loc(@starting_url + url)
 
# Después
xml.loc(url.include?(@starting_url) ? url : (@starting_url + url))

Una vez probado el script hice una tarea rake para poder correrla fácil desde un cronjob :

# lib/tasks/sitemap.rake
require 'lib/crawler'
 
desc "Generate the sitemap file"
task :sitemap => :environment do
  start_url = ENV["URL"] || "http://localhost:3000"
  Crawler.new(start_url, (ENV["CREDS"] if ENV["CREDS"]), ENV["QUIET"] || false, ENV["SITEMAP"] || false, ENV["DEBUG"] || false)
end

Y listo, lo último fue hacer un deploy y configurar un cron.dayli para que cree el sitemap actualizado :

rake sitemap URL=http://www.haciendoelcolon.buenosaires.gob.ar SITEMAP=true

Así una vez por día se actualiza el sitemap y se hace un ping a google para que sepa que debe pasar a reindexar el contenido.

Esto tiene varias desventajas (pero aún así para este sitio sirve a su propópito) :

  • No se puede priorizar cada tipo de contenido fácilmente
  • La fecha de última modificación es inexacta
  • Carga el webserver para generar el sitemap

Código completo : crawler.rb

1 Comment »

Take A Photo – Demo

July 1st, 2009 | by | programación

Jul
01

Hace unos meses publiqué un plugin para tomar fotos instantáneas desde una página web, usando Flash y un plugin para Ruby on Rails.

Hoy me puse un rato a jugar y armé un demo donde se pueden tomar fotos (ojo que todo es público :D ) : TAP.

Las fotos se borran una vez por día y acepta un máximo de 50 fotos por día (para evitar que me llenen el disco). Tengo algunos TODOs que iré viendo de resolver en los tiempos libres (como cambiar el tamaño de la foto, recortar, aplicar efectos, etc).

Necesita Flash 9 o superior y una web cam para que tenga sentido :) . Todavía sigo peleando con el Flash player de Linux y la camarita, sorry :S.

Les recomiendo verlo en Firefox, no me gasté en ver si en IE el CSS no se rompe y no creo que lo haga nunca.

6 Comments »

Un tiempo después

June 11th, 2009 | by | oregano, programación

Jun
11

No, no voy a comerntar sobre el aburrido programa de “Solita” :D , solo que hoy estuve arreglando unos bugs de Oregano y me di cuenta que habían pasado ya unos 18 meses desde el último commit que hice!

En fin, mucho tiempo, pero ya tengo un lindo TODO de cosas a ir haciendo, con muy baja prioridad por la Tesis, pero que quizás para mi cumple esté para hacer otro release, esperemos no colgarme de nuevo.

Mientras tanto se arreglaron dos bugs molestos :

  • Se arregló el export a PNG con fondo transparente
  • Se implementó finalmente la opción de exportar en escala de grises

oregano_grayscale

2 Comments »