13. تمرين [حساب الضرائب] باستخدام خدمة ويب
نعود إلى تمرين [الضرائب] الذي تمت تغطيته بالفعل في الأقسام 4.2 و4.3 و8 و10.
13.1. الخادم
نحن نعمل ضمن بنية العميل-الخادم التالية:
على جانب الخادم، سنستخدم مرة أخرى بنية ثلاثية المستويات. سيتم تنفيذ طبقة [DAO] بواسطة فئة ImpotsMySQL المستخدمة في الإصدار الذي يعمل بنظام إدارة قواعد البيانات MySQL في القسم 10. وسيتم تنفيذ طبقة [business] بواسطة فئة [ImpotsMetier] التي تمت دراستها سابقًا. كل ما تبقى هو كتابة خدمة الويب. تتلقى هذه الخدمة معلمة params من عملائها بالشكل params= yes,2,200000، حيث يشير العنصر الأول إلى ما إذا كان دافع الضرائب متزوجًا أم لا، والثاني إلى عدد أطفاله، والثالث إلى راتبه السنوي.
خدمة الويب هي كما يلي (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
|
ملاحظات:
- السطر 5: استيراد الكائنات من ملف impots.py؛
- السطر 9: قد يكون تصحيح أخطاء البرنامج النصي CGI أمرًا صعبًا. ترسل الوحدة النمطية cgitb سبب التعطل في حالة حدوث خطأ. للقيام بذلك، يجب إعادة توجيه إخراج الخطأ القياسي (sys.stderr) إلى الإخراج القياسي (sys.stdout) (السطر 10)؛
- السطر 26: رأس HTTP الذي يحدد طبيعة المستند الذي يتم إرساله، وهو في هذه الحالة نص غير منسق. تعرض خدمة الويب استجابتها في شكل سطر واحد من النص:
- <error>message</error> في حالة وجود خطأ؛
- <tax>value</tax> حيث value هي مبلغ الضريبة.
- السطر 43: إنشاء مثيلات لطبقتي [dao] و[business]؛
- الأسطر 44-46: في حالة حدوث خطأ في الإنشاء، يرسل البرنامج النصي استجابته وينتهي؛
- الأسطر 49-53: يتم استرداد المعلمة 'params'. إذا كانت مفقودة، يرسل البرنامج النصي استجابته وينتهي؛
- السطر 56: يتم تقسيم المعلمة 'yes,2,200000' إلى 3 حقول في مصفوفة العناصر؛
- الأسطر 58-77: يتم التحقق من صحة الحقول الثلاثة. إذا كان هناك خطأ، يرسل البرنامج النصي استجابته وينتهي؛
- السطر 79: يتم حساب الضريبة؛
- السطر 81: ويتم إرسالها إلى العميل.
في متصفح الويب، يتم الحصول على النتائج التالية:
13.2. عميل مبرمج
البرنامج (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. النتائج
| 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
|