Fullmenu null

 

03 December 2017

Para este post vamos a necesitar los siguientes requisitos: una cuenta en Google y eventos creados en el calendario. Por defecto usaremos el calendario principal (pero se podría usar cualquier otro de los que Google nos permite crear). Así mismo para poder acceder a las APIs de Google deberemos obtener unas credenciales que autoricen a la aplicación para acceder a nuestra cuenta.

En este post vamos a utilizar un GroovyScript para conectar el calendario de Google revisando todos los eventos que se encuentran creados, agrupándolos en función del día de la semana y el "tema" que le hayamos asignado.

Un evento del calendario de por sí NO incluye ningún campo del tipo "tema" por lo que para poder agruparlos podríamos usar, por ejemplo, algún identificador en el texto del mismo. En este ejemplo lo que vamos a usar es la capacidad de asignar un color a un evento, de tal forma que si en nuestro calendario asignamos el color "rojo" a los eventos de tipo "ocio", el color "azul" a los de "formación", etc. podremos de una forma muy fácil agrupar eventos dispares.

Consola Google

En primer lugar deberemos crear una aplicación en la consola de Google en https://console.developers.google.com

En esta aplicación habilitaremos (al menos) la API "Google Calendar API"

Así mismo deberemos crear unas credenciales de "Servicio" obteniendo la posibilidad de descargarlas en un fichero JSON y que deberemos ubicar junto con el script.

Groogle

Para facilitar la parte técnica de autentificación y creación de servicios he creado un proyecto de código abierto, Groogle, disponible en https://gitlab.com/puravida-software/groogle el cual publica un artefacto en Bintray y que podremos usar en nuestros scripts simplemente incluyendo su repositorio.

@GrabResolver(name='puravida', root="https://dl.bintray.com/puravida-software/repo" )
@Grab(group='org.groovyfx',module='groovyfx',version='8.0.0',transitive=false, noExceptions=true)
@Grab(group = 'com.puravida.groogle', module = 'groogle-core', version = '1.0.0')

import com.puravida.groogle.GroogleScript
import com.google.api.services.calendar.CalendarScopes

Groogle nos permitirá realizar el login de nuestra aplicación y guardar la autorización en nuestro disco de tal forma que en ejecuciones posteriores no se requiera de autorizar de nuevo la aplicación (mientras no borremos el fichero con la autorización generada)

InputStream clientSecret = new File('client_secret.json').newInputStream() //(1)

def groogle = new GroogleScript(applicationName:'101-scripts')  //(2)

groogle.login(clientSecret,[CalendarScopes.CALENDAR_READONLY]) //(3)
  1. client-secret.json es el fichero que hemos descargado de la consola de Google y que contiene las credenciales

  2. Groogle realiza el login y guarda la autorización en $HOME/.credentials/${applicationName}

  3. En este post únicamente requerimos acceso a Calendar para lectura

Personalización

Como los temas son personales cada uno deberá configurar los suyos según sus preferencias. Así mismo creará más o menos temas según el grado de detalle que se quiera obtener. Para este post vamos a utilizar únicamente 3 temas:

def colors = [
        '0':'Generico',
        '10':'Reuniones',  //(1)
        '11':'Ocio'
]
  1. Hasta ahora no he encontrado cómo saber el ID del color a priori por lo que deberás ejecutarlo primero y ver cuales te asigna.

Business

La lógica de negocio de este script consistirá básicamente en recorrer todos los eventos que nos devuelva Google y para cada uno de ellos buscar en un mapa si existe un sub-mapa "tema" y si para este sub-mapa existe otro sub-map para el día de la semana donde ir acumulando eventos. En caso de que no exista, simplemente lo inicializaremos a cero para ir acumulando sobre él.

def week = ['D','L','M','X','J','V','S']
def stadistics = [:]

groogle.calendarService.events().list(calendarId).execute().items.each{ event ->   //(1)

    def events = !event.recurrentId ? [event] :  //(2)
            groogle.calendarService.events().instances(calendarId, event.id).execute().items

    events.each{ item ->
        String colorId = item.colorId ?: '0'
        if( !stadistics."${colors[colorId]}"){  //(3)
            stadistics."${colors[colorId]}" =[:]
            (java.util.Calendar.SUNDAY..java.util.Calendar.SATURDAY).each{ day ->
                stadistics."${colors[colorId]}"."${week[day-1]}"= 0
            }
        }
        int day = new Date( (event.start.date ?: event.start.dateTime).value)[java.util.Calendar.DAY_OF_WEEK]
        stadistics."${colors[colorId]}"."${week[day-1]}" +=1 //(4)
    }
}
  1. Recorrer todos los eventos que nos devuelva Google

  2. Un evento puede ser puntual o recurrente. En este caso deberemos obtener todas las instancias del mismo

  3. Buscar en el mapa el tema asociado al evento y si no existe inicializarlo

  4. Incrementar en uno para el día de la semana del evento

