Cambiar a contenido. | Saltar a navegación

Screen styles
Usted está aquí: Inicio Documentación Tutorial Controlar OpenOffice.org desde Python

Controlar OpenOffice.org desde Python

Nota: Ésta es la vista de impresión del Tutorial completo en una sola página. Si lo prefiere puede encontrar la versión original aquí.

Tutorial originalmente publicado en el wiki de Lukasz Szybalski szybalski@gmail.com explica como python puede servirnos como una herramienta unica para automatizar OpenOffice.org Si tienes algun comentario por favor envia un correo a jza arroba openoffice.org

Primer paso

primeros pasos de Python en inciar y conectarnos a OpenOffice.org

En OpenOffice.org amamos a python, tanto es que incluimos toda una distribucion de python dentro de OpenOffice.org junto con un modulo que funciona de puente (bridge) entre nuestro API llamado UNO y el mundo de python. Lo llamamos PYUNO.

UNO contine una serie de servicios e Interfaces que interactuan entre si. La manipulación de OpenOffice.org depende de saber interactuar con estas interfaces y servicios.  

Algo que haremos aqui es poder iniciar a OpenOffice.org con modalidad de servidor, donde tenga un puerto abierto para recibir instrucciones desde fuera. Python empujara estas instrucciones y se logrará el ambiente remoto que se necesita para poder interactuar "en vivo" con OpenOffice.org.

Iniciar OpenOffice.org desde python

Para Iniciar openoffice.org en modo de servidor, se abre un puerto mientras corre la suite y esta se especifica el servicio al que recibira los comandos.

openoffice -accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"

Puedes tambien iniciar OpenOffice.org como un tipo de parametros al comando de inicio.

/usr/bin/openoffice -accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager" -norestore -nofirstwizard -nologo -headless

Conectarse a OpenOffice.org via Python

Para este paso iniciar Python e importar el modulo de UNO, despues importar el OpenOffice.org component context. Para conectarnos al programa necesitamos llamar el servicio de UnoUrlResolver y cargar el contexto el cual estas conectado y puedes accesar via el mecanismo de API.

$ python
>> import uno
>> local = uno.getComponentContext()
>> resolver = local.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local)
>> context = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")

Manejo de documentos

Una vez conectados, podemos empezar a crear documentos, insertar contenido y ser creativos con el contenido.

Una vez que nuestro script ya inicio OpenOffice.org y se conecto al API y la aplicación el cual los servicios se cargaron, en OpenOffice.org hay muchos tipos de servicios y ahora estaremos accesando algunos servicios específicos. Por ejemplo para acceder el servicio a cargo del documento debemos llamar el servicio "com.sun.star.frame.Desktop". Este servicio nos permitirá cargar y crear documentos. 

desktop = context.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", context)

Una vez cargado el servicio de Desktop, debemos cargar el documento via un metodo y crear un cursor en este para empezar a ingresar contenido.

>> document = desktop.getCurrentComponent()
>> cursor = document.Text.createTextCursor()

Cargar un documento nuevo/existente de OpenOffice.org

Ahora podemos ingresar un nuevo documento o como documento existente. Como podemos usar sistemas operativos diferentes ingresamos una condicional para ambos tipos de sistema operativo modificando los paths  y usando la funcion loadComponentFromURL().

document = desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, ())
cursor = document.Text.createTextCursor()

existente:

document = desktop.loadComponentFromURL("file:///home/lucas/myfile.odt" ,"_blank", 0, ())

or

document = desktop.loadComponentFromURL("file:///c:"+filepath+".odt" ,"_blank", 0, ())

Guardar y salir del documento

Para guardar y salir del documento podemos usar métodos simples tales como store() o guardar como usando storeAsURL(). Para salir se necesita dispose().

>> document.store()
>> document.storeAsURL("file:///home/lucas/myfile2.odt",())
>> document.dispose()

Modificando el contenido

Usamos el API y empezamos a llamar a funciones internas de la aplicación para modificar contenido, dar formato e ingresar datos.

Dependiendo del tipo de archivo que estemos trabajando en OpenOffice.org (hoja de calculo, presentaciones o documentos), es como podremos interactuar con la aplicacion. En este How-To solo trataremos con documentos de texto. Asi que para el primer ejemplo insertaremos una linea con el metodo InsertString(), un salto de linea con el caracter \n al finalizar mas una identacion de la misma con el caracter \t.

>> document.Text.insertString(cursor, "This text is being added to openoffice using python and uno package.", 0)
>> document.Text.insertString(cursor, "\n\nThis is a new paragraph.", 0)
>> document.Text.insertString(cursor, "\n\n\tAnd this is another new paragraph.", 0)

