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.
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 :
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.
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.
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