¿Cómo puedo llamar a una macro de Python en una fórmula de celda en OpenOffice.Org Calc?

Para ampliar las capacidades de OpenOffice, he definido algunas macros de Python en un archivo en el directorio de script del usuario (~ / Library / Application Support / OpenOffice.org / 3 / user / Scripts / python /, en mi caso). Las macros son visibles en el organizador de macros de Python. Sin embargo, el uso de las funciones en una fórmula de celda da como resultado “#NAME? ( OO.org error 525).

Supongamos que he definido la siguiente función:

def pytype(val): return str(type(val)) 

¿Cómo puedo llamar a pytype en una fórmula de celda (por ejemplo, =PYTYPE("string") )?

Fondo

Estoy importando algunos datos de Authorize.net a una base de datos MySQL para su análisis. MySQL no puede analizar el formato de fecha y hora utilizado por Authorize.net en un campo DATETIME o TIMESTAMP , por lo que estoy tratando de masajear los datos en un formato que MySQL pueda manejar antes de importar. OpenOffice tampoco reconoce los datos como una fecha y hora y, por lo que he podido determinar, OO.Org no tiene una función de análisis de fecha genérica. Así estoy extendiendo las capacidades de OO.org.

Hay otros enfoques para el problema más grande. Por ejemplo, también podría intentar arreglar los datos en MySQL después de la importación utilizando columnas adicionales. De hecho, esto es lo que hice la primera vez; sin embargo, ahora hay datos existentes en la tabla con los que lidiar. Debido a eso, y porque hay otras tareas en el futuro que espero lograr mediante el uso de macros en fórmulas, por ahora estoy más interesado en llamar macros de Python en fórmulas.

    En los viejos foros de OO.org, el (super) usuario Villeroy publicó una ilustración de cómo llamar a las funciones de Python desde OO.org Basic , que luego se puede usar en fórmulas. La clave es usar el servicio com.sun.star.script.provider.MasterScriptProviderFactory como un puente. Aquí hay una adaptación de su solución, generalizada para llamar funciones arbitrarias en módulos arbitrarios:

     REM Keep a global reference to the ScriptProvider, since this stuff may be called many times: Global g_MasterScriptProvider as Object REM Specify location of Python script, providing cell functions: Const URL_Main as String = "vnd.sun.star.script:" Const URL_Args as String = "?language=Python&location=user" Function invokePyFunc(file AS String, func As String, args As Array, outIdxs As Array, outArgs As Array) sURL = URL_Main & file & ".py$" & func & URL_Args oMSP = getMasterScriptProvider() On Local Error GoTo ErrorHandler oScript = oMSP.getScript(sURL) invokePyFunc = oScript.invoke(args, outIdxs, outArgs) Exit Function ErrorHandler: Dim msg As String, toFix As String msg = Error$ toFix = "" If 1 = Err AND InStr(Error$, "an error occurred during file opening") Then msg = "Couldn' open the script file." toFix = "Make sure the 'python' folder exists in the user's Scripts folder, and that the former contains " & file & ".py." End If MsgBox msg & chr(13) & toFix, 16, "Error " & Err & " calling " & func end Function Function getMasterScriptProvider() if isNull(g_MasterScriptProvider) then oMasterScriptProviderFactory = createUnoService("com.sun.star.script.provider.MasterScriptProviderFactory") g_MasterScriptProvider = oMasterScriptProviderFactory.createScriptProvider("") endif getMasterScriptProvider = g_MasterScriptProvider End Function 

    Esto se puede usar para crear una función básica de OO.org que se pueda llamar en una fórmula. Usando el ejemplo pytype :

     Const libfile as String = "util" REM functions live in util.py Function pytype(value) pytype = invokePyFunc(libfile, "pytype", Array(value), Array(), Array()) End Function 

    Otra implementación potencial es crear un complemento de Python . Sin embargo, esta es una opción mucho más importante ya que requiere la instalación de OpenOffice SDK, y no es obvio para mí si este enfoque funcionaría para funciones gratuitas o solo para clases.

    outis – gracias por tu asombrosa respuesta. ¡Si no fuera por ti, ya estaría enojado escribiendo macros básicas!

    Sin embargo, solo tengo algunas observaciones:

    Los últimos 2 argumentos para invokePyFunc siempre están vacíos, solo use esto:

     const filename = "your_file" Function pyFunc(func as String, args as Array) pyFunc = invokePyFunc(filename, func, args, Array(), Array()) End Function 

    Los arreglos multidimensionales son difíciles de devolver. Si devuelve ((1,2,3), (4,5,6)) calc trata como 2 celdas en una fila que contiene objetos desconocidos.

    Esto se debe a que Basic y Python tratan los arreglos multidimensionales de manera diferente.

    Si devuelve dicha estructura a básica, debe acceder a ella como data(row)(col) y calc espera data(row, col) para matrices multidimensionales.

    Por eso necesita usar una función de conversión para valores de retorno:

     ' Converts python multidimensional arrays to basic arrays. function convPy2Basic(pyvalue) if isarray(pyvalue) then dim lastRow as integer lastRow = ubound(pyvalue) if lastRow = -1 then ' empty array convPy2Basic = "" else if isarray(pyvalue(0)) then ' Multi-dimensional array dim maxCols as integer, lastCol as integer maxCols = ubound(pyvalue(0)) dim res(lastRow, maxCols) for rowIndex = 0 to lastRow lastCol = ubound(pyvalue(rowIndex)) ' Expand array if needed. if lastCol > maxCols then maxCols = lastCol redim preserve res(lastRow, maxCols) end if for colIndex = 0 to lastCol res(rowIndex, colIndex) = pyvalue(rowIndex)(colIndex) next colIndex next rowIndex convPy2Basic = res else ' Single-dimensional array - this is supported by libreoffice convPy2Basic = pyvalue end if end if else convPy2Basic = pyvalue end if end function Function invokeScriptFunc(file AS String, lang, ext, func As String, args As Array, outIdxs As Array, outArgs As Array) sURL = URL_Main & file & "." & ext & "$" & func & "?language=" & lang & "&location=user" oMSP = getMasterScriptProvider() oScript = oMSP.getScript(sURL) invokeScriptFunc = oScript.invoke(args, outIdxs, outArgs) end Function Function invokePyFunc(file AS String, func As String, args As Array, outIdxs As Array, outArgs As Array) res = invokeScriptFunc(file, "Python", "py", func, args, outIdxs, outArgs) invokePyFunc = convPy2Basic(res) end Function 

    Así que mi puente macro de python-básico se ve así:

     ' Keep a global reference to the ScriptProvider, since this stuff may be called many times: Global g_MasterScriptProvider as Object ' Specify location of Python script, providing cell functions: Const URL_Main as String = "vnd.sun.star.script:" ' Converts python multidimensional arrays to basic arrays. function convPy2Basic(pyvalue) if isarray(pyvalue) then dim lastRow as integer lastRow = ubound(pyvalue) if lastRow = -1 then ' empty array convPy2Basic = "" else if isarray(pyvalue(0)) then ' Multi-dimensional array dim maxCols as integer, lastCol as integer maxCols = ubound(pyvalue(0)) dim res(lastRow, maxCols) for rowIndex = 0 to lastRow lastCol = ubound(pyvalue(rowIndex)) ' Expand array if needed. if lastCol > maxCols then maxCols = lastCol redim preserve res(lastRow, maxCols) end if for colIndex = 0 to lastCol res(rowIndex, colIndex) = pyvalue(rowIndex)(colIndex) next colIndex next rowIndex convPy2Basic = res else ' Single-dimensional array - this is supported by libreoffice convPy2Basic = pyvalue end if end if else convPy2Basic = pyvalue end if end function Function invokeScriptFunc(file AS String, lang, ext, func As String, args As Array, outIdxs As Array, outArgs As Array) sURL = URL_Main & file & "." & ext & "$" & func & "?language=" & lang & "&location=user" oMSP = getMasterScriptProvider() oScript = oMSP.getScript(sURL) invokeScriptFunc = oScript.invoke(args, outIdxs, outArgs) end Function Function invokePyFunc(file AS String, func As String, args As Array, outIdxs As Array, outArgs As Array) res = invokeScriptFunc(file, "Python", "py", func, args, outIdxs, outArgs) invokePyFunc = convPy2Basic(res) end Function Function getMasterScriptProvider() if isNull(g_MasterScriptProvider) then oMasterScriptProviderFactory = createUnoService("com.sun.star.script.provider.MasterScriptProviderFactory") g_MasterScriptProvider = oMasterScriptProviderFactory.createScriptProvider("") endif getMasterScriptProvider = g_MasterScriptProvider End Function const filename = "skaiciuokle" Function pyFunc(func as String, args as Array) pyFunc = invokePyFunc(filename, func, args, Array(), Array()) End Function 

    Y se usa así:

     function DamageToArmor(data, damageType as String, armorType as String, dmgPerGun as Integer, guns as Integer) DamageToArmor = pyFunc("dmg2armor", Array(data, damageType, armorType, dmgPerGun, guns)) end function