XIV. Exercice [IMPOTS] avec un service Web▲
Nous revenons à l'exercice [IMPOTS].
XIV-A. Le serveur▲
Nous nous plaçons dans l'architecture client-serveur suivante :
Côté serveur, nous allons utiliser de nouveau une architecture trois couches. La couche [DAO] sera implémentée par la classe ImpotsMySQL utilisée dans la version avec le Sgbd MySQL. La couche [metier] sera implémentée par la classe [ImpotsMetier] déjà étudiée. Il ne reste donc qu'à écrire le service Web. Celui-ci reçoit de ses clients un paramètre params sous la forme params= oui,2,200000 où le premier élément indique si le contribuable est marié ou non, le deuxième son nombre d'enfants, le troisième son salaire annuel.
Le service Web est le suivant (impots_web_01) :
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.
79.
80.
81.
82.
#!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
(
)
sys.stderr=
sys.stdout
# ------------------------------------------------
# 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éfinition des constantes
USER=
"root"
PWD=
""
HOTE=
"localhost"
BASE=
"dbimpots"
TABLE=
"impots"
# on crée la couche [metier]
# Python ne semble pas avoir de support pour les sessions
# en PHP, on aurait utilisé une session pour mémoriser l'objet metier
# ici, on le construit systématiquement à partir des données de la base de données MySQL
# 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ètre alors erreur
if
not
params:
print
"<erreur>Le parametre [params] est absent<erreur>"
sys.exit
(
)
# on exploite le paramètre 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>"
%
(
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>
\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>
\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>
\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>
\n
"
%
(
impot)
# fin
Notes :
- ligne 5 : on importe les objets du fichier impots.py ;
- ligne 9 : déboguer un script Cgi peut être problématique. Le module cgitb envoie, en cas d'erreur, la raison du plantage. Pour cela, il faut rediriger la sortie erreur standard (sys.stderr) vers la sortie standard (sys.stdout) (ligne 10) ;
- ligne 26 : l'entête HTTP qui fixe la nature du document envoyé, ici un texte non formaté. Le service Web renvoie sa réponse sous forme d'une unique ligne de texte :
- <erreur>message</erreur> s'il y a une erreur ;
- <impot>valeur</impot> où valeur est le montant de l'impôt.
- ligne 43: instanciation des couches [DAO] et [metier] ;
- lignes 44-46 : en cas d'erreur de l'instanciation, le script envoie sa réponse et se termine ;
- lignes 49-53 : le paramètre 'params' est récupéré. S'il est absent, le script envoie sa réponse et se termine ;
- ligne 56 : le paramètre 'oui,2,200000' est décomposé en 3 champs dans le tableau items ;
- lignes 58-77 : la validité des 3 champs est vérifiée. S'il y a une erreur, le script envoie sa réponse et se termine ;
- ligne 79 : l'impôt est calculé ;
- ligne 81 : et envoyé au client.
Dans un navigateur Web, on obtient les résultats suivants :
XIV-B. Un client programmé▲
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.
# -*- coding=utf-8 -*-
import
httplib,urllib,re
# constantes
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"
);
# 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 (ligne de texte)
reponse=
connexion.getresponse
(
).read
(
)
lignes=
reponse.split
(
"
\n
"
)
# la ligne de texte est de la forme
# <impot>xxx</impot>
# ou <erreur>xxx</erreur>
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
# fermeture connexion
connexion.close
(
)
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] : 2e parametre invalide
params=oui,2,x, erreur=[oui,2,x] : 3e parametre invalide