Modelos de negocio FLOSS, la universidad, el sector privado y el Estado

Distintos laboratorios de investigación y vinculación de mi facultad del área química y del área computación, junto a algunas empresas de software locales (VATES y otra) y del sector petroquímico (Petroquímica Rio Tercero) están conformando un "Consorcio Asociativo Público Privado (CAPP)" en el marco del programa FSTICS del Ministerio de Ciencia y Tecnología de la Nación, con el fin de desarrollar una plataforma de desarrollo I+D de software para la industria. Para que contextualicemos de que hablamos: el programa estipula un aporte estatal de hasta el 60% para un proyecto de hasta 38 millones de pesos.

Mi participación es minúscula y aledaña: el impulsor de este proyecto es el Dr. Martín Cismondi, director de mi proyecto integrador que se trata de un prototipo del (tipo de) software que se desarrollaría desde esta plataforma.

Cismondi es Phd en ingenieria quimica y sus investigaciones en el campo del equilibrio de fases tienen mucha aplicación en la industria. En particular en el petróleo, donde el impacto que puede tener mejorar el rendimiento de un proceso (por ejemplo de destilación) se mide en millones de morlacos.

Ayer tuve oportunidad de participar de una reunión, sirviendo de una especie de "traductor" entre los quimicos y los informáticos.

Si bien, repito, no tengo voz (mucho menos voto) en nada de lo que allí se resuelva, tengo interés en investigar y transmitir de la manera más clara posible otros modelos de negocio que, por lo que presencié ayer, están lejos de ser tenidos en cuenta. Me refiero, claro, a que, sobre todo (no muy increíblemente) desde la facultad, no se ve más allá de un modelo de negocio de software cerrado de venta por licencia.

Apunto algunas cuestiones sin orden:

- El nicho de mercado es bastante acotado: industrias de gran escala mayormente transnacionales. Separadamente tiene mucha utilidad y aceptación en centros de investigación académicos.

image1 Desde el grupo conformado, no existe conocimiento cabal de las soluciones de software específicas que ese mercado, el industrial, demanda. Sí se sabe que el -*expertise-* y el -*know how-* que el grupo de investigación de Cismondi tiene son el valor diferencial.

image2 No existe hoy peso específico suficiente, desde el punto de vista del producto existente, para competir con las empresas de software proveedoras de este nicho, sobre todo en los mercados de USA y Europa. Por ejemplo Aspen Tech.

image3 Es difícil, a priori inimaginable, desarrollar un producto genérico que satisfaga necesidades de diversos clientes potenciales. La especifidad de los problemas de cada industria se supone grande.

image4 Preguntas para hacerse ¿cuantos clientes reales existen en el mercado? ¿qué "llegada" se tiene con ellos? ¿que posibilidades reales de venderle un software cerrado existen, teniendo en cuenta que no se sabe cabalmente cuales son sus demandas? Supongiendo que se conoce un problema específico ¿cuanto sale desarrollar una solución ? ¿se puede correr el riesgo de desarrollarla sin tener asegurada su comercialización?

Soy un novato en el área negocios con FLOSS (y en negocios, a secas, también) pero lo que se me ocurre viene más o menos por este lado:

1. Generar una estrategia para una fuerte inserción en el ámbito académico internacional

  • Para esto es indispensable la libre disponibilidad del código fuente de manera que los métodos numérico-científicos sean transparentes, reproducibles y verificables. Ver este post en OpenScience Project.

  • Para evitar practicas predatorias sobre este trabajo, que alguien cierre y comercialice, hay que orientar a una licencia FLOSS vírica, donde los trabajos derivados mantengan la condicion libre.

  • De esta manera se apunta a constituir una comunidad tecnologico-científica donde el nucleo de desarrollo se mantendrá en los autores originales pero permitiendo y aprovechando código y feedback de los usuarios.

  • En esta etapa el beneficio no es a priori monetario y en cambio sí en cuanto a calidad del software, inserción y difusión del/los producto/s, fidelización de usuarios y prestigio del equipo de desarrollo.

2. La repercusión de la masa crítica generada abre la puerta al mercado por las siguientes razones:

  • Muchos de los académicos que utilicen y formen parte de un "comunidad" alrededor del sofware FLOSS desde las universidades o llevaran su conocimiento a la industria.

  • Tambien los investigadores dentro de la industria tienen vínculos fluidos con la comunidad científica, sobre todo en estas áreas de investigación de vanguardia de directa aplicabilidad.

  • El mercado a priori imposible de acceder se puede hacer más permeable: los responsables de investigación demandan la utilización del software en los procesos de la industria o sus labores particulares.

  • Si el costo para la industria es nulo o bajo y de calidad, hay muchas probabilidades de que sea aceptado.

  • La ventaja diferencial del cliente es evitar el vendor lock-in. Sin embargo, la ventaja competitiva del equipo de desarrollo original, por su -*know how-* adquirido es insuperable para cualquier potencial competidor.

3. Insertado en el mercado se generan posibilidades de lucro en dos áreas

  • Customización y extensión del software bajo requerimientos específicos. Entra en el marco de SaaS

  • Capacitación y entrenamiento a equipos técnicos de la industria y la academia, tanto en el área de aplicación (quimicos) como de desarrollodo de software.

Muchísimo material para leer sobre el tópico hay en este sitio. Yo empezaré en cuanto tenga tiempo. Invito también, de manera enfática, a leer la justificación de que el software desarrollado en el marco de mi proyecto integrador lo libero como Software Libre

Más allá del negocio

Además de lo expuesto se me cruzan cuestionamientos sobre el rol de la universidad y el estado en este escenario.

Como ciudadano y futuro profesional festejo la inversión que en los últimos años el Estado ha realizado para ciencia y tecnología. Apostar a generar valor en áreas tan estrategicas como el software es necesario, y plantearlo desde el punto de vista de fortalecer la industria nacional (pymes) es entonces doblemente valioso.

Pero...

¿son los laboratorios de investigación de las universidades actores que deben generar recursos por sí mismos? ¿A quien pertenece el conocimiento que se produce en la universidad? ¿A quién debe pertenecer? ¿La universidad actual responde a un proyecto de país ? ¿Existe tal cosa? ¿No debiera el Estado, formar parte societaria en los consorcios para los cuales está aportando grandes recursos, además de ser agente regulador? ¿quién y cómo regula la relación e intereses existentes entre los grupos de investigación y el sector privado? ¿no se puede prestar a malversaciones y abuso del sello de la universidad?

