Skip to content

10. Ejercicio [IMPOTS] con MySQL

10.1. Transferencia de un archivo de texto a una tabla MySQL

El script que se presentará a continuación transferirá los datos del siguiente archivo de texto:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:272.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

a la tabla [impots] de la base de datos MySQL [dbimpots], que es la siguiente:

 

La conexión a la base de datos [dbimpots] se realizará con la identidad (root,"").


Programa (impotstxt2mysql)

Vamos a utilizar la siguiente arquitectura:

El script [console] que vamos a escribir utilizará la clase [ImpotsFile] para acceder a los datos del archivo de texto. El acceso de escritura a la base de datos se realizará mediante los métodos vistos anteriormente.

El código del script es el siguiente:


# -*- coding=utf-8 -*-

# importación del módulo de clases de impuestos
from impots import *
import re

# --------------------------------------------------------------------------
def copyToMysql(limites,coeffR,coeffN,HOTE,USER,PWD,BASE,TABLE):
    # copia las 3 tablas numéricas de límites, coeffR, coeffN
    # en la tabla TABLE de la base de datos MySQL BASE
    #; la base de datos MySQL se encuentra en la máquina HOTE
    # el usuario se identifica mediante USER y PWD

    # se colocan las consultas SQL en una lista
    # eliminación de la tabla
    requetes=["drop table %s" % (TABLE)]
    # creación de la tabla
    requete="create table %s (limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2))" % (TABLE)
    requetes.append(requete)
    # llenado
    for i in range(len(limites)):
        # consulta de inserción
        requetes.append("insert into %s (limites,coeffR,coeffN) values (%s,%s,%s)" % (TABLE,limites[i],coeffR[i],coeffN[i]))
    # se ejecutan las órdenes SQL
    return executerCommandes(HOTE,USER,PWD,BASE,requetes,False,False)
    

def executerCommandes(HOTE,ID,PWD,BASE,requetes,suivi=False,arret=True):
    # utiliza la conexión (HOTE, ID, PWD, BASE)
    #ejecuta en esta conexión los comandos SQL contenidos en la lista de solicitudes
    #: si seguimiento=Verdadero, entonces cada ejecución de un comando SQL se acompaña de un mensaje que indica si se realizó con éxito o falló
    # si «arret» = False, la función se detiene ante el primer error que encuentre; de lo contrario, ejecuta todos los comandos SQL
    # la función devuelve una lista (número de errores, error1, error2, ...)

    # conexión
    try:
        connexion=MySQLdb.connect(host=HOTE,user=ID,passwd=PWD,db=BASE)
    except MySQLdb.OperationalError,erreur:
        return [1,"Erreur lors de la connexion a MySQL sous l'identite (%s,%s,%s,%s) : %s" % (HOTE, ID, PWD, BASE, erreur)]
      
    # se solicita un cursor
    curseur=connexion.cursor()
    # ejecución de las consultas SQL contenidas en la lista de consultas
    # se ejecutan; al principio no hay errores
    erreurs=[0]
    for i in range(len(requetes)):
        # se guarda la consulta en una variable local
        requete=requetes[i]
        # ¿Tenemos una consulta vacía?
        if re.match(r"^\s*$",requete):
            continue
        # ejecución de la consulta i
        erreur=""
        try:
            curseur.execute(requete)
        except Exception, erreur:
            pass
        # ¿Hubo algún error?
        if erreur:
            # otro error
            erreurs[0]+=1
            # mensaje de error
            msg="%s : Erreur (%s)" % (requete[0:len(requete)-1],erreur)
            erreurs.append(msg)
            # ¿Seguimiento en pantalla o no?
            if suivi:
                print msg
            # ¿Nos detenemos?
            if arret:
                return erreurs
        else:
            if suivi: 
                # ¿Mostramos la consulta sin su marcador de fin de línea?
                print "%s : Execution reussie" % (cutNewLineChar(requete))
                # información sobre el resultado de la consulta ejecutada
                afficherInfos(curseur)
    # cierre de la conexión
    try:
        connexion.commit()
        connexion.close()
    except MySQLdb.OperationalError,erreur:
        # otro error
        erreurs[0]+=1
        # mensaje de error
        msg="%s : Erreur (%s)" % (requete,erreur)
        erreurs.append(msg)

    # retorno
    return erreurs


# ------------------------------------------------ principal
# la identidad del usuario
USER="root"
PWD=""
# la máquina host del SGBD
HOTE="localhost"
# identidad de la base de datos
BASE="dbimpots"
# identidad de la tabla de datos
TABLE="impots"
# el archivo de datos
IMPOTS="impots.txt"

# instanciación de la capa [dao]
try:
    dao=ImpotsFile(IMPOTS)
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# se transfieren los datos recuperados a una tabla de MySQL
erreurs=copyToMysql(dao.limites,dao.coeffR,dao.coeffN,HOTE,USER,PWD,BASE,TABLE)
if erreurs[0]:
    for i in range(1,len(erreurs)):
        print erreurs[i]
else:
    print "Transfert opere"
# fin
sys.exit()

