Este capítulo ha sido realizado sobre el trabajo de Federico Martí, quien ha realizado el esbozo incial y casi todo el código.
Este caso de estudio fue planteado en la la sección de nombre Caso de estudio: una agenda para guardar el historial clínico de pacientes en Capítulo 4 del Capítulo 4. Su planteamiento gira en torno a una pila principal que tendrá una serie de controles (Figura 15-1a), que tomando como base el número de historial (Figura 15-1b), podrá abrir una serie de subpilas que mostrarán contenidios asociados a un paciente (Figura 15-1c), y no todos han de tener la misma estructua (Figura 15-1d). Se quiere así aprovechar ampliar la funcionalidad de la agenda construída en uno de los tutoriales básicos para que incluya elementos audiovisuales. El enfoque constituye una primera aproximación a construir una base de datos con características multimedia. En este caso, de pacientes de un centro hospitalario y puede ser necesario asociar imágenes, vídeos e incluso grabaciones sonoras realizadas durante el tratamiento a los pacientes o como notas que un doctor prefiera dejar así asociadas a un paciente.
Sin más dilación veamos ya como Fede decidió instanciar y resolver este problema: el planteamiento de partida es aprovechar la estructura de la agenda vista en el primer tutorial de MetaCard (véase la la sección de nombre Tutorial sobre aplicaciones en Capítulo 1) para realizar una pila ("Historial Clínico", como la representada en la Figura 15-2a) que tenga campos correspondientes a: Apellidos, Nombre, Hospital, Número de Historia, Navegadores, Buscador y Nuevo Registro. El formato final es el que se puede ver en la Figura 15-2b, donde queda básicamente por implementar la funcionalidad de los botones Numero de historia (que lanzará la subpila de historia del paciente) y el de Datos personales (que mostrará los correspondientes al paciente actual) como se indicaba en la propuesta original y que se puede ver en Figura 15-2c.
Nota: Si su visión del interfaz no coincide con esta, no la reescriba. Recuerde que esta es una posible solución: seguro que existen otras.
De lo expuesto hasta ahora, señalar que el código de los botones de navegación y de búsqueda se ha heredado del tutorial, así que no se harán más comentarios al respecto. En su lugar sí que hay que comentar cómo se soluciona el obtener un identificador único. Le proponemos que considere utilizar el número de la tarjeta y así se ha implementado en el campo de texto n historia (el que está a la derecha del que lleva la leyenda de Número historia), donde se ha utilizado el siguiente código:
# # Campo de texto "n historia" (junto a "Número de Historia") # on mouseUp put the number of this card into field "n historia" end mouseUp
Nota: Una pequeña observación: sería interesante que, automáticamente, apareciese el número de "expediente" que no queremos que se tenga que ocupar el usuario; sobre todo para facilitarnos la eliminación de ellos y que no pueda haber un pequeño despiste y se asigne a más de un usuario un mismo índice de historial.
Se puede conseguir este comportamiento utilizando el código de la pila, en este se pude disponer el código que se habrá de ejecutar cada vez que se transita a una nueva tarjeta. Como hay subpilas, a esta pila le llegarán eventos de que se abre una tarjeta cuando se canvie en aquellas, así que habrá que seleccionar cuando actuar. Por ejemplo, se puede hacer de la forma siguiente:
# # Pila pila "Historial Clínico" # on openCard if (there is a to fld "n historia") then send "mouseUp" to fld "n historia" end openCardDe este manera el código que calcula el valor sigue estando en el objeto donde estaba, se puede cambiar si hace falta y el mecanismo de presentar el valor sigue siendo funcional e independiente.
Las otras dos subpilas necesarias se pueden crear en este momento, aprovechando que estamos decidiendo las cuestiones de interfaz, Siguiendo el método habitual de creación de una nueva pila se puede obtener algo similar a lo que se puede observar en la Figura 15-3.
Figura 15-3. Captura de ventana de la supila "datos personales" (fila superior) y de la subpila "hist" (fila inferior) de la aplicación historial médico.
Recordemos que estas pilas deben estar accesibles desde la inicial. Es decir que, mediante un botón se pueda acceder a la pila que contenga los datos personales y, lo mismo ha de ocurrir con la historia. Debería poner un botón que me dirija a ellas: ¿cómo conseguir esto? Tras indagar por la ayuda (Help) de MetaCard[1], se puede deducir que lo que se debe hacer es crear ambas pilas y, en sus propiedades, decirles que la MainStack o pila principal es la inicial ("Historial Clínico"). Así pues, cree dos pilas una con el nombre de "Datos personales" y otra con el nombre de "Historial".
Una vez creadas las pilas y puesto en sus propiedades que sean subpilas, compruebe que en las propiedades de "Historial Clínico", que aparece como componentes las pilas "Datos Personales" e "Historial". Ahora, habrá que añadir el código correspondiente a los botones para ir hacia estas subpilas en la pila inicial: Datos personales y Abrir Historia.
El botón Datos Personales es el más sencillo: abre la pila correspondiente y avanza hasta la tarjeta que corresponde al paciente que se está considerando en la pila inicial:
# # botón "Datos personales" # on mouseUp put field "n historia" into y go to stack "datos personales" open card y end mouseUp
El otro caso es un poco más complejo, puesto que una persona, puede tener que ir varias veces al hospital, e incluso a diferentes servicios. Para que cada vez sea posible añadir algo en su historial. Así pues, para cada paciente, debería haber una pila, es decir, debían existir tantas pilas como pacientes o números de historia. Por lo que el código es:
# # botón "Abrir Historia" # on mouseUp put field "n historia" into y if exists (stack ("hist"&y) of stack "Historial Clínico") then ask "NO puede generar una historia que ya existe" else clone stack "Historial" set the name of the stack it to "hist"&y set the mainStack of the stack ("hist"&y) to "Historial Clínico" ## send mouseUp to button "crear" of card 1 of stack "datos personales" end if end mouseUp
Hay varias cosas que destacar en este código:
Observe cómo se geneara un nombre a partir de un prefijo constante y un valor varaible con el operando de concatenación:
"hist"&y
La comprobación de que puede o no existir la pila con el historial para un paciente dado.
La utilización de la orden clone para crear, sobre la marcha, una pila con un formato dado y su reconfiguración como subpila.
La forma en que se delega en un botón de la pila "datos personales" para la creació de un nuevo historial, en concreto la parte de datos personales que se ha decidido almacenar todas en una sola subpila, puesto que sólo hay un conjunto (que se muestra en una sóla ficha) de ellos.
El código de ese botón en la primera tarjeta de la pila "Datos personales" sólo tiene que asegurarse de crear una nueva tarjeta. Si se hace después de la última creada puede que parezca más lógico, pues nada tendremos algo como lo que sigue:
# # Botón "crear" # on mouseUp go to last card create card end mouseUp
Ya casi hemos terminado tenemos abierto el camino a la asociación a un paciente de datos textuales (Figura 15-4a), sólo nos queda por resolver la inclusión de los ficheros de imágenes, audio o vídeo. Para ellos crearemos, por ejemplo, una pila para cada tipo en cada paciente y esta que sea el contenedor de todos los ficheros de una de esas tres clases. Vamos a verlo para el caso de las imágenes, con un planteamiento bastante poco elaborado para empezar (algo improvisado como lo de la figura Figura 15-4b).
Después de lo visto, seguro que ya tiene una idea de cómo la pila historial (bueno, realmente la versión clonada que se habrá creado para un paciente dado) puede disponer de botones que lancen las pilas correspondientes a cada medio. Para las imágenes puede ser algo como:
# # botón "Imagen" # on mouseUp put field "n_historia" into y go to stack "imagen"&y end mouseUp
Y para poder incorporar los ficheros, ¿qué mejor que preguntarle al usuario por el nombre del mismo y asignarselo a un objeto de MetaCard? Vale, vale, es una solución un poco simple, pero, ¿ha visto las líneas de código que utiliza? Añadir que para los sonidos y los vídeos deberá utilizar el objeto player, de una forma totalmente equivalente a lo que hemos hecho aquí con el control de tipo image, así que no insisto más. El código que puede implementar esto es algo como:
# # botón "Nueva Imagen" # on mouseUp create Image "new image" answer file "Seleccione un archivo de imagen" if it is not empty then set the cursor to watch set the filename of last image to it end if end mouseUp
¿Funciona, no? Sí pero poco, ya lo se. Lo siento pero es que así puedo ir contando un par de cosas que ya he mencionado y que creo que son interesantes.
La primera es la forma en que se referencian las imágenes. Estamos creando un diminuto sistema de información, está íntimanente ligado la información que se maneja y los ficheros asociados. De hecho, lo habitual es acceder a ellos a través de esta aplicación, recuerde que por eso la estábamos creando. Así que, si es preciso, la aplicación debe encargarse de situarlos en un lugar que los haga fàcilmente accesibles despues (por ejemplo copiándolos en una localización determinada) y con ello tendremos control sobre las rutas ...
La segunda es una cuestión de funcionalidad, el usuario no puede de esta manera explorar las diferentes imágenes, ni tener otra información a ellas asociadas que permita recordar las condiciones en que se obtuvieron. Claro que esto es un extra: a quien encargó la aplicación se le olvidó en el pliego de requisitos, así que va aparte. No es estrictamente necesaria tanta funcionalidad, si me lo permite.
En el presente caso de estudio se ha desarrollado una aplicación capaz de mostrar información de naturaleza diferente de la puramente textual, a partir de la extensión de una aplicación anterior.
Si ha implementado la aplicación conforme hemos ido exponiendola habrá observado que es dudosamente buena la elección del número de identificación a partir del de la tarjeta. Sobre todo si ha intentado borrar un paciente (una tarjeta), esto se ha de extender a todas las tarjetas de las subpilas que corresponda (lo que no es mayor problema). Pero entonces hay que arreglar la inconsistencia que se dará al renumerar las tarjetas con en el consiguiente cruce de datos de pacientes. La solución pasa por elaborar un poco más esta elección de número, sin hacerlo coincidir con el de la tarjeta y olvidarse de que sea el número de historial una correspondencia con el de la tarjeta.
Respecto a las nuevas funcionalidades que se pueden incorporar para que el sistema sea de interés hemos comentado la necesidad de definir una estructura de almacenamiento de la información que permita su fácil recuperación. Pero además también se ha mencionado que el usuario puede querer asignar otras informaciones que no se hayan podido incluir en los campos originales y que son propios de cada fichero. Ejemplos de esto serían el tiempo en que ha tomado la muestra, las condiciones en que se ha tomado, la medicación que se había utilizado, etc.
Además esto permite abordar una cosa importante: nuestro sistema tal como está ahora, es totalmente dependiente del punto donde se encuentra y de aquel donde se encuentran los ficheros. Para que esto no sea así, las rutas deberían ser relativas, por ejemplo, al punto de arranque de la aplicación. De este modo se puede reubicar la misma en otro sistema donde las rutas no coincidan y no sólo me refiero a su configuración de GNU/Linux o de Mac. ¡No lo he podido evitar! :-). Algunas trabas importantes al desarrollo de aplicaciones multiplataforma van de la mano del uso de rutas absolutas, uso de identificadores de unidades de disco, uno de separadores ruta de archivos que sólo son válidos en un sistema operativo, ... Cosas como esta última son sencillas de resolver en MetaCard/Revolution: utilice siempre la barra ("/") y ya se ocupará la herramienta de reescribirla, si es necesario.
Como posible solución a este planteamiento cabe considerar que en el punto de arranque de la aplicación se creen tantos directorios como historiales de pacientes se vayan creando, de manera que para cada uno de ellos se creen tres directorios que contengan los diferentes ficheros organizados por el tipo de media a que pertenecen. Esos directorios por paciente se puede nombrar con el número de expediente que se les ha asignado, así será inmediato localizarlos, modificarlos e incluso borrarlos.
Esto nos llevará a modificar la subpila "imagen" que puede quedar de la forma que muestra la Figura 15-5 y para lo que:
He activado la propiedad de Lock Location de la imagen. De esta forma se redimensiona la imagen al tamaño que hemos asignada al contenedor de tipo imagen. Una política más apropiada hubiera sido mostrarla a su tamaño natural, pero habría que redimensionar la pila o habilitar otro mecanismo para no deformarla. Dejaremos esas modificaciones para el lector en este caso.
El punto donde se da de alta un nuevo historial para un nuevo paciente. Para no interferir demasiado en la estructura creada le propongo que cree nuevos botones que añadan las funcionalidades comentadas. En el else de Abrir Historia añada un
send "mouseUp" to btn "Atres inicialitzacions"
antes de enviar el evento mouseUp al botón "crear" de la pila "Datos personales". En ese botón tendremos:
# # botón "Atres inicialitzacions" # on mouseUp set cursor to busy # Recrear la estructura de directorios create directory y create directory (y & "/Imagen") create directory (y & "/Sonido") create directory (y & "/Video") end mouseUp
A lo que acompañará en la pila de imagen (y análogamente en las otras dos) el manejo de los ficheros, distribuido en la forma siguiente:
El "nuevo" botón de "Nueva imagen", ha de alojar la copia del fichero, asignarle su nuevo nombre (de forma relativa, recuerde) y poner este como propiedad de la imagen.
# # botón "Nueva Imagen" # local idNumero on mouseUp # No hace falta crear varias im´agenes, s´olo una por tarjeta # create Image "new image" answer file "Seleccione un archivo de imagen" if it is not empty then set the cursor to watch # Como el nombre de esta pila es de la forma: imagen<numero_historial> # a partir del caracter 7 empieza el número que identifica el historial ... put char 7 to -1 of the short name of this stack into idNumero set itemDelimiter to "/" # Copia el fichero en su lugar put URL( "file:" & it) into URL("file:" & idNumero & "/Imagen" & last item of it) set the filename of image "Nova Imatge" to (idNumero & "/Imagen/" & last item of it) end if end mouseUp
Mencionar que "Nova Imatge" es el nombre que le hemos dado al control de tipo image que, le recuerdo, ahora es único en cada tarjeta.
Disponer de una forma de grabar el texto que quiera asociar a aquella imagen, se me ocurre que podemos utilizar la tecla Enter y el evento returnInField asociado. De esta forma el usuario puede introducir texto, saltos de línea (con la tecla Intro) y cuando se quiere, simplemente con una tecla se guarda en disco lo que se haya escrito.
# # Campo de texto "Anotacions" # local varAux, idNumero on returnInField if (the filename of image "Nova Imatge" is not empty) then set the cursor to busy put char 7 to -1 of the short name of this stack into idNumero set itemDelimiter to "/" put last item of filename of image "Nova Imatge" into varAux put the htmlText of me into URL("file:" & idNumero & "/Imagen" & last item of it & ".txt") else answer warning "¡No puedo asociar un texto a una imagen si no hay imagen!" & return &\ "Cree primero una imagen con 'Nueva Imagen' y reintételo." end if end returnInField
Nota: De hecho, haciendo esto, se puede empezar a pensar en que no hace falta tanta pila, todos podrían utilizar una única. Sólo sería necesario saber que identificador utilizar para mostrar los contenidos de aquel. Eso y retocar unas veinte líneas de código dispersas en varios controles. Pero, lamentablemente, estas cosas ya no se suelen hacer en este punto. Téngalo presente pues en nuevas realizaciones, esto pertenece a la etapa inicial de diseño.
Un par de botones de navegación y uno que busque en el campo de texto "Anotaciones" redondearían el diseño. Por aquello de hacerlo fácil, se puede agrupar todo en un grupo de clase background y así cada vez que se crea una imagen se creará una nueva tarjeta con toda la funcionalidad esperada.
Por último las pilas de imágenes, sonido y vídeo carecen de opciones que permitan el borrado selectivo o la edición del fichero asignado. Estas extensiones básicas quedan a la consideración del lector para que ponga a prueba su aprendizaje.
[1] | En las MetaClasses encontrará más desglosado este mismo tema de creación y manejo de pilas y subpilas. |