Bonito, feito pero efectivo

Hace 5 años que laburo en una organización de trabajo barrial. Como no recibimos aportes privados y rara vez del Estado (a veces presentamos proyectos a programas) siempre andamos buscando formas de sostener económicamente nuestras actividades.

Un recurso, no por viejo en desuso, es el del "bono contribución", que muchas veces incluye un sorteo con algún premio.

Además de la algo ingrata tarea de venderlos, hay que hacerlos. Alguno tiene uno de esos sellos numeradores (un mecanimismo que va incrementando el contador automáticamente ) pero igual cualquiera se vuelve un burócrata (más cuando hay que sellar dos veces - el bono y talón -).

Así que ayer, en 20 minutos hice Bonito, un programa feo pero efectivo.

Primero hice una plantilla en Inkscape donde entran 6 de estos bonitos en un A4.

Como el SVG es XML que es texto, la marca XXXX se puede reemplazar fácilmente por el número que corresponda. Yo quería que me quedaran así:

De esta manera, simplemente tengo que meter 6 broches a la izquierda y recortar, ya quedan ordenados para repartir entre los compañeros y compañeras.

Para algunas cosas, Inkscape se puede usar por línea de comandos, por ejemplo para convertir entre los formatos que soporta. Así paso el SVG con los números reemplazados a un PDF. Después concateno todos los PDF de una tanda (por ejemplo agrupados de a 10, como en el dibujo) con Ghostscript.

Acá el código:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       bonito.py
#
#       Copyright 2010 Martin Gaitán <gaitan(at)gmail.com>
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import sys, os
import subprocess
import glob

def markToNum(content, num, mark='XXXX'):
    num_as_string = '0' * (4 - len(str(num))) + str(num)
    return content.replace(mark, num_as_string, 2)

def main():

    if len(sys.argv) <= 1:
        print "Modo de uso:"
        print sys.argv[0] + " archivo.svg"
        sys.exit(1)
    else:
        with open(sys.argv[1]) as input:
            svg_content = input.read()

        index_from = int(raw_input('Desde [0]') or 0)
        number_by_page = int(raw_input('Num por pagina [6]') or 6)
        grouped_by =  int(raw_input('agrupar de a [20]') or 20)
        planchas = int(raw_input('Planchas [1]') or 1)


        for plancha in range(planchas):
            counter_from = index_from + plancha*number_by_page*grouped_by

            for pagina in range(grouped_by):
                page_content = svg_content
                for bono in range(number_by_page):
                    num_remplazo = counter_from + bono*grouped_by + pagina
                    page_content = markToNum(page_content, num_remplazo)

                with open('temp.svg', 'w') as output_svg:

                    output_svg.write(page_content)

                subprocess.call(["inkscape", "-f", 'temp.svg', '--export-dpi=150', '-A', 'temp%s.pdf' % ("0"*(4 - len(str(pagina))) + str(pagina)) ])

            generator = ['gs',
                         '-q',
                         '-sPAPERSIZE=a4',
                         '-dNOPAUSE',
                         '-dBATCH',
                         '-sDEVICE=pdfwrite',
                         '-sOutputFile=%s-%i-%i.pdf' % (sys.argv[1][:-4], counter_from, num_remplazo),] + \
                        ['temp%s.pdf' % ("0"*(4 - len(str(pagina))) + \
                            str(pagina)) for pagina in range(grouped_by)]

            subprocess.call(generator)

            for temp in glob.glob('temp*'):
                os.remove(temp)

if __name__ == '__main__':
    main()

Nada que no se pueda hacer con Bash, cierto, pero mucho más fácil de escribir (y de leer).

De paso, acá está la plantilla, por si a alguno le sirve.

Django Dash: hacé una aplicación web en 2 dias

Leemos en la web

The Django Dash is a chance for Django enthusiasts to flex their coding skills a little and put a fine point on “perfectionists with deadlines” by giving you a REAL deadline. 48 hours from start to stop to produce the best app you can and have a little fun in the process.

Suena a PyWeek [1], pero en 2 dias. ¿Será indicio de lo rápido que se programa con Django?

Atenti que la inscripción cierra el 8 de agosto (mañana).

Usando PubSub para un panel de mensajes

La aplicación que estoy desarrollando, GPEC, genera muchos mensajes que pueden ser útiles para el usuario.

En softwares con GUI’s sencillas suele utilizarse la barra de estado para mostrar mensajes contextuales e información sobre el resultado de una acción. Pero si estos mensajes son muchos y de diversa índole, este espacio puede no bastar, sobre todo por la volatilidad de la información que la barra de estado muestra.

Una solución posible es usar un panel con un ListCtrl de manera de poder agregar los mensajes quedando un registro completo y cronológico; un log propiamente dicho.

image0 Surge acá un detalle: si los mensajes se generan desde "cualquier parte" del programa, todas esas "partes" deberían tener referencia de la instancia del panel/widget de log.

Un ejemplo: todos las demostraciones de la aplicación de demo de wxPython tienen la siguiente estructura:

class TestPanel(wx.Panel):

    def __init__(self, parent, log):
        self.log = log
        wx.Panel.__init__(self, parent, -1)
        ...

        self.log.WriteText(' message ... ')

Esto vuelve la aplicación muy acoplada: la instancia log (que es un caja de texto en el demo) se pasea por distintos namespaces para estar disponible en todos lados.

Denotemos la falta de flexibilidad: ¿que pasa si queremos ’loggear’ mensajes desde un objeto donde no estaba previsto? ¿qué pasa si queremos cambiar el widget que muestra los mensajes y el método para anexar mensajes tiene otro nombre? ¿y si además de mostrarlos, con algunos los mensajes queremos hacer alguna otra cosa (ejecutar un simple sonido de alarma, por ejemplo) ?

PubSub

Una manera más elegante y eficiente es utilizar PubSub una implentación en Python del paradigma de publicación-suscripción.

System Message: WARNING/2 (<string>, line 56)

Explicit markup ends without a blank line; unexpected unindent.

Su implementación es trivial. Incluso viene incorporado dentro de wxPython.

El Publisher (generalmente importado como pub) envía mensajes (cualquier objeto python) asociados a un tópico (una cadena)

from wx.lib.pubsub import Publisher as pub

pub.sendMessage('log', ('ok', 'Ready! You can send any message from anywhere.') )

En este ejemplo, el tópico, que elegí arbritrariamente, es ’log’, y el mensaje es la tupla ('ok', 'Ready! You can send any message from anywhere.')

