Saturday, November 03, 2012

Volviendo a visitar Python y SAP (con PyRFC)

Hace unos cuantos dias Srdjan Boskovic escribio un blog llamado Python / ABAP Stack. Esto por supuesto, me trajo memorias puesto que trabaje bastante con sapnwrfc de Piers Harding.

Luego de algunos mails con Srdjan y Jonas Kunze estaba listo y preparado para comenzar a trabajar.

Seguro se preguntaran...porque otro conector para Python y SAP? Simple, mientras que el conector de Pier's es impresionante, carece de algunas caracteristicas que SAP necesitaba para sus desarrollos externos, asi que ellos se tomaron el trabajo de hacer desde cero exactamente lo que necesitaban...una de las maravillas de ser programador...puedes tomar una idea...y convetirla en una aplicacion alucinante o en este caso, un conector RFC...

A manera de introducir el PyRFC, el mejor ejemplo es siempre mostrar algo hecho con las tablas de vuelos...que siempre estan disponibles en cualquier instalacion de ERP y como ya lo habia usado antes en SAP HANA and Python? Yes Sir!, Bottle parecia ser una buena opcion...porque ya no quieren ver mas pantallas de linea de comandos, no?

Primero les mostrare el codigo y luego les comentare sobre algunos de los cambios mas significativos que he podido notar al utilizar PyRFC (Y dicho sea de paso...luche conmigo mismo para hacer el ejemplo aun mejor y tener algo de manejo de errores, que siempre es bueno, inclusive para humildes blogs como este).


Bottle_PyRFC.py
from bottle import get, post, request, run, redirect
from sapnwrfc2 import Connection, ABAPApplicationError, LogonError
from ConfigParser import ConfigParser
 
conn = ""
 
@get('/login')
def login_form():
    return '''<DIV ALIGN='CENTER'><BR><BR><BR><BR>
                <H1>Python (Bottle) & SAP - using PyRFC</H1>
                <BR><TABLE BORDER='1' BORDERCOLOR='BLUE'
                     BGCOLOR='WHITE'>
                <FORM METHOD='POST'>
                <TR><TD>User</TD><TD>
                <INPUT TYPE='TEXT' NAME='User'></TD></TR>
                <TR><TD>Password</TD>
                <TD><INPUT TYPE='PASSWORD' NAME='Passwd'></TD></TR>
                <TR><TD COLSPAN='2' ALIGN='CENTER'>
                <INPUT TYPE='SUBMIT' value='Log In' NAME='LOG_IN'>
                <INPUT TYPE='RESET' value='Clear'></TD></TR>
                </FORM>
                <TABLE>
              </DIV>'''
 
@post('/login')
def login_submit():
    global conn
    try:
        user = request.forms.get('User')
        passwd = request.forms.get('Passwd')
        config = ConfigParser()
        config.read('sapnwrfc.cfg')
        params_connection = config._sections['connection']
        params_connection["user"] = user
        params_connection["passwd"] = passwd
        conn = Connection(**params_connection)
        redirect("/choose")
    except LogonError:
        redirect("/error")
 
@get('/choose')
def choose_table():
    return '''<CENTER>
                <FORM METHOD='POST'>
                <INPUT TYPE='TEXT' NAME='Table'><BR>
                <INPUT TYPE='SUBMIT' value='Show Table'
                 NAME='Show_Table'>
                </FORM>
              </CENTER>'''
 
@get('/error')
def error():
    output = "<div align='center'><h1>Invalid username or password</h1></div>"
    return output
 
@post('/choose')
def show_table():
    global conn
    fields = []
    fields_name = []
    counter = 0
    table = request.forms.get('Table')
    try:
        tables = conn.call("RFC_READ_TABLE", QUERY_TABLE=table, DELIMITER='|')
        data_fields = tables["DATA"]
        data_names = tables["FIELDS"]
        long_fields = len(data_fields)
        long_names = len(data_names)
 
        for line in range(0, long_fields):
            fields.append(data_fields[line]["WA"].strip())
        for line in range(0, long_names):
            fields_name.append(data_names[line]["FIELDNAME"].strip())
 
         output = "<div align='center'><h1>%s</h1></center>" % table
 
        output += "<table border='1'><tr>"
        for line in range(0, long_names):
            field_name = fields_name[line]
            output += "<th bgcolor='#B8D5F5'> %s </th>" % field_name
        output += "</tr>"
        for line in range(0, long_fields):
            counter += 1
            if(counter % 2 == 0):
                output += "<tr bgcolor='#DCE1E5'>"
            else:
                output += "<tr>"
            data_split = fields[line].split("|")
            for line in range(0, long_names):
                output += "<td> %s </td>" % data_split[line]
            output += "</tr>"
        output += "</table>"
 
     except ABAPApplicationError:
        output = "<div align='center'><h1>Table %s was not found</h1></div>" % table
        return output
 
     return output
    conn.close()
 
run(host='localhost', port=8080)


Asi que, para mi PyRFC tiene algunos beneficios resaltantes, como la opcion de atrapar ABAPApplicationError y LoginError (Asumo que la version de Piers tambien lo tenia, pero nunca me preocupe por buscarlo...que verguenza), ademas, la manera de llamar al Modulo de Funcioneses mas limpia, una simple funcion en Python que recive como parametros el nombre del FM y los parametros, quitandonos la necesidad de tener que declarar cada parametro como el atributo de un objeto. Tambien, es bastante veloz y puede ser usado en el lado del servidor...pero hablaremos de eso mas tarde...en otro blog...cuando realmente tenga la oportunidad de trabajar con eso...

Ejecutemos este programa y veamos como se ve...cuando lo ejecutamos desde Python debes ir a tu browser y pasar el siguiente enlace (puesto que estamos ejecutando una aplicacion en Bottle):

http://localhost:8080/login


Como siempre, fue muy divertido trabajar con Python, y por supuesto, al mezclarlo con PyRFC la diversion excedio mis expectativas puesto que ya nos mas de las 8:30 pm en una noche del Sabado...y estoy escribiendo un blog de lo que trabaje casi toda la tarde...la programacion es divertida...no lo olviden...

Saludos,

Blag.

No comments: