domingo, 9 de junio de 2024

Procedimientos. Datos

Calc. Crear listados.

Un listado y más aun una tabla son conjuntos o colecciones de datos organizados en formato fila-columna, lo que equivale a decir que equivale a una base de datos (Bd) simple (en realidad, su forma más básica), en la que se pueden identificar entradas y campos: las filas equivalen a las entradas o registros de la Bd y las columnas identifican los campos que componen dicha Bd. Desde esta perspectiva, el servicio Calc, en cuanto que trabaja con hojas de cálculo (Hc) es un recurso que nos permite crear y trabajar con Bd simples (1). 


La
primera y necesaria forma de trabajo dentro desde esta perspectiva es crear una Bd, algo que podemos hacer manualmente, pero también automatizar mediante
OOo Basic. Este será el tema de esta entrada.

Para entender mejor l desarrollo de esta entrada es necesario contextualizarlo. Con este fin, y a modo de ejemplo, vamos a partir de una posible situación o problemática: deseamos generar una tabla o Bd simple en soporte Calc en la que almacenaremos los resultados de la aplicación de una prueba para su posterior análisis. Para ello, en primer lugar, crearemos la estructura de campos (columnas) que contiene la Bd y lo haremos sobre una Hc.Calc específica, esto es: diferenciada del soporte (también Calc, pero no necesariamente) sobre el que construimos el núcleo del docap (2). 
  • La primera implicación de esta decisión es la creación del documento Hc.Calc de Bd.
  • La segunda es construir sobre esta Hc la estructura de campos de la Bd, que no es otra cosa que identificar en las columnas las cabeceras de dichos campos, configurando así una tabla de doble entrada: Filas->Registros; Columnas->Campos.



Esta imagen muestra un ejemplo sencillo de la configuración de una Bd (tabla, si se prefiere) en la que se identifican un total de 18 campos (3):

    • Seis de identificación del alumno
    • Diez para la PD del test
    • Dos para los cálculos (sumatorio PD y valoración del resultado)
  • La tercera es que el docap queda determinado en cuanto al procedimiento de ejecución: desde el gestor de entrada de datos (SoportePrueba.ods) (4) se accede al documento Bd (BDPrueba.ods), con todo lo que esto implica: acceso al documento Hc previamente creado y trabajo con dicho documento. 