Del otro lado del mostrador, cualquiera puede suscribirse a los mensajes con determinado tópico y asociarlos a un método/función.

class LogMessagesPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)

        pub.subscribe(self.OnAppendLog, 'log')

    def OnAppendLog(self, msg):
        data = msg.data<img105|center>
        #do your things with the data!

pub.subscribe bindea los mensajes con tópico ’log’ al método OnAppendLog pasando un objeto msg. Nuestro mensaje real, la tupla que enviamos, está en msg.data

Nada impide que sean muchos los objetos que envien mensajes con tópico ’log’ y muchos otros estén suscriptos a él. Y esto funciona sin importar dónde ocurra cada cosa! [1].

Como ejemplo completo dejo el panel log. Podés probarlo creando otro frame independiente que envie mensajes de log.

#       Log Panel: example of PubSub implementation
#
#       Copyright 2010 Martin Gaitán <gaitan(at)gmail.com>
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.


import wx
from wx.lib.pubsub import Publisher as pub
import time
import sys

from wx.lib.embeddedimage import PyEmbeddedImage

icons = {}
icons['ok'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAK5QTFRF"
    "AAAA////2vO/+P3z+f31VJkEVJcEU5UEUpQEcc0GcMsGcMkGb8gGbcYGbcQGbMEGZLYFXKYF"
    "UJAET48ETo0ETYwEasAGddALd9ANeNERetETe9IVfdIXfdMZftMbf9Mci9cwlNpAlttElttF"
    "l9xHmtxLm91NnN1Pnd1Rnt5ToN9Xod9ZpuFhp+FjqOFlquJnr+RyseR0veiKwuqSy+2j4PTI"
    "9vzv9/zxidYs3fPCGN7g1AAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsT"
    "AAALEwEAmpwYAAAAB3RJTUUH2gcMBC0Irn+MQAAAAINJREFUGNNjYCAJ6Bui8vXkpAxQ+LJa"
    "ylx6CL6utKYGJyszkGVpAuZLaahzsYH4phYKxgwMOpJqatzsID6ThbIGt5G+hKoKDweIz8Ai"
    "YaGtIiOnrMIrxAwxjJlXXl1VSYlPmBlmPLOAuLIivwgzwkJzQV4xUWZkJ5mzi7CgOtqMBbcH"
    "AVouCiZO5Tf/AAAAAElFTkSuQmCC")

#----------------------------------------------------------------------
icons['error'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAA"
    "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1QsKCTIOk1O0sAAAAhpJREFUOMudk71Pk1EU"
    "xn/3bQvlo76gxBpNoGibArYLxJREh466MmnSgRH+IjsydGYjspBAWEQHMJIWmmhj+LAkpC1N"
    "Wyj2PcfhfUsQiRpPcnOTm/P8nuQ59xhuVA5SwDyQBsa95xKwDixlYOt6v7khfjscCi1MJpME"
    "w2F8AwOoKp2zM87LZYqFAtVmM5uBxd8AOXg3nUy+HJmZ4XJ7G+f0FBwHVUUMWEN38U9NUt3d"
    "5VOxuJqBVwC+rvN0Mvn6XixGe3MTbTRABFVBRUEEabboHHyjPxbDtqzoi2r1wTKsmBykhkOh"
    "98/m5rhYW0OdDqqKKqh6YgXFhakxBFMpPm9sUGu1Zi1gfjKR4HJnB3UcEFABFXEP6oEUVMFx"
    "uMjneTwWAZj3A+lg+D4/PnxERRkrf+dPtW/fQao1Ag8fAaT9wLjVP4iKG9jfSkRR7WB6AgDj"
    "FnCV9r8A8PpUBQA/UOrUzyYUg6rDl5ERt0HEvVE3PDdVRN0gtdkCKFnA+vnJCcYeuiL/Iu46"
    "iidWwRocpF2rAaxbwNL+3h6BeNwly3WRNzrtTkNQY/BHIpQODwGWrAxs1VqtbKWQp2fqqQu5"
    "5op3qygYQ080RuPggHq7nc3Alg9gGVaeVyqzts8XHUgkcBoN5KJ99RMVMHaI3vgEjXKZwvHx"
    "agbe3LpMdl/fwpPRUQK2jentRUVwmk069Tpfj466zou3buP/rPNPwkdmHrlYdncAAAAASUVO"
    "RK5CYII=")

#----------------------------------------------------------------------
icons['info'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAA"
    "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1QMCBiAyOlCc9wAAADV0RVh0Q29tbWVudAAo"
    "YykgMjAwNCBKYWt1YiBTdGVpbmVyCgpDcmVhdGVkIHdpdGggVGhlIEdJTVCQ2YtvAAACq0lE"
    "QVQ4y5WSS0hUcRTGv//cO+O9OtdHJllqZlI+CEUiE8ShBiXUpKKQbGLKcGG0CBOyRcsQMapF"
    "j02ii4igAgMDJcgQSXQmSkrNijSVTJ1xdJy5d6739W/loDURfqtz4Pt+nHM4BBFUXNnUTCkc"
    "BiUpIISYiDHLEPq0/2Vr459edn1TVN6UwTCk72JtZVq5vQBxAg9VN+DxBVN7ej9cISbmLKV6"
    "SX9X69eIAJYlb++11G/fk7ljLBBSRmd9khJaVVlF06NtJflZ6enJe1tuPXYBiF/LMGtFSdXV"
    "2/Xnj5YVH8z5rKi6m7eYFQBEpyCqTg3vSmiWi+YQzXM7dSYlc3L8bScAmMIkhj1Xbi9AIKSO"
    "sSxDLWbGIIRQUBiabiiUwuganOwuKswBw3Kn1nJhwKpC4wUrD1nRV1XNSDQzpguJsZxzV7Lg"
    "yN+91QElkDrlEf08zyEgqVxtXYNpww0IIUTRdEiyajIMKiZYo+D1Sy5RCjErQUkbnpaGFUU3"
    "qaqxFuEBiGFADM9KXl8gRpRVy4qk+jOSYzH6w/deVHQ6tyQp7m+LHsFqSfD6g4gTeLWjrVnc"
    "sIKZ0QZ63gyDj2JzJ34tiwDw0yfqHye8/t7hmYUlUTZX7E+rGHKNwcph4q8bbBNCxzu7+uWZ"
    "6fnstCTroWvtAzfPHM665P4yH2JYkui0Z1X7F5dy3IMjqsD6SsOrr/+DY6cbq2QqPC8rLbIU"
    "HshGNM9B1XX4lkUMucbxzjWixXHS5SftNx5EBABAbV1DYohucQVkpDtOJDGEACwLdHXPBRnN"
    "v6+j7c4U/qeaGrvV6bRRz2IfFWUX/T75jDqdNhrJy/6DoQLA/bsPkZ2bjempaWxaTqeNzsy+"
    "MgLSEP00+sLY7AQAUFV98nqNIMQfWVhYeJ2XF/sokuk3Vkw2XnyKHQQAAAAASUVORK5CYII=")

