Using Python to glue Windows and AS/400 applications together

Download Report

Transcript Using Python to glue Windows and AS/400 applications together

{ Serfs }
Using Python to glue Windows and
iSeries applications together
Johan Lindberg
Nordea Life & Pensions, IT
[email protected]
EuroPython 2005
2005-06-27
16:30-17:00
{ Serfs }
What is a glue application?
•
A glue application is a piece of code that makes two or more
applications work together.
{ Serfs }
Our example: the problem
•
•
About 300 customer letters (up to 10 pages) have to be produced
manually with Microsoft Word using information from our insurance
system.
Some are used very rarely, some very often and all of them take a lot of
time to produce.
Server
Client (Windows desktop)
IBM Personal
Communications
Find values for each field
in the Word template.
Lifa,
Insurance System
(IBM iSeries)
Microsoft Word
Enter the value from
screen in the form field.
{ Serfs }
Our example: a solution
•
The solution we decided on is a small wxPython program, distributed
as a Windows executable, that performs the tasks neccessary to
complete the letter.
Server
Client (Windows desktop)
IBM Personal
Communications
Lifa,
Insurance System
(IBM iSeries)
glue application
Microsoft Word
Select Word
template.
{ Serfs }
A first, high-level, look at our glue
application (Example.py)
•
•
Find a running instance of Word (or start one).
Create a screen scraper object
•
•
Ask user which Word template to use
Create a document using the template
•
For each form field in document:
– Read the name of the field
– Read the corresponding value from screen
– Write that value to the form field
•
•
Tell the user we’re done
Quit
{ Serfs }
Application hierarchy
•
The glue application is split into 4 different source files
Glue application
Example.py
UI.py
Lifa.py
iSeries.py
Word
wxPy.
Personal Comm.
Lifa,
Insurance System
(IBM iSeries)
{ Serfs }
Example.py
import win32com.client
import UI, Lifa
# get references to applications
word= win32com.client.Dispatch(”Word.Application”)
lifa= Lifa.Proxy()
# ask user which template to use and create a document using it
template= UI.OpenFile(u”Välj en brevmall”, ”C:\\Templates\\”, ””, ”*.dot”)
doc= word.Documents.Add(template)
# go through all form fields in the document
...
# tell the user we’re done and quit
UI.DisplayMessage(u”Klart!”)
sys.exit()
{ Serfs }
Loop through all form fields
for field in doc.FormFields:
if field.Name.startswith(”LIFA”):
if int(field.Type)== 70: # wdFieldFormTextInput
field.Result= getValueFromScreen(field.Name)
elif int(field.Type)== 83: # wdFieldFormDropDown
# convert screen value to drop down index
result= getValueFromScreen(field.Name)
if field.Name== ”LIFA010101”: # gender
if result== ”M”: # male
field.DropDown.Value = 1
elif result== ”K”: # female
field.DropDown.Value = 2
elif ...
{ Serfs }
A closer look at a form field
•
•
Below is the properties dialog of a Textfield in Microsoft Word.
The bookmark (field.Name property) consists of four parts:
–
–
–
–
A label (LIFA)
A page identifier (01)
A row identifier (04)
A column identifier (24)
{ Serfs }
getValueFromScreen
cache= {}
def getValueFromScreen(bookmark):
value= None
# use cached value if it exists
if bookmark in cache:
value= cache[bookmark]
else:
page, row, col= bookmark[4:6], bookmark[6:8], bookmark[8:10]
lifa.gotoPage(page)
value= lifa.conn.getText(int(row), int(col))
cache[bookmark]= value
return value
{ Serfs }
The Lifa proxy object (1/2)
# part of Lifa module
import iSeries
class Proxy:
def __init__(self):
self.conn= iSeries.ConnectionManager()
availableConnections= self.conn.getAvailableConnections()
selected= None
# ask user to select a connection if there are more than one available
if len(availableConnections)> 1:
selected= UI.SelectOption(availableConnections)
elif len(availableConnections)== 1:
selected= 0
# open and set active session
self.conn.openSession(availableConnections[selected])
self.conn.setActiveSession(availableConnections[selected])
{ Serfs }
The Lifa proxy object (2/2)
class Proxy: (continued)
def gotoPage(self, page):
onPage= False
while not onPage:
currPage= self.conn.getText(1, 1, 2) # page ID
dp= int(page)- int(currPage)
if dp== 0:
onPage= True
elif dp< 0:
key= ”[pf9]” # F9 = previous page
elif dp> 0:
key= ”[pf10]” # F10 = next page
self.conn.sendKeys(abs(dp), u”%s” % (key), 1, 1)
...
time.sleep(1)
{ Serfs }
The iSeries module (1/3)
class ConnectionManager:
def __init__(self):
self.PCommConnMgr= win32com.client.Dispatch(”PCOMM.autECLConnMgr”)
self.connList= self.PCommConnMgr.autECLConnList
self.activeSession= None
self.sessions= {}
# return value from screen at position (row, col)
def getText(self, row, col, length= None, session= None):
result= None
tempSession= self.activeSession
if session is not None:
tempSession= self.sessions[session]
if length is None:
tempSession.autECLPS.autECLFieldList.Refresh()
field= tempSession.autECLPS.autECLFieldList.FindFieldByRowCol(row, col)
length= field.Length
result= tempSession.autECLPS.GetText(row, col, length)
return result
{ Serfs }
The iSeries module (2/3)
class ConnectionManager: (continued)
def sendKeys(self, count, key, row= None, col= None, session= None):
n= 0
while n< count:
tempSession= self.activeSession
if session is not None:
tempSession= self.sessions[session]
if row is None or col is None:
tempSession.session.autECLPS.SendKeys("%s" % (key))
else:
tempSession.session.autECLPS.SendKeys("%s" % (key), row, col)
n+= 1
{ Serfs }
The iSeries module (3/3)
class ConnectionManager: (continued)
def getAvailableConnections(self):
self.connList.Refresh()
connections= []
for i in range (self.connList.Count):
connections.append(self.connList(i+ 1).Name)
return connections
def openSession(self, session)
_session= win32com.client.Dispatch("PCOMM.autECLSession")
_session.SetConnectionByName(session)
if not _session.Ready:
_session.StartCommunication()
self.sessions[session]= _session
{ Serfs }
Part of the UI module
# part of UI module
import wx
class OpenFileDialog(wx.FileDialog):
def __init__(self, caption, dir, file, wildcard):
wx.FileDialog.__init__(self, None, caption, dir, file, wildcard)
def OpenFile(caption, dir, file, wildcard):
path= None
dialog= OpenFileDialog(caption,
dir, file, wildcard)
if dialog.ShowModal()== wx.ID_OK:
path= dialog.GetPath()
dialog.Destroy()
return path
{ Serfs }
Learn more…
Python and Windows
http://www.python.org/windows/
Python on iSeries
http://www.iseriespython.com/
Python and COM tutorials
http://www.reportlab.com/ftp/talks/PythonWindowsTutorial.doc
http://starship.python.net/crew/pirx/spam7/COMtut.PPT
Microsoft Office OLE documentation
http://msdn.microsoft.com/office/
http://msdn.microsoft.com/office/understanding/word/
IBM Personal Communications
http://publib.boulder.ibm.com/infocenter/pcomhelp/topic/com.ibm.pcomm.doc/host_access08.htm
{ Serfs }
Using Python to glue Windows and
iSeries applications together
This presentation, sample code and
links to references can be found at:
http://www.pulp.se/ep2005/
Questions?