Modelo básico de docap
Calificar de básico a este modelo equivale a decir concreción del tipo 2 visto en [esta entrada] y decir docap incide en lo mismo, especificando que nos vamos a limitar en la presente al uso de OOo Basic como lenguaje de referencia.
Me limito a desarrollar como docap-modelo el de tipo 2 por ser el de uso más frecuente en el posible desarrollo de herramientas de evaluación y también por facilitar el desarrollo de una alternativa en Python, así como por similitud con el soporte básico de recogida de datos en la sección Análisis. Esto no quiere decir que no vaya a crear docap de tipo 1, pero como éstos están ligados estrechamente al contenido de aprendizaje, no parece conveniente extenderse en estos momentos en explicar algo que va a sufrir tantas modificaciones como deriven de los contenidos concretos con los que se vaya a trabajar.
Entrando ya en materia y completando lo indicado en la entrada citada al inicio de ésta, el docap-modelo que presento ahora no se diferencia demasiado del presentado [en esta entrada] respecto al trabajo con documentos. Salvando algunas diferencias (que puede haberlas), la mayor y más significativa es que el docap actual añade una base de datos a los componentes de ese docap; el resto de componentes no tiene por qué ser diferente, aunque sí lo será el procedimiento de análisis. No obstante, como este proceso no será desarrollado en esta explicación por los mismos motivos (más otros añadidos) (1) que en aquel caso, las diferencias reales entre ambos en términos de programación se reducen significativamente. Por estos motivos, no resultará demasiado complicado explicar y entender lo que a continuación voy a exponer: en gran medida será suficiente con repetir lo ya visto.
Para concretar un docap-modelo de evaluación se debe primero especificar en que nivel se recoge la información, ya que es posible diferenciar tres: respuestas del alumno, puntuaciones obtenidas por ítem y puntuación total de la actividad o prueba. Considero que para que resulte realmente operativo (en términos de automatización) se debe contemplar como nivel básico al menos el segundo, pero en función de la característica del docap, desarrollaré el primer nivel: recogida de las respuestas, que implica que la puntuación de las mismas se resuelve mediante código.
Una segunda cuestión, aunque afecta al análisis y dije antes que en esto no iba a entrar, es con qué referentes se van a calificar los resultados. Esta cuestión requiere aclarar previamente qué tipo de evaluación se propone: normativa, criterial o mixta. Estas cuestiones no corresponden a esta entrada, ya que es necesario clarificar el significado concreto de esos conceptos y sus implicaciones, por eso opto aquí por una solución de compromiso muy simplificada, como corresponde a un docap básico (2).
La primera fase del proyecto es, como en los modelos documentales, el trabajo con la hoja de cálculo, empezando por la creación del formulario (hoja Form) (3). Este podría ser el resultado:
No he pretendido hacer nada sofisticado, por lo que es mejorable, pero sirve para el objetivo de este docap-básico. Contiene algunas diferencias con el docap-básico de trabajo con documentos, que explico a continuación:
- En el formulario se diferencian tres partes, frente a las dos de los modelos anteriores: a parte de los botones de comando, los controles de entrada se diferencian por su función: los tres primeros son para datos de identificación y los restantes para la recogida de las respuestas de los ítem.
- Esta diferencia se reproduce en la asignación de esos controles a celdas: los controles de datos de identificación se asocian a tres celdas de la columna I, y los de los controles de ítem a siete celdas de la columna J.
- Esta diferenciación por columnas puede no ser necesaria, pero sí va a serlo la diferenciación en el acceso a cada uno de los dos tipos de datos, como veremos en el código correspondiente. Después explicaré el motivo.
Además, y esa es la principal diferencia con los docap-modelo documentales, deberemos crear una segunda hoja (Datos) donde crearé la tabla de datos, concretamente los encabezamientos de las columnas que sirven para identificar sus campos.
Una vez preparado el documento Calc, corresponde desarrollar el código que nos permita acceder a los datos introducidos por el usuario. Para ello declaro las variables (4) y accedo al documento y a la hoja Form, siguiendo el procedimiento ya conocido...
Dim oHoja As Object, oCelda As ObjectDim mDatosper() As String, mCeldasi() As StringDim mResp() As String, mCeldasj () As String, mAcierto() As StringDim mPtos() As Integer, PD As IntegerDim n1 As Integer, nj As IntegerDim i As IntegerDim Md As Single, Dt As Single, Pz As Single, Pc As SingleDim mDatos() As VariantoHoja = ThisComponent.getSheets().getByName("Form")
Observa que he declarado dos matrices para contener los datos (mDatosper() y mResp()), ya que me interesa trabajar de forma independiente con cada uno de los dos bloques de datos. Como dije antes, esta diferenciación obedece al diferente tratamiento que tendrán los datos de identificación y los datos de respuestas a los ítem: los primeros no presentan novedad respecto a otras ocasiones, pero los segundos sí: sobre ellos realizaremos operaciones específicas, como tendremos ocasión de ver en su momento. Repito: con los datos personales nos limitamos acceder a ellos, pero los datos de respuesta a los ítem serán usados para generar los elementos de la tercera matriz (mPtos()), que contendrá las puntuaciones de los ítem resultante de la corrección de la prueba, las cuales están contenidas en una cuarta matriz (mAcierto()).
Por ello, el acceso a las respuestas del alumno, con la que finaliza la primera fase del script principal Main se resuelve de forma similar a cómo se resolvió el acceso a los datos personales, esto es: mediante un bucle For (For i = 0 To UBound(mResp()))
ni = 2ReDim mDatosper(ni)mCeldasi= Array("I1","I2","I3")For i = 0 To UBound(mDatosper())oCelda = oHoja.getCellRangeByName(mCeldasi(i))mDatosper(i) = oCelda.getString()Next
A continuación entramos en la segunda fase de Main, en la que nos corregimos las respuestas dadas por el alumno. En este caso el sistema es muy sencillo, pero puede ser mucho más laborioso, lo que hace recomendable la diferenciación que antes expliqué entre datos de identificación y datos de respuestas.
Para realizar esta corrección damos contenido a la matriz que contiene las respuestas correctas (mAcierto() = Array("A","B","C","D","E","F","G")) y mediante un nuevo bucle (For i = 0 To UBound(mResp())) y un condicional (If mResp(i) = mAcierto(i) Then) recorremos la matriz mResp() y comparamos su contenido con el esperado (mResp(i) = mAcierto(i)), asignando la puntuación que corresponde a cada ítem según se cumpla o no dicha condición. Aprovecho este mismo bucle para calcular el sumatorio de las puntuaciones del alumno (PD = PD + mPtos(i)), lo que supone entrar en la tercera fase del script.
mAcierto() = Array("A","B","C","D","E","F","G")For i = 0 To UBound(mResp())If mResp(i) = mAcierto(i) ThenmPtos(i) = 1ElsemPtos(i) = 0End IfPD = PD + mPtos(i)Next
Como dije, este sumatorio constituye el inicio de la tercera fase, la cual está dedicada a realizar los cálculos que nos permitirán calificar los resultados que obtiene el alumno: el porcentaje de aciertos, en función del tamaño del test y su puntuación directa (Pc = (PD/(nj+1))*100), y la puntuación típica (Pz = (PD - Md)/Dt), en función de supuestos valores de media (Md = 5.38) y la desviación típica (Dt = 0.47) (5).
A continuación se desarrolla la cuarta fase del script principal (Main) que tiene como objetivo construir con los datos parciales anteriores una matriz única de datos con la que trabajaremos en las subrutinas a las que se llama al final de Main (PasarDatos(mDatos()) y Info(mDatos())) (quinta fase).
Aunque podríamos ahorrarnos esta cuarta fase, por los motivos que dije en su momento puede no ser la mejor opción; pero, en todo caso, sí es conveniente crear una matriz única con todos los datos necesarios, ya que será muy útil para el correcto funcionamiento de las subrutinas que veremos más adelante.
Para crear esta matriz-síntesis recurriré de nuevo a los bucles como medio para automatizar la asignación de contenidos, aunque algunos de ellos es necesario asignarlos de forma directa, como se puede observar en el código:
ReDim mDatos(19)For i = 0 To UBound (mDatosper())mDatos(i) = mDatosper(i)NextFor i = 0 To UBound( mResp())mDatos(i+3) = mResp(i)NextFor i = 0 To UBound(mPtos())mDatos(i+10) = mPtos(i)NextmDatos(17) = PDmDatos(18) = PcmDatos(19) = Pz
Primero redimensionamos la matriz-resumen (ReDim mDatos(19)) y después vamos asignando los datos, empezando por los personales (mDatos(i) = mDatosper(i)), siguiendo por las respuestas del alumno (mDatos(i+3) = mResp(i)), después los resultantes de la corrección de la prueba (mDatos(i+10) = mPtos(i)), finalizando con los datos-resumen, resultantes de los cálculos (vg. mDatos(17) = PD) (6).
Paso ahora a explicar los dos procedimientos que restan y que se ejecutan de forma independiente del script principal, mediante subrutinas:
- La primera (Sub PasarDatos(Datos() As Variant)) está pensada para construir la base de datos, trasladando los datos de la matriz-resumen (mDatos()) a la tabla creada en la hoja Datos.
- La segunda (Sub Info(Datos() As Variant)), ya conocida de [esta entrada], para crear el informe.
Estas subrutinas constituyen una especie de "externalización de procedimientos" del algoritmo, pero esto, además de ejemplificar el uso del principio de la programación modular, resulta necesario más que meramente conveniente, dada la relativa complejidad de ambos subprocesos y su independencia respecto al objetivo principal del script Main.
Dado que la segunda ya es conocida desde la publicación de [esta entrada], me limitaré a explicar la primera. Con ella automatizamos la creación de la tabla o base de datos, dotando a nuestro docap de un potencial muy interesante: recopilar los resultados de las aplicaciones individuales de una prueba, lo que facilita el posterior análisis de dichos datos (7).
Sub PasarDatos(Datos() As Variant)Dim oHojaBD As Object, oCeldaInicio As Object, oCeldaNuevoRegistro As Object, oCeldaId As ObjectDim i As Integer, a As Integer, b As Integer, c As Integer, d As IntegeroHojaBD = ThisComponent.getSheets().getByName("Datos")c = 1000For i = 0 To coCeldaInicio = oHojaBD.getCellRangeByName("A" & CStr(i+2))If oCeldaInicio.getValue() = 0 Thena = i + 2d = i + 1Exit ForEnd IfNextMsgBox "Id de la nueva entrada: " & d'Escritura de los datos en el registro vacíooCeldaId = oHojaBD.getCellRangeByName("A" & CStr(a))oCeldaId.setValue(d)For b = 0 To UBound(Datos())oCeldaNuevoRegistro = oHojaBD.getCellByPosition(b+1,a-1)oCeldaNuevoRegistro.setString(Datos(b))NextEnd Sub
Esta subrutina cuenta con un argumento que remite a la matriz-resumen (PasarDatos(Datos() As Variant)) de los datos del script principal Main, resultando ahora evidente la necesidad de su creación: gracias a ellos se simplifica la escritura de esos datos en la tabla.
Pero antes de dar ese paso debemos acceder a la hoja Datos (oHojaBD = ThisComponent.getSheets().getByName("Datos")) y posicionarnos adecuadamente en la primera fila no escrita (8) mediante un bucle condicionado
For i = 0 To coCeldaInicio = oHojaBD.getCellRangeByName("A" & CStr(i+2))If oCeldaInicio.getValue() = 0 Thena = i + 2d = i + 1Exit ForEnd IfNext
Cuando encontramos la celda de la columna A (oCeldaInicio = oHojaBD.getCellRangeByName("A" & CStr(i+2))) que cumple la condición (oCeldaInicio.getValue() = 0) asignamos el valor de inicio a la variable-contador que nos van a permitir recorrer las diferentes columnas de la tabla (a = i + 2) y de captura el valor Id de la fila A que precede a la que no cumple el criterio del condicional (d = i + 1). Después interrumpimos el bucle.
Después volvemos a posicionarnos en la celda de la columna A localizada mediante el bucle anterior (oCeldaId = oHojaBD.getCellRangeByName("A" & CStr(a))) y escribimos en ella el valor Id que corresponde (oCeldaId.setValue(d)) y de nuevo, mediante un bucle dimensionado en base a la matriz-resumen (For b = 0 To UBound(Datos())), recorremos ahora las columnas (campos) que se corresponden con la fila (oCeldaNuevoRegistro = oHojaBD.getCellByPosition(b+1,a-1)) y trasladamos a cada celda los datos de la matriz (oCeldaNuevoRegistro.setString(Datos(b))).
Documento. Desde [este enlace puedes acceder a DocapBaseEval.ods]
NOTAS
(1) Si en el caso de los docap-documento el tema del procesamiento remitía a las dificultades para tratar de forma simplificada el problema del procesamiento, en el caso de los docap-evaluación la problemática se acentúa, ya que de una prueba de evaluación se derivan muchos más datos cuantitativos y de categorización que requieren procesos de análisis de la bifurcación de mayor complejidad. No obstante el tratamiento de la opcionalidad en el caso de los contenidos textuales es realmente más complejo, ya que se ajustan en menor medida que los datos de un test a un planteamiento de tipo algorítmico.
(2) Por lo que no resulta satisfactorio desde ninguna de las perspectiva de análisis, pero tiene la ventaja de incluir referencias a ambos modelos (y al mixto). Además, y es lo que importa, evidencia los mecanismos básicos del análisis desde ambas perspectivas (la normativa y la criterial), aunque de forma muy simplificada.
(3) Dado que el informe se crea sobre un documento en blanco, no es necesario crearlo. También es posible utilizar un documento previamente creado y formateado, con lo que implica de trabajo previo y de modificación del código de acceso. Ver al respecto el modelo básico 2 de docap de documento en [esta entrada]
(4) Incluyendo las que serán necesarias para el procesamiento posterior y que explicaré en su momento.
(5) Ambos valores sirve únicamente a efectos del funcionamiento del docap, pero no reflejan valores reales de ninguna prueba en concreto.
(6) Puedes apreciar que los tres primeros bloques se ejecutan mediante bucles (reajustando los valores de los índices de la matriz-resumen) y en los últimos la asignación se realiza de forma directa.
(7) Este análisis es anticipatorio pero diferentes del que nos planteamos en la sección Análisis del Blog. También lo es este docap del que podemos crear dentro de ese campo de trabajo.
(8) Recuerda que una fila de una tabla equivale a un registro en una base de datos. Las columnas de la tabla equivalen a los campos de la base de datos y serán tratadas como variables en el análisis de datos.
No hay comentarios:
Publicar un comentario
Comenta esta entrada