Translate

domingo, 28 de junio de 2015

Python y MySQL CONNECTOR

Es posible que estés desarrollando un programa en PYTHON y debas acceder a una base de datos MySQL.

Si haces una búsqueda en GOOGLE, seguramente obtendrás muchas respuestas que te informan sobre MySQLdb como la mejor librería para acceder desde PYTHON a MySQL, además de una serie de pruebas de velocidad donde verás la clasificación por librerías y tiempo para 500, 1000 o 10000 inserciones, actualizaciones... .Estas mejoras en velocidad estan soportadas en código C, que debe compilarse para cada plataforma. Incluso existen paquetes de instalación para la mayoría de las plataformas.

Pero siendo fieles a nuestra incorregible conducta de mirar hasta debajo de la alfombra hemos buscado otras soluciones. Encontramos una librería desarrollada en código PYTHON y soportada con todas las garantías por el propio soporte de MySQL (INCLUSO Tiene instalador MSI para Windows). Se Trata de MySQL Connector . Si compruebas los test de velocidad antes mencionados, seguramente no es la más veloz a la hora de hacer grandes operaciones de inserción y actualización, pero todo es relativo, alguna vez has tenido la tentación de comprar el ordenador más potente que podías pagar? Y cuando has escrito en su teclado compruebas que escribe a la misma velocidad que lo has hecho siempre. Lo que nos lleva a reivindicar que cualquier librería es buena siempre y cuando cubra nuestras necesidades.

A partir de aquí mostraré algo de código necesario para utilizar MySQL Connector:

#!/usr/bin/python
# -*- coding: utf-8 *-*

import sys
import os
import re
import time
import logging
import importlib
import mysql.connector
from mysql.connector import errorcode


class SQLdb(object):
    instance = None

    #Tratamiento de errores en LOG
    __name__ = "SQLdb"
    sys.stdout = sys.stderr

    logPath = ''
    LOG = logging.getLogger(__name__)
    hdlr = logging.FileHandler(logPath+__name__+'.log')
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
    hdlr.setFormatter(formatter)
    LOG.addHandler(hdlr)
    LOG.info("SQLdb modo local INICIO")

    _module = None
    _errorcode = None
    cnx = None
    cursor = None


Para utilizar la librería debe importar  mysql.connector  y errorCode,  que podrás identificar en el código anterior en color azul.


    def __new__(cls, *args, **kargs):
        if cls.instance is None:
            cls.instance = object.__new__(cls, *args, **kargs)
        return cls.instance


El código anterior es una forma de escribir un patrón SINGLETON que es algo fuera de este artículo pero para explicarlo de una forma breve, diré que su finalidad en garantizar que una clase solo tenga una instancia ( en un hilo ) y proporcionará un punto de acceso global para toda la aplicación.

    def __init__(self,*args, **kwargs):
        """Abrir acceso a bbdd"""
        self.cnx = None;
        self.cursor = None;
        self._host = kwargs['host'] if kwargs.has_key('host') else '127.0.0.1';
        self._port = kwargs['port'] if kwargs.has_key('port') else '3306';
        self._username = kwargs['username']
        self._password = kwargs['password']
        self._database = kwargs['database']
        self._driver = kwargs['driver'] if kwargs.has_key('driver') else 'mysql';
        self._max_idle_time = kwargs['max_idle_time'] if kwargs.has_key('max_idle_time') else 2*3600;
        self._time_zone = kwargs['time_zone'] if kwargs.has_key('time_zone') else "+2:00";
        self._sql_mode = kwargs['sql_mode'] if kwargs.has_key('sql_mode') else "TRADITIONAL";
        self._charset = kwargs['charset'] if kwargs.has_key('charset') else "utf8";
        self._collation = kwargs['collation'] if kwargs.has_key('collation') else "utf8_spanish_ci";
        self._conection_timeout = kwargs['connection_timeout'] if kwargs.has_key('connection_timeout') else 100;
        self._compress = kwargs['compress'] if kwargs.has_key('compress') else False;
        self._autocommit = kwargs['autocommit'] if kwargs.has_key('autocommit') else False;
        self._use_pure = kwargs['use_pure'] if kwargs.has_key('use_pure') else True;

        #encloser characters for system identifiers(key_delim) and strings(str_delim)
        self._key_delim = '"';
        self._str_delim = "'";

        if self._driver == 'mysql':
            #self._module = importlib.import_module('mysql.connector')
            #self._errorcode = importlib.import_module("mysql.connector","errorcode")
            self._key_delim = '`';
        elif self._driver == 'pgsql':
            self._module = importlib.import_module('psycopg2')
        else:
            self.LOG.error('Driver Error Unknown database driver !!!')
            raise Exception("Unknown database driver")




        # self.rows permite obtener las filas afectadas
        self.rows = 0
        self.lastId = 0

        self._last_use_time = time.time()
        try:
            if self.reconnect():
                if getattr(self,"cursor",None) is not None:
                    self.cursor.close()
                    self.cursor = None
        except Exception,e:
            tb = sys.exc_info()[2]
            self.LOG.error('Error <%s> en linea %s !!!' % (str(e),tb.tb_lineno))


