Análisis de datos (IX)
Expedientes Excel (b)
Parece que ya tenemos definido el concepto de expediente SEO digitalizado, tanto en términos cuantitativos como cualitativos, así que ahora toca pasar al análisis en función de los objetivos indicados en la [entrada anterior]. Pero se nos presentan varios problemas que habrá que solucionar antes de empezar con el análisis propiamente dicho.
El primero es disponer de datos que consultar, o mejor dicho, determinar cuáles son las fuentes de datos, ya que no podemos utilizar el listado inicial (no nos aporta más que el total de elementos (archivos) por directorio (expediente) ni podemos usar el procedimiento que nos sirvió para identificar y extraer los datos que nos sirvió para realizar el análisis de los repositorios de documento único.
En este caso, por diferentes motivos prácticos (ya están disponibles) pero también por avanzar en el aprendizaje de procedimientos (acceso secuencial al contenido de múltiples archivos), me propongo trabajar con una colección de archivos sobre hoja de cálculo, resultantes de un DocAp que se expone en esta misma sección del blog, que contienen datos cuantitativos y de estructura de un conjunto amplio de expedientes SEO.
El problema que nos encontramos es que estos archivos están en formato Calc y por motivos que no tengo claros, no me es posible acceder a su contenido mediante la [biblioteca ezodf], así que me veo obligado a buscar una solución que facilite el posterior acceso a los archivos desde Python. Esto es necesario tanto por las limitaciones de OOo Basic para trabajar con múltiples documentos como por el interés que tiene el uso de Python en el análisis de datos.
Y digo esto como planteamiento inicial, porque la solución encontrada bien podría considerarse una demostración práctica de la potencia de OOo Basic para automatizar el manejo de un conjunto amplio de documentos; lo que no nos lleva a modificar el proyecto inicial de usar Python como herramienta fundamental para el análisis de datos.
Empecemos por clarificar algunas cuestiones. Dispongo de un total de 244 archivos Calc, resultantes de la aplicación de [este DocAp]. Este total es suficientemente elevado como para considerarlo acorde con el volumen total de expedientes disponible (483). Sabiendo que esos documentos no recogen loa datos de los expedientes de 1 y 2 documentos, podemos considerarlos además, representativos de aquellos que caen dentro del concepto amplio de expedientes, aunque algunos de ellos puedan estar dentro del subgrupo de expedientes fallidos.
A efectos prácticos vamos a considerar este dato como desconocido; en realidad podremos identificarlos visualmente o por análisis cruzados del listado con la visualización del contenido del directorio donde se encuentran. Precisamente es este trabajo "visual" el que pretendemos evitar.
Así que, aunque ya tenemos identificada nuestra fuente de datos, y podemos considerarla ajustada a nuestro objetivo, es necesario transformar el tipo de documento de Calc a Excel, para poder desarrollar después procedimientos de acceso a datos basados en Python y en su [biblioteca OpenPyxl]. Si queremos evitar realizar manualmente esta conversión desde Calc, deberemos recurrir un script o a una macro OOo Basic; si bien antes es conveniente aportar a ese soporte el listado de los documentos Calc que vamos a convertir en Excel, y para ello necesitamos recurrir a Python, concretamente a su [librería os]. Como puedes ver, todo un complejo enredo de interdependencia . En realidad una complejidad más aparente que real. Veremos por qué.
Dado que se impone como necesidad la conversión de archivos Calc a Excel (recuerda que Calc lee .xls/xlsx, pero Excel no lee .ods), la solución que se me ocurre es utilizar Calc para realizar esa conversión, automatizando el procedimiento mediante un script basado en una macro. Ello lleva (como veremos) a desarrollar un procedimiento basado en tres subrutinas enlazadas en tándem: la primera llama a la segunda y la segunda a la tercera.
Esta solución, que requiere explicación específica, implica automatizar el proceso en parte, pero deja pendiente hacerlo a su inicio, dado que no dispongo de solución OOo Basic que facilite el acceso a los nombres de todos los archivos del directorio. Sin embargo esto es posible hacerlo sin dificultad mediante Python, concretamente usando su módulo os.
Resuelvo, pues, la automatización del acceso a la colección de documentos Calc mediante un script Python…
import osdirectorio = "ExpedCalc/"contenido = os.listdir(directorio)n_elem = 0print('Listado de componentes \n')for elemento in contenido:n_elem = n_elem +1print (f'Elemento número {str(n_elem)} -> {elemento}')print(f'\n TOTAL elementos del directorio {str(n_elem)}')archivo = open('listaExped.txt','x', encoding='utf-8')for elemento in contenido:archivo.write(elemento+'\n')archivo.close()
… que accede al directorio que contiene los documentos Calc (directorio = "ExpedCalc/"), extrae el contenido (contenido = os.listdir(directorio)) y después de mostrarlo en pantalla lo copia en un documento txt (archivo = open('listaExped.txt','x', encoding='utf-8')) mediante un bucle (for elemento in contenido: -> archivo.write(elemento+'\n'))...
A continuación copio el contenido de este documento txt en un archivo Calc en blanco y obtengo un listado parecido a éste.
Como puedes ver se reproduce perfectamente el listado obtenido con el script Python, por lo que nos sirve para generar el código OOo Basic necesario para realizar la transformación de documentos Calc en Excel.
Pero el desarrollo de este procedimiento (te ahorro ese recorrido fallido) permite considerar que no es buena idea incluir la extensión del archivo como parte del texto a manejar (ya veremos el motivo), así que necesitamos trabajar con este listado para quedarnos sólo con el nombre y prescindir de la extensión .ods.
Para ello recurro a funciones built-in de Calc, concretamente a dos del grupo Texto: LARGO() e IZQUIERDA(). La primera calcula la longitud (n caracteres) de una cadena de texto y la segunda permite extraer una parte de esa cadena empezando desde la izquierda. Así que primero calculamos la longitud de cada uno de los elementos del listado y después aplicamos la función IZQUIERDA() pasando como primer parámetro la cadena y como segundo la diferencia (LARGO(Ax)-4), siendo 4 la longitud de la extensión más el punto (.ods).
Lo que obtenemos es un documento Calc que contiene una triple lista: la de los nombres íntegros de los archivos, la de la longitud de cada nombre y la de los identificadores de los archivos (nombres sin la extensión). Esta última (columna C) es la que nos interesa para desarrollar los script OOo Basic.
Partiendo de esta nueva lista sin las extensión .ods puedo aplicar un conjunto de tres script (en realidad un script y dos subrutinas) que trabajan en tandem y que (esto es muy importante) es necesario ubicar en el directorio Mis macros y diálogos, biblioteca Standard del IDE (no en el directorio de script del documento).
El primero de estos tres script OOo Basic (acceso_lista) permite tomar cada elementos del listado para automatizar el acceso a los archivos Calc. Por ello es necesario activarlo desde el documento Calc sobre el que creamos la lista que vemos en la imagen anterior (podemos llamarlo lista_al.ods, por ejemplo). Aquí tienes el código de este primer script:
Sub acceso_listaDim oHoja As ObjectDim oCelda As ObjectDim oDato As ObjectDim i As IntegerDim n As IntegerDim Archivo As Stringn = 244oHoja = ThisComponent.getSheets().getByName("lista")
for i = 1 To noCelda = oHoja.getCellRangeByName( "C" & i)Archivo = oCelda.getString()AbrirDocumento(Archivo)NextEnd Sub
Como puedes ver, este script permite acceder a la lista Calc mediante el procedimiento conocido de acceso a libro->hoja->celda. Utiliza un bucle For…Next para recorrer dicha lista, entregando cada uno de los datos obtenidos a la subrutina AbrirDocumento() encargada de acceder al documento Calc.
Sub AbrirDocumento (alumno)Dim sRuta As StringDim mOpciones(0) As New "com.sun.star.beans.PropertyValue"Dim oDoc As ObjectmOpciones(0).Name = "MacroExecutionMode"mOpciones(0).Value = 4Dim ruta As Stringruta = "D:/EXPEDIENTES/ExpedientesOriginales/" & alumno & ".ods"sRuta = ConvertToUrl(ruta)oDoc = StarDesktop.loadComponentFromURL( sRuta, "_blank", 0, mOpciones() )Dim oHoja As ObjectDim oCelda As ObjectDim iDato As IntegerDim archivo As StringoHoja = ThisComponent.getCurrentController.getActiveSheet()oCelda = oHoja.getCellRangeByName("C10")iDato=oCelda.getValue()if iDato <= 4 ThenoDoc.close(True)Elsearchivo = alumnograbar_excel (archivo)End IfEnd Sub
Esta subrutina permite acceder al archivo Calc, capturar el valor de la celda C10 que contiene el dato del total de documentos del expediente y, en función del (in)cumplimiento de una condición (if iDato <= 4 Then) llamar a la subrutina siguiente (grabar_excel (archivo)) pasando como parámetro el mismo dato que recibió ella también como parámetro (Sub AbrirDocumento (alumno)), antes de convertirla en una URL (sRuta = ConvertToUrl(ruta)), lo que garantiza el acceso al documento con independencia de la plataforma, pero también con total seguridad (evidentemente esa dirección debe existir y contar con los documentos Calc a los que se desea acceder).
Finalizamos el procedimiento haciendo entrar en escena la segunda subrutina y tercer componente del tandem (grabar_excel (documento)), que se encarga de grabar el archivo como Excel y en su correspondiente directorio.
Sub grabar_excel (documento)dim document as objectdim dispatcher as objectdocument = ThisComponent.CurrentController.Framedispatcher = createUnoService("com.sun.star.frame.DispatchHelper")dim archivo As Stringarchivo = "file:///D:/EXPEDIENTES/ExpedientesExcel/" & documento & ".xlsx"dim args1(1) as new com.sun.star.beans.PropertyValueargs1(0).Name = "URL"args1(0).Value = archivoargs1(1).Name = "FilterName"args1(1).Value = "Calc MS Excel 2007 XML"dispatcher.executeDispatch(document, ".uno:SaveAs", "", 0, args1())document.close(True)end sub
Como puedes ver, esta subrutina se basa en código macro posteriormente modificado. Esta modificación se concreta como transformación de la macro en subrutina y como sustitución de la ruta absoluta directa por una variable string previamente modificada (archivo = "file:///D:/EXPEDIENTES/ExpedientesExcel/" & documento & ".xlsx") para ajustarla a los valores múltiples que va a presentar a lo lago del proceso, ya que todo ello (y en ambas subrutinas) está dentro del bucle For definido en el script principal.
El resultado es que obtenemos una copia de archivos Excel que cumplen el criterio de contener datos sobre expedientes SEO con 5 y más documentos. Estos archivos quedan ahora a disposición de script Python, objetivo último de esta fase del proceso. El directorio en el que se encuentran es el establecido en la segunda subrutina ("D:/EXPEDIENTES/ExpedientesExcel/"), siendo el de procedencia el indicado en la primera ("D:/EXPEDIENTES/ExpedientesOriginales/"). Ambas son direcciones ficticias y ambas deberán ser adaptadas a la realidad concreta de tu organización de los archivos. Cada documento queda identificado por su nombre (aquí utilizamos claves de identificación), coincidiendo éste con el que consta en el listado Calc que sirve de base de datos al conjunto del proceso y que es consultada sistemática y sucesivamente en el script inicial y principal (Sub acceso_lista) mediante el bucle For que enmarca el resto del procedimiento
for i = 1 To noCelda = oHoja.getCellRangeByName( "C" & i)Archivo = oCelda.getString()AbrirDocumento(Archivo)Next
En la entrada que sigue explicaré el acceso al contenido de los documentos (ahora) Excel mediante Python y su librería OpenPyXL. Eso y alguna cosa más.