#----------------------------------------------------------------------
icons['warning'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA"
    "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gIQDictt+6SdwAAAehJREFUOMuVk8FKG2EU"
    "hb87mc40iTODEJlK7UZDxCCGQUqaMASCSGODdBe6ECJddOcwG/EJXBt0ZcBCwEVJVgWtr1AI"
    "PoBQXAQKhRZc1EBJMX831hLItPbAvYvL4Ttnc4UIhfDIgLcAA3jdgC/jfHoUwISjeXgOcAFH"
    "QHWcT4tI91KwWlhY0J6kUloKVkPw7g0wYS8NemJ3l0yzSRp0E/buBQihOAXFbLksej6Pns+T"
    "LZdlCoohFPmXdqB7AsNep6M8z1Oe56lep6NOYLgD3b82CGHFhaWM74vh+ziOg+M4GL5PxvfF"
    "haUQViIBJuxnwUgEAaJpWJaFZVmIppEIArJgmLA/FhDC+jSk5woFjFIJEcG2bWzbRkQwSiXm"
    "CgWmIR3C+gggBDGgsXibruk6IvKngQiarpMIAhbBMKARgsDtCuHVLLReLC8bk2dnEIuhlKLf"
    "7wOQTCYREbi54WptjQ/n54NLqDfgnYQQewxXT8GaPz7mYbWKUgqlFLVaDYB2u42IICL8OD3l"
    "YmODLnz/DJOawGYczJlcjnilcmcUEVzXxXXdkVu8UmEmlyMOpsCmbMPXl5CaPThgol6/S/89"
    "wAhARLhutbjc2uI9fJNDGD4DecD/6SfwEZT+CQ4H8GYi4i+idA3DHjR/AZfefQgctOETAAAA"
    "AElFTkSuQmCC")

class LogMessagesPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)

        self.list = wx.ListCtrl(self, -1,  style=  wx.LC_REPORT|wx.SUNKEN_BORDER)

        self.setupList()

        sizer = wx.BoxSizer()
        sizer.Add(self.list, 1, wx.EXPAND)
        self.SetSizerAndFit(sizer)

        pub.subscribe(self.OnAppendLog, 'log')

    def setupList(self):
        """sets columns and append a imagelist """

         #setup first column (which accept icons)
        info = wx.ListItem()
        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
        info.m_image = -1
        info.m_format = 0
        info.m_text = "Message"
        self.list.InsertColumnInfo(0, info)
        self.list.SetColumnWidth(0, 550)

        #insert second column
        self.list.InsertColumn(1, 'Time')
        self.list.SetColumnWidth(1, 70)

        #setup imagelist and an associated dict to map status->image_index
        imgList = wx.ImageList(16, 16)


        self.icon_map = {}
        for key, bitmap in icons.iteritems():
            indx = imgList.Add( bitmap.GetBitmap() )
            self.icon_map[key] = indx
        self.list.AssignImageList(imgList, wx.IMAGE_LIST_SMALL)

    def OnAppendLog(self, msg):
        ico = self.icon_map[msg.data[0]]
        message = msg.data[1]
        index = self.list.InsertImageStringItem(sys.maxint, message, ico)
        self.list.SetStringItem(index, 1, time.strftime('%H:%M:%S'))

        self.list.EnsureVisible(index) #keep scroll at bottom

class TestFrame(wx.Frame):
    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, "Log Panel demo")
        self.log = LogMessagesPanel(self, -1)
        self.SetSize((620,150))
        self.SetMinSize((620,150))

if __name__ == "__main__":

    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    main_frame = TestFrame(None, -1)
    app.SetTopWindow(main_frame)
    main_frame.Show()

    pub.sendMessage('log', ('ok', 'Ready! You can send any message from anywhere.') )
    pub.sendMessage('log', ('info', "Just import pubsub.Publisher and send a 'log' message") )
    pub.sendMessage('log', ('warning', "The message data is a tuple ('icon', 'message') ") )

Mantener el scroll vertical de wx.ListCtrl abajo

Un típico uso de un ListCtrl es usarlo para mostrar un Log de la aplicación que muestra mensajes al usuario, similar a lo que puede hacer la barra de estada, pero con la ventaja (complementaria, si se quiere), de mantener un historial de los eventos ocurridos.

System Message: WARNING/2 (<string>, line 9)

Explicit markup ends without a blank line; unexpected unindent.

En tal caso, primero, hay una decisión de diseño que hacer: ¿los nuevos mensajes se agregan al inicio (como un blog) o al final de la lista (como los comentarios) ?

No sé si existe una respuesta canónica a la cuestión, pero infiriendo (de los pelos) el sentido de lectura occidental (de izquierda a derecha y de arriba hacia abajo) decidí que "un nuevo contenido" debe estar abajo del anterior.

Eso trae aparejado un nuevo problema: si se agrega un nuevo item a ListCtrl al final y ya hay más de los que caben en el espacio visible del control, el último (el más importante) no se verá en pantalla y el usuario debería hacer un scroll hasta el final para verlo. Es decir, para un panel de Log, el scroll vertical siempre debería mantenerse abajo.

Por suerte no hay que liarse con generar eventos programaticamente, ni manipular el scroll, ni obtener dimensiones del widget. He aquí una solución "fits your brain": el método EnsureVisible(index)

Por ejemplo:

def OnAppendLog(self, msg):
    ico = self.icon_map[msg.data[0]]
    message = msg.data[1]
    index = self.list.InsertImageStringItem(sys.maxint, message, ico)
    self.list.SetStringItem(index, 1, time.strftime('%H:%M:%S'))

    self.list.EnsureVisible(index) #keep scroll at bottom

Preparados, listos... en un rato

Hace aproximadamente dos meses que estoy trabajando en lo que será mi proyecto final, para recibir el título de ingeniero en computación.