En el código anterior obtenemos todos los parámetros para acceder a la base de datos y en azul, tendriamos el código necesario si queremos realizar una clase que pueda acceder a más de un tipo de base de datos - incluso podría realizarse la importación de librerías de forma personalizada para cada tipo de base de datos - , siempre que cumplan la especificación DB V2.0 API  ( aunque en este código solo se indica como un mero apunte para quien desee profundizar más ).


Y ahora comenzamos la conexión o mejor dicho, la reconexión. Si una aplicación tiene una conexión abierta demasiado tiempo, es posible que el servidor MySQL corte la comunicación por timeout, si no se ha previsto este aspecto en el código, intentaremos abrir el cursor (sobre la conexión ya cerrada ) y obtendremos un error. Como en nuestro código ya hemos previsto este aspecto, más adelante podrá comprobar nuestro método 'ensure_connected' .

    def __del__(self):
        self.close()


    def close(self):
        """Cierra la conexion a la base de datos"""
        try:
            if getattr(self,"cnx",None) is not None:
                if getattr(self,"cursor",None) is not None:
                    self.cursor.close()
                    self.cursor = None
                self.cnx.close()
                self.cnx = None
        except mysql.connector.Error as err:
            self.LOG.error(err)
            print 'error %s' % err


    def reconnect(self):
        """Cierra la conexion con la base de datos y vuelve a abrirla"""
        try:
            self.close()
            config = {'host': self._host, 'port': self._port, 'user': self._username};
            if self._driver == 'mysql':
                config['password'] = self._password
                config['database'] = self._database
                config['time_zone']= self._time_zone
                config['charset'] = self._charset
                config['collation'] = self._collation
                config['sql_mode']= self._sql_mode
                config['compress']= self._compress
                config['autocommit']= self._autocommit
                config['raise_on_warnings']= True
                #config['use_pure']= self._use_pure
            elif self._driver == 'pgsql':
                # SI EL
                config['database'] = self._dbname
                config['password'] = self._password
            self.cnx = mysql.connector.connect(**config)
            #self.cnx('connection_timeout') = self._connection_timeout
            self.cursor = self.cnx.cursor()
            return True
        except mysql.connector.Error as err:
            if err.errno == self._errorcode.errorcode.ER_ACCESS_DENIED_ERROR:
                self.LOG.error('Something is wrong with your user name or password !!!')
                print 'Something is wrong with your user name or password'
            elif err.errno == self._errorcode.errorcode.ER_BAD_DB_ERROR:
                self.LOG.error('Database does not exist !!!')
                print 'Database does not exist'
            else:
                self.LOG.error(err)
                print 'error %s' % err
            return False