Vista

Para la vista usaremos un GroovyFX que nos muestre una gráfica de barras barChart tal que nos permite visualizar agrupados por temas los diferentes contadores de los días de la semana. Sin embargo este componente nos pide que le proporcionemos la serie de datos en una sucesión de 'clave', 'valor', 'clave', 'valor' en lugar de un mapa, por lo que lo primero que haremos será transformar nuestro mapa en uno más adecuado:

def flatten =[:]
stadistics.each{
    flatten."$it.key" = it.value.collect{ [it.key,it.value]}.flatten() //(1)
}

Y ahora ya podemos construir la vista, generando las series de una forma dinámica.

start {
    stage(visible:true,width:640,height:480) { //(2)
        scene{
            tilePane() {
                barChart(
                        yAxis:new NumberAxis(label:'Ocupacion', tickLabelRotation:90),
                        xAxis:new CategoryAxis(label:'Actividad'),
                        barGap:3,
                        categoryGap:20) {
                    flatten.each{   //(1)
                        series(name: it.key, data:it.value)
                    }
                }
            }
        }
    }
}
  1. Creamos dinámicamente las series en función de los temas que tengamos inicialmente.

Mi calendario quedaría de esta forma:

calendar

Script
//tag::dependencies[]
@GrabResolver(name='puravida', root="https://dl.bintray.com/puravida-software/repo" )
@Grab(group='org.groovyfx',module='groovyfx',version='8.0.0',transitive=false, noExceptions=true)
@Grab(group = 'com.puravida.groogle', module = 'groogle-core', version = '1.0.0')

import com.puravida.groogle.GroogleScript
import com.google.api.services.calendar.CalendarScopes
//end::dependencies[]

import static groovyx.javafx.GroovyFX.start
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.application.Platform
import javafx.scene.SnapshotParameters
import javax.imageio.ImageIO
import java.awt.image.BufferedImage
//tag::login[]
InputStream clientSecret = new File('client_secret.json').newInputStream() //(1)

def groogle = new GroogleScript(applicationName:'101-scripts')  //(2)

groogle.login(clientSecret,[CalendarScopes.CALENDAR_READONLY]) //(3)
//end::login[]

String calendarId = 'primary'
/*
Si quieres saber todos tus calendarios simplemente ejecuta esta linea
println groogle.calendarService.calendarList().list().execute().items*.id
*/

//tag::temas[]
def colors = [
        '0':'Generico',
        '10':'Reuniones',  //(1)
        '11':'Ocio'
]
//end::temas[]

//tag::business[]
def week = ['D','L','M','X','J','V','S']
def stadistics = [:]

groogle.calendarService.events().list(calendarId).execute().items.each{ event ->   //(1)

    def events = !event.recurrentId ? [event] :  //(2)
            groogle.calendarService.events().instances(calendarId, event.id).execute().items

    events.each{ item ->
        String colorId = item.colorId ?: '0'
        if( !stadistics."${colors[colorId]}"){  //(3)
            stadistics."${colors[colorId]}" =[:]
            (java.util.Calendar.SUNDAY..java.util.Calendar.SATURDAY).each{ day ->
                stadistics."${colors[colorId]}"."${week[day-1]}"= 0
            }
        }
        int day = new Date( (event.start.date ?: event.start.dateTime).value)[java.util.Calendar.DAY_OF_WEEK]
        stadistics."${colors[colorId]}"."${week[day-1]}" +=1 //(4)
    }
}
//end::business[]

//tag::flatten[]
def flatten =[:]
stadistics.each{
    flatten."$it.key" = it.value.collect{ [it.key,it.value]}.flatten() //(1)
}
//end::flatten[]

//tag::vista[]
start {
    stage(visible:true,width:640,height:480) { //(2)
        scene{
            tilePane() {
                barChart(
                        yAxis:new NumberAxis(label:'Ocupacion', tickLabelRotation:90),
                        xAxis:new CategoryAxis(label:'Actividad'),
                        barGap:3,
                        categoryGap:20) {
                    flatten.each{   //(1)
                        series(name: it.key, data:it.value)
                    }
                }
            }
        }
    }
}
//end::vista[]