El proyecto es una aplicación de uso "científico y académico" implementada en Python que sirve para obtener diagramas de equilibro termodinámico entre fases fluidas de sistemas binarios. Toda la batahola está en este repo.

En concreto, el proyecto se circunscribe a la generación y [análisis sintáctico] de archivos de texto con el que la aplicación se comunica con los programas que implementan los algoritmos de cálculo, que están implementados en Fortran, y son, literalmente, unas cajas negras. Se procesan esos archivos para obtener arrays con los que se plotean las distintas curvas.

Utilizo principalmente wxPython, Matplotlib y Numpy. Vale destacar, ya que mucho de un trabajo final es documentación, que escribo en reStructuredText, renderizando con Sphinx.

Video SIS 771/671 en Ubuntu

System Message: WARNING/2 (<string>, line 5); backlink

Duplicate explicit target name: "este xorg.conf".

Esto es una ayuda memoria para mi yo dubitativo del futuro: para hacer funcionar el video de la notebook que es SiS 671/771 en Ubuntu (a partir de la versión 9.10) instalar este paquete y [STRIKEOUT:este xorg.conf] este xorg.conf.

Para pantalla dual con LCD externo a resolución full 1280x1024

# /etc/X11/xorg.conf (Xorg X Window System server configuration file)
#
# This file was generated by fll_xorgconfig, the F.U.L.L.S.T.O.R.Y.
# Xorg Configuration tool.
#
# Edit this file with caution, and see the xorg.conf(5) manual page.
# (Type "man xorg.conf" at the shell prompt.)
#
Section "ServerLayout"
    Identifier  "Xorg Configured"
    Screen      "Screen 0"
EndSection

#Section "ServerFlags"
#   Option      "AllowMouseOpenFail"    "true"
#   Option      "DontZap"       "false"
#EndSection

Section "Device"
    Identifier  "Device 0"
    Driver      "sis671"
    #Driver     "vesa"
    BoardName   "Silicon Integrated Systems [SiS] 771/671 PCIE VGA Display Adapter"
    Option      "EnableSisCtrl" "yes"
    Option      "DRI" "off"
    Option      "MergedFBAuto" "true"
    Option      "MergedNonRectangular" "on"
    Option      "Merged DPI" "100 100"
    Option      "MetaModes" "1280x1024-1280x800 640x460-640x460 1280x800-1280x1024"
    BusID       "PCI:1:0:0"
EndSection

