Skip to content

10. Esercizio [IMPOTS] con MySQL

10.1. Trasferimento di un file di testo in una tabella MySQL

Lo script che seguirà trasferirà i dati dal seguente file di testo:

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

nella tabella [impots] del database MySQL [dbimpots]:

 

La connessione al database [dbimpots] avverrà con le credenziali (root,"").


Programma (impotstxt2mysql)

Utilizzeremo la seguente struttura:

Lo script [console] che scriveremo utilizzerà la classe [ImpotsFile] per accedere ai dati del file di testo. L'accesso in scrittura al database avverrà tramite i metodi visti in precedenza.

Il codice dello script è il seguente:


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

# importazione del modulo delle classi Impostazioni
from impots import *
import re

# --------------------------------------------------------------------------
def copyToMysql(limites,coeffR,coeffN,HOTE,USER,PWD,BASE,TABLE):
    # copia le 3 tabelle numeriche dei limiti, coeffR, coeffN
    # nella tabella TABLE del database MySQL BASE
    # il database MySQL si trova sul computer HOTE
    # l'utente è identificato da USER e PWD

    # si inseriscono le query SQL in un elenco
    # eliminazione della tabella
    requetes=["drop table %s" % (TABLE)]
    # creazione della tabella
    requete="create table %s (limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2))" % (TABLE)
    requetes.append(requete)
    # compilazione
    for i in range(len(limites)):
        # query di inserimento
        requetes.append("insert into %s (limites,coeffR,coeffN) values (%s,%s,%s)" % (TABLE,limites[i],coeffR[i],coeffN[i]))
    # si eseguono i comandi SQL
    return executerCommandes(HOTE,USER,PWD,BASE,requetes,False,False)
    

def executerCommandes(HOTE,ID,PWD,BASE,requetes,suivi=False,arret=True):
    # utilizza la connessione (HOTE,ID,PWD,BASE)
    # esegue su questa connessione i comandi SQL contenuti nell'elenco delle richieste
    # se monitoraggio=True, allora ogni esecuzione di un comando SQL viene seguita da una visualizzazione che ne indica il successo o il fallimento
    # se «arresto=False», la funzione si interrompe al primo errore riscontrato; in caso contrario, esegue tutti i comandi SQL
    # la funzione restituisce un elenco (numero di errori, errore1, errore2, ...)

    # connessione
    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)]
      
    # si richiede un cursore
    curseur=connexion.cursor()
    # esecuzione delle query SQL contenute nell'elenco delle query
    # vengono eseguite - inizialmente nessun errore
    erreurs=[0]
    for i in range(len(requetes)):
        # si inserisce la richiesta in una variabile locale
        requete=requetes[i]
        # la query è vuota?
        if re.match(r"^\s*$",requete):
            continue
        # esecuzione della query i
        erreur=""
        try:
            curseur.execute(requete)
        except Exception, erreur:
            pass
        # si è verificato un errore?
        if erreur:
            # un altro errore
            erreurs[0]+=1
            # messaggio di errore
            msg="%s : Erreur (%s)" % (requete[0:len(requete)-1],erreur)
            erreurs.append(msg)
            # monitoraggio dello schermo o no?
            if suivi:
                print msg
            # ci si ferma?
            if arret:
                return erreurs
        else:
            if suivi: 
                # visualizziamo la query senza il carattere di fine riga
                print "%s : Execution reussie" % (cutNewLineChar(requete))
                # informazioni sul risultato della richiesta eseguita
                afficherInfos(curseur)
    # chiusura della connessione
    try:
        connexion.commit()
        connexion.close()
    except MySQLdb.OperationalError,erreur:
        # un altro errore
        erreurs[0]+=1
        # messaggio di errore
        msg="%s : Erreur (%s)" % (requete,erreur)
        erreurs.append(msg)

    # ritorno
    return erreurs


# ------------------------------------------------ main
# identità dell'utente
USER="root"
PWD=""
# il computer host del SGBD
HOTE="localhost"
# identificativo del database
BASE="dbimpots"
# identità della tabella dei dati
TABLE="impots"
# il file dei dati
IMPOTS="impots.txt"

# istanza del livello [dao]
try:
    dao=ImpotsFile(IMPOTS)
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# si trasferiscono i dati recuperati in una tabella 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"
# fine
sys.exit()

