miércoles, 3 de junio de 2009

HAL y la burocracia (VII - interactividad)

En un remoto pasado de nuestras conversaciones, enredados en las peculiaridades digestivas de HAL, jugábamos a procurar el alimento de órdenes como grep o cut por la vía directa de lo que llamamos entonces la cañería principal ---el standard input (STDIN), para los redichos--- conectada por defecto con nuestro terminal o consola. En aquel momento se trataba sólo de un curioso experimento preparatorio para comprender el significado y función de las imprescindibles tuberías. El pasatiempo de aquellos primeros contactos con las maravillas de la anatomía de HAL nos vendrá ahora de perlas para añadir interactividad a nuestro guión.

Volvamos a reproducir el experimento, pero ahora con otra orden capaz igualmente de alimentarse a través de la entrada estándar, la orden cat (no confundir con cut). cat es una abreviatura de concatenate y su función inicial es la de concatenar los ficheros que se le den como argumentos e imprimirlos línea a línea en pantalla. Si se le da un único fichero lo imprime tal cual. ¿Pero, qué sucede si no le damos ningún fichero como argumento? Probémoslo:

cat

Tras pulsar 'Enter', nos encontramos en una situación ya familiar, la de poder emitir líneas de texto en la entrada estándar para que cat haga lo que habitualmente hace con las entradas que se le proporcionan, devolverlas tal cual a la salida estándar, que por defecto también está conectada con nuestro terminal. El juego continúa sin fin hasta que solicitamos salir de él con Ctrl+D:

hola
hola
cat
cat
Ctrl+D

Hasta aquí nada nuevo. cat se comporta de una manera bastante estúpida ---pensaría alguno---, se limita a reproducir cada línea introducida, como si se tratase de un echo interactivo.

Parece que algo tan tonto no puede dar mucho de sí. Por el contrario, la utilidad de cat para nuestro propósito se desvela si añadimos a la orden una redirección:

cat >conversacion

Lo único que hace este añadido es redirigir la salida al fichero conversacion. Por tanto, si reproducimos la anterior sesión interactiva, donde la salida de cat ya no la veremos en pantalla ---porque se redirige a ese fichero--- estaremos copiando en él tal cual las líneas que hayamos escrito antes de pulsar Ctrl+D.

Inmediatamente se comprende la utilidad que este comportamiento de cat puede tener para nuestro guión.

Habíamos dejado pendiente la sección correspondiente a las propuestas de actividades que el alumno debe realizar para superar los objetivos y contenidos. Puesto que estas propuestas ---vamos a suponer---, no pueden, en principio, establecerse de antemano, sino que tienen que redactarse de modo expreso para cada alumno suspenso, proporcionar al guión por medio de cat un acceso a la entrada estándar, nos permitirá realizar la redacción de tales propuestas antes de que las sustituciones en la plantilla informe_plantilla.tex tengan efecto.

Esto nos lleva a modificar el guión obtenido el día pasado del modo siguiente [en rojo las nuevas líneas]:

# Directorio que contiene la programación del curso
PROGRAMACION="$HOME/guiones/informe_suspensos"

# Fichero que contiene la plantilla LaTeX del informe
PLANTILLA=informe_plantilla.tex

# Fichero que contiene las notas de los alumnos
NOTAS=notas

# Sufijo de los ficheros de objetivos por cursos
OBJETIVOS=objetivos.tex

# Sufijo de los ficheros de contenidos por cursos
CONTENIDOS=minimos.tex

# La nota mínima para aprobar el curso
NOTA_MINIMA=5

