Crear informes automáticos con Data Studio y planillas en la nube

La entrega de informes periódicos es una de las tareas típicas en todos los niveles de organización. La consistencia de los mismos es crucial para evitar que cada informe tengas diferencias que nos hacen dudar del mismo y gastar tiempo en entenderlo en lugar de analizar.

Una buena forma de sistematizar la entrega de informes es utilizar las siguientes herramientas combinadas para automatizar la generación de informes:

  1. Planillas en la nube con una estructura definida de antemano.
  2. Apps Script para importar informacion desde APIs, leer correos desde nuestras casillas en forma automatica o importar desde otras planillas o Excel de sistemas offline.
  3. Data Studio para leer la planilla como si fuera una base de datos.

El primer paso es definir una estructura general de planilla, definiendo las categorias o areas y el periodo de tiempo, por ejemplo un año dividido en meses o semanas. Hecho esto debemos generar hojas aparte que alimenten a ese cuadro, estas hojas aparte será donde importaremos informacion desde otras fuentes.

El segundo paso es utilizar formulas como IMPORTRANGE (que tiene sus limitaciones, es para algunos cientos de celdas) o bien Apps Script para copiar rangos o leer archivos XLSX. Es importante definir controles de consistencia de datos, para evitar que por ejemplo los XLSX subidos sean distintos cada vez.

Por último una vez que nuestra planilla de Drive es consistente pasamos a Data Studio para leer la misma como si fuera una base de datos, con la ventaja de que podemos ver la información desplegada y modificarla para hacer pruebas.

En Data Studio vamos al menu «Recurso» y accedemos a «Administrar las fuentes de datos…». Clic en «Añadir una fuente de datos» y buscamos la opción «Hojas de cálculo de Google». Buscamos la Hoja de Cálculo e indicamos la hoja en particular que vamos a usar. Podemos agregar distintas hojas como distintas fuentes.

Una vez agregada la fuente de datos debemos verificar que cada campo tiene el Tipo adecuado. Por ejemplo los campos con los cuales queremos graficar valores, sumar, promediar, debemos inficar Tipo: Numero. Para los campos por los cuales vamos a distribuir en los ejes, por grupos o periodos de tiempo debemos indicar Tipo: Texto o alguno de los de Fecha según nuestros datos.

Una buena idea es aplicar cálculos a los campos como números para redondear o pasar a millones o miles si los números son muy grandes, esto lo hacemos agregando campos calculados:

Los campos calculados se indican con un fx, clic en el icono para editarlo.
Podemos usar formulas y calculos.

Luego utilizamos estos campos calculados en las gráficas:

Dimension del periodo: las fechas. Dimension: las categorias. Métrica: los numeros a sumar.

Cada vez que actualizamos los datos de la planilla fuente de datos hacemos clic arriba a la derecha para actualizar y listo:

Lo interesante es que el informe se visualiza como una página web simple y no requiere de utilizar formulas avanzadas. Podemos aplicar filtros a las gáficas simplemente agrupando los mismos para indicar cuales filtros aplican a cuales gráficas:

Por último para los campos calculados podemos utilizar la función CASE para realizar un Si lógico, es decir presentar la información que viene de la planilla con un texto distinto de acuerdo a los valores. Por ejemplo si creamos un campo calculado podemos tomar otro campo y presentar distintas opciones para mejorar la lectura:

Así en las graficas y filtros podemos ver textos en lugar de números o códigos.

Copiar rangos de datos entre planillas de Google Spreadsheets

Una de las funciones que suelen atraer a muchos a utilizar planillas en la nube es IMPORTRANGE que nos permite importar un rango de celdas desde otra planilla. Cuando la fuente remota se actualiza la planilla que usa IMPORTRANGE «ve los cambios» e importa el rango actualizado.

Es análogo a los orígenes de datos de Excel pero solo que en la nube.

Las dos limitaciones que suelen aparecer usando IMPORTRANGE se dan cuando:

  1. Se quieren importar rangos demasiado grandes y se alcanza el limite de la función IMPORTRANGE.
  2. El origen de datos va cambiando muy seguido y nuestra planilla no termina nunca de cambiar…

Cuando suceden estos problemas es probable que sea conveniente utilizar un script para copiar los datos entre planillas, podemos crear un menu para iniciar la ejecución a demanda.