Y a continuación, un ejemplo de cómo se construyen SELECT, INSERT, UPDATE, DELETE con los parámetros pasados. Básicamente, en el parámetro 'sql' enviaremos nuestro SQL p.e. :
SELECT * FROM products WHERE code='%s';
y 'parameters' puede ser una tupla o solo un dato o nada (si en 'sql' hubieramos completado todos los datos necesarios), en este caso sería solo un datos p.e. :  03456
a partir de estos parámetros el método execute construye la sentencia SQL completa, y dependiendo de qué tipo de sentencia se trate, nos devolverá el id del registro INSERT, el número de registros afectados por UPDATE o DELETE, o los datos solicitados mediante SELECT.

    def execute(self, sql=None , parameters=None):
        """Ejecuta consultas teniendo en cuenta transacciones
        sql parametro con el codigo
        parameters son las variables que se pueden incorporar al codigo sql
        """
        self.ensure_connected()
        try:
            if sql:
                if parameters is not None:
                    if type(parameters[0]).__name__ == 'tuple':
                        self.rows = self.cursor.executemany(sql, parameters)
                    else:
                        self.rows = self.cursor.execute(sql, parameters)
                else:
                    self.rows = self.cursor.execute(sql)
                if sql[0:6].upper()=='INSERT':
                    # Informa de nº de última fila autoincremental
                    self.lastId = self.cursor.lastrowid
                    return self.lastId

                elif sql[0:6].upper()=='UPDATE' or sql[0:6].upper()=='DELETE':
                    # Informa de nº filas afectadas
                    self.rows = self.cursor.rowcount
                    return self.rows

                elif sql[0:6].upper()=='SELECT':
                    # Devuelve informacion de todas las filas seleccionadas
                    return self.cursor.fetchall()

        except mysql.connector.Error as err:
            self.LOG.error(err)
        finally:
            if hasattr(self.cnx,"commit"):
                self.cnx.commit()
            if getattr(self,"cursor",None) is not None:
                self.cursor.close()
                self.cursor = None


Y por último, el método que permite comprobar y abrir la conexión, si fuera necesario tras un timeout, o solo abrir el cursor.

    def ensure_connected(self):
        """ Mysql por defecto cierra las conexiones de clientes que están inactivos por
        8 horas, pero la biblioteca de cliente no reporta este hecho hasta
        intentar realizar una nueva consulta y en ese momento falla.
        Preventivamente se cierra y vuelve a abrir la conexion si no se ha utilizado
        durante mucho tiempo"""
        try:
            if (getattr(self,"cursor",None) is not None or (time.time() - self._last_use_time > self._max_idle_time)):
                self.reconnect()
            else:
                self.cursor = self.cnx.cursor()
            self._last_use_time = time.time()
        except mysql.connector.Error as err:
            self.LOG.error(err)


Esto proporciona un punto de partida para utilizar MySQL CONNECTOR en su codificación PYTHON y solo debería trabajar un poquito más para aplicar TRANSACTION, COMMIT y ROLLBACK. Salvo errores tipográficos, he probado este código y funciona correctamente.

Espero que haya sido de utilidad. 





miércoles, 3 de junio de 2015

PYMETRICK para un comercio electrónico sencillo

Seguramente este artículo será de tu interés si te dedicas a comercializar productos o a desarrollar software para comercio electrónico.

Últimamente no dejan de bombardearnos con estadísticas sobre cuanto han crecido las cifras del comercio electrónico, también nos proporcionan listas de países donde el comercio electrónico supone un 70% de transacciones con respecto al comercio tradicional, publican subvenciones a empresas que desarrollen el e-commerce,....etc. Todo esto nos lleva a preguntarnos ¿ Por qué ? ¿ Cuándo ? ¿Cuánto? ¿Dónde? ¿Cuál? ¿Quién? ¿Cómo?

¿Por qué?
Es necesario que cualquier empresa o comercio desarrolle un sistema de comercio electrónico ? Seguramente no. Para responder a esta pregunta es necesario ser muy práctico. El comercio electrónico está creciendo lentamente en el sur de Europa y existen grandes competidores como Amazón, EBay y Alibaba. Las grandes cadenas comerciales ya han apostado por el comercio electrónico pero representa una cifra muy pequeña en sus balances.

¿Cuándo?
Disponer de tiempo y recursos para ponerse manos a la obra. Qué periodo es más idóneo para acometer este proyecto?

¿Cuánto?
La buena noticia es que existen soluciones donde no deberíamos apostar una gran suma para empezar y esperar acontecimientos -no vamos a adelantarte nada, pero podrás leer la solución al final-.