Notas:

  • líneas 106-110: se instancia la clase [ImpotsFile] presentada en el párrafo 8.1;
  • línea 113: los tableros limites, coeffR y coeffN se transfieren a una base MySQL;
  • línea 8: la función copyToMysql ejecuta esta transferencia. La función copyToMysql crea una lista de consultas a ejecutar y las hace ejecutar mediante la función executerCommandes, línea 25;
  • líneas 28-89: la función executerCommandes es la misma que ya se presentó anteriormente, en el párrafo 9.7, con una diferencia: en lugar de estar en un archivo de texto, las consultas se encuentran en una lista;

Resultados

El archivo de texto impots.txt:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:272.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

Resultados en pantalla:

Transfert opéré

Verificación con phpMyAdmin:

 

10.2. El programa de cálculo del impuesto

Ahora que los datos necesarios para el cálculo del impuesto se encuentran en una base de datos, podemos escribir el script de cálculo del impuesto. Volvemos a utilizar una arquitectura de tres capas:

La nueva capa [dao] se conectará a SGBD y MySQL, y será implementada por la clase [ImpotsMySQL]. Ofrecerá a la capa [metier] la misma interfaz que antes, compuesta por el único método getData, que devuelve la tupla (límites, coeffR, coeffN). Por lo tanto, la capa [metier] no cambiará con respecto a la versión anterior.

10.3. La clase [ImpotsMySQL]

La capa [dao] ahora está implementada por la siguiente clase [ImpotsMySQL] (archivo impots.py):


class ImpotsMySQL:

    # constructor
    def __init__(self,HOTE,USER,PWD,BASE,TABLE):
        # inicializa los atributos de límites, coeffR, coeffN
        # los datos necesarios para el cálculo del impuesto se han colocado en la tabla mysqL TABLE
        #, que pertenece a la base de datos BASE. La tabla tiene la siguiente estructura
        # límites decimal(10,2), coeffR decimal(10,2), coeffN decimal(10,2)
        # la conexión a la base de datos MySQL de la máquina HOTE se realiza con la identidad (USER,PWD)
        # lanza una excepción en caso de error
        
        # conexión a la base de datos MySQL
        connexion=MySQLdb.connect(host=HOTE,user=USER,passwd=PWD,db=BASE)
        # se solicita un cursor
        curseur=connexion.cursor()
        
        # lectura en bloque de la tabla TABLE
        requete="select limites,coeffR,coeffN from %s" % (TABLE)
        # ejecuta la consulta [requete] en la base de datos [base] de la conexión [connexion]
        curseur.execute(requete)
        # procesamiento del resultado de la consulta
        ligne=curseur.fetchone()
        self.limites=[]
        self.coeffR=[]
        self.coeffN=[]
        while(ligne):
            # línea actual
            self.limites.append(ligne[0])
            self.coeffR.append(ligne[1])
            self.coeffN.append(ligne[2])
            # línea siguiente
            ligne=curseur.fetchone()
        # desconexión
        connexion.close()

    def getData(self):
        return (self.limites, self.coeffR, self.coeffN)

Notas:

  • línea 18: la consulta SQL SELECT que solicita los datos de la base de datos MySQL. Las líneas de resultados de SELECT son luego procesadas una por una por [curseur.fetchone] (líneas 22 y 32) para crear las tablas limites, coeffR y coeffN (líneas 28-30);
  • el método getData de la interfaz de la capa [dao].

10.4. El script de consola

El código del script de consola (impots_04) es el siguiente:


# -*- coding=utf-8 -*-

# importación del módulo de las clases Impuestos*
from impots import *

# ------------------------------------------------ main
# la identidad del usuario
USER="root"
PWD=""
# la máquina host del SGBD
HOTE="localhost"
# identidad de la base de datos
BASE="dbimpots"
# identidad de la tabla de datos
TABLE="impots"
# archivo de entrada
DATA="data.txt"
# archivo de salida
RESULTATS="resultats.txt"

# instancia de la capa [metier]
try:
    metier=ImpotsMetier(ImpotsMySQL(HOTE,USER,PWD,BASE,TABLE))
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# los datos necesarios para el cálculo del impuesto se han colocado en el archivo IMPOTS
# a razón de una línea por tabla en el formato
# val1:val2:val3,...

# lectura de los datos
try:
    data=open(DATA,"r")
except:
    print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
    sys.exit()

# apertura del archivo de resultados
try:
    resultats=open(RESULTATS,"w")
except:  
    print "Impossible de creer le fichier des résultats [RESULTATS]"
    sys.exit()

# utilidades
u=Utilitaires()

# se procesa la línea actual del archivo de datos
ligne=data.readline()
while(ligne != ''):
    # se elimina el posible carácter de fin de línea
    ligne=u.cutNewLineChar(ligne)
    # se recuperan los tres campos «casado:hijos:salario» que conforman la línea
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # se calcula el impuesto
    impot=metier.calculer(marie,enfants,salaire)
    # se registra el resultado
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # se lee una nueva línea
    ligne=data.readline()
# se cierran los archivos
data.close()
resultats.close()

Notas:

  • línea 23: instanciación de las capas [dao] y [metier];
  • el resto del código ya se conoce.

Resultados

Los mismos que con las versiones anteriores del ejercicio.