# El nombre del alumno y curso con el formato que tendrán en la salida impresa
ALUMNO_OUTPUT=$(cut -d':' -f1 $NOTAS | sed -e 's/-/ /g')
CURSO_OUTPUT=$(cut -d':' -f2 $NOTAS | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')

# Las propuestas de trabajo se deben introducir interactivamente
echo "Introduzca propuestas de trabajo para $ALUMNO_OUTPUT [\item ... (^D para salir)]: "
cat > trabajo_tmp.tex



sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
-e "s/CURSO/$CURSO_OUTPUT/" \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CONTENIDOS/\\input{1-ge-minimos}/' \
-e 's/RECUPERACIÓN/\\input{trabajo_tmp}/' $PLANTILLA > Sancho-Panza.tex

pdflatex Sancho-Panza.tex

La instrucción echo produce un mensaje para el usuario, mientras que la instrucción cat abre la entrada estándar para que el usuario introduzca los datos que se almacenarán en el fichero trabajo_tmp.tex. Este fichero es luego incluido en el fichero Sancho_Panza.tex por obra de la última expresión del sed final.

Se habrá advertido que el usuario debe introducir las propuestas precedidas de '\item', puesto que el entorno LaTeX del que acabarán formando parte es un entorno lista. Esta última complicación se puede evitar fácilmente de forma que al usuario le baste introducir cada propuesta como un línea única (sin el \item). En tal caso, el \item debería añadirlo el propio guión, justo después del cat, con una instrucción como ésta:

sed -i 's/^\(.*\)/\\item \1/' trabajo_tmp.tex

Que la versión presentada en el primer capítulo de esta serie no contenga la simplificación aludida aceleró unos segundos la redacción del guión, aunque a expensas de complicar en parte la interfaz del usuario. Es lo que tiene el modo de obrar "rápido y sucio".

Queda, finalmente, añadir la sección relativa a las propuestas en nuestra plantilla:

...
\begin{document}
\begin{datosAlumno}
Alumno: ALUMNO & CURSO
\end{datosAlumno}
\begin{Objetivos}
OBJETIVOS
\end{Objetivos}
\begin{Contenidos}
CONTENIDOS
\end{Contenidos}
\begin{Propuestas}
RECUPERACIÓN
\end{Propuestas}

\end{document}

A estas alturas, el lector no debería hallar dificultad alguna en realizar por su cuenta las modificaciones que convengan y volver a poner a prueba el guión mejorado.

Resumen

  • La orden cat concatena los ficheros que se le proporcionan como argumentos y los imprime en la salida estándar, por defecto, la pantalla. Si no recibe argumentos toma su entrada de la entrada estándar.

  • Una de las aplicaciones comunes de la orden cat es permitir la creación interactiva de un fichero.

martes, 2 de junio de 2009

HAL y la burocracia (VI)

Antes de dar un paso más en la elaboración de nuestro guión, conviene que el lector lo ponga a prueba, si no lo ha hecho ya por cuenta propia.

Creamos, para empezar un directorio informe_suspensos bajo nuestro subdirectorio de guiones:

mkdir ~/guiones/informe_suspensos

Conviene recordar los ficheros recién creados para nuestra prueba [en caso de que el lector no los haya creado todavía, deberá hacerlo ahora con cualquier editor de texto plano]:

  • informe_plantilla.tex (la plantilla para generar el fichero .tex por alumno [Añado un preámbulo adecuado, modifico ligeramente la sección correspondiente a los datos del alumno y suprimo la sección correspondiente a las propuestas, que ha quedado pendiente, como se recordará]):

    \usepackage[spanish]{babel}
    \usepackage[T1]{fontenc}
    \usepackage[utf8]{inputenc}
    \usepackage{bera}
    \usepackage[usenames,dvipsnames]{color}
    \usepackage{framed}
    \usepackage{enumitem}

    \newenvironment{datosAlumno}%
    {\begin{framed}%
    \begin{center}%
    \begin{tabular*}{\textwidth}%
    {@{\extracolsep{\fill}}lr}}%
    {\end{tabular*}\end{center}\end{framed}}

    \newenvironment{Objetivos}%
    {\begin{framed}%
    \section*{Objetivos}%
    \end{framed}%
    \begin{framed}%
    \begin{itemize}[label=\fbox{\textcolor{white}{X}}]}%
    {\end{itemize}\end{framed}}

    \newenvironment{Contenidos}%
    {\begin{framed}%
    \section*{Contenidos}%
    \end{framed}%
    \begin{framed}%
    \begin{itemize}[label=\fbox{\textcolor{white}{X}}]}%
    {\end{itemize}\end{framed}}

    \newenvironment{Propuestas}%
    {\begin{framed}%
    \section*{Propuestas}%
    \end{framed}%
    \begin{framed}%
    \begin{itemize}}%
    {\end{itemize}\end{framed}}

    \begin{document}
    \begin{datosAlumno}
    Alumno: ALUMNO & CURSO
    \end{datosAlumno}
    \begin{Objetivos}
    OBJETIVOS
    \end{Objetivos}
    \begin{Contenidos}
    CONTENIDOS
    \end{Contenidos}

  • 1-ge-objetivos.tex (el fichero con los objetivos del curso 1-ge):

    \item Reconocer la diferencia entre los refranes y el buen decir.
    \item Adquirir el hábito del buen decir.
    \item Refrenar el hábito malsano del refraneo sin ton ni son.

  • 1-ge-minimos.tex: (el fichero con los contenidos mínimos del curso 1-ge):

    \item El refrán y sus muchos peligros.
    \item El decir del caballero versus el decir del bufón.
    \item Síntomas alarmantes de la verborrea refranil.

  • notas: (el fichero con las notas):

    Sancho-Panza:1-ge:3

  • generar_informes: (el guión propiamente dicho [Unimos lo que obtuvimos en el análisis de datos y en el esbozo del modelo simple, con las comillas corregidas]):

    # Directorio que contiene la programación del curso
    PROGRAMACION="$HOME/guiones/informe_suspensos"

    # Fichero que contiene la plantilla LaTeX del informe
    PLANTILLA=informe_plantilla.tex

    # Fichero que contiene las notas de los alumnos
    NOTAS=notas

    # Sufijo de los ficheros de objetivos por cursos
    OBJETIVOS=objetivos.tex

    # Sufijo de los ficheros de contenidos por cursos
    CONTENIDOS=minimos.tex

    # La nota mínima para aprobar el curso
    NOTA_MINIMA=5

    # El nombre del alumno y curso con el formato que tendrán en la salida impresa
    ALUMNO_OUTPUT=$(cut -d':' -f1 $NOTAS | sed -e 's/-/ /g')
    CURSO_OUTPUT=$(cut -d':' -f2 $NOTAS | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
    -e 's/ge/EE/' \
    -e 's/gm/EP/')


    sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
    -e "s/CURSO/$CURSO_OUTPUT/" \
    -e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
    -e 's/CONTENIDOS/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

    pdflatex Sancho-Panza.tex

(Nótese que en este último fichero hemos modificado la variable PROGRAMACIÓN para que tenga en cuenta la nueva ubicación de nuestros ficheros de objetivos y mínimos, aunque no la vayamos a usar en las órdenes que hemos producido hasta ahora. También hemos añadido una línea al final, para que, al ejecutarse el guión, pdflatex procese el fichero .tex resultante de la orden sed que le precede.)

Todos estos ficheros deben estar ahora bajo el subdirectorio $HOME/guiones/informe_suspensos

Queda cambiar el modo del guión:

chmod u+x informe_suspensos

y ejecutarlo:

./informe_suspensos

El resultado, aparte de otros ficheros temporales generados por pdflatex, es el fichero Sancho-Panza.pdf, que, en un lector de pdfs como okular, se muestra con el siguiente aspecto:

HAL y la burocracia (V - las comillas)

Como advertíamos ayer, por los pequeños detalles merodea el diabólico encantamiento. Para desenmascararlo, vamos a realizar unos pocos experimentos.

En primer lugar, crearemos un variable y le daremos como valor una cadena:

ESCUDERO='Sancho Panza'

Nótese que las comillas son aquí necesarias. De no haberlas puesto, el espacio, un carácter especial en la lengua de HAL (el delimitador que sirve, por ejemplo, para determinar donde termina una orden y empiezan sus argumentos) hubiese sido interpretado por HAL como un separador. Más exactamente, si hubiésemos escrito:

ESCUDERO=Sancho Panza

HAL hubiese creado la variable ESCUDERO con el valor Sancho y hubiese tratado de ejecutar la inexistente orden Panza, con el consabido resultado de command not found. Ni siquiera la variable ESCUDERO hubiese retenido su valor, Sancho, porque, como sabemos desde hace mucho, la definición de variables en la misma línea y justo delante de una orden sólo afecta a la ejecución de esa orden.

Por tanto, las comillas en la anterior definición tienen una función importantísima, permitir que el espacio se interprete literalmente y no como un signo con un valor sintáctico especial. Lo que, en ese ejemplo concreto, implica que el valor de la variable ESCUDERO será Sancho Panza (espacio incluido), como podemos comprobar con un simple:

echo $ESCUDERO

que devuelve:

Sancho Panza

¿Hubiese sido diferente utilizar dobles comillas en lugar de comillas simples?

Lo probamos:

CABALLERO="Don Quijote"
echo $CABALLERO

Que devuelve, de forma semejante:

Don Quijote

Parece, pues, que no hay ninguna diferencia entre las comillas simples y las dobles comillas, que es cosa más de gusto que de gramática. Pero eso, justamente, es lo que quieren que creamos los pérfidos encantadores. De hecho, nosotros hemos utilizado hasta ahora ambos tipos de comillas sin mayor cuestionamiento. Pero las apariencias engañan. Veámoslo:

echo 'El compañero de $CABALLERO es $ESCUDERO'

produce:

El compañero de $CABALLERO es $ESCUDERO

Es algo que deberíamos haber esperado. Las comillas preservan también aquí el significado literal del carácter '$' y evitan que se interprete como un signo sintáctico especial, a saber, el de proporcionar el valor de la variable que le sigue.

Sin embargo,

echo "El compañero de $CABALLERO es $ESCUDERO"

devuelve, sorprendentemente:

El compañero de Don Quijote es Sancho Panza

La conclusión de nuestro experimento parece obvia, las comillas simples son rejas más seguras que las dobles. En general, de las comillas simples ningún carácter se evade de ser interpretado literalmente, mientras que hay caracteres especiales que no se doblegarán a las comillas dobles y seguirán exigiendo ser interpretados como tales signos sintácticos especiales. Entre estos pocos signos que han conseguido la prevalencia de su característica identidad sintáctica está nuestro querido '$'.

Lo que tan interesante conocimiento implica para el código propuesto al final del día pasado es que pone a las claras el origen de su error. Recordemos cuál era el código:

sed -e 's/ALUMNO/$ALUMNO_OUTPUT/' \
-e 's/CURSO/$CURSO_OUTPUT/' \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CONTENIDOS/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

Las dos primeras expresiones de la orden sed contienen el signo especial '$'. Ahora bien, la expansión del valor de las variables ALUMNO_OUTPUT y CURSO_OUTPUT no se producirá, puesto que '$' se interpretará literalmente. Acabaremos con un fichero Sancho-Panza.tex con las líneas:

\begin{datosAlumno}
Alumno: $ALUMNO_OUTPUT
Curso: $CURSO_OUTPUT
\end{datosAlumno}

La solución, tras lo aprendido hoy, es bien simple, utilizar dobles comillas en las líneas díscolas:

sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
-e "s/CURSO/$CURSO_OUTPUT/" \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CONTENIDOS/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

Un lector espabilado podría imaginar una solución incluso más sencilla. Ya que las comillas esconden tantas triquiñuelas, ¿por qué no suprimirlas directamente?:

sed -e s/ALUMNO/$ALUMNO_OUTPUT/ \
-e s/CURSO/$CURSO_OUTPUT/ \
...

Desgraciadamente, no funcionaría, aunque sí funcionaría la siguiente solución:

sed -e s/ALUMNO/"$ALUMNO_OUTPUT"/ \
-e s/CURSO/"$CURSO_OUTPUT"/ \
...

Dejamos como ejercicio para el lector la experimentación y comprensión de las causas del fracaso y el éxito de las dos últimas propuestas.

Resumen

  • Las comillas simples permiten que todos los caracteres especiales de la lengua de HAL encerrados en ellas se interpreten literalmente.

  • Las comillas dobles hacen que casi todos los caracteres especiales de la lengua de HAL se interpreten literalmente, salvo algunos pocos (en especial, el carácter '$'), que seguirán manteniendo su peculiar función sintáctica.

domingo, 31 de mayo de 2009

HAL y la burocracia (IV)

Nuestro siguiente paso es imaginar un proceso que, indefectiblemente, nos lleve desde los ficheros de entrada descritos ayer a los informes en pdf requeridos. No es nada de orden diferente a lo que hemos venido haciendo desde el principio de estas conversaciones. En realidad, se trata siempre de lo mismo: pensar en las transformaciones que deben sufrir ciertos datos de entrada para que de ello surja una determinada salida y aplicar las herramientas que HAL pone a nuestra disposición para efectuar dichas transformaciones.

Podemos, pues, como primer acercamiento, aplicar sin miedo el principio explicado hace tiempo: reducir el problema a un caso muy simple y crear un modelo de solución para ese caso.

Asumamos que, en lugar de diferentes informes para múltiples alumnos, queremos generar un único informe para un único alumno ---que sabemos suspenso de antemano. Dicho de otra forma, supongamos que nuestro fichero notas contiene esta única entrada:

Sancho-Panza:1-ge:3

Supongamos, además, que los únicos ficheros de objetivos y contenidos mínimos con los que contamos son ---¡bendita la suerte!--- los de 1-ge, es decir:

1-ge-objetivos.tex
1-ge-contenidos.tex

Supongamos, finalmente ---y para hacer más divertido el ejemplo--- que el contenido de tales ficheros es el siguiente:

  • Contenido de 1-ge-objetivos.tex

    \item Reconocer la diferencia entre los refranes y el buen decir.
    \item Adquirir el hábito del buen decir.
    \item Refrenar el hábito malsano del refraneo sin ton ni son.

  • Contenido de 1-ge-minimos.tex

    \item El refrán y sus muchos peligros.
    \item El decir del caballero versus el decir del bufón.
    \item Síntomas alarmantes de la verborrea refranil.

Para generar con pdflatex un pdf como el que se muestra en la imagen propuesta en el primer artículo de esta serie ---actualizado con los objetivos y contenidos que acabamos de presentar---, deberíamos disponer de un fichero .tex, con un aspecto semejante al siguiente [El preámbulo se omite, y se presupone, sin mayor explicación, que los entornos Objetivos, Contenidos y Propuestas han sido definidos en dicho preámbulo como tipos especiales de listas. (Ver este artículo para refrescar la memoria sobre los entornos y listas en LaTeX)]:

\begin{document}
\begin{datosAlumno}
Alumno: Sancho Panza
Curso: 1.º EE
\end{datosAlumno}

\begin{Objetivos}
\input{1-ge-objetivos}
\end{Objetivos}

\begin{Contenidos}
\input{1-ge-minimos}
\end{Contenidos}

\begin{Propuestas}
???
\end{Propuestas}
\end{document}

Compárese con la plantilla redactada el día pasado:

\begin{document}
\begin{datosAlumno}
Alumno: ALUMNO
Curso: CURSO
\end{datosAlumno}

\begin{Objetivos}
OBJETIVOS
\end{Objetivos}

\begin{Contenidos}
CONTENIDOS
\end{Contenidos}

\begin{Propuestas}
RECUPERACIÓN
\end{Propuestas}
\end{document}

Resulta evidente, que salvo para el caso de las propuestas de recuperación [en púrpura], que discutiremos más adelante, nuestro trabajo, esto es, el trabajo que HAL debería realizar para obtener el fichero .tex del informe de Sancho Panza, digamos, Sancho-Panza.tex, es simplemente sustituir lo que en la plantilla aparece en rojo por lo que, en el mismo color, debe aparecer en el citado informe.

Representemos en lengua de HAL las sustituciones deseadas:

sed 's/ALUMNO/Sancho Panza/' informe_plantilla.tex > Sancho-Panza.tex
sed 's/CURSO/1.º EE/' informe_plantilla.tex > Sancho-Panza.tex
sed 's/OBJETIVOS/\\input{1-ge-objetivos}/' informe_plantilla.tex > Sancho-Panza.tex
sed 's/CONTENIDOS/\\input{1-ge-minimos}/' informe_plantilla.tex > Sancho-Panza.tex

Si utilizamos la opción -e de sed podemos enviar varias órdenes, una tras otra, a sed y reducir la redundancia en las líneas anteriores. Podemos, además, referirnos al fichero que contiene la plantilla a través de la variable PLANTILLA que con ese propósito creamos el día pasado:

sed -e 's/ALUMNO/Sancho Panza/' \
-e 's/CURSO/1.º EE/' \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CONTENIDOS/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

Mientras que las dos últimas ordenes de sustitución de la instrucción anterior son válidas tal cual, las dos primeras exigen un tratamiento especial. Las cadenas de caracteres "Sancho Panza" y "1.º EE" no forman parte de nuestros datos de entrada. Debemos, pues, obtenerlas a partir de ellos mediante alguna clase de manipulación. El nombre y curso del alumno, como ya hemos comentado, forman parte del contenido del fichero notas, concretamente, son su primer y segundo campo, respectivamente, donde cada campo está separado por el carácter ':'. Para una operación de extracción de este tipo podemos utilizar, como sabemos, una orden como cut. Así, el campo del nombre se obtendría mediante la orden:

cut -d':' -f1 notas

O, aplicando la variable NOTAS, tal como hemos hecho con la variable PLANTILLA en la instrucción sed anterior:

cut -d':' -f1 $NOTAS

Mutatis mutandis, la siguiente orden extrae del fichero notas el campo relativo al curso:

cut -d':' -f2 $NOTAS

Ahora bien, el contenido de estos campos no es exactamente el que buscamos. El contenido es, respectivamente, Sancho-Panza y 1-ge, pero en el informe deseamos que, en lugar del guión, sea un espacio el que separe nombre y apellido del alumno y que, en lugar de 1-ge aparezca la expresión 1º. EE como identificador del curso ('EE' y 'EP' están por enseñanzas elementales y profesionales, nueva denominación oficial de lo que hace pocos años se denominaban grado elemental y medio, para inri del hacedor de guiones).

sed puede ayudarnos de nuevo a la hora de efectuar las conversiones aludidas.

's/-/ /g'

sustituiría los guiones por espacios en la línea de entrada.

Por su parte la más compleja serie de órdenes

's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/'
's/ge/EE/'
's/gm/EP/'

convertiría 1-ge en 1.º EE [La tercera sustitución, que convierte gm en EP no es necesaria en el caso simple actual, pero la incluimos ya desde ahora mismo con el fin de evitar referirnos a un proceso tan elemental de sustitución en explicaciones posteriores].

Las dos últimas órdenes de esta operación de conversión son obvias, pero la primera puede resultar algo difícil de entender a simple vista. Valga su explicación de repaso de algunas minucias de sed.

Cualquier serie de números que aparezca en la línea de entrada encajará con la expresión '[[:digit:]]*' y cualquier serie de caracteres con la expresión '.*'. Por tanto, el molde '[[:digit:]]*-.*' nos permitirá "cazar" cualquier serie de caracteres constituida por cualquier sucesión de números, seguida de guión y seguido éste último por cualesquiera otros caracteres. En consecuencia, algo como nuestra cadena "1-ge" encajará con ese molde. Puesto que, en la sustitución, queremos reutilizar, por un lado, el número inicial y, por otro, lo que sigue al guión para su posterior conversión mediante las órdenes de sed subsiguientes, guardamos lo que encaje con ambas expresiones en registros temporales, esto es, encerramos entre paréntesis convenientemente escapados los correspondientes moldes, y aplicamos, luego, en el patrón de sustitución, las construcciones especiales \1 y \2 para recuperar lo guardado en dichos registros. El resto es también trivial, el guión acaba sustituido por ".º ", donde el punto debe escaparse, al tratarse de una carácter especial de sed.

Si reunimos todas las piezas, los cut que extraen el nombre y curso del alumno, tal como aparecen en el fichero notas, y los sed que convertirían tales datos al formato que tendrán en el fichero Sancho-Panza.tex de salida, obtenemos estas dos órdenes complejas:

cut -d':' -f1 $NOTAS | sed -e 's/-/ /g'

que devolverá:

Sancho Panza

y la más engorrosa:

cut -d':' -f2 $NOTAS | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/'

que devolverá:

1.º EE

El paso final está a un tiro de piedra. El resultado de estas dos órdenes debe aparecer como segundo miembro en las instrucciones sed de sustitución propuestas al principio. Este resultado se puede obtener mediante el recurso sintáctico de la sustitución de órdenes. O sea, que nuestro esbozo:

sed -e 's/ALUMNO/Sancho Panza/' \
-e 's/CURSO/1.º EE/' \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CONTENIDOS/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

debe completarse como es debido de la forma siguiente:

sed -e 's/ALUMNO/$(cut -d':' -f1 $NOTAS | sed -e 's/-/ /g')/' \
-e 's/CURSO/$(cut -d':' -f2 $NOTAS | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')
/' \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CURSO/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

