lunes, 6 de mayo de 2024

Procedimientos. Textos.

Campos calculados

Entiendo por campo calculado aquel que resulta de la aplicación de al menos una operación aritmética. Este tipo de campo es más propio de un docap de evaluación y está muy escasamente representado en los documentos prescriptivos, pero no ausente.



En efecto, tanto el dictamen como el informe contiene uno de esos campos, el mismo en realidad: el del cálculo de la edad del sujeto.


En la segunda tabla (1. Datos personales del alumno o alumna) del dictamen encontramos este tipo de campo. Como recordarás, en un primer momento lo tratamos como campo simple (introducir el dato directamente mediante InputBox()), trasladando al usuario la realización del calculo con o sin la ayuda de alguna herramienta de cálculo(1); pero en esta entrada vamos aprender a ejecutar el cálculo desde un script, lo que equivale a tratar dicho campo como campo calculado.

Como en todo cálculo son necesario datos y operadores, lo que nos lleva al necesario conocimiento de ambos en el lenguaje OOo Basic (en esta [entrada] sobre variables y operadores numéricos). Aquí encontramos los datos en dos campos: el tercero de la primera tabla (Fecha del dictamen) y en el tercero de la segunda (Fecha de nacimiento), ya que el cálculo de la edad resulta de:

Edad = Fecha del dictamen - Fecha de nacimiento

Este es un cálculo sencillo en cuanto a operaciones, pero no carente de cierta complejidad al constar una fecha de diferentes unidades de tiempo (año-mes-día). El procedimiento de la operación queda expresado (frecuentemente) en las carátulas de las pruebas de evaluación...


... que es una forma gráfica de expresar la siguiente igualdad:

e(a:m:d) = f(a*x + m*y + d) - i(a*x + m*y + d)
  • siendo e = edad, f = fecha final o actual e i = fecha inicial o de nacimiento
  • siendo a = años, m = meses y d = días
  • Siendo x = 365 e y = 30 (esto es, año standard de 365 días y mes ajustado a 30 días) 
Aunque en un campo calculado el procedimiento a seguir está en función de las operaciones (cálculos), en términos generales, y no con independencia de la complejidad del cálculo, se nos presentan dos posibilidades:
  • Incorporar el cálculo al script principal
  • O extraerlo del script principal y derivarlo a una función a la que asociar a una variable del script principal.
Ambas opciones son válidas en cualquier caso, pero la primera es aceptable cuando las operaciones son pocas (al menos pocas) y la segunda es aconsejable cuando debemos aplicar un número de operaciones que pueden afectar a la inteligibilidad del script principal.

Volviendo al cálculo de la edad, que es al que nos enfrentamos en el caso de los documentos prescriptivos tratados (dictamen e informe), el modo en que planteemos el script va a depender de la forma en que nos planteemos resolver el cálculo. De hecho podemos utilizar al menos tres procedimientos diferentes.
  • El primero y más sencillo consiste, básicamente y en primer lugar, en calcular la diferencia (en días) entre la fecha final y la inicial mediante la fórmula...
Edad = FechaFinal - FechaInicial

... factible siempre y cuando FechaFinal y FechaInicial hayan sido definidas como variables tipo Date, lo que requiere el uso de la función de conversión CDate() a la solicitud de datos al usuario mediante InputBox().

  • El segundo es más complejo puesto que no realizamos el cálculo de forma directa, sino que descomponemos cada una de las variables de fecha haciendo uso de tres funciones disponibles en Calc (year(), Month() y Day()) que devuelven respectivamente el año, mes y día de una fecha expresado en formato fecha (v.g., dd/mm/aaaa)
La primera fórmula es, en principio, perfectamente asumible desde el script principal (por lo que no sería necesario derivar el cálculo a una función), pero la segunda ya resulta suficientemente compleja como para que valoremos la pertinencia de usar una función.

No obstante, en ambas opciones, y hasta ese momento solamente hemos obtenido el número de días transcurridos desde la fecha inicial (nacimiento) hasta la actual...

