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 the Impots* class module
from impots import *
import cgi, cgitb, re
# Enable display of debug information
cgitb.enable()
sys.stderr = sys.stdout
# ------------------------------------------------
# the tax web service
# ------------------------------------------------
# The data needed to calculate the tax has been placed in the MySQL table TABLE
# belonging to the BASE database. The table has the following structure
# limits decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# The parameters for taxable individuals (marital status, number of children, annual salary)
# are sent by the client in the form params=marital status, number of children, annual salary
# the results (marital status, number of children, annual salary, tax due) are returned to the client
# in the form <tax>value</tax>
# or in the form <error>msg</error>, if the parameters are invalid
# the server returns unformatted text to the client
print "Content-Type: text/plain\n"
# definition of constants
USER="root"
PWD=""
HOST="localhost"
DATABASE="dbimpots"
TABLE="taxes"
# Create the [business logic] layer
# Python does not seem to support sessions
# In PHP, we would have used a session to store the business object
# here, we systematically build it from the data in the MySQL database
# instantiating the [business] layer
try:
business = TaxBusiness(TaxMySQL(HOST, USER, PWD, DATABASE, TABLE))
except (IOError, ImpotsError) as info:
print("<error>An error occurred: {0}</error>".format(info))
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 "<error>The parameter [params] is missing<error>"
sys.exit()
# process the params parameter
items = params[0].strip().lower().split(',')
# there should only be 3 fields
if len(items) != 3:
print "<error>[%s]: invalid number of parameters</error>" % (params[0])
sys.exit()
# The first parameter (marital status) must be yes/no
married = items[0].strip()
if married != "yes" and married != "no":
print "<error>[%s]: Invalid first parameter</error>\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 "<error>[%s]: Invalid second parameter</error>\n"% (params[0])
sys.exit()
children = 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 "<error>[%s]: Invalid third parameter</error>\n" % (params[0])
sys.exit()
salary = int(match.groups()[0])
# calculate the tax
tax = job.calculate(spouse, children, salary)
# return the result
print "<tax>%s</tax>\n" % (tax)
# 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
# -*- coding=utf-8 -*-
import httplib, urllib, re
# constants
HOST="localhost"
URL="/cgi-bin/impots_web_01b.py"
data=("yes,2,200000","no,2,200000","yes,3,200000","no,3,200000","x,y,z,t","x,2,200000", "yes,x,200000","yes,2,x");
# connection
connection = httplib.HTTPConnection(HOST)
# logging
#connection.set_debuglevel(1)
# loop through the data to be sent to the server
for params in data:
# Parameters must be URL-encoded before being sent to the server
parameters = urllib.urlencode({'params': params})
# Send the request
connection.request("POST", URL, parameters)
# Process the response (text string)
response = connection.getresponse().read()
lines = response.split("\n")
# the text line is in the form
# <impot>xxx</impot>
# or <error>xxx</error>
tax=re.match(r"^<tax>(\S+)</tax>\s*$",lines[0])
if impot:
print "params=%s, impot=%s" % (params, impot.groups()[0])
else:
error = re.match(r"^<error>(.+)</error>\s*$", lines[0])
if error:
print "params=%s, error=%s" % (params, error.groups()[0])
else:
print lines
# Close connection
connection.close()