Como podemos ver el metodo InsertString() depende de la variable documento y texto, esto son los servicios que previamente habiamos insertado al principio de este documento.

Dando formato al contenido

Modificaremos el color del texto para lo cual necesitaremos enfocarnos en las propiedades de los metodos, primero necesitamos capturar las propiedades del cursor, por ejemplo para el tamaño del cursor usaríamos CharHeight. Para los otras propiedades mostraremos color, tipografia y tamaño.

>> cursor.setPropertyValue("CharHeight", 20)
>> cursor.setPropertyValue("CharFontName", "Arial")
>> cursor.setPropertyValue("CharWeight", 150)

Creando una Tabla

 Ahora intentaremos algo mas complejo, estaremos ingresando una tabla en el documento, para esto necesitaremos invocar nuevos servicios como TextTable, InsertTextContext, setString.

>> mytable = document.createInstance("com.sun.star.text.TextTable")
>> mytable.initialize(6,2)
>> document.Text.insertTextContent(cursor, mytable, 0)
>> mytable.getCellByName("A1").setString("This is first column")
>> mytable.getCellByName("B1").setString("This is second column")

con este script podemos ver como se ingresa contenido linea por linea dentro de la tabla. Ahora intentaremos manejar alguna funcion de nuestra herramienta como es el buscar y reemplazar.

Modificando el contenido: Buscar y reemplazar

Esta funcion nos permetira operarla desde el codigo de forma similar como un usuario en el escritorio. Usamos servicios diferentes como es el createSearDecriptor(), metodos como SearchString y findFirst.

>> import string
>> #Crear un decriptro de busqueda
>> search = document.createSearchDescriptor()
>> #Que buscar
>> search.SearchString = u"Lucas"
>> #Cadena encontrada
>> found = document.findFirst( search )
>> 
>> while found:
>> found.String = string.replace( found.String, u"Lucas", u"NewLucas" )
>> found = document.findNext( found.End, search)
>> #Save
>> document.store()

 

Integrando con otros proyectos

Este es un ejemplo donde combinamos el poder del bridge con otros modulos poderosos como SQLAlchemy para conectarnos a bases de datos externas e integrarla a nuestro documento.

Este pequeño programa invocara SQLAlchemy y llenar los campos de OpenOffice.org. Tambien puedes cambiar el nombre de la base de datos, de las tablas y finalmente los nombres de los campos. 

"""Este programa adquireen los registros desde sqlalchemy y reemplaza los campos de la plantilla OpenOffice.org
Copyright Lukasz Szybalski szybalski@gmail.com 
Licencia: GNU General Public License 2 (GPL2)"""
#A continuacion si estamos importado un archivo, o configurandolo con uncronjob. Esto cambiara a donde .py se localiza.
import os
workfolder=os.path.dirname(__file__)
if workfolder:
        print __file__
        os.chdir(workfolder)

#Comienza el error de registro
import logging
logging.basicConfig()
#Configura el config
log = logging.getLogger("MyApp")
log.setLevel(logging.DEBUG) #set verbosity to show all messages of severity >= DEBUG
#log.setLevel(logging.INFO) #set verbosity to show all messages of severity >= DEBUG
log.info("Starting my app")

import sqlalchemy
#Mysql
e = sqlalchemy.create_engine('mysql://user:pass@hostname/db_name')
#Postgres
#e = sqlalchemy.create_engine('postgres://user:pass@hostname/db_name')
#Mssql
#e = sqlalchemy.create_engine("mssql://user:pass@hostname:1433/db_name?driver=TDS&odbc_options='TDS_Version=8.0'")
#e.echo=True
e.echo=False
metadata=sqlalchemy.MetaData(e)
log.info('Connection Set')

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=e, autoflush=True, autocommit=False)
session = Session()
#Conecctando a una base de datos
log.info("Connected to a Database")


#----Definicion de tabla en el objeto Python que mapea a esto-------
from sqlalchemy.orm import mapper
#Addressbook master File
addressbookrecords_table = sqlalchemy.Table('Addressbook', metadata, autoload=True)
#Python Object
class AddressbookRecords(object):
       pass
#Mapper
mapper(AddressbookRecords,addressbookrecords_table)
log.info('Connected to a database, and have all the field names')


#---------Comienzo de la seccion OpenOffice.org------------
"""Este programa hara un busqueda y reemplazo y enviara a un documento diferente"""
#Load necessary items
import uno
import sys
#You need to set few variables when using OpenOffice
local = uno.getComponentContext()
resolver = local.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local)
#Una de las variables en contexto el cual conecta a OpenOffice.org. Probamos si OpenOffice.org esta escuchando,
#si no lo comenzamos
try:
    #Soy una tabla para conectar?
      context = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
      log.debug('Connecting to OpenOffice')