iDias = fFinal - fInicial

... pero no la edad del sujeto tal y como se expresa normalmente (años, meses y, en su caso, días). Para ello necesitamos conocer, como los valores meses transcurridos y los años transcurridos (2) como paso previo para saber la edad.

iMeses = iDias\30
iAnnos = iDias\365

A partir de estos cálculos podremos obtener los valores edad en años y en meses. En realidad el de los años (de edad) resulta de la operación previa (iAnnos = iDias\365), pero el de meses (de edad) requiere restar el total de meses menos los que suponen los años.

iMesesEdad = iMeses -(iAnnos*12)

Es para el cálculo de los días de la expresión edad donde podemos optar por dos formas diferentes de realizar el cálculo:
  • Operando con los datos disponibles: iDiasEdad = iDias - ((iAnnos*365)+(iMesesEdad*30))
  • Utilizando el operador aritmético Mod para calcular el resto de la división de los días transcurridos entre 30: iDiasEdad = iDias Mod 30
Dado que con ambas operaciones se obtiene el mismo resultado, parece lógico que optemos por el operador Mod.

La tercera opción, que se ajusta claramente al uso de una función, reproduce el procedimiento empleado en el cálculo manual de la edad que vimos representado en la última imagen. Incluye el uso de funciones de identificación de años, mes y días de ambas fechas (funciones Calc Year(), Month() y Day()) y procedimientos de corrección para casos particulares. Pero detengo aquí la explicación porque esta función ya ha sido explicada en esta [entrada correspondiente] a la que remito para más detalle (3)

Analizo a continuación una de las formulaciones dentro de las posibles antes comentadas.

 Sub Edad2

'Variables

Dim oMarca As Object
Dim mMarcadores(2) As String
Dim mDatos(2) As String
Dim fDictamen As Date, fNacimiento As Date
Dim sEdad As String
Dim i As Integer

'Asignación de contenido

mMarcadores() = Array("d0","d1","d2")
fDictamen = CDate(InputBox("Fecha del dictamen:","DICTAMEN DE ESCOLARIZACIÓN"))
fNacimiento = CDate(INputBox("Fecha de nacimiento:","ALUMNO/A. DATOS PERSONALES"))
sEdad= EdadA(fNacimiento,fDictamen)
mDatos(0) = CStr(fDictamen)
mDatos(1) = CStr( fNacimiento)
mDatos(2) = sEdad

'Escritura de datos en el documento

For i=LBound(mMarcadores()) To UBound(mMarcadores())
oMarca = ThisComponent.getBookmarks().getByName(mMarcadores(i))
oMarca.getAnchor.setString(mDatos(i))
Next

End Sub

'Función para cálculo de edad

Function EdadA(fInicial As Date, fFinal As Date) As String

'Calculo de duración de los periodos temporales (en días, meses y años)

Dim iDias As Integer, iMeses As Integer, iAnnos As Integer

iDias = fFinal - fInicial
iMeses = iDias\30
iAnnos = iDias\365

'Cálculo de valores

Dim iMesesEdad As Integer, iDiasEdad As Integer

iMesesEdad = iMeses -(iAnnos*12)
iDiasEdad = iDias - ((iAnnos*365)+(iMesesEdad*30))

'Expresión edad

Dim sEdad As string

sEdad = CStr(iAnnos) & " años, " & Cstr(iMesesEdad) & " meses y " & CStr(iDiasEdad) & " días."

EdadA = sEdad

End Function

  • Dividimos el procedimiento en dos partes: un script (principal) y una función (para el cálculo de la edad). 
  • Desde el script se llama a la función para asignar el resultado obtenido mediante la variable sEdad
  • El script es el responsable de la asignación de datos y de su escritura en el documento.
  • En él, por claridad expositiva, diferencio entre las variables para asignación de datos...