Note:

  • righe 106-110: si istanzia la classe [ImpotsFile] presentata nel paragrafo 8.1;
  • riga 113: gli array limites, coeffR, coeffN vengono trasferiti in un array MySQL;
  • riga 8: la funzione copyToMysql esegue tale trasferimento. La funzione copyToMysql crea un array delle query da eseguire e le fa eseguire dalla funzione executerCommandes, riga 25;
  • righe 28-89: la funzione executerCommandes è quella già presentata in precedenza, al paragrafo 9.7, con una differenza: invece di trovarsi in un file di testo, le query sono contenute in un elenco;

Risultati

Il file di testo 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

Risultati sullo schermo:

Transfert opéré

Verifica con phpMyAdmin:

 

10.2. Il programma di calcolo delle imposte

Ora che i dati necessari per il calcolo dell'imposta sono in un database, possiamo scrivere lo script per il calcolo dell'imposta. Utilizziamo nuovamente un'architettura a tre livelli:

Il nuovo livello [dao] sarà collegato a SGBD e MySQL e sarà implementato dalla classe [ImpotsMySQL]. Esso offrirà al livello [metier] la stessa interfaccia di prima, costituita dall’unico metodo getData che restituisce la tupla (limiti, coeffR, coeffN). Pertanto, il livello [metier] non subirà modifiche rispetto alla versione precedente.

10.3. La classe [ImpotsMySQL]

Il livello [dao] è ora implementato dalla seguente classe [ImpotsMySQL] (file impots.py):


class ImpotsMySQL:

    # costruttore
    def __init__(self,HOTE,USER,PWD,BASE,TABLE):
        # inizializza gli attributi limite, coeffR, coeffN
        # i dati necessari per il calcolo dell'imposta sono stati inseriti nella tabella mysqL TABLE
        # appartenente al database BASE. La tabella presenta la seguente struttura
        # limiti decimal(10,2), coeffR decimal(10,2), coeffN decimal(10,2)
        # la connessione al database MySQL del server HOTE avviene con le credenziali (USER,PWD)
        # genera un'eccezione in caso di errore
        
        # connessione al database MySQL
        connexion=MySQLdb.connect(host=HOTE,user=USER,passwd=PWD,db=BASE)
        # viene richiesto un cursore
        curseur=connexion.cursor()
        
        # lettura in blocco della tabella TABLE
        requete="select limites,coeffR,coeffN from %s" % (TABLE)
        # esegue la query [requete] sul database [base] della connessione [connexion]
        curseur.execute(requete)
        # elaborazione del risultato della query
        ligne=curseur.fetchone()
        self.limites=[]
        self.coeffR=[]
        self.coeffN=[]
        while(ligne):
            # riga corrente
            self.limites.append(ligne[0])
            self.coeffR.append(ligne[1])
            self.coeffN.append(ligne[2])
            # riga successiva
            ligne=curseur.fetchone()
        # disconnessione
        connexion.close()

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

Note:

  • riga 18: la richiesta SQL SELECT che richiede i dati dal database MySQL. Le righe risultanti da SELECT vengono quindi elaborate una per una da [curseur.fetchone] (righe 22 e 32) per creare le tabelle limites, coeffR, coeffN (righe 28-30);
  • il metodo getData dell'interfaccia del livello [dao].

10.4. Lo script della console

Il codice dello script da console (impots_04) è il seguente:


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

# importazione del modulo delle classi Impostazioni*
from impots import *

# ------------------------------------------------ main
# l'identità dell'utente
USER="root"
PWD=""
# il computer host del SGBD
HOTE="localhost"
# identificativo del database
BASE="dbimpots"
# identificativo della tabella dei dati
TABLE="impots"
# file di input
DATA="data.txt"
# file di output
RESULTATS="resultats.txt"

# istanza del livello [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()

# i dati necessari per il calcolo dell'imposta sono stati inseriti nel file IMPOTS
# con una riga per ogni tabella nella forma
# val1:val2:val3,...

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

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

# utilità
u=Utilitaires()

# si elabora la riga corrente del file dei dati
ligne=data.readline()
while(ligne != ''):
    # si rimuove l'eventuale carattere di fine riga
    ligne=u.cutNewLineChar(ligne)
    # si recuperano i 3 campi coniuge:figli:stipendio che compongono la riga
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # si calcola l’imposta
    impot=metier.calculer(marie,enfants,salaire)
    # si inserisce il risultato
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # si legge una nuova riga
    ligne=data.readline()
# si chiudono i file
data.close()
resultats.close()

Note:

  • riga 23: istanziazione dei livelli [dao] e [metier];
  • il resto del codice è noto.

Risultati

Gli stessi delle versioni precedenti dell'esercizio.