Section "Monitor"
    Identifier  "Monitor 0"
    ModelName   "Default Monitor"
    HorizSync   28-96
    VertRefresh 50-75
    # 640x350 @ 85.00 Hz (GTF) hsync: 31.28 kHz; pclk: 25.02 MHz
    Modeline "640x350"  25.02  640 656 720 800  350 351 354 368  -HSync +Vsync
    # 640x400 @ 85.00 Hz (GTF) hsync: 35.78 kHz; pclk: 29.20 MHz
    Modeline "640x400"  29.20  640 664 728 816  400 401 404 421  -HSync +Vsync
    # 640x480 @ 60.00 Hz (GTF) hsync: 29.82 kHz; pclk: 23.86 MHz
    Modeline "640x480"  23.86  640 656 720 800  480 481 484 497  -HSync +Vsync
    # 640x480 @ 72.00 Hz (GTF) hsync: 36.07 kHz; pclk: 29.43 MHz
    Modeline "640x480"  29.43  640 664 728 816  480 481 484 501  -HSync +Vsync
    # 640x480 @ 75.00 Hz (GTF) hsync: 37.65 kHz; pclk: 30.72 MHz
    Modeline "640x480"  30.72  640 664 728 816  480 481 484 502  -HSync +Vsync
    # 640x480 @ 85.00 Hz (GTF) hsync: 42.92 kHz; pclk: 35.71 MHz
    Modeline "640x480"  35.71  640 672 736 832  480 481 484 505  -HSync +Vsync
    # 640x480 @ 100.00 Hz (GTF) hsync: 50.90 kHz; pclk: 43.16 MHz
    Modeline "640x480"  43.16  640 680 744 848  480 481 484 509  -HSync +Vsync
    # 720x400 @ 85.00 Hz (GTF) hsync: 35.78 kHz; pclk: 32.64 MHz
    Modeline "720x400"  32.64  720 744 816 912  400 401 404 421  -HSync +Vsync
    # 768x576 @ 60.00 Hz (GTF) hsync: 35.82 kHz; pclk: 34.96 MHz
    Modeline "768x576"  34.96  768 792 872 976  576 577 580 597  -HSync +Vsync
    # 768x576 @ 72.00 Hz (GTF) hsync: 43.27 kHz; pclk: 42.93 MHz
    Modeline "768x576"  42.93  768 800 880 992  576 577 580 601  -HSync +Vsync
    # 768x576 @ 75.00 Hz (GTF) hsync: 45.15 kHz; pclk: 45.51 MHz
    Modeline "768x576"  45.51  768 808 888 1008  576 577 580 602  -HSync +Vsync
    # 768x576 @ 85.00 Hz (GTF) hsync: 51.42 kHz; pclk: 51.84 MHz
    Modeline "768x576"  51.84  768 808 888 1008  576 577 580 605  -HSync +Vsync
    # 768x576 @ 100.00 Hz (GTF) hsync: 61.10 kHz; pclk: 62.57 MHz
    Modeline "768x576"  62.57  768 816 896 1024  576 577 580 611  -HSync +Vsync
    # 800x600 @ 56.00 Hz (GTF) hsync: 34.72 kHz; pclk: 35.55 MHz
    Modeline "800x600"  35.55  800 832 912 1024  600 601 604 620  -HSync +Vsync
    # 800x600 @ 60.00 Hz (GTF) hsync: 37.32 kHz; pclk: 38.22 MHz
    Modeline "800x600"  38.22  800 832 912 1024  600 601 604 622  -HSync +Vsync
    # 800x600 @ 72.00 Hz (GTF) hsync: 45.07 kHz; pclk: 46.87 MHz
    Modeline "800x600"  46.87  800 840 920 1040  600 601 604 626  -HSync +Vsync
    # 800x600 @ 75.00 Hz (GTF) hsync: 47.02 kHz; pclk: 48.91 MHz
    Modeline "800x600"  48.91  800 840 920 1040  600 601 604 627  -HSync +Vsync
    # 800x600 @ 85.00 Hz (GTF) hsync: 53.55 kHz; pclk: 56.55 MHz
    Modeline "800x600"  56.55  800 840 928 1056  600 601 604 630  -HSync +Vsync
    # 800x600 @ 100.00 Hz (GTF) hsync: 63.60 kHz; pclk: 68.18 MHz
    Modeline "800x600"  68.18  800 848 936 1072  600 601 604 636  -HSync +Vsync
    # 1024x600 @ 60.00 Hz (GTF) hsync: 37.32 kHz; pclk: 48.96 MHz
    Modeline "1024x600"  48.96  1024 1064 1168 1312  600 601 604 622  -HSync +Vsync
    # 1024x768 @ 60.00 Hz (GTF) hsync: 47.70 kHz; pclk: 64.11 MHz
    Modeline "1024x768"  64.11  1024 1080 1184 1344  768 769 772 795  -HSync +Vsync
    # 1024x768 @ 70.00 Hz (GTF) hsync: 56.00 kHz; pclk: 76.16 MHz
    Modeline "1024x768"  76.16  1024 1080 1192 1360  768 769 772 800  -HSync +Vsync
    # 1024x768 @ 75.00 Hz (GTF) hsync: 60.15 kHz; pclk: 81.80 MHz
    Modeline "1024x768"  81.80  1024 1080 1192 1360  768 769 772 802  -HSync +Vsync
    # 1024x768 @ 85.00 Hz (GTF) hsync: 68.60 kHz; pclk: 94.39 MHz
    Modeline "1024x768"  94.39  1024 1088 1200 1376  768 769 772 807  -HSync +Vsync
    # 1024x768 @ 100.00 Hz (GTF) hsync: 81.40 kHz; pclk: 113.31 MHz
    Modeline "1024x768"  113.31  1024 1096 1208 1392  768 769 772 814  -HSync +Vsync
    # 1152x864 @ 75.00 Hz (GTF) hsync: 67.65 kHz; pclk: 104.99 MHz
    Modeline "1152x864"  104.99  1152 1224 1352 1552  864 865 868 902  -HSync +Vsync
    # 1152x864 @ 60.00 Hz (GTF) hsync: 53.70 kHz; pclk: 81.62 MHz
    Modeline "1152x864"  81.62  1152 1216 1336 1520  864 865 868 895  -HSync +Vsync
    # 1152x864 @ 85.00 Hz (GTF) hsync: 77.10 kHz; pclk: 119.65 MHz
    Modeline "1152x864"  119.65  1152 1224 1352 1552  864 865 868 907  -HSync +Vsync
    # 1152x864 @ 100.00 Hz (GTF) hsync: 91.50 kHz; pclk: 143.47 MHz
    Modeline "1152x864"  143.47  1152 1232 1360 1568  864 865 868 915  -HSync +Vsync
    # 1152x900 @ 77.00 Hz (GTF) hsync: 72.46 kHz; pclk: 112.45 MHz
    Modeline "1152x900"  112.45  1152 1224 1352 1552  900 901 904 941  -HSync +Vsync
    # 1152x900 @ 66.00 Hz (GTF) hsync: 61.71 kHz; pclk: 94.79 MHz
    Modeline "1152x900"  94.79  1152 1224 1344 1536  900 901 904 935  -HSync +Vsync
    # 1280x768 @ 60.00 Hz (GTF) hsync: 47.70 kHz; pclk: 80.14 MHz
    Modeline "1280x768"  80.14  1280 1344 1480 1680  768 769 772 795  -HSync +Vsync
    # 1280x800 @ 60.00 Hz (GTF) hsync: 49.68 kHz; pclk: 83.46 MHz
    Modeline "1280x800"  83.46  1280 1344 1480 1680  800 801 804 828  -HSync +Vsync
    # 1280x960 @ 60.00 Hz (GTF) hsync: 59.64 kHz; pclk: 102.10 MHzx
    Modeline "1280x960"  102.10  1280 1360 1496 1712  960 961 964 994  -HSync +Vsync
    # 1280x960 @ 72.00 Hz (GTF) hsync: 72.07 kHz; pclk: 124.54 MHz
    Modeline "1280x960"  124.54  1280 1368 1504 1728  960 961 964 1001  -HSync +Vsync
    # 1280x960 @ 75.00 Hz (GTF) hsync: 75.15 kHz; pclk: 129.86 MHz
    Modeline "1280x960"  129.86  1280 1368 1504 1728  960 961 964 1002  -HSync +Vsync
    # 1280x960 @ 85.00 Hz (GTF) hsync: 85.68 kHz; pclk: 149.43 MHz
    Modeline "1280x960"  149.43  1280 1376 1512 1744  960 961 964 1008  -HSync +Vsync
    # 1280x960 @ 100.00 Hz (GTF) hsync: 101.70 kHz; pclk: 178.99 MHz
    Modeline "1280x960"  178.99  1280 1376 1520 1760  960 961 964 1017  -HSync +Vsync
    # 1280x1024 @ 60.00 Hz (GTF) hsync: 63.60 kHz; pclk: 108.88 MHz
    Modeline "1280x1024"  108.88  1280 1360 1496 1712  1024 1025 1028 1060  -HSync +Vsync
    # 1280x1024 @ 67.00 Hz (GTF) hsync: 71.29 kHz; pclk: 123.19 MHz
    Modeline "1280x1024"  123.19  1280 1368 1504 1728  1024 1025 1028 1064  -HSync +Vsync
    # 1280x1024 @ 75.00 Hz (GTF) hsync: 80.17 kHz; pclk: 138.54 MHz
    Modeline "1280x1024"  138.54  1280 1368 1504 1728  1024 1025 1028 1069  -HSync +Vsync
    # 1280x1024 @ 76.00 Hz (GTF) hsync: 81.32 kHz; pclk: 141.82 MHz
    Modeline "1280x1024"  141.82  1280 1376 1512 1744  1024 1025 1028 1070  -HSync +Vsync
    # 1280x1024 @ 85.00 Hz (GTF) hsync: 91.38 kHz; pclk: 159.36 MHz
    Modeline "1280x1024"  159.36  1280 1376 1512 1744  1024 1025 1028 1075  -HSync +Vsync
    # 1280x1024 @ 100.00 Hz (GTF) hsync: 108.50 kHz; pclk: 190.96 MHz
    Modeline "1280x1024"  190.96  1280 1376 1520 1760  1024 1025 1028 1085  -HSync +Vsync
    # 1368x768 @ 60.00 Hz (GTF) hsync: 47.70 kHz; pclk: 85.86 MHz
    Modeline "1368x768"  85.86  1368 1440 1584 1800  768 769 772 795  -HSync +Vsync
    # 1440x900 @ 60.00 Hz (GTF) hsync: 55.92 kHz; pclk: 106.47 MHz
    Modeline "1440x900"  106.47  1440 1520 1672 1904  900 901 904 932  -HSync +Vsync
    # 1400x1050 @ 60.00 Hz (GTF) hsync: 65.22 kHz; pclk: 122.61 MHz
    Modeline "1400x1050"  122.61  1400 1488 1640 1880  1050 1051 1054 1087  -HSync +Vsync
    # 1400x1050 @ 72.00 Hz (GTF) hsync: 78.77 kHz; pclk: 149.34 MHz
    Modeline "1400x1050"  149.34  1400 1496 1648 1896  1050 1051 1054 1094  -HSync +Vsync
    # 1400x1050 @ 75.00 Hz (GTF) hsync: 82.20 kHz; pclk: 155.85 MHz
    Modeline "1400x1050"  155.85  1400 1496 1648 1896  1050 1051 1054 1096  -HSync +Vsync
    # 1400x1050 @ 85.00 Hz (GTF) hsync: 93.76 kHz; pclk: 179.26 MHz
    Modeline "1400x1050"  179.26  1400 1504 1656 1912  1050 1051 1054 1103  -HSync +Vsync
    # 1400x1050 @ 100.00 Hz (GTF) hsync: 111.20 kHz; pclk: 214.39 MHz
    Modeline "1400x1050"  214.39  1400 1512 1664 1928  1050 1051 1054 1112  -HSync +Vsync
    # 1440x900 @ 60.00 Hz (GTF) hsync: 55.92 kHz; pclk: 106.47 MHz
    Modeline "1440x900"  106.47  1440 1520 1672 1904  900 901 904 932  -HSync +Vsync
    # 1600x1200 @ 60.00 Hz (GTF) hsync: 74.52 kHz; pclk: 160.96 MHz
    Modeline "1600x1200"  160.96  1600 1704 1880 2160  1200 1201 1204 1242  -HSync +Vsync
    # 1600x1200 @ 65.00 Hz (GTF) hsync: 80.99 kHz; pclk: 176.23 MHz
    Modeline "1600x1200"  176.23  1600 1712 1888 2176  1200 1201 1204 1246  -HSync +Vsync
    # 1600x1200 @ 70.00 Hz (GTF) hsync: 87.43 kHz; pclk: 190.25 MHz
    Modeline "1600x1200"  190.25  1600 1712 1888 2176  1200 1201 1204 1249  -HSync +Vsync
    # 1600x1200 @ 75.00 Hz (GTF) hsync: 93.97 kHz; pclk: 205.99 MHz
    Modeline "1600x1200"  205.99  1600 1720 1896 2192  1200 1201 1204 1253  -HSync +Vsync
    # 1600x1200 @ 85.00 Hz (GTF) hsync: 107.10 kHz; pclk: 234.76 MHz
    Modeline "1600x1200"  234.76  1600 1720 1896 2192  1200 1201 1204 1260  -HSync +Vsync
    # 1600x1200 @ 100.00 Hz (GTF) hsync: 127.10 kHz; pclk: 280.64 MHz
    Modeline "1600x1200"  280.64  1600 1728 1904 2208  1200 1201 1204 1271  -HSync +Vsync
    # 1680x1050 @ 60.00 Hz (GTF) hsync: 65.22 kHz; pclk: 147.14 MHz
    Modeline "1680x1050"  147.14  1680 1784 1968 2256  1050 1051 1054 1087  -HSync +Vsync
    # 1792x1344 @ 60.00 Hz (GTF) hsync: 83.46 kHz; pclk: 202.97 MHz
    Modeline "1792x1344"  202.97  1792 1920 2112 2432  1344 1345 1348 1391  -HSync +Vsync
    # 1792x1344 @ 75.00 Hz (GTF) hsync: 105.23 kHz; pclk: 259.27 MHz
    Modeline "1792x1344"  259.27  1792 1928 2128 2464  1344 1345 1348 1403  -HSync +Vsync
    # 1856x1392 @ 60.00 Hz (GTF) hsync: 86.46 kHz; pclk: 218.57 MHz
    Modeline "1856x1392"  218.57  1856 1992 2192 2528  1392 1393 1396 1441  -HSync +Vsync
    # 1856x1392 @ 75.00 Hz (GTF) hsync: 108.97 kHz; pclk: 277.23 MHz
    Modeline "1856x1392"  277.23  1856 2000 2200 2544  1392 1393 1396 1453  -HSync +Vsync
    # 1920x1200 @ 60.00 Hz (GTF) hsync: 74.52 kHz; pclk: 193.16 MHz
    Modeline "1920x1200"  193.16  1920 2048 2256 2592  1200 1201 1204 1242  -HSync +Vsync
    # 1920x1440 @ 60.00 Hz (GTF) hsync: 89.40 kHz; pclk: 234.59 MHz
    Modeline "1920x1440"  234.59  1920 2064 2272 2624  1440 1441 1444 1490  -HSync +Vsync
    # 1920x1440 @ 75.00 Hz (GTF) hsync: 112.73 kHz; pclk: 297.59 MHz
    Modeline "1920x1440"  297.59  1920 2072 2280 2640  1440 1441 1444 1503  -HSync +Vsync
