Skip to content

15. Esercizio [Calcolo delle imposte] con XML

In questo esercizio, che abbiamo già trattato molte volte, il server restituisce i risultati al client sotto forma di flusso XML:

  • <response><error>msg</error></response> in caso di errore;
  • <response><tax>value</tax></response> se l'imposta è stata calcolata.

Utilizziamo ciò che abbiamo appena imparato sull'analisi di un documento XML.

15.1. Il servizio Web

Il servizio web non differisce da quello studiato in precedenza, tranne per il fatto che la risposta XML inviata al client è leggermente diversa. L'architettura rimane la stessa:


Il servizio web (impots_web_02)

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

#    -*- coding=Utf-8 -*-
#    import of Impots* class module
from impots import *
import cgi,cgitb,re

#    allow debugging information to be displayed
cgitb.enable()

# ------------------------------------------------ 
#  tax web service
# ------------------------------------------------ 

#  the data required to calculate the tax has been placed in table mysqL TABLE
#  belonging to the BASE database. The table has the following structure
#    limits decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
#  taxable person parameters (marital status, number of children, annual salary)
#  are sent by the customer in the form params=marital status, number of children, annual salary
#  results (marital status, number of children, annual salary, tax payable) are returned to the customer
#  in the form <impot>value</impot>
#  or as <error>msg</error>, if parameters are invalid

#  the server returns unformatted text to the client
print "Content-Type: text/plain\n"
#    start of answer
print "<reponse>"

#    definition of constants
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

#    instantiation layer [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()

#  retrieve the line sent by the client to the server
params=cgi.FieldStorage().getlist('params')
#  if no parameters then error
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

#  we use the params parameter  
#   print "parameters received --> %s\n" %(params)
items=params[0].strip().lower().split(',')
#  there must be only 3 fields
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
#  first parameter (marital status) must be yes/no
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
#  the second parameter (number of children) must be an integer
match=re.match(r"^\s*(\d+)\s*$",items[1])
if not match:
    print "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
enfants=int(match.groups()[0])
#  the third parameter (salary) must be an integer
match=re.match(r"^\s*(\d+)\s*$",items[2])
if not match:
    print "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
salaire=int(match.groups()[0])
#  tax calculation
impot=metier.calculer(marie,enfants,salaire)
#  return the result
print "<impot>%s</impot></reponse>\n" % (impot)
#    end

Note:

Questo servizio web differisce dal precedente solo per la natura della sua risposta:

<response><error>msg</error></response> in caso di errore invece di <error>msg</error>

<response><tax>value</tax></response> se l'imposta è stata calcolata invece di <tax>value</tax>

15.2. L'applicazione client

Il nostro client deve analizzare la risposta XML inviata dal servizio web. Applichiamo ciò che abbiamo imparato nell'analisi di un documento XML.


Il programma (client_impots_web_02)

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

import httplib,urllib,re
import xml.sax, xml.sax.handler
#    management class XML
class XmlHandler(xml.sax.handler.ContentHandler):

    #  function called when a start tag is encountered
    def startElement(self,name,attributs):
        #  note the current element
        global elementcourant
        elementcourant=name.strip().lower()

    #  the function called when an end tag is encountered
    def endElement(self,name):
        #    we do nothing
        pass

    #  the data management function
    def characters(self,data):
        #    data
        global elementcourant,elements

        #  data are retrieved
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()

def getResultatsXml(reponse):
    #  we analyze the XML response
    xml.sax.parseString(reponse,XmlHandler())
    #  we return the results
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

#    ------------------------------------------------------------ main
#    constant
HOST="localhost"
URL="/cgi-bin/impots_web_02b.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");
#    global variables
elementcourant=""
elements={}

#    connection
connexion=httplib.HTTPConnection(HOST)
#    follow-up
#   connexion.set_debuglevel(1)
#  loop over the data to be sent to the server
for params in data:
    #  parameters must be encoded before being sent to the server
    parametres = urllib.urlencode({'params': params})
    #  send request
    connexion.request("POST",URL,parametres)
    #    response processing (stream XML)
    reponse=connexion.getresponse().read()
    #    xml file processing
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Note:

  • Il feed XML proveniente dal servizio web viene elaborato dalla funzione getResultatsXml (riga 60);
  • riga 29: la funzione getResultatsXml;
  • riga 31: la risposta XML del servizio web viene analizzata da un'istanza della classe XmlHandler definita alla riga 6;
  • riga 6: la classe XmlHandler implementa i tre metodi startElement, endElement e characters. Utilizzando questi tre metodi, viene creato un dizionario. Le chiavi sono i nomi dei tag <error> e <import>, mentre i valori sono i dati associati a questi due tag;
  • righe 33–36: la funzione getResultatsXml restituisce una tupla con due elementi:
    • (error, "") se l'analisi del flusso XML ha rilevato il tag <error>. error rappresenta quindi il contenuto di questo tag;
    • ("", impot) se l'analisi del flusso XML ha rivelato la presenza del tag <impot>. impot rappresenta quindi il contenuto di questo tag.
  • Riga 60: il risultato della funzione getResultatsXml viene recuperato e quindi elaborato nelle righe 61–64.

15.3. I risultati

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