13. Exercise [Tax Calculation] with a Web Service
We return to the [TAXES] exercise already covered in sections 4.2, 4.3, 8, and 10.
13.1. The server
We are working within the following client-server architecture:
On the server side, we will again use a three-tier architecture. The [DAO] layer will be implemented by the ImpotsMySQL class used in the version with the MySQL DBMS in section 10. The [business] layer will be implemented by the [ImpotsMetier] class already studied. All that remains is to write the web service. This service receives a params parameter from its clients in the form params= yes,2,200000, where the first element indicates whether the taxpayer is married or not, the second their number of children, and the third their annual salary.
The web service is as follows (impots_web_01):
| # !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()
sys.stderr=sys.stdout
# ------------------------------------------------
# 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"
# definition of constants
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"
# we create the [metier] layer
# Python doesn't seem to support sessions
# in PHP, we would have used a session to memorize the metier object
# here, it is systematically constructed from data in the MySQL database
# 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>"
sys.exit()
# we use the params parameter
items=params[0].strip().lower().split(',')
# there must be only 3 fields
if len(items)!=3:
print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (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>\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>\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] : 3ieme parametre invalide</erreur>\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>\n" % (impot)
# end
|
Notes:
- line 5: import the objects from the impots.py file;
- line 9: Debugging a CGI script can be problematic. The cgitb module sends the reason for the crash in case of an error. To do this, you must redirect the standard error output (sys.stderr) to the standard output (sys.stdout) (line 10);
- line 26: the HTTP header that specifies the nature of the document being sent, in this case unformatted text. The web service returns its response as a single line of text:
- <error>message</error> if there is an error;
- <tax>value</tax> where value is the tax amount.
- line 43: instantiation of the [dao] and [business] layers;
- lines 44–46: if an instantiation error occurs, the script sends its response and terminates;
- lines 49-53: the 'params' parameter is retrieved. If it is missing, the script sends its response and terminates;
- line 56: the parameter 'yes,2,200000' is broken down into 3 fields in the items array;
- lines 58-77: the validity of the 3 fields is checked. If there is an error, the script sends its response and terminates;
- line 79: the tax is calculated;
- line 81: and sent to the client.
In a web browser, the following results are obtained:
13.2. A programmed client
The program (client_impots_web_01)
| # -*- coding=utf-8 -*-
import httplib,urllib,re
# constant
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");
# 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)
# answer processing (text line)
reponse=connexion.getresponse().read()
lignes=reponse.split("\n")
# the text line is of the form
# <impot>xxx</impot>
# or <error>xxx</error>
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
# locking connection
connexion.close()
|
13.3. Results
| 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
|