EndSection

Section "Screen"
    Identifier  "Screen 0"
    Monitor     "Monitor 0"
    DefaultColorDepth 24
    SubSection "Display"
        Depth   24
        Modes   "1200x800"
    EndSubSection
EndSection

Section "Extensions"
    Option  "Composite" "off"

Hemisferio derecho

This is perhaps the most beautiful time in human history; it is really pregnant with all kinds of creative possibilities made possible by science and technology which now constitute the slave of man, if man is not enslaved by it.*

Jonas Salk

No sólo de la técnica vive el hombre.

Monetizar pagos virtuales en Argentina sin costo

Para los trabajadores online freelance (programadores, diseñadores, traductores, etc) trabajar para el exterior es un objetivo primordial. Aunque en algunos sectores la competencia es leonina y se pauperizan los honorarios, el tipo de cambio del peso respecto a otra monedas fuertes hace que siga siendo muy provechoso.

En traducción, que tiene un sistema de presupuesto lineal basado en cantidad de palabras del texto del idioma origen, es fácil cuantificar la diferencia. En Argentina se cobra entre $0,10 y $0,15 (pesos argentinos) por palabra, mientras que en europa se cobra entre 0.05 € y 0.07€. O sea, se gana hasta el triple por realizar el mismo trabajo.