El primer paso consiste en especificar el modo de acceso a dicho documento desde el script principal. Para ello, desde éste ejecutaremos una subrutina (AccesoBD(mDatos()) que llama desde el script principal (Main, en nuestro caso) al scritp (subrutina) que gestiona el trabajo con la Hc.Bd (BDPrueba.ods)

El script de trabajo con el documento Bd se constituye como subrutina, por lo que contiene un parámetro (Sub AccesoBD(Datos() As Variant)) que traslada a la subrutina el contenido de la matriz de datos (originariamente, esto es, en el script Main, la matriz mdatos(), que como parámetro de la subrutina equivale a la matriz Datos()) resultantes de la fase input y de procesamiento (que se desarrolla en el script Main) (5).

Pasemos a explicar el funcionamiento de la subrutina AccesoBD(mDatos(), que es la que nos interesa ahora, y en la que se diferencian 3 fases o subprocesos, los cuales especificaré a continuación:

Sub AccesoBD(Datos() As Variant)

Dim sRuta As String
Dim mArg()
Dim oBD As Object, oHojaBD As Object, oCeldaCampo As Object, oCeldaNuevoRegistro As Object
Dim i As Integer, a As Integer, b As Integer
Dim sEntrada1 As String

 Fase 1.  Acceso al documento externo

'Acceso a los objetos documento (BDPruebas.ods) y Hoja (DatosPrueba)

sRuta = ConvertToUrl("D:\Pruebas\BDPrueba.ods")
oBD = StarDesktop.loadComponentFromURL(sRuta, "_blank", 0, mArg())
oHojaBD = oBD.getCurrentController.getActiveSheet()

Fase 2. Trabajo con la Bd. Registro de datos

'Búsqueda del primer registro vacío

For i = 0 To 100
oCeldaCampo = oHojaBD.getCellRangeByName("A" & CStr(i+1))
sEntrada1 = oCeldaCampo.getString()
If sEntrada1 = "" Then
a = i
Exit For
End If
Next

'Escritura de los datos en el registro vacío

For b = 0 To UBound(Datos())
oCeldaNuevoRegistro = oHojaBD.getCellByPosition(b,a)
oCeldaNuevoRegistro.setString(Datos(b))
Next

Fase 3.  Almacenamiento de los datos del nuevo registro

'Guardar cambios

oBD.store()

'Cerrar BDPruebas.ods

oBD.close(True)

End Sub

  • Tras la declaración de las variables necesarias para el algoritmo específico de la subrutina...
Dim sRuta As String
Dim mArg()
Dim oBD As Object, oHojaBD As Object, oCeldaCampo As Object, oCeldaNuevoRegistro As Object
Dim i As Integer, a As Integer, b As Integer
Dim sEntrada1 As String
  • ... viene la primera fase, que consiste en el acceso y apertura del documento Hc.Bd...
sRuta = ConvertToUrl("D:\Pruebas\BDPrueba.ods")
oBD = StarDesktop.loadComponentFromURL(sRuta, "_blank", 0, mArg())

... y a la hoja donde está construida la Bd (6

oHojaBD = oBD.getCurrentController.getActiveSheet()

  • La segunda fase es la más compleja y en ella se desarrollan dos subprocesos: la identificación del primer registro sin datos (el que sigue al último que sí contiene datos), lo que equivale y se concreta como búsqueda de la primera fila vacía y el traslado de los datos enviados desde el script principal a la subrutina mediante la asociación de parámetros (mDatos() -> Datos()). Dada la complejidad (e importancia) de estos dos subprocesos, paso a explicar detenidamente cada uno de ello.

Identificación de la primera fila (registro) vacía. Consiste en buscar mediante un bucle que recorre las filas de la hoja aquella (primera) que no contiene aun datos. Para ello creamos una estructura For...Next ...

For i = 0 To 100
oCeldaCampo = oHojaBD.getCellRangeByName("A" & CStr(i+1))
sEntrada1 = oCeldaCampo.getString()
If sEntrada1 = "" Then
a = i
Exit For
End If

Next

... con la que inicialmente repetimos el proceso un número determinado de veces (For i = 0 To 100) (7). Dicho proceso consiste en acceder al contenido de la celda (campo 1 del registro) de forma secuencial...

 oCeldaCampo = oHojaBD.getCellRangeByName("A" & CStr(i+1))

...  asignar ese contenido a una variable de gestión del dato...

sEntrada1 = oCeldaCampo.getString()

... y someter el mismo (presencia vs. ausencia de contenido) a un condicional If simple...

 If sEntrada1 = "" Then

a = i
Exit For
End If

... en el que si se cumple la condición de ausencia de contenido (celda vacía).. 

Si sEntrada1 = "" -> Es True

... entonces a la variable a (que se empleará en el el subproceso siguiente como referencia de la fila sobre la que trabajar) se le asigna el valor correspondiente de la variable i, que equivale al identificador numérico de la celda vacía de la primera columna ("A" & CStr(i+1)). De este modo, la variable a contendrá el valor que identifica la primera fila son datos de la tabla.

Dentro de esta estructura condicional incluimos la sentencia de cierre/cese del bucle (Exit For)  para ajustar éste a la solución del problema (encontrar la celda vacía), acortando el tiempo de ejecución de algoritmo a lo estrictamente necesario (8)

Inclusión de datos en el registro. Consiste en pasar los datos contenidos en la matriz-parámetro Datos() en el registro de la Bd (en la fila de celdas vacía de contenido localizada gracias al proceso anterior)

For b = 0 To UBound(Datos())
oCeldaNuevoRegistro = oHojaBD.getCellByPosition(b,a)
oCeldaNuevoRegistro.setString(Datos(b))

Next

Empleamos para ello un segundo bucle For que trabajará con dicha matriz (9). En cada ciclo asignamos a la variable objeto oCeldaNuevoRegistro las distintas posiciones (objetos celda) de la fila (oHojaBD.getCellByPosition(b,a)) empleado como referencias las posiciones que ocupan en el entramado columna-fila (que no el nombre, como hicimos en el proceso anterior) mediante la función getCellByPosition(b,a). Observa que hace referencia a dos variables ((b,a)): primera (b) referencia cada una de las columnas que recorremos mediante el bucle y la segunda (a) referencia la fila (la primera fila vacía) (10).

Una vez identificada-seleccionada la celda, pasamos el contenido de la matriz a la celda mediante la función-método setString()) (setString(Datos(b))).

  • Finalmente, en la tercera fase procedemos a grabar y cerrar el soporte.Hc (BDPrueba.ods) que hemos referenciado-asociado a la variable-objeto oBD (11). La grabación del documento lo realizamos mediante la función store() (oBD.store()) y el cierre mediante la función close() (oBD.close(True)) con valor True (12). 
Documentos. Para acceder a los dos documentos indicados en esta entrada, [consulta la siguiente]. En ella se explica con detalle el funcionamiento de un docap-modelo que contiene (entre otras funcionalidades) el procedimiento que se explica en la actual.

NOTAS

(1) De hecho, LO.Calc cuenta con funciones que nos permiten trabajar con Bd creadas en formato listado-tabla. Ver Asistente para funciones | Categoría -> Base de datos.
(2) Podemos crear la Bd en una hoja diferente de las de entrada de datos pero perteneciente al mismo libro en el que creamos el soporte.gestor del docap. No obstante, con frecuencia (y es especialmente si deseamos trabajar de forma independiente con la colección de datos resultante de la aplicación de la prueba, es preferible que la Bd y el soporte.gestor del docap sean documentos diferenciados. Cuanto más específico y complejo sea el tratamiento que queramos dar a los datos recopilados, más conveniente es que la Bd se cree en un documento Calc específico.
(3) Huelga decir que se trata de un ejemplo concreto y que para cada Bd la estructura de campos se deberá planificar previamente a su configuración en formato documental. Dicha configuración forma parte del proceso de planificación previo y afecta al diseño del docap en todas sus dimensiones. De ahí su gran importancia.
(4) En este caso o ejemplo, desde el documento.gestor del docap se requerirá especificar el código que facilite el acceso al documento externo que contiene la Bd.
(5) Este script principal Main no es objeto de trabajo en esta entrada, ya que en ella nos centramos en el acceso y el trabajo con el documento.Hc de Bd. Para una mejor comprensión del funcionamiento global del algoritmo del que aquí se explica sólo una parte, [ver esta entrada].
(6) En este caso el documento Hc.Bd sólo consta de una hoja, pero es preferible asegurar el posicionamiento en ella como posicionamiento específico, identificado por el nombre de la hoja, y no como posicionamiento genérico (esto es, como hoja activa).
(7) Bucle de 100 repeticiones según concreto en este ejemplo, pero puede ser cualquier otro número de repeticiones. Se aconseja, no obstante, ser "generosos" al respecto, aunque el criterio de referencia para definir el tamaño del bucle es la previsión del número de registro que se espera contenga la Bd.
(8) Este sencillo procedimiento resulta de mucha utilidad: acorta el desarrollo del bucle al número de repeticiones necesario y nos da libertad para establecer éstas de forma aproximada y con el suficiente margen de seguridad, sin que la posible sobredimensión del bucle tenga un coste de tiempo de ejecución. No obstante, esto no implica que no tomemos referencias a la hora de determinar el número de repeticiones del bucle (ver nota 7)
(9) Para asegurarnos de ello utilizamos como referencia superior del recorrido el resultado de la función UBound() que identifica el último elemento de la matriz (UBound(Datos()))
(10) Recuerda que deriva del proceso previo en el que la asociamos al valor que alcanza la variable-contador i cuando se cumple el criterio que buscamos (If sEntrada1 = "" Then)
(11) En la primera fase hemos asociado dicho archivo a esta variable mediante la instrucción oBD = StarDesktop.loadComponentFromURL(sRuta, "_blank", 0, mArg()) que a su vez contiene como parámetro  la variable (sRutaque asociamos en la instrucción inmediatamente anterior al procedimiento de acceso al documento (sRuta = ConvertToUrl("D:\Pruebas\BDPrueba.ods"))
(12) Interesa destacar aquí el hecho de que ambas son funciones-métodos aplicadas sobre el objeto-documento con el que trabajamos (BDPrueba.ods), el cual, como tal, es un objeto de la clase Hoja-de-cálculo dentro del programa LibreOffice. El uso de la variable asociada a dicho documento (oBD.) es lo que nos asegura actuar sobre ese documento y no sobre (por ejemplo) la Hc.Gestor que contiene y desde la que actúa el script-subrutina AccesoBD()