except Exception ,e :
     #Si tengo un error
      if 'connect to socket' in str(e):
             import os
            #Comienzo 
            os.system('''/usr/bin/openoffice -accept="socket,host=localhost,port=2002;urp;"''')
            import time
            #Espera 3 segundos para cargar el openoffice.org
            time.sleep(3)
            #Ver si lo podemos conectar de nuevo.
            context = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
            log.debug('OpenOffice was not opened. I have openned it and connected')
     #Resto de las variables necesarias.
         desktop = context.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", context)

log.debug('Done Setting up all OpenOffice Variables')

#----------Start manipulating openoffice document--------
import string

#Usaremos esta funcion para buscar y reemplazar cosas en la hoja
def findandreplace(document=None,search=None,find=None,replace=None):
      """This function searches and replaces. Create search, call function findFirst, and finally replace what we found."""
      #Que buscar
       search.SearchString = unicode(find)
       #search.SearchCaseSensitive = True
       #search.SearchWords = True
       found = document.findFirst( search )
       if found:
             #log.debug('Found %s' % find)
             pass
      while found:
            found.String = string.replace( found.String, unicode(find),unicode(replace))
            found = document.findNext( found.End, search)

#Este sera nuestra mini definicion de archivo. Aqui podemos especificar el campo que se reemplaza. 
from datetime import datetime
def replace_data(record=None):
      """Key and Value for replacement. Keys are defined by you values come from sqlalchemy. Here we define what $FirstName in odt file will be replaced with."""
      data={}
      data['$FirstName']=record.FIRSTNAME
      data['$LastName']=record.LASTNAME
      data['$Address']=record.ADDRESS_1
      data['$City']=record.CITY.strip()
      data['$State']=record.STATE
      data['$Zipcode']=record.ZIP_CODE
      data['$TodaysDate']=datetime.now().date()
      data['$User']=record.USER_NO
      data['$CaseID']=str(record.CASE_NUMBER)
      return data


#Importaremos un salto de linea ya que haremos un loop
from com.sun.star.style.BreakType import PAGE_BEFORE, PAGE_AFTER

#vamos a crear un documento (document2 and cursor2) que usaremos para usar nuestras herramientas de reemplazo.
document2 = desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, ())
cursor2 = document2.Text.createTextCursor()

#Guarda el documento
document2.storeAsURL("file:///home/lucas/ReminderPages.odt",())
log.info('Created New Document: ReminderPages.odt')

#Selecciona una fecha para mi carta. Todos los registros creados hace 35 dias.
from datetime import timedelta
days35=timedelta(days=35)
day35=datetime.now().date()-days35
import time
day35a=time.strftime('%Y%m%d',day35.timetuple())

#Selecciona un registro de la base de datos.
allrecords=session.query(AddressbookRecords).filter(AddressbookRecords.CASE_OPEN_DATE==day35a).order_by(AddressbookRecords.USER_NO).all()

#Imprime cuantos registros tenemos
log.info('We have %s Records from the database.' % len(allrecords))

#Vamos a comenzar el loop
log.debug('Starting to Loop Through Records')
for record in allrecords:
    #Carga la plantilla del archivo
document = desktop.loadComponentFromURL("file:///home/lucas/ReminderPagesTemplate.odt" ,"_blank", 0, ())
    #No necesitamos un cursor ya que solo estamos reemplazando el contenido
#cursor = document.Text.createTextCursor()

    #Crear un descriptor de seleccion
search = document.createSearchDescriptor()

#Pass in a sqlalchemy record to our recplace_data which will fill in the values for each field we want. We will use these values to replace inside the odt.
data=replace_data(record)

#Do a loop of the data and replace the content.We pass in a dictionary data which has keys and values. We find keys and replace them with values.
for find,replace in data.items():
          findandreplace(document,search,unicode(find),unicode(replace))
          log.debug(str(find)+','+str(replace))
          #Save replaced document as a temporary file.
          #Trying to find out how to skip this part but for now we do it.
          document.storeAsURL("file:///home/lucas/temp.odt",())
          #Close File
          document.dispose()

#We now append our temp file to our document that will hold all pages. 
cursor2.gotoEnd(False)
cursor2.BreakType = PAGE_BEFORE
cursor2.insertDocumentFromURL("file:///home/lucas/temp.odt", ())
document2.store()
#2nd copie
#cursor2.gotoEnd(False)
#cursor2.BreakType = PAGE_BEFORE
#cursor2.insertDocumentFromURL("file:///home/lucas/temp.odt", ())
#document2.store()
#Exit
document2.dispose()
log.info('Done.I am exiting')