15. Exercise [Tax Calculation] with XML
In this exercise, which we have already covered many times, the server returns the results to the client in the form of an XML stream:
- <response><error>msg</error></response> in case of an error;
- <response><tax>value</tax></response> if the tax could be calculated.
We use what we have just learned about parsing an XML document.
15.1. The Web Service
The web service is no different from the one studied previously, except that the XML response sent to the client is slightly different. The architecture remains the same:
The web service (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
|
Notes:
This web service differs from the previous one only in the nature of its response:
<response><error>msg</error></response> in case of an error instead of <error>msg</error>
<response><tax>value</tax></response> if the tax could be calculated instead of <tax>value</tax>
15.2. The client application
Our client must parse the XML response sent by the web service. We apply what we learned in the analysis of an XML document.
The program (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)
|
Notes:
- The XML feed from the web service is processed by the getResultatsXml function (line 60);
- line 29: the getResultatsXml function;
- line 31: the web service's XML response is parsed by an instance of the XmlHandler class defined on line 6;
- line 6: the XmlHandler class implements the three methods startElement, endElement, and characters. Using these three methods, a dictionary is created. The keys are the names of the <error> and <import> tags, and the values are the data associated with these two tags;
- lines 33–36: the getResultatsXml function returns a tuple with two elements:
- (error, "") if the XML stream analysis detected the <error> tag. error then represents the content of this tag;
- ("", impot) if the XML stream analysis revealed the presence of the <impot> tag. impot then represents the content of this tag.
- Line 60: The result of the getResultatsXml function is retrieved and then processed in lines 61–64.
15.3. The results
| 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
|