Introduction au langage Python par l'exemple


précédentsommaire

XVI. Exercice [IMPOTS] avec XML

Dans cet exercice déjà étudié de nombreuses fois, le serveur renvoie les résultats au client sous la forme d'un flux XML :

  • <reponse><erreur>msg</erreur></reponse> en cas d'erreur ;
  • <reponse><impot>valeur</impot></reponse> si l'impôt a pu être calculé.

Nous utilisons ce que nous venons d'apprendre sur l'analyse d'un document XML.

Image non disponible

XVI-A. Le service Web

Le service Web n'est pas différent du service Web étudié précédemment si ce n'est que la réponse XML envoyée au client est légèrement différente. L'architecture reste la même :

Image non disponible
Le service web (impots_web_02)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
#!D:\Programs\ActivePython\Python2.7.2\python.exe

# -*- coding=Utf-8 -*-
# import du module des classes Impots*
from impots import *
import cgi,cgitb,re

# on autorise l'affichage d'informations de débogage
cgitb.enable()

# ------------------------------------------------ 
# le service Web des impôts
# ------------------------------------------------ 

# les données nécessaires au calcul de l'impôt ont été placées dans la table mysqL TABLE
# appartenant à la base BASE. La table a la structure suivante
# limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# les paramètres des personnes imposables (statut marital, nombre d'enfants, salaire annuel)
# sont envoyés par le client sous la forme params=statut marital, nombre d'enfants, salaire annuel
# les résultats (statut marital, nombre d'enfants, salaire annuel, impôt à payer) sont renvoyés au client
# sous la forme <impot>valeur</impot>
# ou sous la forme <erreur>msg</erreur>, si les paramètres sont invalides

# le serveur renvoie au client du texte non formaté
print "Content-Type: text/plain\n"
# début de la réponse
print "<reponse>"

# définition des constantes
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# instanciation couche [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()

# on récupère la ligne envoyée par le client au serveur
params=cgi.FieldStorage().getlist('params')
# si pas de paramètres alors erreur
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

# on exploite le paramètre params  
#print "parametres recus --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# il ne doit y avoir que 3 champs
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# le premier paramètre (statut marital) doit être oui/non
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# le second paramètre (nbre d'enfants) doit être un nombre entier
match=re.match(r"^\s*(\d+)\s*$",items[1])
if not match:
    print "<erreur>[%s] : 2e parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
enfants=int(match.groups()[0])
# le troisième paramètre (salaire) doit être un nombre entier
match=re.match(r"^\s*(\d+)\s*$",items[2])
if not match:
    print "<erreur>[%s] : 3e parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
salaire=int(match.groups()[0])
# on calcule l'impôt
impot=metier.calculer(marie,enfants,salaire)
# on renvoie le résultat
print "<impot>%s</impot></reponse>\n" % (impot)
# fin

Notes :

Ce service Web ne diffère du précédent que par la nature de sa réponse :

<reponse><erreur>msg</erreur></reponse> en cas d'erreur au lieu de <erreur>msg</erreur>

<reponse><impot>valeur</impot></reponse> si l'impôt a pu être calculé au lieu de <impot>valeur</impot>

XVI-B. Le client programmé

Notre client doit analyser la réponse XML envoyée par le service Web. Nous appliquons ce qui a été vu dans l'analyse d'un document XML.

Le programme (client_impots_web_02)
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
# -*- coding=utf-8 -*-

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

    # fonction appelée lors de la rencontre d'une balise de début
    def startElement(self,name,attributs):
        # on note l'élément courant
        global elementcourant
        elementcourant=name.strip().lower()

    # la fonction appelée lors de la rencontre d'une balise de fin
    def endElement(self,name):
        # on ne fait rien
        pass

    # la fonction de gestion des données
    def characters(self,data):
        # données
        global elementcourant,elements

        # les données sont récupérées
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # on analyse la reponse XML
    xml.sax.parseString(reponse,XmlHandler())
    # on rend les résultats
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# constantes
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");
# variables globales
elementcourant=""
elements={}

# connexion
connexion=httplib.HTTPConnection(HOST)
# suivi
#connexion.set_debuglevel(1)
# on boucle sur les données à envoyer au serveur
for params in data:
    # les paramètres doivent être encodés avant d'être envoyés au serveur
    parametres = urllib.urlencode({'params': params})
    # envoi de la requête
    connexion.request("POST",URL,parametres)
    # traitement de la réponse (flux Xml)
    reponse=connexion.getresponse().read()
    # exploitation du fichier xml
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Notes :

  • le flux XML du service Web est exploité par la fonction getResultatsXml (ligne 60) ;
  • ligne 29 : la fonction getResultatsXml ;
  • ligne 31 : la réponse XML du service Web est analysée par une instance de la classe XmlHandler définie ligne 6 ;
  • ligne 6 : la classe XmlHandler implémente les trois méthodes startElement, endElement, characters. À l'aide de ces trois méthodes, on crée un dictionnaire. Les clés sont les noms des balises <erreur> et <impot> et les valeurs sont les données associées à ces deux balises ;
  • lignes 33-36 : la fonction getResultatsXml retourne un tuple à deux éléments :
  • (erreur,"") si l'analyse du flux XML a montré l'existence de la balise <erreur>. erreur représente alors le contenu de cette balise ;
  • ("",impot) si l'analyse du flux XML a montré l'existence de la balise <impot>. impot représente alors le contenu de cette balise.
  • ligne 60 : le résultat de la fonction getResultatsXml est récupéré puis exploité lignes 61-64.
Les résultats
Sélectionnez
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] : 2e parametre invalide
erreur[oui,2,x]=[oui,2,x] : 3e parametre invalide

précédentsommaire

  

Licence Creative Commons
Le contenu de cet article est rédigé par Serge Tahé et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.