¡Buf! algo difícil de digerir de una tacada. Conviene reducir la complejidad a la vista mediante el uso de variables cuyos valores sean las sustituciones de órdenes que acabamos de encajar con calzador:

ALUMNO_OUTPUT=$(cut -d':' -f1 $NOTAS | sed -e 's/-/ /g')
CURSO_OUTPUT=$(cut -d':' -f2 $NOTAS | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')

Lo que permite obtener esta expresión final algo más digerible:

sed -e 's/ALUMNO/$ALUMNO_OUTPUT/' \
-e 's/CURSO/$CURSO_OUTPUT/' \
-e 's/OBJETIVOS/\\input{1-ge-objetivos}/' \
-e 's/CONTENIDOS/\\input{1-ge-minimos}/' $PLANTILLA > Sancho-Panza.tex

No estáría mal para un entremés, Sancho amigo, si no fuera porque contiene un grave error. Y es que no hay que descuidarse de los encantamientos que, agazapados en nuestros más inocuos utensilios de cocina, pueden amargar hasta la olla mejor cocinada.

sábado, 30 de mayo de 2009

HAL y la burocracia (III - los datos)

Una de las primerísimas cosas que hacemos cuando escribimos un guión para HAL es el análisis de los datos que queremos procesar y de los datos resultantes. En guiones sumamente simples, como los escritos hasta ahora, este análisis se suele realizar de una manera inconsciente. Cuando el guión es algo menos simple resulta casi siempre necesario y, en general, más que recomendable, plantearse conscientemente con qué clase de cosas nos vamos a ensuciar las manos. En guiones complejos, un análisis precipitado conduce, en el mejor de los casos, a un diseño ineficaz y, en el peor, a un resultado infumable.

El guión que nos ocupa es simple y no hace falta demorarse demasiado en esta fase inicial. Por otra parte, hay que buscar un equilibrio entre lo que deseamos hacer y el tiempo que deseamos dedicar a hacerlo. Para un asunto engorroso e impremeditado, como el que seguramente supondría para cualquiera la escritura del actual guión, puede ser suficiente una solución relativamente rápida y sucia. De hecho, el guión propuesto es realmente rápido y sucio. Se puede (¿se debe?) mejorar en muchos sentidos. Pero el tiempo apremia y las ganas son pocas. No obstante, e incluso desde estas premisas, no conviene infringir de entrada absolutamente todas las normas de buen comportamiento. Al menos, veamos por el rabillo del ojo qué cosas están presentes y cuáles aparecerán en nuestra caótica mesa.

Empecemos por lo que queremos obtener. Se recordará de la anterior entrada que lo que se nos pide es un informe por alumno (por cada alumno suspenso). Un fichero pdf por alumno es lo más aconsejable en este caso para obtener una copia impresa, y si además lo generamos vía pdflatex, mejor que mejor.

¿Con qué contamos para poder generar esta serie de ficheros?

Contamos, naturalmente, con la programación de la asignatura. Por suerte, esta programación ---un escrito tan largo como aburrido, plagado de repeticiones internas---, ha sido preparado con LaTeX y está dividido en ficheros independientes que luego se componen en un fichero único. Esta técnica de escribir documentos largos, que tantos quebraderos de cabeza supone para los usuarios de procesadores de textos, es pan comido en el mundo de TeX con una instrucción LaTeX como \input{fichero} o \include{fichero}, que permite incluir el contenido de un fichero dentro de otro. Por tanto, disponemos, para empezar, de ficheros .tex adecuadamente nombrados. Así, por ejemplo, los ficheros que definen los contenidos mínimos por curso, poseen la denominación genérica: curso-nivel-mínimos.tex, donde curso está por 1, 2, 3, etc, y nivel está por ge o gm, abreviaturas de la denominación oficial, en el momento en que se redactó la programación, del grado o nivel de los cursos correspondientes ("grado elemental" y "grado medio", respectivamente).
La sintaxis interna de cada uno de estos ficheros es la que cabe esperar de un fichero LaTeX para listas, en concreto, un texto etiquetado con las siguientes etiquetas, como mínimo:

\begin{itemize}
\item Texto del primer item
...
\end{itemize}

Lo segundo con lo que contamos es con un fichero de texto plano que contiene las notas de los alumnos, el fichero notas. Dicho fichero está compuesto por líneas, cada de una de las cuales es un registro por alumno de la forma:

alumno:curso:nota

y donde, a su vez, el nombre del alumno, primer campo del fichero, responde a la convención sintáctica

Nombre-Apellido1-Apellido2

En concreto, nuestro fichero notas es el siguiente:

Don-Quijote-de-la-Mancha:6-gm:10
Sancho-Panza:1-ge:3
Sansón-Carrasco:3-gm:4
Dulcinea-del-Toboso:1-gm:5

Casi sobra insistir en la decisiva importancia que tiene seguir convenciones sintácticas uniformes en ficheros de texto procesables por HAL, como el descrito.

¿Son estos ficheros suficientes para producir la deseada salida en pdf? Evidentemente, no. Puesto que necesitamos un fichero distinto para cada alumno, debemos crear una plantilla común para todos ellos que se llene con los datos específicos de cada alumno en particular. Puesto que, además, deseamos recurrir a pdflatex para obtener el pdf de cada alumno, este plantilla tiene que ser, ella misma, un fichero .tex. Podremos, entonces, utilizar la instrucción \input para añadir los ficheros de contenidos u objetivos deseados dentro de esa plantilla y utilizar fragmentos de textos apropiados para designar los campos variables en ella. Una plantilla con estas características, que se amolda al documento de salida requerido tendría la forma siguiente:

\begin{document}
\begin{datosAlumno}
Alumno: ALUMNO
Curso: CURSO
\end{datosAlumno}

\begin{Objetivos}
OBJETIVOS
\end{Objetivos}

\begin{Contenidos}
CONTENIDOS
\end{Contenidos}

\begin{Propuestas}
RECUPERACIÓN
\end{Propuestas}
\end{document}

Se habrá observado que hemos omitido, para abreviar, el preámbulo del documento LaTeX, donde, además, se tendrían que incluir las definiciones apropiadas de los entornos datosAlumno, Objetivos, Contenidos y Propuestas, definiciones que se establecen mediante la instrucción \newenvironment, que hace las veces de \newcommand, pero en la definición de entornos personalizados, y que no vamos a comentar ahora por tratarse de cuestiones específicas de LaTeX que nos desviarían de nuestro objetivo actual.

En resumen, los datos de entrada imprescindibles de nuestro guión, lo que HAL utilizará para obtener los pdfs, son los siguientes ficheros:

  • Ficheros .tex de los objetivos y mínimos por cursos, ubicados en el directorio que contiene la programación (/home/luis/conservatorio/programacion-loe):

    1-ge-objetivos.tex
    2-ge-objetivos.tex
    ...
    1-gm-objetivos.tex
    2-gm-objetivos.tex
    ...
    1-ge-minimos.tex
    2-ge-minimos.tex
    ...

  • El fichero informe_plantilla.tex que contiene la plantilla para generar los informes por alumno y que reside en el mismo directorio que contiene el guion.

  • El fichero de texto notas que contiene registros por alumno con el formato alumno:curso:nota, ubicado también en el directorio donde reside el guión.


Tras el análisis de los datos de entrada, podemos proceder a indicárselos a HAL:

# Directorio que contiene la programación del curso
PROGRAMACION=/home/luis/conservatorio/programacion-loe

# Fichero que contiene la plantilla LaTeX del informe
PLANTILLA=informe_plantilla.tex

# Fichero que contiene las notas de los alumnos
NOTAS=notas

# Sufijo de los ficheros de objetivos por cursos
OBJETIVOS=objetivos.tex

# Sufijo de los ficheros de contenidos por cursos
CONTENIDOS=minimos.tex

A éstas variables podemos añadir la que corresponde a la nota mínima que debe obtener el alumno para aprobar el curso, un valor que, sin lugar a dudas, tendremos que utilizar en algún momento:

# La nota mínima para aprobar el curso
NOTA_MINIMA=5

Hemos puesto los cimientos. Se observará que estas líneas reproducen tal cual las líneas iniciales del guión completo presentado el día pasado, con la excepción de la variable NOLATEX, cuyo sentido se explicará a su debido tiempo.

Resumen:

  • El análisis de los datos de entrada de un guión es un requisito previo imprescindible en la elaboración de guiones o programas.

  • Los nombres de ficheros de entrada y de los directorios donde residen son candidatos ideales a aparecer como valores de las variables iniciales de un guión.

HAL y la burocracia (II)

El porqué y el para qué del guión propuesto el día pasado es bien simple. A HAL, por descontado, no le interesa lo más mínimo, porque él opera en un nivel de abstracción de orden radicalmente distinto al nuestro, un nivel en el que es totalmente indiferente el asunto sobre el que va a girar nuestro guión del caso. Pero a nosotros, los que leemos o escribimos el guión, nos resulta indispensable, como a cualquier buen novelista, tener algo más que una noción ligera de la materia sobre la que vamos a tratar. En terminología técnica, el asunto sobre el versa en general un programa ---y un guión es un miniprograma, como ya hemos visto--- se suele denominar su dominio.

El dominio de nuestro guión, como resulta fácil de deducir por el nombre dado a algunas de sus variables, tiene que ver con la enseñanza y, en concreto, con el proceso que los pedagogos llaman evaluación. Hasta ahí todos tenemos idea de qué va: un alumno tiene una nota, resultado de su evaluación. El alumno puede aprobar o suspender. En nuestro país la nota mínima suele ser el 5, aunque sobre eso hay discusiones, a veces enconadas ---particularmente, entre alumnos o padres, de un lado, y profesores, del otro---, en las que no vamos a entrar.

Hasta aquí nada nuevo bajo el cielo, ha sido así toda la vida. Pero lo que quizá algunos lectores no sepan es que, una vez que los psicopedagogos y otros teóricos decidieron tomar cartas en el asunto educativo ---con los resultados que todos conocen y que tampoco vamos a comentar en este momento---, se ha producido una proliferación imparable de tecnicismos y de sus correspondientes encarnaciones escritas, que incluyen toda clase de informes, documentos y estadísticas con nombres de significado difuso o simplemente ininteligible y funciones imposibles de describir por una mente como la del autor. En este abigarrado universo lingüístico-burocrático se desenvuelven cotidianamente una parte de las actividades de la comunidad educativa, y cómo no, el procedimiento de evaluación, o séase, el acto de poner el aprobado o el suspenso al alumno.

Como no merece la pena extenderse en el asunto más de lo debido, basten unas mínimas aclaraciones, las suficientes para poder comprender el dominio de nuestro guión.

Las legislaciones educativas del tiempo presente establecen que el contenido de la enseñanza sea programado ---y que se redacte la correspondiente "programación"--- de acuerdo a unos apartados generales definidos por defecto, entre los cuales se encuentran los "objetivos" y los "contenidos", que vienen a ser algo así como la cuenta al detalle en forma de elementos de una lista, de lo que se va a enseñar ---como si esto fuera posible. Dentro de los contenidos, están los "mínimos", que definen, dicho en castizo, lo que el alumno debe saber si no quiere romperse los codos en verano.

Desentrañar la diferencia existente entre "objetivos" y "contenidos" rebasa la competencia lingüística y probablemente mental de la mayoría, incluido el autor, que da por inútil e improcedente el intento de acometer tal empresa.

Pues bien, resulta que de un tiempo a esta parte, cuando un alumno suspende, el profesor está obligado a confeccionar un informe en el que consten, por este orden:

  1. Los "objetivos" que no ha superado.

  2. Los "contenidos" (mínimos) que tampoco ha superado.

  3. Lo que, en concreto, tiene que hacer para superar o "recuperar" dichos "objetivos"-"contenidos" ---y no cuela lo de "estudia, amigo, más te vale".


Si con este conocimiento del dominio volvemos a mirar el código de nuestro guión, veremos que las variables ya tienen todas un significado claro. Del contenido de esas variables, en otras palabras, de los datos concretos ---el papeleo existente y consiguiente--- que vamos a manejar, trataremos en la próxima conversación. Por cierto, que entre tanta pedagogía de nuevo cuño, HAL ha quedado mudo en ésta :-(

Resumen:

  • El conocimiento del dominio sobre el que versa un programa o un guión es un requisito fundamental, previo a cualquier intento de escritura.

domingo, 24 de mayo de 2009

HAL y la burocracia (I)

Entre los muchos impedimentos que han hecho imposible al autor de estos artículos mantener el frenético ritmo de escritura de meses pasados se cuentan las obligaciones laborales, algunas de las cuales han resultado, sin embargo, menos onerosas gracias a la ayuda impagable del propio HAL.

¿Por qué no regresar, al menos provisionalmente, al recuento de estas amigables conversaciones justo para demostrar la utilidad de HAL en esos menesteres tan potencialmente aburridos como inevitables de las tareas burocráticas, ésas de las que echamos pestes, pero que, sin arreglo posible, consumen nuestro precioso tiempo creativo en lo que parece un irrefrenable y pantagruélico festín del papeleo por todos los ámbitos profesionales?

Sí, abandonamos el terreno de las aplicaciones, lo dejamos pendiente hasta mejor ver, para adentrarnos un poco más en el de la conversación compleja, esto es, la escritura de guiones, cuyo colofón aconteció en esta conversación y en las que la precedieron.

Sin mayor explicación y, sin duda, con brutalidad excesiva, veamos el guión que vamos a obtener a lo largo de los próximos días y su efecto inmediato.

Paciencia. El porqué, el para qué y el cómo se explicarán a su debido tiempo (muy pronto). Pero por un sitio hay que empezar y hacerlo de esta forma tan brusca e inopinada es un manera de comprometer al autor y de evitar que siga enmudecido por tiempo indeterminado.

El guion:

#!/bin/sh

#### Directorios y ficheros
# Directorio que contiene la programación del curso
PROGRAMACION=/home/luis/conservatorio/programacion-loe
# Fichero que contiene la plantilla LaTeX del informe
PLANTILLA=informe_plantilla.tex
# Fichero que contiene las notas de los alumnos
NOTAS=notas
# Fichero sed para limpiar etiquetas LaTeX
NOLATEX=limpiar_tex.sed

#### Datos estáticos (constantes)
# Sufijo de los ficheros de objetivos por cursos
OBJETIVOS=objetivos.tex
# Sufijo de los ficheros de contenidos por cursos
CONTENIDOS=minimos.tex
# La nota mínima para aprobar el curso
NOTA_MINIMA=5

for linea in $(cat $NOTAS)
do
if [ $(echo $linea | cut -d':' -f3) -lt $NOTA_MINIMA ]
then
# Nombre y curso del alumno tal como constan en el fichero $NOTAS
ALUMNO=$(echo $linea | cut -d':' -f1)
CURSO=$(echo $linea | cut -d':' -f2)

# Nombre y curso con el formato que tendrán en la salida impresa
ALUMNO_OUTPUT=$(echo $ALUMNO | sed -e 's/-/ /g')
CURSO_OUTPUT=$(echo $CURSO | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')

# Ficheros que contienen los objetivos y contenidos del curso
# en que el alumno está matriculado
F_OBJETIVOS=${PROGRAMACION}/${CURSO}-$OBJETIVOS
F_CONTENIDOS=${PROGRAMACION}/${CURSO}-$CONTENIDOS

# Los objetivos y contenidos en un formato apto para la plantilla
# a partir de la que se genera el informe
sed -f $NOLATEX $F_OBJETIVOS > objetivos_tmp.tex
sed -f $NOLATEX $F_CONTENIDOS > contenidos_tmp.tex

# Las propuestas de trabajo se deben introducir interactivamente
echo "Introduzca propuestas de trabajo para $ALUMNO_OUTPUT [\item ... (^D para salir)]: "
cat > trabajo_tmp.tex

# Construcción del fichero .tex del informe a partir de la plantilla
sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
-e "s/CURSO/$CURSO_OUTPUT/" \
-e 's/OBJETIVOS/\\input{objetivos_tmp}/' \
-e 's/CONTENIDOS/\\input{contenidos_tmp}/' \
-e 's/RECUPERACIÓN/\\input{trabajo_tmp}/' $PLANTILLA > ${ALUMNO}.tex

# Generación del pdf del informe e impresión
pdflatex ${ALUMNO}.tex > /dev/null
# okular ${ALUMNO}.pdf 2> /dev/null &
lp ${ALUMNO}.pdf > /dev/null

# Eliminación del los ficheros temporales creados
rm *_tmp.tex
rm $(ls ${ALUMNO}* | grep -v '\.pdf')
fi
done

Un fragmento del resultado: