Mostrando entradas con la etiqueta OOo Basic. Mostrar todas las entradas
Mostrando entradas con la etiqueta OOo Basic. Mostrar todas las entradas

domingo, 7 de diciembre de 2025

MAV. OOo Basic


Presentación de secuencias de imágenes





Podemos realizar presentaciones de forma totalmente automatizada sin necesidad de crearlas previamente, con imágenes adaptadas al cumplimiento de determinadas condiciones. De este modo es posible automatizar la presentación del input, ajustándolo a necesidades específicas y cambiantes.


Está mal que utilice el icono de Impress para ilustrar el ítem de la entrada, ya que precisamente no es éste el servicio al que se accede (por cierto que por defecto). Tampoco se trata de presentaciones en sentido estricto, aunque se puede simular el funcionamiento básico de una presentación sencilla.

En realidad el servicio que se abre por defecto es Draw, ya que es éste el que en LibreOffice está asociado a los formatos más comunes de imágenes, de modo que, al cargar una imagen lo que sucede es que el sistema de LibreOffice recurre al servicio que tiene disponible.

Pero realmente esto no es lo importante. Sí lo es la utilidad que tiene este procedimiento y su versatilidad para adaptar el contenido (gráfico) del ítem que podemos presentar. No sólo no necesitamos tener preparada una determinada presentación, es que podemos adaptarla de forma automatizada (e interactiva) de acuerdo con las condiciones que deseemos, siempre que, claro está, hayamos organizado convenientemente los materiales gráficos.

Como siempre es mejor explicar sobre la base de un proyecto concreto, en este caso lo voy a hacer sobre el que me ha llevado hasta aquí, aunque ahora tome un breve desvío: la automatización de la presentación de las imágenes del test PLON-R Fonología.

Para simplificar me limito a una cuestión de base en este test: los ítem de trabajo estén en función de la edad del niño, aunque de forma acumulativa. Me explico: a los niños de tres años se les aplican los ítem de su grupo de edad, a los de cuatro los ítem de tres y los ítem de cuatro años, y así sucesivamente hasta llegar a los seis años, edad máxima de la batería PLON-R.

Realmente el procedimiento que estoy desarrollando en relación con ese test es más complejo y diferente en cuanto a prioridades, pero lo que aquí te presento ha surgido a raíz de ese trabajo, motivo por el que será ese nuestro actual contexto: generar presentaciones de imágenes en función de la edad del sujeto.

Se trata, en realidad, de una especie de simulacro (en miniatura) de aplicación de un test de Fonología, ya que no es posible utilizar las mismas imágenes y no tengo ningún interés en sobrecargar el trabajo con una extensa lista de imágenes. Pero estas limitaciones no suponen ninguna merma para la potencialidad de la propuesta.

Empezaré por describir los soportes: una hoja de cálculo como gestor, que incluyen el listado de componentes (hoja Listados) y el código, y una carpeta que contiene las imágenes que vamos a emplear, exactamente 15 imágenes, a razón de 5 por grupo de edad (3, 5 y años).


Aunque, lo verdaderamente interesante está oculto en el IDE de plon_lista.ods, también es importante el listado de edades y de palabras, ya que a ellos se acude para acceder a los contenidos. Este acceso se realiza desde código, ubicado con el IDE tal y como muestra la imagen que precede, concretamente en sus dos módulos (Edad y cargarImg).

El primero, el fundamental, contiene un único script (AccesoPorEdad) que es el que tienes que activar para que funciones el conjunto. En cargarImg se encuentran los auxiliares que hacen posible la carga de las imágenes: una subrutina (AbrirImagen) y una función (AbrirImg): la primera es llamada desde el script y la segunda desde la subrutina. Veamos primero el script.

Sub AccesoPorEdad

Dim oHoja As Object
Dim sHoja As String
Dim oCelda As Object
Dim col As String
Dim i As Integer

'Matrices para acceso a datos (EDAD y PALABRAS)
Dim mEdad(14) As Integer
Dim edad As Integer
Dim mPal(14) As string
Dim Pal As string
 
'Acceso al libro (ThisComponent), y a la hoja (sHoja)

sHoja = "Listados"
oHoja =ThisComponent.getSheets().getByName(sHoja)

'Acceso al contenidos de las listas

For i = 0 to UBound(mEdad())
'Acceso a los datos de edad
oCelda =  oHoja.getCellRangeByName( "A" & i+1)
edad = oCelda.getValue()
mEdad(i) = edad
'Acceso a los datos Palabras
oCelda =  oHoja.getCellRangeByName( "B" & i+1)
Pal = oCelda.getString()
mPal(i) = Pal
Next

'Muestra de elementos (palabras) en función de la edad

Dim edad_sujeto As Integer

edad_sujeto = CInt(InputBox("Edad del alumno/alumna (sólo LOS años)"))

For i = 0 To UBound(mEdad())
If mEdad(i) <= edad_sujeto Then
AbrirImagen(mPal(i)) 'Llamada a subrutina
End If
Next

End Sub
Tras acceder al libro y a la hoja que contiene las tablas (oHoja =ThisComponent.getSheets().getByName(sHoja)), mediante un bucle (For i = 0 to UBound(mEdad())) accedemos al contenido de las listas (esto es, a las celdas) para pasar los datos a sendas matrices (Dim mEdad(14) As Integer Dim mPal(14) As string), mediante un procedimiento sencillo y repetitivo pero muy útil, que se realiza en tres pasos:
  • Acceso a celda: oCelda =  oHoja.getCellRangeByName( "A" & i+1)
  • Asignar el contenido de la celda a variable: edad = oCelda.getValue()
  • Y paso del contenido de la variable a la matriz: mEdad(i) = edad
Una vez que ya hemos pasado los datos a las matrices toca el turno de establecer la condición que va a determinar la segunda parte del procedimiento (la carga de las imágenes).

En este caso, lo que determina la condición en la edad del alumno, así que la solicitamos al usuario (edad_sujeto = CInt(InputBox("Edad del alumno/alumna (sólo LOS años)"))).