Evaluar los costes iniciales y de operación siempre nos evitará sustos. Existen muchas ofertas a nivel local e internacional, pero cuál se ajustará más a lo que hemos pensado ?  Siempre es posible que nos prometan más de lo que vamos a obtener por nuestro dinero - con eso ya deberíamos contar -. 

¿Donde?
Si ya se ha decidido por alguna oferta o cualquier otra solución, debe tener cuidado sobre dónde se ubique el servidor que permita comercializar sus productos y cuente con los datos de sus cliente. Existen unas normas estrictas de la Agencia de Protección de Datos ( España ) y de la C.E.E. para ubicar datos personales de forma protegida y segura, ya que tendrá que comunicar y dar de alta su base de datos en la APD.

¿Cuál?
Qué objetivos perseguimos, B2C buscamos expandir nuestro mercado o tenemos productos innovadores que necesitan ser publicados a un publico más extenso ?  También es posible que deseemos realizar un comercio B2B y nuestros comerciales no puedan ampliar ilimitadamente su area comercial.

¿Quién?
Debemos buscar quien se encargará del desarrollo, implantación, seguimiento y operación. Si cuenta con un proveedor especializado en estas soluciones, tiene muchas posibilidades de llegar a buen puerto, aunque también es posible apostar por una STARTUP que le permita una inversión más acompasada con los objetivos.

¿Cómo?
Puedes empezar con un servidor ubicado en su instalación o bien, alquilar un servidor virtual o dedicado en un proveedor.  Adquirir un certificado electrónico (no es imprescindible pero le dará visos de seriedad), un dominio y el software necesario para que todo funcione.

Lo único cierto de todo esto es que quien no apuesta, no gana. Aunque tampoco debería apostar lo que no tiene o le hará falta para continuar.

Creo que el futuro del comercio electrónico está en disponer de recursos más seguros y asequibles (pasarelas de pago, logística, comunicaciones, identificación electrónica de usuarios,...), relacionarse con los clientes de una forma más simple -no hace falta pedir datos que no serán necesarios- y explorar métodos más flexibles para mostrar los productos o servicios - buscar un producto puede ser muy complicado o muy fácil, depende de tí -

Una gran parte del éxito de una implantación de comercio electrónico se basa en la disponibilidad de stock y del tratamiento de garantías y devoluciones, deberíamos ponernos más en los zapatos del cliente y menos en los zapatos de la empresa si deseamos que los clientes confíen en nuestro comercio. Además de cumplir las leyes escrupulosamente existe algo que se llama sentido común, y llegaremos a solucionar conflictos generando confianza para posteriores transacciones.

te damos a conocer 10 reglas de oro que no puedes dejar pasar.

1. Cada vez que se ingrese a una sección del sitio, ésta debe decir dónde está el cliente. Es decir, Inicio> Categoría> Sub-categoría> Producto

2. Es necesario que sea posible ordenar los productos según distintas opciones. Por ejemplo, del precio, de más barato al más caro o del más nuevo al más antiguo, o por temporadas (invierno, primavera, verano, otoño).

3. Muestra todos tus productos con foto y descripción, con el fin de que los potenciales clientes se entusiasmen con lo que ofreces. Obtener las fotos es un trabajo arduo ( obtener la foto y dimensionarla adecuadamente, relacionarla con la codificación del producto, y subirla a la web. Así una tras otra hasta completar todo el catálogo), pero si sigues leyendo puede convertirse en algo hasta divertido.

4. Si tienes productos más baratos que en el mercado normal, di lo que cuesta realmente y cuánto está gastando el cliente. De esta forma es más fácil mostrar que estás entregando un buen producto, a buen precio y que conviene comprar más barato. Aunque no deberías fundamentar tus ventas solo en precios, la competencia también puede tomar este camino y puede ser un problema para tu modelo de negocio.

5. Ubica una barra de búsqueda de fácil acceso, con el fin de que los clientes puedan encontrar lo que buscan y no abandonen tu página, si además, es posible buscar con ñ, mejor.

6. Crea una búsqueda avanzada, con el fin de refinar los términos y que las personas puedan incluir más datos para encontrar lo que buscan específicamente.

