Skip to content

15. Ejercicio [IMPOTS] con XML

En este ejercicio, ya estudiado en numerosas ocasiones, el servidor devuelve los resultados al cliente en forma de un flujo XML:

  • <respuesta><error>msg</error></respuesta> en caso de error;
  • <respuesta><impuesto>valor</impuesto></respuesta> si se ha podido calcular el impuesto.

Utilizamos lo que acabamos de aprender sobre el análisis de un documento XML.

15.1. El servicio web

El servicio web no difiere del servicio web estudiado anteriormente, salvo que la respuesta XML enviada al cliente es ligeramente diferente. La arquitectura sigue siendo la misma:


El servicio web (impots_web_02)


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

# -*- coding=Utf-8 -*-
# importación del módulo de clases Impots*
from impots import *
import cgi,cgitb,re

# se permite la visualización de información de depuración
cgitb.enable()

# ------------------------------------------------ 
# el servicio web de impuestos
# ------------------------------------------------ 

# los datos necesarios para el cálculo del impuesto se han colocado en la tabla mysqL TABLE
# perteneciente a la base de datos BASE. La tabla tiene la siguiente estructura
# límites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# los parámetros de las personas sujetas a impuestos (estado civil, número de hijos, salario anual)
# son enviados por el cliente en el formato params=estado civil, número de hijos, salario anual
# los resultados (estado civil, número de hijos, salario anual, impuesto a pagar) se devuelven al cliente
# en el formato <impuesto>valor</impuesto>
# o en el formato <error>msg</error>, si los parámetros no son válidos

# el servidor devuelve al cliente texto sin formato
print "Content-Type: text/plain\n"
# inicio de la respuesta
print "<reponse>"

# definición de las constantes
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# instanciación de la capa [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()

# se recupera la línea enviada por el cliente al servidor
params=cgi.FieldStorage().getlist('params')
# si no hay parámetros, se produce un error
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

# se procesa el parámetro params  
#print "parámetros recibidos --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# solo debe haber 3 campos
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# el primer parámetro (estado civil) debe ser sí/no
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# el segundo parámetro (número de hijos) debe ser un número entero
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])
# el tercer parámetro (salario) debe ser un número entero
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])
# se calcula el impuesto
impot=metier.calculer(marie,enfants,salaire)
# se devuelve el resultado
print "<impot>%s</impot></reponse>\n" % (impot)
# fin

Notas:

Este servicio web solo se diferencia del anterior por la naturaleza de su respuesta:

<respuesta><error>msg</error></respuesta> en caso de error, en lugar de <error>msg</error>

<respuesta><impuesto>valor</impuesto></respuesta> si se ha podido calcular el impuesto, en lugar de <impuesto>valor</impuesto>

15.2. El cliente programado

Nuestro cliente debe analizar la respuesta XML enviada por el servicio web. Aplicamos lo visto en el análisis de un documento XML.


El programa (client_impots_web_02)


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

import httplib,urllib,re
import xml.sax, xml.sax.handler
# clase de gestión XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # función llamada al encontrar una etiqueta de inicio
    def startElement(self,name,attributs):
        # se anota el elemento actual
        global elementcourant
        elementcourant=name.strip().lower()

    # función llamada al encontrar una etiqueta de fin
    def endElement(self,name):
        # no se realiza ninguna acción
        pass

    # la función de gestión de datos
    def characters(self,data):
        # datos
        global elementcourant,elements

        # se recuperan los datos
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # se analiza la respuesta XML
    xml.sax.parseString(reponse,XmlHandler())
    # se devuelven los resultados
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# constantes
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");
# variables globales
elementcourant=""
elements={}

# conexión
connexion=httplib.HTTPConnection(HOST)
# seguimiento
#connexion.set_debuglevel(1)
# se realiza un bucle sobre los datos que se van a enviar al servidor
for params in data:
    # los parámetros deben codificarse antes de enviarse al servidor
    parametres = urllib.urlencode({'params': params})
    # envío de la solicitud
    connexion.request("POST",URL,parametres)
    # procesamiento de la respuesta (flujo XML)
    reponse=connexion.getresponse().read()
    # procesamiento del archivo xml
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Notas:

  • el flujo XML del servicio web es procesado por la función getResultatsXml (línea 60);
  • línea 29: la función getResultatsXml;
  • línea 31: la respuesta XML del servicio web es analizada por una instancia de la clase XmlHandler definida en la línea 6;
  • línea 6: la clase XmlHandler implementa los 3 métodos startElement, endElement y characters. Con ayuda de estos 3 métodos, se crea un diccionario. Las claves son los nombres de las etiquetas <error> e <impot> y los valores son los datos asociados a estas dos etiquetas;
  • líneas 33-36: la función getResultatsXml devuelve una tupla de dos elementos:
    • (error, "") si el análisis del flujo XML ha detectado la existencia de la etiqueta <error>. En ese caso, error representa el contenido de dicha etiqueta;
    • ("", impot) si el análisis del flujo XML ha detectado la existencia de la etiqueta <impot>. En ese caso, impot representa el contenido de dicha etiqueta.
  • línea 60: se recupera el resultado de la función getResultatsXml y se procesa en las líneas 61-64.

15.3. Los resultados

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