Al usar Apps script para copiar entre planillas tenemos la capacidad de copiar rangos practicamente ilimitados con mejor performance que usando la formula IMPORTRANGE. Definamos una planilla de origen por su link y las columnas a copiar:


// Link a planilla desde la cual vamos a copiar hacia la planilla actual
var linkPlanillaOrigenDatos = ‘https://…’;


// Columnas a copiar desde la hoja de origen, para poder cambiar el orden, indice inicia en 1
var arrColsOrigen = [3, 1, 20, 6];

// Cada hoja de la planilla de origen

for (i in sheets){
  var sheetName = sheets[i].getSheetName().substring(0,15);

  // Si en origen estamos en la hoja que queremos copiar
  if (sheetName == hojaBDOrigen) {
    SpreadsheetApp.getActiveSpreadsheet().toast(‘Copiando datos ‘+hojaBDDestino+’…’, ‘Estado’);
    var sourcesheet = sourceSS.getSheetByName(sheetName);


    // Cada columna a importar
    for (var iCol in arrColsOrigen) {
      // Leemos
      var sourcerange = sourcesheet.getRange(2,arrColsOrigen[iCol],sourcesheet.getLastRow()-1,1);
      var sourcevalues = sourcerange.getValues();
      // Escribimos
      var destrange = destsheet.getRange(iniRow,colPos,sourcevalues.length,1);
  destrange.setValues(sourcevalues);
      colPos++;
    }

….

Este script lo ejecutamos adjunto a una planilla. Lo primero es obtener el enlace completo de la planilla desde la cual vamos a copiar los datos. Luego indicamos las columnas a copiar. Cuando accedemos a la planilla de origen debemos recorrer las hojas e identificar la hoja que es fuente de informacion. Una vez que estamos en la hoja de origen copiar las columnas en el orden que necesitamos a la planilla actual.

Para ver el código completo en Github:

Demo3 – Copiar rangos entre planillas
https://github.com/nrodriguezm/app-scripts-demo/blob/master/demo3-copiar-rangos-planillas/Code.js




Restringir ejecución de apps script por usuario o dominio

A medida que se desarrollan herramientas y automatizaciones en la organización es posible que se maneje información sensible que solo deba ser ejecutado por ciertos usuarios o areas de la empresa.

En estos casos podemos limitar la ejecución de apps script mediante el archivo manifest.json definiendo la configuración y acceso del script.

Según la documentación del manifest debemos agregar en el parametro webapp la propiedad access restringiendo por ejemplo al dominio, asi si corremos los scripts con un usuario @empresa podemos evitar que usuarios externos a la organización utilicen los mismos.

En el editor de script.google.com, abrimos el menu Ver para mostrar el archivo de manifiesto:

Abrimos el archivo appscript.json y debemos agregar dentro de webapp lo siguiente:

«webapp»: {
«access»: «DOMAIN»,
«executeAs»: «USER_DEPLOYING»
},

Quedando de la siguiente forma:

El parametro executeAs nos permite definir a nombre de que usuario se ejecuta el script, puede ser a nombre del desarrollador que hace el deploy o bien a nombre del usuario que pide la ejecución.

Leer mails de Gmail e insertar en una planilla de Drive

El correo suele ser la forma típica de comunicación para eventos de facturación, impuestos, relaciones oficiales y otras que suelen perderse en bandejas de entrada son cientos de correos sin leer.

Una forma práctica de procesar cadenas de mail que no se deben perder es procesar todos los mensajes de una casilla y generar una planilla donde dar seguimiento a todos los correos sin excepción.

Vamos a utilizar la API de Gmail para leer los mensajes en App Scripts via GmailApp:
https://developers.google.com/apps-script/reference/gmail/gmail-message

El primer paso es realizar una busqueda para obtener las conversaciones pertinentes, luego recorrer los hilos y dentro de los hilos los mensajes:

var searchTerm = «Google Cloud»;
var threads = GmailApp.search(searchTerm,0,20);
//Logger.log(«Search term: «+searchTerm);

var mailsData = [];
var arrIdsMails = [];

// Conversaciones
for (var i = 0; i < threads.length; i++) {
Logger.log(«Each Thread: «+threads[i].getFirstMessageSubject());

var thread = threads[i];
var messages = GmailApp.getMessagesForThread(thread);
// Mensajes dentro de conversaciones
for (var j = 0 ; j < messages.length; j++) {
//Logger.log(«Each Message: «+threads[i].getFirstMessageSubject());

var messageId = messages[j].getId();
var messageDate = messages[j].getDate();
var messageTitle = messages[j].getSubject();

// Se puede obtener el mensaje completo
var messageBody = messages[j].getBody();
….

Es importante guardar el ID de cada mensaje para luego al escribir las celdas comparar si el ID ya está insertado. Si no controlamos esto vamos a volver a insertar los mismos mensajes cada vez.
Para procesar el cuerpo del mensaje podemos buscar textos o bien expresiones regulares. El cuerpo del mensaje puede ser excesivamente grande especialmente si se trata de un HTML con estilos.
El código completo se puede ver en el repositorio en Github:

Añadir un indicador de avance de ejecución en App Scripts

Al ejecutar funciones en App Scripts es posible que la ejecución tome cierto tiempo, para lo cual es útil mostrar al usuario algún mensaje de avance para evitar que quiera trabajar sobre datos incompletos.

Para esto podemos usar los toast de app scripts que indican al usuario el estado del script:

// Mensaje de esto para el usuario
SpreadsheetApp.getActiveSpreadsheet().toast(‘Finalizamos!’, ‘Estado’, 2);

Para ver la demo completa ver la siguiente planilla:

Demo1 nrm.uy – Leer API
https://docs.google.com/spreadsheets/d/1ap2IQeQ73049NE6lj_oRDBFQ-Wjz0GDiZoXPkFf-a4I/

Código en Github del proyecto:
https://github.com/nrodriguezm/app-scripts-demo/blob/master/demo1-api-menu-toast/Code.js

Crear un menu personalizado en planilla de Google Drive

Continuando con el ejemplo de como consumir una API externa usando app scripts en una planilla de Google Drive, vamos a agregar un menú personalizado que nos permita ejecutar el script desde la planilla para que el usuario no tenga que acceder al código del proyecto.

Para eso vamos a utilizar la función onOpen de las planillas que se dispara cuando el usuario abre la misma, luego vamos a insertar una entrada de menú:

// Preparamos menu para que el usuario pueda actualizar desde la planilla sin entrar al editor de codigo
function onOpen() {
// La función onOpen se ejecuta automáticamente cada vez que se carga un Libro de cálculo
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];

menuEntries.push({
name : «Actualizar Datos»,
functionName : «getDatosAPI»
});
menuEntries.push(null);

ss.addMenu(«Actualizar», menuEntries);
}

Con este código logramos un menú como el siguiente:

Cuando el usuario hace clic en el menú personalizado de «Actualizar Datos», se dispara la función de App Scripts «getDatosAPI».

Para ver la demo completa ver la siguiente planilla:

Demo1 nrm.uy – Leer API
https://docs.google.com/spreadsheets/d/1ap2IQeQ73049NE6lj_oRDBFQ-Wjz0GDiZoXPkFf-a4I/

Código en Github del proyecto:
https://github.com/nrodriguezm/app-scripts-demo/blob/master/demo1-api-menu-toast/Code.js

 

Leyendo datos desde una API en planillas de Google

Cuando trabajamos con planillas en la nube tenemos la posibilidad de leer datos desde nuestro sistema de gestión para tener la planilla sincronizada a la realidad de la operación.

Por ejemplo podemos traer la facturación, compras o costos de RRHH de nuestra empresa analizando los números en tiempo real, día a día sin esperar reportes manuales que pueden y suelen tener errores (olvidos, cambios en el formato, etc.).

Para lograr esto podemos utilizar App Scripts en las planillas de Google para leer una API. En el menú superior: Herramientas > Editor de secuencia de comandos

Vamos a definir una función auxiliar para llamadas a APIs externas que responden con JSON:

// Llamada a API externa
function getApiData(url,query) {

try {
var response = UrlFetchApp.fetch(url);
var responseData = response.getContentText();
var json = JSON.parse(responseData);
return json;
}
catch (e) {
Logger.log(e);
return [«Error:», e];
}
}

El código quedará mas o menos así:

Con esto tenemos una función auxiliar para llamadas a APIs externas que responden con formato JSON.

Para ver la demo completa ver la siguiente planilla:

Demo1 nrm.uy – Leer API
https://docs.google.com/spreadsheets/d/1ap2IQeQ73049NE6lj_oRDBFQ-Wjz0GDiZoXPkFf-a4I/

Código en Github del proyecto:
https://github.com/nrodriguezm/app-scripts-demo/blob/master/demo1-api-menu-toast/Code.js