https://www.gsuiteparaeducacion.tk/2019/02/una-aplicacion-web-de-consulta-de-notas.html
Una aplicación web de consulta de notas con Google Apps Script
Un artículo más en este espacio y una nueva oportunidad para hablar de Google Apps Script (GAS, en adelante), un tema que este curso se ha convertido en mi obsesión particular. Ojalá todas (las obsesiones) fueran tan productivas e interesantes.
En esta ocasión voy a hablar de las aplicaciones web o webapps, un tipo particular de scripts que abren un amplio horizonte de posibilidades por lo que hace al uso de documentos nativos de Google en entornos educativos (y, por supuesto, también de otro tipo) y a su integración con, oh sorpresa, elementos web.
A diferencia de los scripts tradicionales, que han sido protagonistas de alguna que otra publicación en este espacio y también en mi reciente sitio web TIC_Tools, al que aprovecho para invitarte, las webapps disponen de una interfaz HTML accesible directamente a través de una URL más o menos convencional utilizando un navegador de Internet. No se trata, por tanto, de guiones inevitablemente asociados e invocables a través del correspondiente menú de un documento nativo de Drive (aunque también pueden serlo) o de elementos instalables sobre él a modo de complemento, sino de auténticas aplicaciones web (de ahí su nombre) que, sin embargo, pueden manipular los objetos y datos expuestos por la interfaz de programación de Google Apps Script. O lo que es lo mismo, acceder a la información almacenada en nuestros documentos de texto, hojas de cálculo, presentaciones, formularios y otros servicios de Google.
De hecho, después de pasarme el último año aprendiendo Google Apps Script siguiendo el camino estrictamente práctico del que va montando cosas, y publicar hace unas semanas mi primer complemento en la tienda de Google, Form Response Control, el siguiente paso natural era abordar el proceso de creación de una de estas webapps. Sí, el aprender haciendo no es solo para nuestros alumnos.
Form Response Control en la tienda de Google. No, esa única valoración no es mía. |
¿Por qué una webapp?
Vale, así que una webapp. Pero, ¿para qué? Y más importante... ¿por qué?
La semana que viene se celebran en los centros, públicos y privados, de Educación Secundaria y Formación Profesional de la Comunidad Valenciana unas pruebas oficiales cuya superación permite a sus alumnos obtener el certificado de competencia de nivel básico (A2 MCER) de inglés, francés, alemán o italiano.
En mi centro venimos gestionando la inscripción de nuestros alumnos mediante un sencillo formulario de Google, cuyas respuestas (solicitudes) se vuelcan en una hoja de cálculo asociada. En una segunda pestaña de esta hoja de cálculo se registran, tras las pruebas, las puntuaciones de cada candidato, que mediante el conocido complemento formMule, son posteriormente comunicadas por correo electrónico. Y fin de la historia. Como ves, un flujo de trabajo muy G Suite... bueno, bonito y barato.
No obstante, para las de este año me planteé montar un tinglado que posibilitara que cada alumno pudiera consultar sus notas directamente a través de la web del centro, previa introducción de algún tipo de información de autenticación como por ejemplo DNI y correo electrónico utilizado para realizar la inscripción.
Como seguramente sepas, una hoja de cálculo, e incluso un rango específico en ella, pueden ser fácilmente incrustados en una web (no necesariamente creada con Google Sites). El problema surge cuando queremos limitar el acceso de un usuario concreto únicamente a sus propios datos (resultados, en este caso) personales. Si bien es posible controlar de modo muy granular qué secciones de una hoja de cálculo son editables por los distintos colaboradores (editores) de la misma, carecemos de un mecanismo análogo para hacer lo propio con la visualización. Es más, las personas que deben acceder a esa información ni siquiera tienen por qué disponer de una cuenta de Google / G Suite.
Para resolverlo tenemos que subir la apuesta.... Y ahí entran las webapps. Veamos un esquema de lo que vamos a construir.
Funcionamiento de la webapp de consulta personal de notas. |
La cosa creo que queda bastante clara en el esquema anterior:
- El candidato introduce sus datos de autenticación en un formulario web.
- Esos datos son recibidos por la webapp, que busca las calificaciones del candidato que queda identificado por ellos en la hoja de cálculo de resultados, que es privada, no está publicada y tan solo compartida con las personas del centro autorizadas que intervienen en la administración de las pruebas.
- La webapp muestra los resultados en el navegador del candidato.
Como puedes intuir, las posibilidades son enormes. El mecanismo genérico descrito permite que el visitante de una página web acceda a información de su expediente académico, administrativo o a cualquier tipo de notificación o información de su interés, sin que sea en absoluto necesario que utilice para el acceso una cuenta específica de Google o G Suite. Se me ocurre que podría emplearse para facilitar información sobre admisión al centro, becas, o cualquier otra información adicional de interés para futuros alumnos, alumnos y familias de un modo sencillo, elegante y mucho más compatible con el Reglamento General de Protección de Datos que la simple publicación de listados en abierto.
Aunque ando en estos momentos dándole vueltas a la creación de una herramienta para generalizar la publicación de cualquier tipo de información del modo descrito, por el momento la idea está en pañales, por lo que si quieres hacer algo parecido a lo que te cuento, pero adaptado a otro contexto, tendrás que seguir leyendo y asumir que ciertos conocimientos de GAS por tu parte son, por ahora, inevitables.
En lo que sigue hay abundante código de copiar & pegar. Pero esto no va de copiar a lo loco, sino de entender el proceso y llegar a ser capaz de adaptar las técnicas descritas a las propias necesidades. No obstante, vaya por delante que esto tampoco es (ni pretende ser) un curso de programación en GAS... quizás en un futuro.
Pero, como dijo Jack el Destripador, vamos por partes.
Pero, como dijo Jack el Destripador, vamos por partes.
La hoja de cálculo es tu base de datos (1 / 6)
Lo primero, claro está, es tener datos que mostrar. En esta hoja de cálculo están los míos. Me los he inventado, naturalmente. Cualquier parecido con personas reales es pura coincidencia. Puedes utilizarla como plantilla y trabajar sobre ella.
Plantilla de HdC de resultados |
Pocas cosas reseñables en su interior, tan solo un QUERY para trasladar cierta información de la hoja de datos conectada con el formulario a la de resultados del modo adecuado, un SI, probablemente simplificable, para calcular la nota final y algún que otro formato condicional.
Este es nuestro "backend", una hoja de cálculo de Google privada. |
Tenemos por tanto nuestra base de datos. Sigamos.
La estrategia satisfactoria (2 / 6)
Hay varias formas de implementar una webapp que resuelva el problema que se nos plantea. Yo voy a describir con cierto detalle una de ellas y conformarme con sugerir la otra, algo más compleja y exigente por cuanto a los conocimientos necesarios de programación en GAS para hacerla tuya.
Empecemos pues con la primera.
Empecemos pues con la primera.
Aunque podemos publicar como webapp un script creado dentro de una hoja de cálculo, yo he preferido optar por uno totalmente independiente para recalcar el hecho de que una de estas aplicaciones web no necesita para nada del andamiaje que le proporciona un documento de Google para existir. Nada de Herramientas → Editor de secuencias de comandos. Hay más razones, pero esta para mi es más que suficiente en estos momentos. Por tanto, nos adentraremos en terreno desconocido y en su lugar haremos otra cosa:
Creando un script independiente. |
¿Sabías que los scripts GAS independientes pueden compartirse directamente con otros usuarios? ¿No? Pues ya lo sabes. Otra razón para usarlos, de modo preferente. Aquí tienes el que nos ocupa.
Script GAS v1 |
Tendrás que hacerte una copia para trastear con él (Archivo → Crear una copia).
Haciendo una copia de un script GAS independiente compartido. |
Vamos a mirar bajo el capó. Comprobarás que el código consta de dos grupos de archivos:
Código.gs: Es el código GAS que realmente resuelve el problema. Como sabemos, Google Apps Script no es más que JavaScript ligeramente tuneado con una API por debajo para interactuar con los servicios de Google.
resultados.html / error.html: Se trata de sendos documentos HTML. Realmente pueden contener tanto código HTML (contenido) como JavaScript (código similar a GAS) o CSS (presentación del contenido ¡y más!). El primero muestra los resultados de la consulta contra la hoja de cálculo. El segundo, mensajes de error apropiados cuando no pueden recuperarse las calificaciones, bien sea porque no se han introducido correctamente los datos de autenticación, bien porque no han sido aún publicadas.
Dentro de un proyecto como el nuestro puede haber varios archivos de cada tipo, que siempre estarán caracterizados por sus respectivas extensiones, .gs o .html.
Pero antes de seguir, reflexionemos sobre estos tres principios fundamentales recogidos en las tablas de la ley de la programación (ya no tan básica) en GAS:
- Los componentes de tipo GAS se ejecutan (procesan) en el contexto de los servidores de Google y, si están debidamente autorizados, pueden acceder a la información contenida en la cuenta de Google / G Suite del usuario.
- Los componentes de tipo HTML se ejecutan, en cambio, en el contexto del cliente, es decir, el navegador de quien utiliza el script, y no pueden acceder directamente a los datos del usuario ni a las variables que manejan los elementos GAS.
- Existen diversos mecanismos de intercambio de información entre ambos contextos, varias técnicas que nos permiten acercar ambos mundos. Cliente y servidor son como Rutger Hauer y Michelle Pfeiffer en Lady Halcón, viven en mundos separados. Afortunadamente, no necesitamos a Matthew Broderick para que se encuentren.
Y sí, el punto 3 es el quid de la cuestión, del que se habla extensamente en la que para mi es una de las secciones clave de la página de soporte de GAS de Google. Pero ya he explicado antes que esto no es un curso de programación, así que tiro adelante comentando la solución adoptada.
Cuando la comunicación es únicamente unidireccional desde el contexto del servidor al del cliente, como es nuestro caso, la forma más inmediata de materializarla es a través de unos artilugios denominados scriptlets. Los scriptlets son secuencias de código GAS que se insertan dentro de los componentes HTML.
La gracia del invento (y también su debilidad) es que estos scriptlets se procesan (evalúan) en el servidor una única vez y su resultado sirve para generar una página HTML de manera dinámica, que es a continuación enviada al navegador (recuerda, cliente) del usuario. Los scriptlets pueden acceder a todo lo que queda al alcance del código GAS del lado del servidor, pero una vez en el contexto del cliente desaparecen de la faz del navegador y de ellos solo queda el resultado de su ejecución.
Bueno, en principio resuelven nuestro problema ¿no? Pues venga.
Entrando en detalles, nuestra primera versión de la webapp será algo como esto:
Entrando en detalles, nuestra primera versión de la webapp será algo como esto:
Primera versión de la webapp, con plantillas HTML y scriptlets explícitos (mi traducción libre de "printing scriptlets"). |
El código (3 / 6)
¿Te has hecho ya una copia del código? ¿No? El Script GAS v1 de más arriba ¿recuerdas?
Espero.
Ábrela en una ventana aquí al lado y vamos comentado cosas. Comencemos por el archivo resultados.html. Lo interesante está al final, en las líneas 30 - 38. Dirígete allí. ¿Ves esos elementos delimitados por símbolos en azul?
Por ejemplo:
<?= notaSpeaking ?>
Eso, amigo o amiga, es un scriptlet. Y además uno de tipo explícito (el término real en inglés es printing scriptlet). Los hay de dos tipos más, pero no nos vamos a meter en ese berenjenal.
Los scriptlets explícitos son una de las formas más rápidas, si no la más rápida, de conseguir inyectar en una página web información que reside en el lado del servidor, donde viven tanto la parte GAS de nuestra webapp como la hoja de cálculo que contiene los resultados. Justo antes de que nuestro código GAS sirva la página al navegador del usuario, estos elementos serán interpretados y sustituidos, directamente, por el valor que representan en el documento HTML.
Puedes echarle también un vistazo a error.html. Seguro que ya no se te escapa su utilidad: mostrar una página con cierto mensaje de error, en lugar de la de resultados, en función de la circunstancia que se produzca. Dado que es más de lo mismo, permíteme que no le dedique más tiempo.
Ahora solo nos queda por resolver cómo asignarles los valores correspondientes a estos scriptlets.
Nos vamos a Código.gs.
Las webapps reciben sus parámetros (datos de entrada) desde un formulario web a través de un mecanismo de tipo GET o POST. Cada método tiene sus ventajas e inconvenientes. Yo he optado en este caso por el primero.
Función de entrada a nuestra webapp. |
Esta elección condiciona el uso de una función doGet() o doPost(). Sí, con esos nombres a piñón fijo. Dicha función será la que primero se ejecutará de entre todas las que formen parte del código de la webapp. Por eso la denominaremos función de entrada.
De un modo u otro, esta función de entrada recibirá un objeto, a modo de contenedor de parámetros, que es el que utilizaremos en este caso para recoger los datos de identificación del usuario. ¿Todas las webapps reciben información de entrada de esta manera? En absoluto, pero la nuestra, con su diseño actual, sí necesita recoger el DNI y el email desde un formulario web. No temas, cerraremos el círculo en el siguiente apartado.
A continuación sigue un fragmento de código en el que se definen ciertas variables importantes utilizadas en el resto de la función:
Variables importantes, siempre a mano. |
En la línea 9 se identifica la hoja de cálculo a través de su ID. Esta secuencia de caracteres se obtiene fácilmente a partir de su URL:
En la línea 10 se determina el nombre de la hoja de datos dentro de la hoja de cálculo. En la 25, por su parte, se obtiene el objeto correspondiente que se utilizará en el código para obtener los datos almacenados en las celdas de la hoja.
Utilizo las líneas 13 - 23 para declarar las posiciones en la tabla de datos de celdas, filas y columnas que contienen información significativa. De este modo dispongo de esta información a la vista en cuanto abro el script, lo que facilitará sin duda cualquier modificación o adaptación del código en otras situaciones. En mi opinión es esta una buena práctica que resulta preferible a enterrar referencias absolutas a las celdas relevantes en diferentes partes del código.
A continuación (línea 29), se comprueba si la información de la hoja de cálculo está publicada, y debe ser facilitada al usuario, o no.
Para ello se verifica el estado de la celda E3 de la hoja de datos Resultados de nuestra hoja de cálculo, que tal vez recuerdes, contenía una casilla de verificación insertada por medio de la función de validación de datos. Un modo extremadamente sencillo de controlar la publicación de los resultados desde el backend por personas que no tienen por qué saber nade de guiones GAS, HTML, formularios, servidores web ni nada que se le parezca.
Para ello se verifica el estado de la celda E3 de la hoja de datos Resultados de nuestra hoja de cálculo, que tal vez recuerdes, contenía una casilla de verificación insertada por medio de la función de validación de datos. Un modo extremadamente sencillo de controlar la publicación de los resultados desde el backend por personas que no tienen por qué saber nade de guiones GAS, HTML, formularios, servidores web ni nada que se le parezca.
Un clic sobre la casilla de verificación es todo lo que se necesita para activar o desactivar la webapp. |
Cositas interesantes. ¿Cómo se leen los parámetros que recibe nuestra webapp desde el código? Por medio del objeto e.parameter, que queda instanciado en la declaración de doGet(e). Este objeto está formado por una secuencia de pares de tipo clave / valor. Cada clave se corresponde con uno de los parámetros de entrada (líneas 33 - 34). ¿Y quién determina el nombre de estas claves? Es una convención que establezco yo mismo en función de la secuencia que he decidido utilizar como sufijo del URL de la webapp. Calma, todo cuadrará en el apartado 5.
Lo que sigue (líneas 36 - 57) es código GAS poco trascendente en el que se tratan de filtrar situaciones que impedirían recuperar la información correspondiente a las calificaciones solicitadas:
- No se han indicado DNI o email.
- El DNI introducido no se encuentra en la hoja de datos de resultados.
- El DNI sí se encuentra pero el email que recibe la webapp no coincide con el registrado en la tabla para ese candidato. Es esta una sencilla comprobación de seguridad adicional, tal vez innecesaria o quizás insuficiente. A valorar.
Las líneas 42 - 43, no obstante, son las que buscan en la tabla los resultados del candidato caracterizado por un DNI e email determinados mediante un sencillo bucle while:
Si el alumno en cuestión está en la tabla y las credenciales introducidas en el formulario son válidas la cosa sigue y, a partir de la línea 58, se pone más interesante.
En este punto es donde se instancian los scriptlets a los valores leídos de la tabla de resultados. Para ello, antes que nada, se construye sobre la variable (objeto) plantillaHTML un objeto de tipo plantilla HTML, utilizando para ello la clase HtmlService y su método .createTemplateFromFile, basado en el código HTML del archivo resultados.html.
¡Ojo! Este método se da un aire al similar .createHtmlOutputFromFile, utilizado habitualmente para desplegar interfaces de usuario HTML en paneles laterales y ventanas modales y no modales. Pero no es igual, precisamente porque entra en juego la evaluación de los dichosos scritplets.
Si el alumno en cuestión está en la tabla y las credenciales introducidas en el formulario son válidas la cosa sigue y, a partir de la línea 58, se pone más interesante.
En este punto es donde se instancian los scriptlets a los valores leídos de la tabla de resultados. Para ello, antes que nada, se construye sobre la variable (objeto) plantillaHTML un objeto de tipo plantilla HTML, utilizando para ello la clase HtmlService y su método .createTemplateFromFile, basado en el código HTML del archivo resultados.html.
¡Ojo! Este método se da un aire al similar .createHtmlOutputFromFile, utilizado habitualmente para desplegar interfaces de usuario HTML en paneles laterales y ventanas modales y no modales. Pero no es igual, precisamente porque entra en juego la evaluación de los dichosos scritplets.
Con el objeto HTML en el bolsillo ahora se trata de inicializar los scriptlets con los valores a mostrar en el documento HTML. Y nuevamente se consigue considerándolos propiedades del objeto plantillaHTML (líneas 59 - 69):
Por ejemplo, el valor que adoptará el scriptlet...
<?= notaSpeaking ?>
...se asigna simplemente mediante la expresión:
plantillaHtml.notaSpeaking = ...
Sorprendentemente fácil, ¿verdad?
Pues ya lo tenemos. El paso final es solicitar que las expresiones de tipo scriptlet introducidas en el archivo HTML sean evaluadas (recuerda, en el contexto del servidor) y se genere una página HTML dinámica, que es la que se desplegará en el navegador del usuario. Esto se consigue en la línea 72 mediante el método .evaluate():
Fíjate en que lo que realmente está sucediendo es que la función doGet() devuelve (return) el objeto (página) HTML que se ha construido a partir de:
- La plantilla HTML.
- Los scriptlets a los que se les han asignado valores desde el código GAS.
En la misma instrucción se encadena el método .setTitle(), que simplemente permite establecer el título de la página HTML que mostrará el navegador.
Lo que queda del código (líneas 74 - 95) solo están ahí para cazar el resto de condiciones de error descritas anteriormente.
El despliegue como webapp (4 / 6)
Ha llegado el momento de ver si esto funciona. Para publicar la webapp hay que dirigirse al comando Publicar → Implementar como aplicación web del editor de scripts.
En definitiva, de lo que se trata es de obtener el URL de la webapp, que introduciremos posteriormente en cualquier navegador para interactuar con ella. En mi caso el botón inferior en la captura de pantalla muestra el texto Actualizar dado que ya ha sido desplegada con anterioridad.
Panel de publicación de la webapp. |
Desde aquí se establece bajo qué identidad se ejecuta la aplicación, la de su creador o la de la persona que accede a ella a través de la web. Y es precisamente este un punto crucial.
Deberemos ajustar la webapp para que se ejecute utilizando la identidad del creador (en este caso yo, claro). De ese modo el código GAS podrá acceder a la hoja de datos, que también será de su (mi) propiedad, y mostrarle los resultados a quienquiera que se identifique ante ella mediante el DNI e email correctos. Como era de esperar, al igual que con cualquier otro script GAS, la aplicación deberá ser autorizada antes de ser usada por primera vez, bien por su propietario cuando se ejecuta bajo el paraguas de su cuenta, bien por cualquiera que acceda a ella, en otro caso.
Por último, también existe un ajuste para limitar el acceso a la webapp, de modo que su uso quede restringido a su creador, a los usuarios del dominio G Suite, a cualquier usuario con cuenta de Google o simplemente pueda ser utilizada públicamente de modo no autenticado.
Cada vez que realicemos cambios en un elemento del proyecto deberemos actualizar el despliegue, obteniendo un nuevo URL en este panel de publicación. Además, para facilitar el proceso de desarrollo, se nos facilitará también otro URL (último código), esta vez totalmente privado y solo accesible para el propietario del proyecto, que siempre conduce a la webapp que ejecuta el código más reciente.
¿Y qué pasará si ahora escribimos el URL en la barra de direcciones de un navegador? Compruébalo tú mismo, que al fin y al cabo esto está ya en el aire.
Webapp is in the air... |
Pues ahí la tenemos, vivita y coleando... pero ¿cómo demonios le hacemos llegar ahora el DNI e email del candidato cuyos resultados deseamos consultar?
Sigue leyendo.
El formulario (5 / 6)
Nuestra webapp recibe sus parámetros por medio de una función doGet(). Esto quiere decir que los toma de su propia URL, concretamente los espera anexados, como un sufijo introducido por el carácter ? y con el símbolo & como separador entre cada uno de ellos, de este modo:
.../exec?DNI=95461354S&EMAIL=al95461354S@micentro.es
Invocando a la webapp con parámetros de modo manual vía GET. |
Pero lógicamente no es aceptable esperar que nadie tenga que urdir URLs de este tipo, así que mejor vamos pensando otra cosa. Y esa otra cosa es un formulario publicado en la web de, por ejemplo, nuestro centro (que en absoluto tiene por qué estar montada sobre Google Sites). El candidato introducirá así comodamente sus datos de identificación. Este formulario invocará al script, publicado como webapp, y le pasara esta información a través del atríbuto action de la etiqueta HTML <form>:
Un sencillo código HTML para un formulario como el descrito podría ser este:
1 2 3 4 5 6 7 | <form action="https://script.google.com/macros/s/AKfycbzlDk0jJzHB5zIeb78PEWRYb_TOi5wY7RfTdDe3c3Oyac_S2Is/exec" method="get" target="_blank"> <label>DNI</label><br /> <input name="DNI" required="" type="text" /><br /><br /> <label>Correo electrónico</label><br /> <input name="EMAIL" required="" type="email" /><br><br> <button>Buscar</button> </form> |
El URL en la línea 1 es el de la webapp, que obtuvimos en nuestra última visita al panel de publicación en el apartado anterior.
Por su parte, a cada campo de texto del formulario le asignamos un nombre (atributo name) idéntico al del parámetro que representa (líneas 4, 7). Esta nomenclatura debe mantenerse intacta hasta el código GAS que captura sus valores por medio de las propiedades correspondientes en la función doGet() de la webapp.
Todo listo por tanto. Puedes probar el formulario, que encontrarás aquí, con los pares (DNI, email) que desees de la tabla de resultados. Por ejemplo, (95461354S, al95461354S@micentro.es).
Probando el formulario y la webapp. |
La estrategia (más) correcta (6 / 6)
Ya has podido comprobar que esto funciona. Y probablemente de modo satisfactorio. Pero se puede mejorar, aunque a costa de complicar ligeramente las cosas.
La principal carencia de la estrategia en que se basa esta primera versión de la webapp reside en el hecho de que el formulario no está integrado dentro de ella.
Además, se usan scriptlets explícitos, que como ya sabemos se evalúan una sola vez y admiten la comunicación en un solo sentido entre los contextos de servidor y cliente, por lo que no es posible hacer cosas más sofisticadas, como por ejemplo incluir un botón para que el usuario solicite que se le envíen las calificaciones por correo electrónico tras consultarlas.
Además, se usan scriptlets explícitos, que como ya sabemos se evalúan una sola vez y admiten la comunicación en un solo sentido entre los contextos de servidor y cliente, por lo que no es posible hacer cosas más sofisticadas, como por ejemplo incluir un botón para que el usuario solicite que se le envíen las calificaciones por correo electrónico tras consultarlas.
¿Y como se podría ir más allá? Prescindiendo de scriptlets y plantillas HTML e implementando en su lugar un mecanismo de comunicación bidireccional y totalmente dinámico entre el lado del cliente y el del servidor, operativo en tiempo de ejecución. Dicho de otro modo, se trata de construir una interfaz HTML mínima con la que el usuario de la webapp pueda interactuar en todo momento.
El diagrama funcional de esta versión 2 sería algo parecido a esto:
Segunda versión de la webapp, con el formulario integrado y comunicación bidireccional entre cliente y servidor. |
Y este es el aspecto que tiene en funcionamiento, ahora ya en mi dominio G Suite:
Versión 2 de la webapp en acción, en el dominio en producción. |
Como probablemente aprecies, el uso de un modelo de comunicación cliente - servidor más potente permite hilar ahora mucho más fino: aparecen distintos mensajes de estado, los elementos interactivos de la interfaz de usuario, cuadros de texto y botones, se activan y desactivan según el contexto, etc.
Además, tenemos una función adicional de envío de emails implementada mediante scriptlets explícitos, lo que en este caso tiene todo el sentido del mundo puesto que se trata de generar un elemento HTML, estático tras su producción, y enviarlo embebido en el cuerpo de un mensaje de correo electrónico.
Aspecto de las notificaciones por email de esta versión. Sí, las notas son distintas, se trata de otra prueba (buena vista). |
Pero voy a dejar para otra ocasión el análisis de los recovecos de esta nueva implementación, entre los que se cuenta el uso de jQuery, librería JavaScript que comencé a utilizar en un desarrollo anterior y para mi ya imprescindible en estos momentos.
Creo que este artículo ya se ha extendido más allá de lo soportable, así que me despido, por ahora, no sin antes agradecerte tu atención y tu paciencia para llegar hasta aquí. Sin duda volveremos a hablar de Google Apps Script en este espacio. Y espero que sea más pronto que tarde.
Como siempre, la caja de comentarios aquí abajo es toda tuya.
Actualización 27/11/21:
Lo cierto es que llevo tiempo ¡años! 😅 con la segunda segunda parte de este artículo pendiente... pero no ha podido ser. Ten en cuenta que escribí esta entrada de blog en febrero de 2019, cuando estaba aprendiendo Apps Script (ojo con el código, que es muy mejorable), y desde entonces ha llovido mucho, pero mucho, y aunque sigo publicando con frecuencia materiales sobre Apps Script en mi perfil de Twitter y en mi otro blog, esto se ha quedado en el cajón de tareas pendientes.
Como no sé si en algún momento este artículo tendrá continuidad, aquí tienes ya no la V2, sino una V3, que además registra en la columna L de la tabla de notas la fecha en la que cada calificación fue consultada por primera vez. Cuidado porque este nuevo campo de información desplaza hacia la derecha la columna de comentarios en la hoja de cálculo que facilitaba como plantilla en el artículo.
Esta versión además es notablemente más rápida dado que lee los datos de la tabla de notas de un modo más eficiente que el utilizado por la V1, que tan solo pretendía mostrar un caso de uso donde una webapp puede resultar de utilidad, sin entrar en excesivas sutilezas con el código.
Estaré encantado de resolver cualquier duda que pueda surgir con el código de esta versión respondiendo a los comentarios de aquí abajo.
Un saludo y gracias por leerme todos estos años.
No hay comentarios:
Publicar un comentario