Con este dato disponible iniciamos un segundo bucle (For i = 0 To UBound(mEdad())) que va a llamar reiteradamente a la subrutina (AbrirImagen(mPal(i))) pasando como parámetro el nombre de la imagen (siempre que se cumpla el criterio establecido (la edad, menor o igual) mediante un condicional (If mEdad(i) <= edad_sujeto Then)

Lo que sucede a continuación es responsabilidad de la subrutina y de la función a la que ésta llama, pero está todo ello presidido por el cumplimiento de la condición que establecemos en el script y en la reiteración de ese proceso mientras se cumpla esa condición. Veamos esos auxiliares:

Sub AbrirImagen(img as String)
Dim sDocum As String
sDocum = "D:/Docap/presenta_plon/img/" & img &".png"
AbrirImg(sDocum) 'Llama a la función
End Sub
La subrutina se encarga de establecer la ruta absoluta en la que se encuentra la imagen (en este caso un directorio de un soporte externo de memoria: sDocum = "D:/Docap/presenta_plon/img/" & img &".png") y de llamar a la función que se encarga de cargar la imagen cuyo identificador hemos pasados desde el listado.

Function AbrirImg(cRuta As String,Optional aOpciones()) As Object
Dim oDocumento As Object
cRuta = ConvertToURL(cRuta)
If IsMissing (aOpciones) Then aOpciones = Array()
              AbrirCualquierDoc = StarDesktop.LoadComponentFromURL(cRuta,"_blank",0, aOpciones())
      oDocumento = AbrirCualquierDoc  
wait 1000
oDocumento.close(True)

End Function
El correcto funcionamiento de este conjunto depende del escrupuloso respeto del código de esta función, así que debes copiarla tal cual, a excepción del tiempo de espera (wait 1000) que puedes no establecer o hacerlo de mayor o menor duración, y del cierre de los documentos Draw  que se van abriendo y que mediante esta instrucción (oDocumento = AbrirCualquierDoc -> oDocumento.close(True)), que también puedes omitir, se cierran automáticamente transcurrido el tiempo establecido.

Documentos. Este [archivo comprimido] contiene el DocAp, incluyendo la carpeta de imágenes. Recuerda que si lo guardas en otra estructura de carpetas debes adaptar la instrucción correspondiente (subrutina AbrirImagen() instrucción asociada a sDocum).




viernes, 5 de diciembre de 2025

Lenguaje

Discriminación auditiva

Test de Wepman (ADT) (IV)



Muchas son las posibles mejoras que podemos introducir en este material, ya que muchas son sus limitaciones, así que me voy a plantear mejorar el análisis de resultados.


En este caso no se trata de automatizar esta fase del proceso, puesto que esto ya está logrado, si bien se podría mejorar, por ejemplo, sustituyendo las funciones Calc por código OOo Basic, puesto que sabemos que este recurso tiene ciertas limitaciones cuando los análisis son de cierta complejidad y los informes que de ellos derivan tienen cierta extensión. Ahora bien, con los datos con que se trabaja en este soporte y el informe que de su análisis deriva no estaría justificado ese cambio. Menos aun si pensamos que la utilidad de este test y su frecuencia de uso son bastante limitadas desde la perspectiva de la intervención del SEO.

Pero es posible que esta situación cambie si fuera necesario realizar modificaciones en el análisis de los datos, lo que supondría:
  • Identificar los datos con los que trabajar y definir los nuevos cálculos a realizar con ellos.
  • Crear el código para automatizar estos cálculos y su análisis.
  • Modificar el informe de resultados de la prueba y automatizar su redacción.
Estos cambios implican realizar modificaciones que se pueden abordar, cierto es, con los mismos recursos con los que se creó inicialmente el soporte, pero se pueden considerar también una oportunidad para mejorar el procedimiento.

Para plantear cambios en el procedimiento de análisis me voy a centrar en la existencia de dos tipos de ítem en la prueba, dado que considero de interés tener en cuenta esta diferencia dada la función que cumple en relación con lo que considero el principal punto débil del test: el elevado riesgo de que las respuestas al azar incidan en gran medida en los resultados.

Al tratarse de una respuesta dicotómica (igual vs. diferentes), además de garantizar la correcta comprensión de ambos conceptos (tarea que corresponde resolver explícitamente mediante entrenamiento previo), se hace necesario incluir algún tipo de procedimiento que evite que las respuestas aleatorias definan desapercibidamente el resultado. Posiblemente la forma que el autor ha implementado es incluir pares iguales, de modo que está justificado un procedimiento de respuesta tan débil, pero necesario en función de la edad de los sujetos a los que se aplica la prueba. 

No existe problema en solicitar al niño que diga si dos palabras le suenan igual o diferentes, pero sí en que identifique entre dos palabras que se diferencian por un sonido, cuales son los sonidos que las diferencian. En realidad este sería el tipo de respuesta que confirmaría si el niño presenta o no habilidad vs. dificultad para la discriminación auditiva, pero resulta inviable como procedimiento de evaluación en determinadas edades.

Pero al incluir 11 pares iguales (restan 29 pares diferentes) se modifica la referencia para el análisis, dado que las causas de posible error son asimilables a a las previstas (discriminación auditiva) en los pares en los que existe realmente una diferencia, pero no podemos tener esa seguridad cuando el error se comente en pares realmente iguales: en ellos si el alumno dice que esas palabras son diferentes no podemos hacer una atribución causal necesariamente relacionada con una falsa dificultad de discriminación auditiva sin proceder a solicitar una respuesta explicativa, respuesta que la propia formulación del test impide. En este caso el error podría deberse a que sus respuestas sean al azar o por otras causas (¿atencionales?), o por ambas...

Lo que propongo es considerar los errores de forma diferenciada en función del tipo de ítem en el que se produzcan, de modo que no tengan en ambos casos el mismo significado, ya que no lo tienen conceptualmente, o pueden no tenerlo. Es factible atribuirlo a un déficit de discriminación/diferenciación entre dos sonidos cuando tal diferencia existe, pero no cuando no existe; en ese caso es factible considerar el error debido a otras causas. 

Si el peso de ese segundo tipo de error en el conjunto de errores cometidos es igual o superior al peso de los ítem iguales en la prueba, se puede considerar que los errores no lo son por déficit de discriminación auditiva, pero si es inferior y la mayoría de los errores se deben a fallos de discriminación de pares diferentes, entonces sí podríamos hablar de déficit de discriminación auditiva. En todo caso, ese déficit se observaría en relación a los pares de fonemas afectados.

Considero que el replanteamiento del análisis que ahora propongo está en línea con el objetivo de incrementar la utilidad de la información que el SEO aporta a los especialistas del ámbito sanitario. Por ello ahora planteo:
  • Calcular el acierto total y el error total y sus respectivos porcentaje.
  • Cuando el error total alcance un porcentaje determinado (vg. sea superior al 15%), se procede a identificar la fuente del error: sobre ítem igual vs. ítem diferentes y se calcula su peso porcentual respectivo sobre el total de errores.
  • Comparar el porcentaje de error debido a los pares iguales frente al peso de esos ítem en la prueba (11/40 - > 27,5%). Si el % de esos errores sobre el total de errores es superior a ese nivel, se considera que factores no necesariamente identificables como déficit de discriminación auditiva pueden estar teniendo un peso relevante en el conjunto de errores que comete el sujeto, siendo un factor importante en la explicación causal de los mismos. En caso contrario se considera que es probable que los errores observados lo sean específicamente por dificultades de discriminación auditiva. 
  • Seleccionar como pares de fonemas alterados (no discriminados correctamente) únicamente aquellos que presentan oposición fonológica. Informar cualitativamente sobre ellos iniciando la enumeración con una expresión similar a la siguiente: "En todo caso, los posibles errores de discriminación auditiva se presentaría ante los siguientes pares de fonemas:"
Para el desarrollo del algoritmo me basaré en el lenguaje OOo Basic. Mediante este algoritmo se automatizará la puntuación de la prueba y el análisis según los procedimientos previstos anteriormente. Igualmente se automatizará la redacción del informe de resultados. Aquellos cálculos que puedan ser resueltos mediante fórmulas Calc lo serán, por lo que OOo Basic sólo se empleará en esos casos como procedimiento para acceder a las celdas que contienen el resultados. Opto por este procedimiento no por ser el más adecuado, sino por ser el más cercano al original. Se puede considerar que en este caso estamos planteando una solución próxima a la original en términos de desarrollo de nuestra competencia digital.

Muestro en la imagen que sigue la estructura de script que componen esta propuesta, un DocAp que calificamos como complejo dado que usamos dos servicios: Calc y Writer, aunque éste último únicamente a modo de soporte para el informe del test.


Queda señalado en la imagen el script principal (acceso) que será el primero que explique y aquel que deberás activar bien desde Herramientas | Macros | Ejecutar macro (que es el procedimiento propuesto por defecto), bien creando un botón de comando en el soporte Calc asociado a dicho script. 

Además puedes observar otros tres módulos, dos de ellos contienen sedas funciones, cuyos nombres las ilustran, y el tercero (modTexto) contiene una subrutina que permite generar el informe de resultados del test.

Pasemos a analizar más detenidamente el script acceso por ser el principal. Del análisis de los restantes te ocupas tú si te parece necesario.

Sub acceso

Dim oHoja As Object
Dim sHoja As String

Dim Celdas() As String
Dim oDatos(6) As Object
Dim mDatos(6) As String
Dim i As Integer

Dim Aciertos As Integer
Dim Fallos As Integer
Dim Aciertos_p As Double
Dim porcentaje_lim As Double
Dim iValoraError As Integer
Dim sListaErrores As String

Dim sDatosId As String
Dim sResultado As String
Dim sAnalisis As String
Dim sTextoInfo As String

sHoja = "Datos"

oHoja =ThisComponent.getSheets().getByName(sHoja)

'Lista de celdas

Celdas() = Array("B6","C6","E6","B7","E7","C11")

'Construcción del listado de celdas con datos relevantes

For i = LBound(Celdas()) To UBound(Celdas())
oDatos(i) = oHoja.getCellRangeByName(Celdas(i))
mDatos(i)=oDatos(i).getString()
Next

Aciertos = CInt(mDatos(5))
Fallos = 40 - Aciertos
Aciertos_p = (Aciertos/40)*100

'Exposición de datos (y resultados)

Dim sTxt(4) As String

sTxt(0) = "El test de Wepman es una sencilla prueba diseñada para evaluar la discriminación auditiva. En ella se plantea al alumno una tarea de decisión (IGUAL-DIFERENTE). Se considera el 85% de éxito como límite entre la correcta discriminación (>= límite) y la presencia de dificultades (<límite)."
sTxt(1) = mDatos(0) & " ha resuelto satisfactoriamente " & mDatos(5) & " ítem de los 40 de que consta la prueba, lo que, en principio, "

porcentaje_lim = 85

If Aciertos_p >= porcentaje_lim Then
sTxt(2) = "permite considerar que no presenta dificultades de discriminación auditiva."
ElseIf Aciertos_p < porcentaje_lim Then
iValoraError = tipo_error(Fallos)
If iValoraError = 0 Then
sTxt(3) = "Realizado este análisis, a la vista de la naturaleza de los errores cometidos, es posible considerar que éstos se deban al azar o a otras causas no necesariamente atribuibles a dificultades de discriminación auditiva."
Else
sListaError = ListaErrores() 
sTxt(4) = sListaError
sTxt(3) = CHr(13) & "Realizado este análisis, el tipo de errores cometidos permiten suponer que es probable que existan dificultades específicas de discriminación auditiva que afectarían a los siguientes pares de fonemas: " & sTxt(4)
End if
sTxt(2) = "obliga a analizar los datos obtenidos en el test, especialmente los errores cometidos, ya que éstos superan el límite establecido (15% como error máximo). " & chr(13) & sTxt(3)
End If

'Textos para informe

sDatosId = "Test de Discriminación auditiva de Wepman" & CHr(13) & CHr(13) & "Datos de identificación: " & CHr(13) & "Alumno: " &_
mDatos(0) & " " & mDatos(1) & "   Edad: " & mDatos(2)
sResultado = "RESULTADOS: " & CHr(13) & "Puntuación: " & mDatos(5) & "/40 aciertos." 

sAnalisis = sTxt(0) & CHr(13) & sTxt(1) & sTxt(2)

sTextoInfo = sDatosId & CHr(13) & CHr(13) & sResultado & CHr(13) & CHr(13) & sAnalisis

CrearInfo(sTextoInfo)

End Sub

Como puedes ver, este script se divide en tres partes: 

  • En la primera se accede a datos relevantes del soporte Calc, necesarios para el desarrollo del procedimiento, incluida la automatización del informe.
  • En la segunda se construye el contenido del análisis de los resultados, y dentro de este proceso (el más complejo del script) se accede, cuando resulta necesario, a las funciones para obtener los datos que son precisos: el peso del error de los ítem IGUAL (tipo_error(Fallos)) y el listado de fonemas afectados por errores de discriminación (ListaErrores()). Ambas funciones están destacadas en el script en negrita.  Dado que ambos subprocesos están en función del cumplimiento de determinadas condiciones, todo ello se encuentra enmarcado dentro de una estructura condicional compleja que se inicia con la instrucción If Aciertos_p >= porcentaje_lim Then
  • La tercera y final consiste en pasar el contenido textual mediante parámetro a la subrutina de escritura del informe (CrearInfo(sTextoInfo)). Obsérvese, por su interés, en modo en que se construye los textos que finalmente se concretan como parámetro. Por ejemplo sTextoInfo = sDatosId & CHr(13) & CHr(13) & sResultado & CHr(13) & CHr(13) & sAnalisis.
Documentos.
  • Soporte Calc (DocAp)
  • Ejemplo de informe. El informe puede ser modificado en cuanto a formato y transformado en pdf, todo ello desde Writer y manualmente, aunque se pueden crear macros para ejecutar estas tareas, las cuales deberán situarse el directorio IDE Mis macros y diálogos.

miércoles, 26 de noviembre de 2025

Evaluación.


Automatización de la evaluación

Análisis de datos




Hablar del análisis de datos es hacerlo de tres realidades diferentes, aunque interrelacionadas: 
  • Del análisis de los datos contenidos en la documentación técnica del test.
  • De los datos recopilados en la aplicación de la prueba, incluyendo los resultantes del interés por la construcción de baremos locales (de sector o de centro)
  • Y de los datos resultantes de la aplicación individual del test a un alumno concreto dentro del proceso de evaluación psicopedagógica.
Mientras que los dos primeros implican el uso de herramientas de análisis que van desde las funciones Calc hasta el uso de script y bibliotecas específicas, siempre a modo de instrumentos facilitadores del análisis, sólo la tercera es susceptible de automatización en el mismo sentido que hemos hablado de automatización en las fases previas del proceso.

También aquí las primeras formas se han basado en fórmulas y funciones Calc o en funcionalidades equivalentes, pero los resultados sólo han podido ser parciales. No ha sido hasta el desarrollo de script (en OOo Basic, pero no sólo) que dicha automatización se puede considerar una opción viable... y aun así con dificultades que en estos momentos no puedo considerar plena y satisfactoriamente superadas. 

Mientras vamos mejorando, nos conformamos con resultados parcialmente satisfactorios. Otros no han conseguido mucho más, aun contando con muchos más recursos y conocimientos. Para muestra, aunque expresados como informes, el análisis de datos que subyace en los modelos de informe que emiten los sistemas de corrección de las editoriales.

Hago esta reflexión por dos motivos:
  • Para remarcar las limitaciones que estos informes presentan, las cuales son debidas en lo fundamental a las limitaciones de los análisis de datos que realizan... o que se atreven a realizar, que es otra cuestión, aunque el resultado sea el mismo.
  • Y para establecer la íntima relación que existe entre la automatización de la corrección del test, el análisis de los resultados obtenidos y la elaboración de un informe de resultados.
De hecho, en la práctica, es el informe el que visualiza el resultado del análisis de los datos... y sus limitaciones. Hablemos entonces de ese informe.

Evaluación.

Automatización de la evaluación

Corrección del test



La aplicación de la prueba no es la única fase que nos interesa automatizar; es más, es posible que ni siquiera interese automatizarla o no merezca la pena el esfuerzo que implica. Pero de lo que sí podemos estar seguros es que sí es posible e interesante automatizar la corrección del test. 

De hecho es la automatización de este subproceso la que mayor interés despierta en las editoriales. Al menos así es para algunas que ofrecen servicios de corrección on-line que asocian a la adquisición de los cuadernillo (doble negocio) y llegan a omitir los datos que permitirían lo corrección y/o la puntuación de la prueba para forzar al uso de sus servicios de corrección. Una práctica totalmente legal, por lo visto, pero moralmente más que cuestionable, pienso. 

Es cierto que estas plataformas de corrección ofrecen un servicio que va más allá de la mera corrección de la prueba, incluyendo la valoración de los resultados y la devolución de un informe técnico-descriptivo de los mismos. De paso la empresa, si así lo desea, puede recopilar gratuitamente los datos que precisa para mejorar la baremación de la prueba, ahorrándose los gastos correspondientes.

Dejando estas cuestiones al margen y volviendo a lo que nos interesa, decir que automatizar la corrección de una prueba puede realizarse mediante el servicio Calc sin necesidad de emplear script OOo Basic. Para las formas más básicas de corrección, puede ser suficiente con las funciones que incorpora la propia hoja de cálculo. 

De hecho, de este modo se han construido muchos soportes de digitalización de la evaluación, sin ser conscientes de estar avanzando en la automatización de la prueba. Esta automatización es necesariamente parcial, pero real. Por ese motivo el tratamiento de muchos de los recursos de evaluación que presento en las secciones correspondientes del blog se inicia con esos soportes Calc.

No obstante, para realizar el análisis de datos (la fase  siguiente en este proceso hacia la automatización) es necesario algo más que las funciones Calc, lo que lleva a la creación de código. Y una vez "metidos en harina", resolver la corrección de la prueba mediante script puede resultar más lógico y hasta más sencillo que hacerlo mediante funciones Calc. También contribuyen a ello los distintos tipos de ítem. 

Quien dice programar en OOo Basic dice también hacerlo en otros lenguajes. Python es una buena alternativa.

Evaluación.


Automatización de la evaluación

Aplicación de la prueba




No se trata sólo de empezar por el principio, también pretendo sistematizar los distintos momentos y subprocesos que se pueden diferenciar en el proceso de implementación de una prueba, recurso o test de evaluación y, consecuentemente, identificar (y diferenciar) los diferentes subprocesos o fases en que se concreta la automatización de la evaluación.

Lo lógico, me parece a mi, es empezar por la presentación de los materiales para que el alumno resuelva la o las tareas que se le proponen, lo que no es otra cosa que la aplicación de la prueba, que también incluye las instrucciones y todo lo relativo a la explicación de cómo se espera que se desarrolle el proceso, tarea ésta que corresponde al examinador (el OE en nuestro caso).

Cierto es que existen diferentes tipos de pruebas, también por el modo en que se presentan qué tipo de inputs y se esperan del alumno qué tipo de respuestas. En función de todo esto tenderemos también diferentes niveles y modos de automatizar, e incluso de que esta automatización sea innecesaria.

La forma más compleja de automatización, pero también la más sencilla de implementar por estar bien acotada, es aquella de ítem de elección múltiple en la que el alumno debe seleccionar una imagen entre varias en respuesta a una demanda del examinador. Pero no es la única.

Verdad que no es la única, pero sí la que nos puede servir para ilustrar en qué consiste la automatización en este punto de su desarrollo; y hacerlo de forma muy concreta y precisa, hasta el punto que podemos decir que el resto de los procedimientos no son otra cosa que expresiones diferentes (y normalmente más simples) de este modelo. Veremos algunas de estas variantes, pero antes nos centraremos en cómo implementar ésta que hemos considerado prototípica.

La automatización de la aplicación de una prueba de evaluación tal y como la he concretado antes tiene un precedente ajeno a la automatización propiamente dicha, pero en línea con la digitalización de las pruebas de evaluación. Me refiero al uso de presentaciones tipo pptx que contienen las láminas para mostrar al niño  y que, al igual que con las de papel, seleccionar la que corresponda, 1 entre 4, normalmente.

El siguiente paso ya hace posible la automatización de la aplicación de la prueba y se basa en implementar macros o código (vg. VBA o OOo Basic), estableciendo alguna forma de interacción del niño con los comandos incorporados el soporte, incluyendo su uso sobre la imagen. Esto también tiene un precedente en el uso de las presentaciones y los mecanismos de interacción que incorporan, pero este precedente no permite interacciones como las que se requieren para automatizar la aplicación de la prueba, aunque aparenten ser similares.

Una alternativa al uso de las presentaciones es emplear hojas de cálculo, dado que admiten el uso interactivo de imágenes, la recogida de datos en sus celdas y la incorporación de script para la gestión del soporte. Un ejemplo de ello lo tenemos en las posibilidades que presentan los documentos Calc y la construcción de DocAp basados en ellas. 

Ciertamente no son la única solución, pero sí una de las más sencillas, generalizables y sostenibles. La sencillez viene dada por las tres características explicadas en el párrafo anterior. Las otras dos cualidades derivan de que ese uso está al alcance de cualquier profesional que muestre interés por crear este tipo de recursos.

sábado, 15 de noviembre de 2025

Expedientes. Análisis


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 os

directorio = "ExpedCalc/"

contenido = os.listdir(directorio)

n_elem = 0

print('Listado de componentes \n')

for elemento in contenido:
    n_elem = n_elem +1
    print (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_lista

Dim  oHoja As Object
Dim oCelda As Object
Dim oDato As Object
Dim i As Integer
Dim n As Integer
Dim Archivo As String

n = 244

oHoja = ThisComponent.getSheets().getByName("lista") 
for i = 1 To n
oCelda = oHoja.getCellRangeByName( "C" & i)
Archivo = oCelda.getString()
AbrirDocumento(Archivo)
Next

End 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 String
Dim mOpciones(0) As New "com.sun.star.beans.PropertyValue"
Dim oDoc As Object
mOpciones(0).Name = "MacroExecutionMode"
mOpciones(0).Value = 4
Dim ruta As String
ruta = "D:/EXPEDIENTES/ExpedientesOriginales/" & alumno & ".ods"

sRuta = ConvertToUrl(ruta)
oDoc = StarDesktop.loadComponentFromURL( sRuta, "_blank", 0, mOpciones() )

Dim oHoja As Object
Dim oCelda As Object
Dim iDato As Integer
Dim archivo As String
oHoja = ThisComponent.getCurrentController.getActiveSheet()
oCelda = oHoja.getCellRangeByName("C10")
iDato=oCelda.getValue()

if iDato <= 4 Then
oDoc.close(True)
Else
archivo = alumno
grabar_excel (archivo)
End If

End 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 object
dim dispatcher as object

document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dim archivo As String
archivo = "file:///D:/EXPEDIENTES/ExpedientesExcel/" & documento & ".xlsx"

dim args1(1) as new com.sun.star.beans.PropertyValue
args1(0).Name = "URL"
args1(0).Value = archivo
args1(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 n
oCelda = 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.