Skip to content

13. Esercizio [IMPOTS] con un servizio WEB

Torniamo all’esercizio [IMPOTS] già trattato nei paragrafi 4.2, 4.3, 8 e 10.

13.1. Il server

Ci collochiamo nella seguente architettura client-server:

Sul lato server, utilizzeremo nuovamente un'architettura a tre livelli. Il livello [dao] sarà implementato dalla classe ImpotsMySQL utilizzata nella versione con SGBD e MySQL al paragrafo 10. Il livello [metier] sarà implementato dalla classe [ImpotsMetier] già esaminata. Non resta quindi che scrivere il servizio web. Questo riceve dai propri clienti un parametro params nella forma params= sì,2,200000, dove il primo elemento indica se il contribuente è sposato o meno, il secondo il numero di figli, il terzo il suo stipendio annuale.

Il servizio web è il seguente (impots_web_01):


#!D:\Programmi\ActivePython\Python2.7.2\python.exe

# -*- coding=Utf-8 -*-
# importazione del modulo delle classi Impostazioni*
from impots import *
import cgi,cgitb,re

# si abilita la visualizzazione delle informazioni di debug
cgitb.enable()
sys.stderr=sys.stdout

# ------------------------------------------------ 
# il servizio web delle imposte
# ------------------------------------------------ 

# 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(6,2), coeffN decimal(10,2)
# i parametri dei soggetti imponibili (stato civile, numero di figli, stipendio annuo)
# vengono inviati dal cliente nel formato params=stato civile, numero di figli, stipendio annuo
# i risultati (stato civile, numero di figli, stipendio annuo, imposta da pagare) vengono restituiti al cliente
# nel formato <imposta>valore</imposta>
# oppure nel formato <errore>msg</errore>, se i parametri non sono validi

# il server restituisce al cliente del testo non formattato
print "Content-Type: text/plain\n"

# definizione delle costanti
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"


# si crea il livello [metier]
# Python non sembra supportare le sessioni
# in PHP, si sarebbe utilizzata una sessione per memorizzare l'oggetto di business
# qui lo si costruisce sistematicamente a partire dai dati del database MySQL

# istanziazione del livello [metier]
try:
    metier=ImpotsMetier(ImpotsMySQL(HOTE,USER,PWD,BASE,TABLE))
except (IOError, ImpotsError) as infos:
    print ("<erreur>Une erreur s'est produite : {0}</erreur>".format(infos))
    sys.exit()

# si recupera la riga inviata dal client al server
params=cgi.FieldStorage().getlist('params')
# se non ci sono parametri, si genera un errore
if not params:
  print "<erreur>Le parametre [params] est absent<erreur>"
  sys.exit()

# si elabora il parametro params  
items=params[0].strip().lower().split(',')
# devono esserci solo 3 campi
if len(items)!=3:
  print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (params[0])
  sys.exit()
# il primo parametro (stato civile) deve essere sì/no
marie=items[0].strip()
if marie!="oui" and marie != "non":
  print "<erreur>[%s] : 1er parametre invalide</erreur>\n"% (params[0])
  sys.exit()
# il secondo parametro (numero di figli) deve essere un numero intero
match=re.match(r"^\s*(\d+)\s*$",items[1])
if not match:
  print "<erreur>[%s] : 2ieme parametre invalide</erreur>\n"% (params[0])
  sys.exit()
enfants=int(match.groups()[0])
# il terzo parametro (stipendio) deve essere un numero intero
match=re.match(r"^\s*(\d+)\s*$",items[2])
if not match:
  print "<erreur>[%s] : 3ieme parametre invalide</erreur>\n"% (params[0])
  sys.exit()
salaire=int(match.groups()[0])
# si calcola l'imposta
impot=metier.calculer(marie,enfants,salaire)
# si restituisce il risultato
print "<impot>%s</impot>\n" % (impot)
# fine

Note:

  • riga 5: si importano gli oggetti dal file impots.py;
  • riga 9: il debug di uno script CGI può risultare problematico. Il modulo cgitb invia, in caso di errore, il motivo dell'arresto anomalo. A tal fine, è necessario reindirizzare l'uscita standard degli errori (sys.stderr) verso l'uscita standard (sys.stdout) (riga 10);
  • riga 26: l'intestazione HTTP che definisce la natura del documento inviato, in questo caso un testo non formattato. Il servizio web restituisce la sua risposta sotto forma di un'unica riga di testo:
    • <erreur>message</erreur> in caso di errore;
    • <impot>valeur</impot> dove valeur è l'importo dell'imposta.
  • riga 43: istanziazione dei livelli [dao] e [metier];
  • righe 44-46: in caso di errore nell'istanziazione, lo script invia la sua risposta e termina;
  • righe 49-53: viene recuperato il parametro 'params'. Se è assente, lo script invia la risposta e termina;
  • riga 56: il parametro 'oui,2,200000' viene scomposto in 3 campi nell'array items;
  • righe 58-77: viene verificata la validità dei 3 campi. Se si verifica un errore, lo script invia la risposta e termina;
  • riga 79: viene calcolata l'imposta;
  • riga 81: e inviata al cliente.

In un browser web si ottengono i seguenti risultati:

13.2. Un cliente programmato


Il programma (client_impots_web_01)


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

import httplib,urllib,re

# costanti
HOST="localhost"
URL="/cgi-bin/impots_web_01b.py"
data=("oui,2,200000","non,2,200000","oui,3,200000","non,3,200000","x,y,z,t","x,2,200000", "oui,x,200000","oui,2,x");

# connessione
connexion=httplib.HTTPConnection(HOST)
# monitoraggio
#connexion.set_debuglevel(1)
# si esegue un ciclo sui dati da inviare al server
for params in data:
    # i parametri devono essere codificati prima di essere inviati al server
    parametres = urllib.urlencode({'params': params})
    # invio della richiesta
    connexion.request("POST",URL,parametres)
    # elaborazione della risposta (riga di testo)
    reponse=connexion.getresponse().read()
    lignes=reponse.split("\n")
    # la riga di testo ha il seguente formato
    # <impot>xxx</impot>
    # oppure <errore>xxx</errore>
    impot=re.match(r"^<impot>(\S+)</impot>\s*$",lignes[0])
    if impot:
        print "params=%s, impot=%s" % (params,impot.groups()[0])
    else:
        erreur=re.match(r"^<erreur>(.+)</erreur>\s*$",lignes[0])
        if erreur:
            print "params=%s, erreur=%s" % (params,erreur.groups()[0])
        else:
            print lignes
# chiusura della connessione
connexion.close()

13.3. Résultats

1
2
3
4
5
6
7
8
params=oui,2,200000, impot=22504.0
params=non,2,200000, impot=33388.0
params=oui,3,200000, impot=16400.0
params=non,3,200000, impot=22504.0
params=x,y,z,t, erreur=[x,y,z,t] : nombre de parametres invalide
params=x,2,200000, erreur=[x,2,200000] : 1er parametre invalide
params=oui,x,200000, erreur=[oui,x,200000] : 2ieme parametre invalide
params=oui,2,x, erreur=[oui,2,x] : 3ieme parametre invalide