7. Antes de que los clientes compren, notifica si el producto está en stock o no. De esta forma te ahorrarás malos ratos y también se lo ahorrarás a tus clientes, que sabrán que lo que compran llegará a sus manos y no tendrán que esperar tiempo de más. Cuidados con los comercios que tienen una tienda física y una tienda web, el stock debería estar compartido y bloqueado por el cliente que haya realizado la compra. También puedes indicar un stock mínimo de seguridad y una vez superado, informar al cliente de que debe ponerse en contacto con el comercio físico.

8. Muestra las opciones de entrega. A algunas personas les gusta conocer las opciones de entrega que tienen al momento de comprar, por lo que es necesario que las tengas disponibles antes de que se confirme el envío. También puede jugar a favor de las tiendas físicas, la recogida el producto en el propio comercio.

9. Si el producto se va a atrasar, notifica a tu cliente a través de un correo electrónico o teléfono. De esta forma estará tranquilo y sabrán que eres una empresa seria.

10. Una vez que la compra sea confirmada, entrega el máximo de detalles a tus clientes para que se sientan tranquilos y sepan que hicieron la compra correcta.

DropShipping. El sitio web Nuevos Emprendedores.net lo define como un modelo de negocio online, donde a través de una página en Internet puedes vender productos sin tener que contar con un lugar específico donde tengas que guardarlos. Esto, gracias a que tus proveedores son quienes almacenan y distribuyen los productos y tú quien los promociona y los comercializa a través de Internet

Y por último pero no menos importante, hoy día es absolutamente necesario que la misma web sea accesible desde cualquier dispositivo con la misma facilidad de uso que si el cliente se encontrara ante un ordenador de sobremesa.

Desde el proyecto PYMETRICK, se incorporan todas las ventajas técnicas y exigencias legales en un plazo muy corto (hablamos de días), lo que nos permite una mejora continua y adaptarnos a las necesidades del mercado y nos hemos hecho todas la preguntas posibles y lo que resulta más difícil, las hemos respondido. No desvelamos nada si decimos que PYMETRICK es un proyecto muy reciente con un desarrollo continuo desde 2012, pero que aún no ha finalizado su ciclo de desarrollo y no dispone de ningún tipo de financiación, solo tiempo y ganas.  Es un proyecto soportado en una licencia GPL y por tanto, se cederá el código de forma libre una vez finalizado - aunque no nos importaría contar con otros desarrolladores que aportaran su visión, toda ayuda es bienvenida -.

En 2015 se ha iniciado el despliegue de varias web soportadas en PYMETRICK como COOP.COM. S. GARRACHON Y PEREZ BLANCO, FERROSAT, PEREZ Y RINCON S.A. , todas ellas se han desplegado con la misma filosofía, no incrementar el trabajo diario de un comercio físico en su día a día. Esto incluye actualizaciones de datos de forma autónoma ( productos, stock, precios, imágenes...) y periódica e informar al cliente en todo momento de la disponibilidad del producto y facilitarle un correo electrónico o teléfono desde el mismo botón de compra (según el dispositivo con el que accede a la web).

Llegados a este punto, y aunque PYMETRICK está desarrollado en PYTHON 2.X (y este es un BLOG sobre PYTHON), necesitábamos llegar más allá saltando de plataforma y mejorar todo lo relacionado con la captura de datos e imágenes. Creamos KEPIDE (ya en su versión 1.1.7) una APP ANDROID 4.4.X disponible en GOOGLE PLAY de forma gratuíta (y sin publicidad), que complementa todos los desarrollos que hemos implantado y que seguirán en el futuro. KEPIDE sigue en todo el rastro del desarrollo de su hermano mayor PYMETRICK, tiempo y ganas, e igual que él, GPL.  Aunque su ciclo de desarrollo no ha finalizado, ya permite muchas alegrías, sobre todo a los comerciales que la utilizan a diario realizando sus pedidos e incorporando productos e imágenes (dimensionándolas adecuadamente, relacionando la imagen y la codificación del producto y enviándola a la web para su publicación ) en pocos minutos.

Espero que este tema haya sido interesante, os espero en una próxima entrega aún por definir.