El problema, al menos desde Argentina, es cobrar. Aquí comparto mi experiencia esperando ayudar a otros.

De Adsense a la billetera

image0 Una forma simple y rápida pero bastante onerosa es Western Union. Es un servicio de transferencias de dinero persona a persona (física o jurídica), con sedes en casi todo el mundo. Tiene dos tipos de servicio: Quick Cash y 24 horas.

Western Union tiene multiples instancias de lucro, que lo vuelven un ser. Se le cobra entre el 2.5 y 5% al emisor (desde Europa a Argentina el límite de transferencia es 500€) y además, el receptor cobra en moneda local a un tipo de cambio definido por WU que siempre está entre un 3 y 6% por debajo del tipo de cambio del dia. Sobre todo los días que hay corridas en suba, el tipo de cambio queda desfasado (tiene una actualización muy lenta) y es un mal momento para cobrar.

image1 Existe una forma de salvar este cargo solapado para el receptor: si el envío es tipo Quick Cash y en dólares estadounidenses, se puede retirar en dólares en los locales Western Union de los supermercados Walmart (que es el representante oficial de WU en Argentina). Este servicio no tiene costo para el receptor.

Esto es muy útil para quienes utilizan Google Adsense en sus sitios web, ya que Google ofrece este servicio de pago. Basta anotar el número de transferencia (MCU) y documento en mano aclarar en Wester Union de Walmart que queremos cobrar en dólares.

Mucho más fácil, rápido y sin perder un sope que pedir el cheque y venderlo en una financiera.

Cobrar del extranjero

image2 Internacionalmente está casi estandarizado el uso de Paypal como medio de pago de honorarios (y casi cualquier otro tipo de transacción en internet) pero este sistema no tiene integración con el sistema bancario argentino lo cual complica la tarea de monetizar los fondos.

Además, un usuario no puede cargar crédito a su cuenta paypal con una tarjeta de crédito, aunque esta sea válida internacionalmente. Un detalle que complica a quienes quieren comprar o donar en el extranjero.

Sin embargo, Paypal sigue siendo interesante para todos los que necesitamos cobrar desde el extranjero. Además de facilitarle la vida al emisor (cliente) que seguramente ya está acostumbrado a este sistema, la transferencia entre cuentas paypal personales no tiene costo. Esto significa que si el cliente tiene una cuenta paypal con fondos (que en la mayoría de los paises pudo haber cargado con tarjeta o desde su cuenta bancaria sin costo) la transacción total es gratuita. Además, la operación es instantánea.

Si el cliente envia el dinero a través de paypal pero habiendolo cargado desde una cuenta bancaria o tarjeta de crédito específicamente para dicha transacción, el costo de la operanción es 3,4% + 0,30 USD. El emisor decide quien abona este cargo.

Una observación importante es que predeterminadamente las cuentas PayPal son en una moneda principal (en dólares estadounidenses para Argentina) y al recibir un pago en otra moneda (euros, por ejemplo) la conversión de divisa se realiza al tipo de cambio del día con una retención del 2.5%. El tipo de cambio es actualizado 2 veces por día.

Una sólución es abrir cuentas en distintas monedas. En vez de convertir el pago a la divisa principal, se mantiene en nuestra cuenta en la moneda en que se recibió. Cuando necesitamos realizar un pago o transferencia en esta moneda, nos ahorraremos tener que reconvertirla nuevamente (ahorrando un 5% en total). Claro que a la hora de monetizar los fondos se deberán convertir, pero esto se puede hacer en cualquier momento que creamos conveniente.

Paypal aclara que ofrece este servicio (Multiples divisas) para facilitar el comercio electrónico internacional y no debe utilizarse con fines especulativos. Pero los timberos decidirán.

De Paypal a la billetera

|image4|

Pero, como decía un amigo, la plata no es plata hasta que no sirve para comprar fernet en la despensa.

Como dije más arriba, no se puede asociar una cuenta bancaria argentina una cuenta Paypal (ni para añadir ni para retirar fondos). Esto deja como única alternativa oficial para retirar fondos el envio de un cheque.

Paypal envia un cheque via correo postal certificado por el monto que solicitemos con un costo de u$s 15 fijo, independientemente del monto retirado. El envío para Argentina se realiza a través de Ocasa, y tiene una demora aproximada de 10 días hábiles.

El problema es que este cheque tampoco se puede cobrar en ningún banco, por lo que hay que venderlo en una financiera o agencia de viajes, que cobran una comisión de entre el 2 y el 5%.

Por suerte existe xoom.com. Es un servicio de transferencia de dinero que intenta competir con Western Union, pero a diferencia de este, la operación se realiza únicamente online, el destino puede ser una cuenta bancaria argentina o efectivo en pesos o dólares, y, hete aquí la clave, **el origen de los fondos puede ser una cuenta Paypal. **

System Message: WARNING/2 (<string>, line 118); backlink

Inline strong start-string without end-string.

Problema solucionado: nos hacemos una transferencia a nosotros mismos, retirando fondos de Paypal y enviandolos a nuestra cuenta bancaria, o mejor, en dólares que se cobran en cualquier sucursal de Banco Francés.

A diferencia de Wester Union, los cargos de servicio no son porcentuales fijos sino que están tabulados en rangos del monto enviado lo que significa un ahorro considerable en montos grandes. Para cifras montos menores a u$s500, el costo de servicio ronda le 4.5%

image5 Además hay que considerar que al cobrar en pesos, Xoom realiza una conversión de divisa a un tipo de cambio que, al igual que Wester Union, está un par de puntos por debajo de la cotización de mercado. La opción de cobrar en dolares evita esta pérdida, pero tiene un cargo de servicio un 40% mayor, lo que significa un costo de operación real del más de 6%.

image6 Pero hay una forma de salvar esta plata, que convertida a pesos seguramente significarán varios fernés: conseguir un código de descuento. Retailmenot suele tener cupones válidos, ¡de hasta el 100%!

Conclusión: si cobramos desde Paypal (entre cuentas personales) y retiramos los fondos a través de Xoom.com en dólares consiguiendo un cupón del 100%, tendremos toda nuestra platita en el bolsillo, en dólares y sin perder nada en el camino.