Skip to content

15. تمرين [IMPOTS] مع XML

في هذا التمرين الذي تمت دراسته عدة مرات من قبل، يقوم الخادم بإرجاع النتائج إلى العميل في شكل تدفق XML:

  • <reponse><erreur>msg</erreur></reponse> في حالة حدوث خطأ؛
  • <reponse><impot>valeur</impot></reponse> إذا تم حساب الضريبة بنجاح.

نستخدم ما تعلمناه للتو حول تحليل مستند XML.

15.1. خدمة الويب

لا تختلف خدمة الويب عن خدمة الويب التي درسناها سابقًا، باستثناء أن الرد XML المرسل إلى العميل يختلف قليلاً. تظل البنية كما هي:


خدمة الويب (impots_web_02)


#!D:\Programs\ActivePython\Python2.7.2\python.exe

# -*- coding=Utf-8 -*-
# استيراد وحدة الفئات Impots*
from impots import *
import cgi,cgitb,re

# السماح بعرض معلومات التصحيح
cgitb.enable()

# ------------------------------------------------ 
# خدمة الويب الخاصة بالضرائب
# ------------------------------------------------ 

# تم وضع البيانات اللازمة لحساب الضريبة في الجدول mysqL TABLE
# التابعة لقاعدة البيانات BASE. الجدول له البنية التالية
# حدود decimal(10,2)، coeffR decimal(6,2)، coeffN decimal(10,2)
# معلمات الأشخاص الخاضعين للضريبة (الحالة الاجتماعية، عدد الأبناء، الراتب السنوي)
# يتم إرسالها من قبل العميل بالصيغة params=الحالة الاجتماعية، عدد الأبناء، الراتب السنوي
# يتم إرسال النتائج (الحالة الاجتماعية، عدد الأبناء، الراتب السنوي، الضريبة المستحقة) إلى العميل
# بالصيغة <ضريبة>القيمة</ضريبة>
# أو في شكل <خطأ>رسالة</خطأ>، إذا كانت المعلمات غير صالحة

# يرسل الخادم نصًا غير منسق إلى العميل
print "Content-Type: text/plain\n"
# بداية الرد
print "<reponse>"

# تعريف الثوابت
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# إنشاء مثيل لطبقة [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()

# يتم استرداد السطر الذي أرسله العميل إلى الخادم
params=cgi.FieldStorage().getlist('params')
# إذا لم تكن هناك معلمات، فسيحدث خطأ
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

# معالجة المعلمة params  
#print "المعلمات المستلمة --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# يجب ألا يكون هناك سوى 3 حقول
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# يجب أن يكون المعلمة الأولى (الحالة الاجتماعية) نعم/لا
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# يجب أن يكون المعلمة الثانية (عدد الأطفال) عددًا صحيحًا
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])
# يجب أن يكون المعامل الثالث (الراتب) عددًا صحيحًا
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])
# يتم حساب الضريبة
impot=metier.calculer(marie,enfants,salaire)
# يتم إرجاع النتيجة
print "<impot>%s</impot></reponse>\n" % (impot)
# النهاية

ملاحظات:

لا تختلف خدمة الويب هذه عن السابقة إلا في طبيعة ردها:

<reponse><erreur>msg</erreur></reponse> في حالة وجود خطأ بدلاً من <erreur>msg</erreur>

<reponse><impot>valeur</impot></reponse> إذا تم حساب الضريبة بدلاً من <impot>valeur</impot>

15.2. العميل المبرمج

يجب على عميلنا تحليل الرد XML المرسل من خدمة الويب. نطبق ما تمت مناقشته في تحليل المستند XML.


البرنامج (client_impots_web_02)


# -*- coding=utf-8 -*-

import httplib,urllib,re
import xml.sax, xml.sax.handler
# فئة الإدارة XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # الوظيفة التي يتم استدعاؤها عند العثور على علامة البداية
    def startElement(self,name,attributs):
        # يتم تسجيل العنصر الحالي
        global elementcourant
        elementcourant=name.strip().lower()

    # الدالة التي يتم استدعاؤها عند العثور على علامة نهاية
    def endElement(self,name):
        # لا يتم القيام بأي شيء
        pass

    # وظيفة إدارة البيانات
    def characters(self,data):
        # البيانات
        global elementcourant,elements

        # يتم استرداد البيانات
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # يتم تحليل الاستجابة XML
    xml.sax.parseString(reponse,XmlHandler())
    # يتم عرض النتائج
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# الثوابت
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");
# المتغيرات العالمية
elementcourant=""
elements={}

# الاتصال
connexion=httplib.HTTPConnection(HOST)
# المتابعة
#connexion.set_debuglevel(1)
# يتم تكرار البيانات المراد إرسالها إلى الخادم
for params in data:
    # يجب ترميز المعلمات قبل إرسالها إلى الخادم
    parametres = urllib.urlencode({'params': params})
    # إرسال الطلب
    connexion.request("POST",URL,parametres)
    # معالجة الرد (تدفق XML)
    reponse=connexion.getresponse().read()
    # معالجة ملف xml
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

ملاحظات:

  • يتم تشغيل تدفق XML الخاص بخدمة الويب بواسطة الدالة getResultatsXml (السطر 60)؛
  • السطر 29: الدالة getResultatsXml؛
  • السطر 31: يتم تحليل استجابة خدمة الويب XML بواسطة مثيل للفئة XmlHandler المُعرَّفة في السطر 6؛
  • السطر 6: تُنفذ الفئة XmlHandler الطرق الثلاث startElement و endElement و characters. وبمساعدة هذه الطرق الثلاث، يتم إنشاء قاموس. المفاتيح هي أسماء العلامات <erreur> و <impot>، والقيم هي البيانات المرتبطة بهاتين العلامتين؛
  • الأسطر 33-36: تُرجع الدالة getResultatsXml مجموعة مكونة من عنصرين:
    • (erreur,"") إذا أظهر تحليل التدفق XML وجود العلامة <erreur>. وعندئذٍ يمثل erreur محتوى هذه العلامة؛
    • ("",impot) إذا أظهر تحليل التدفق XML وجود العلامة <impot>. وعندئذٍ يمثل impot محتوى هذه العلامة.
  • السطر 60: يتم استرداد نتيجة الدالة getResultatsXml ثم معالجتها في الأسطر 61-64.

15.3. النتائج

1
2
3
4
5
6
7
8
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