Skip to content

13. Ćwiczenie [IMPOTS] z wykorzystaniem usługi WEB

Wracamy do ćwiczenia [IMPOTS], omówionego już w punktach 4.2, 4.3, 8 i 10.

13.1. Serwer

Rozważamy następującą architekturę klient-serwer:

Po stronie serwera ponownie zastosujemy architekturę trójwarstwową. Warstwa [dao] zostanie zaimplementowana przez klasę ImpotsMySQL, wykorzystaną w wersji z SGBD i MySQL w punkcie 10. Warstwa [metier] zostanie zaimplementowana przez klasę [ImpotsMetier], którą już omówiliśmy. Pozostaje więc jedynie napisać usługę internetową. Usługa ta otrzymuje od swoich klientów parametr params w postaci params= tak,2,200000, gdzie pierwszy element wskazuje, czy podatnik jest w związku małżeńskim, drugi – liczbę dzieci, a trzeci – roczne wynagrodzenie.

Usługa internetowa wygląda następująco (impots_web_01):


#!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()
sys.stderr=sys.stdout

# ------------------------------------------------ 
# 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"

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


# tworzy się warstwę [metier]
# Wydaje się, że Python nie obsługuje sesji
# w PHP użyto by sesji do zapamiętania obiektu biznesowego
# tutaj obiekt ten jest systematycznie tworzony na podstawie danych z bazy danych MySQL

# instancjonowanie 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()

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

# przetwarzamy parametr „params”  
items=params[0].strip().lower().split(',')
# musi zawierać tylko 3 pola
if len(items)!=3:
  print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (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>\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>\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] : 3ieme parametre invalide</erreur>\n"% (params[0])
  sys.exit()
salaire=int(match.groups()[0])
# obliczamy podatek
impot=metier.calculer(marie,enfants,salaire)
# zwracamy wynik
print "<impot>%s</impot>\n" % (impot)
# koniec

Uwagi:

  • wiersz 5: importujemy obiekty z pliku impots.py;
  • wiersz 9: debugowanie skryptu CGI może być problematyczne. Moduł cgitb wysyła, w przypadku błędu, przyczynę awarii. W tym celu należy przekierować standardowe wyjście błędów (sys.stderr) do standardowego wyjścia (sys.stdout) (wiersz 10);
  • wiersz 26: nagłówek HTTP określający rodzaj wysyłanego dokumentu, w tym przypadku tekst nieformatowany. Serwis internetowy zwraca odpowiedź w postaci pojedynczego wiersza tekstu:
    • <erreur>message</erreur>, jeśli wystąpi błąd;
    • <impot>valeur</impot>, gdzie valeur to kwota podatku.
  • wiersz 43: instancjonowanie warstw [dao] i [metier];
  • wiersze 44–46: w przypadku błędu podczas instancjonowania skrypt wysyła odpowiedź i kończy działanie;
  • wiersze 49–53: pobierany jest parametr „params”. Jeśli go nie ma, skrypt wysyła odpowiedź i kończy działanie;
  • wiersz 56: parametr „oui,2,200000” jest rozkładany na 3 pola w tablicy items;
  • wiersze 58–77: sprawdzana jest poprawność trzech pól. Jeśli wystąpi błąd, skrypt wysyła odpowiedź i kończy działanie;
  • wiersz 79: obliczany jest podatek;
  • wiersz 81: i wysyłany do klienta.

W przeglądarce internetowej otrzymujemy następujące wyniki:

13.2. Zaprogramowany klient


Program (client_impots_web_01)


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

import httplib,urllib,re

# stałe
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");

# logowanie
connexion=httplib.HTTPConnection(HOST)
# monitorowanie
#connexion.set_debuglevel(1)
# przechodzimy do pętli przetwarzania danych, które mają zostać wysłane na serwer
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 (wiersz tekstu)
    reponse=connexion.getresponse().read()
    lignes=reponse.split("\n")
    # wiersz tekstu ma postać
    # <impot>xxx</impot>
    # lub <błąd>xxx</błąd>
    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
# zamknięcie połączenia
connexion.close()

13.3. Résultats

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