Skip to content

15. Ćwiczenie [IMPOTS] z XML

W tym ćwiczeniu, które było już wielokrotnie omawiane, serwer zwraca wyniki do klienta w postaci strumienia XML:

  • <reponse><erreur>msg</erreur></reponse> w przypadku błędu;
  • <reponse><impot>valeur</impot></reponse>, jeśli udało się obliczyć podatek.

Wykorzystujemy to, czego właśnie się nauczyliśmy, do analizy dokumentu XML.

15.1. Usługa internetowa

Usługa internetowa nie różni się od omówionej wcześniej, z wyjątkiem tego, że odpowiedź XML wysyłana do klienta jest nieco inna. Architektura pozostaje taka sama:


Usługa internetowa (impots_web_02)


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

# -*- coding=Utf-8 -*-
# import modułu klas Impots*
from impots import *
import cgi,cgitb,re

# zezwalamy na wyświetlanie informacji debugowania
cgitb.enable()

# ------------------------------------------------ 
# serwis internetowy urzędu skarbowego
# ------------------------------------------------ 

# dane niezbędne do obliczenia podatku zostały umieszczone w tabeli mysqL TABLE
# należącej do bazy danych BASE. Tabela ma następującą strukturę
# limity decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# parametry osób podlegających opodatkowaniu (stan cywilny, liczba dzieci, roczne wynagrodzenie)
# są przesyłane przez klienta w postaci params=stan cywilny, liczba dzieci, roczne wynagrodzenie
# wyniki (stan cywilny, liczba dzieci, roczne wynagrodzenie, podatek do zapłaty) są zwracane klientowi
# w postaci <podatek>wartość</podatek>
# lub w postaci <błąd>msg</błąd>, jeśli parametry są nieprawidłowe

# serwer zwraca klientowi tekst nieformatowany
print "Content-Type: text/plain\n"
# początek odpowiedzi
print "<reponse>"

# definicja stałych
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# utworzenie instancji warstwy [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()

# odbieramy wiersz wysłany przez klienta do serwera
params=cgi.FieldStorage().getlist('params')
# jeśli nie ma parametrów, to błąd
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

# przetwarzamy parametr params  
#print "otrzymane parametry --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# powinny być tylko 3 pola
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# pierwszy parametr (stan cywilny) musi mieć wartość „tak” lub „nie”
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# drugi parametr (liczba dzieci) musi być liczbą całkowitą
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])
# trzeci parametr (wynagrodzenie) musi być liczbą całkowitą
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])
# obliczamy podatek
impot=metier.calculer(marie,enfants,salaire)
# zwracamy wynik
print "<impot>%s</impot></reponse>\n" % (impot)
# koniec

Uwagi:

Ta usługa internetowa różni się od poprzedniej jedynie charakterem odpowiedzi:

<reponse><erreur>msg</erreur></reponse> w przypadku błędu zamiast <erreur>msg</erreur>

<reponse><impot>valeur</impot></reponse>, jeśli udało się obliczyć podatek, zamiast <impot>valeur</impot>

15.2. Klient programowy

Nasz klient musi przeanalizować odpowiedź XML przesłaną przez serwis internetowy. Stosujemy to, co omówiono podczas analizy dokumentu XML.


Program (client_impots_web_02)


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

import httplib,urllib,re
import xml.sax, xml.sax.handler
# klasa zarządzania XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # funkcja wywoływana po napotkaniu znacznika początkowego
    def startElement(self,name,attributs):
        # zapisywany jest bieżący element
        global elementcourant
        elementcourant=name.strip().lower()

    # funkcja wywoływana po napotkaniu znacznika końcowego
    def endElement(self,name):
        # nie wykonuje się żadnej czynności
        pass

    # funkcja zarządzania danymi
    def characters(self,data):
        # dane
        global elementcourant,elements

        # dane są pobierane
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # analizowana jest odpowiedź XML
    xml.sax.parseString(reponse,XmlHandler())
    # wyniki są zwracane
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# stałe
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");
# zmienne globalne
elementcourant=""
elements={}

# połączenie
connexion=httplib.HTTPConnection(HOST)
# śledzenie
#connexion.set_debuglevel(1)
# przechodzimy do pętli przetwarzania danych, które mają zostać wysłane do serwera
for params in data:
    # parametry muszą zostać zakodowane przed wysłaniem na serwer
    parametres = urllib.urlencode({'params': params})
    # wysyłanie żądania
    connexion.request("POST",URL,parametres)
    # przetwarzanie odpowiedzi (strumień XML)
    reponse=connexion.getresponse().read()
    # przetwarzanie pliku XML
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Uwagi:

  • strumień XML serwisu internetowego jest obsługiwany przez funkcję getResultatsXml (wiersz 60);
  • wiersz 29: funkcja getResultatsXml;
  • wiersz 31: odpowiedź XML z serwisu internetowego jest analizowana przez instancję klasy XmlHandler zdefiniowanej w wierszu 6;
  • wiersz 6: klasa XmlHandler implementuje trzy metody: startElement, endElement oraz characters. Za pomocą tych trzech metod tworzony jest słownik. Kluczami są nazwy tagów <erreur> i <impot>, a wartościami są dane powiązane z tymi dwoma tagami;
  • wiersze 33–36: funkcja getResultatsXml zwraca krotkę zawierającą 2 elementy:
    • (erreur,""), jeśli analiza strumienia XML wykazała obecność tagu <erreur>. erreur reprezentuje wówczas zawartość tego tagu;
    • ("",impot), jeśli analiza strumienia XML wykazała obecność tagu <impot>. impot reprezentuje wówczas zawartość tego tagu.
  • wiersz 60: wynik funkcji getResultatsXml jest pobierany, a następnie przetwarzany w wierszach 61–64.

15.3. Wyniki

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