Dim fDictamen As Date, fNacimiento As Date
Dim sEdad As String

  •  ... una matriz para su escritura en el documento (Dim mDatos(2) As String) (4) y una segunda para contener los nombres de los marcadores. De este modo podemos resolver la escritura de los datos en los marcadores del documento de forma eficiente mediante un bucle.

For i=LBound(mMarcadores()) To UBound(mMarcadores())
oMarca = ThisComponent.getBookmarks().getByName(mMarcadores(i))
oMarca.getAnchor.setString(mDatos(i))
Next
  • Centrándonos en la función, primeramente calculamos la diferencia en días entre los valores pasados como atributos desde el script.
  • Primero creo tres variables para el cálculo del tiempo transcurrido desde la fecha de nacimiento hasta la actual (Dim iDias As Integer, iMeses As Integer, iAnnos As Integer) y realizo los cálculos pertinentes, empezando por el datos de los días (iDias = fFinal - fInicial) y, a partir de éste, los de los meses (iMeses = iDias\30) y años (iAnnos = iDias\365)
  • El valor años es útil directamente, pero no así el de los meses y días transcurridos. Por ello necesitamos dos variables (Dim iMesesEdad As Integer, iDiasEdad As Integer) que nos permitan asignar a ellas el resultados de las operaciones requeridas.
  • El cálculo del valor meses de edad (iMesesEdad = iMeses -(iAnnos*12)) requiere descontar del total de meses los contenidos en los años de vida del sujeto...
  • Y el cálculo de los días de la edad, como explicamos antes, puede ser resultante de dos operaciones diferentes: en este script utilizo la forma básica, que no la mejor (iDiasEdad = iDias - ((iAnnos*365)+(iMesesEdad*30))), como ya expliqué antes.
  •  Finalmente componemos la expresión que informa sobre la edad del sujeto sobre una variable (Dim sEdad As string) y creo dicha expresión mediante la concatenación de variables y cadenas de texto (sEdad = CStr(iAnnos) & " años, " & Cstr(iMesesEdad) & " meses y " & CStr(iDiasEdad) & " días."
  • Finalmente se asigna al identificador o nombre de la función (EdadA) la variable anterior (EdadA = sEdad), con lo que se entrega al script que llama esta función el dato (string) asociado (el contenido de sEdad)
Para facilitar el análisis de las diferentes opciones dejo [acceso] al sencillo docap resultante.

NOTAS

(1) Además de no ser un cálculo de especial dificultad, que bien puede resolverse manualmente, existen en la Web calculadoras de edad, como la que pone a disposición de todos la editorial Pearson. También podemos crear una [calculadora de edad] mediante las funciones integradas en Calc.

(2) Obsérvese que el operador utilizado en ambos cálculos es el de la división entera (\), no el de la división general (/).

(3) En realidad la diferencia pequeña diferencia entre las dos primeras opciones y la tercera tampoco no es tan importante para el objetivo que se plantea en los documentos prescriptivos, especialmente si limitados los datos de edad a años y meses, datos normalmente suficientes para los objetivos que se persiguen en ambos documentos. Únicamente en caso de nuevas escolarizaciones (en 1º de E. Infantil) y cuando el número de días se aproxima al mes se puede considerar de cierta relevancia el número de días de edad; en el resto de las circunstancias, conocer los años y meses de edad será suficiente.

(4) En realidad nos podríamos ahorrar las variables y utilizar los elementos de la matriz mDatos() para asignar directamente los valores. Con ello nos ahorraríamos algunas líneas de código (la reasignación de los datos de las variables a los elementos de la matriz, por ejemplo), pero deberíamos hacer otros cambios en el script y en la función. En este caso deberíamos optar por declarar la matriz de tipo Variant o de tipo String, pero de tomar esta segunda decisión, tendríamos que modificar la categoría de las variables argumento de la función y realizar dentro de ésta un cambio de tipología (de String a Date) mediante la función CDate(). Como podemos ver, se comprueba de nuevo esa máxima de que en programación existen múltiples soluciones posible.

No hay comentarios:

Publicar un comentario

Comenta esta entrada