Tempo di lettura: 7 minuti

Nei primi 5 articoli abbiamo introdotto il linguaggio di programmazione Apps Script e descritto come quest’ultimo sia molto semplice e ben integrato nella suite Google.

Ora siamo pronti per realizzare un’applicazione vera e propria, che possa essere utile e interagisca con diversi servizi Google. Per fare questo vedremo l’interazione tra Google Calendar e Google Spreadsheet. Uso personalmente l’applicativo, che andremo a descrivere, perché facilita la creazione di consuntivi delle varie attività a fine mese e per noi si presenta come un’ottimo esempio di come possiamo sfruttare Apps Script. Ricordo che Apps Script è uno strumento gratuito e a disposizione di tutti i possessore di un account Google sia esso privato che di Google Workspace.

KALGEST

L’obiettivo del programma che chiameremo KalGest è quello di riassumere le attività del mese e conteggiare le ore dedicate a singoli progetti. Ogni mese ognuno di noi si occupa di varie attività, come tanto per fare degli esempi studiare uno specifico argomento, tagliare il giardino, fare ripetizioni di matematica, seguire un certo cliente etc… A fine mese sarebbe ottimo avere un bel rapportino su foglio di calcolo che conteggi quante ore ho dedicato ad una attività piuttosto che ad un’altra. Inoltre, pensando che questo programma possa tornare utile a piccole società od organizzazioni, dovrebbe poter gestire più calendari e ad ogni calendario associare un collaboratore. Questo è proprio quello che fa KalGest: potrà quindi anche rendicontare le attività di commessa eseguite da più collaboratori in contemporanea.

INTEGRAZIONE CON GOOGLE CALENDAR

Per interagire con Google Calendar si dovrà creare un calendario specifico e renderlo pubblico in modo da poter usare l’id nella programmazione. Dopo aver creato da Calendar il calendario con poche righe di codice lo possiamo integrare in Apps Script. Ecco come:

 
  var calendar = CalendarApp.getOwnedCalendarById(calendario);
  var inizio = new Date(anno, mese-1, 1);
  var ultimo = ultimo_gg_mese(anno,mese);
  var fine = new Date(anno, mese-1, ultimo,23,59,0);
  var events = calendar.getEvents(inizio, fine);
  for (var i = 0; i < events.length; i++){
    var event = events[i];
    valorizza(event,collaboratore);
  }

Nella prima riga di codice prendiamo possesso del calendario tramite il suo id, subito dopo definiamo l’inizio che sarà il primo giorno del mese selezionato e fine che sarà l’ultimo. Il comando getEvents(inizio, fine) scaricherà tutti gli eventi presenti in calendario in quel mese. Successivamente grazie ad un ciclo for , questi eventi verranno passati ad una funzione per essere riportati nel relativo foglio del mio Spreadsheet.
Per come è fatta la funzione valorizza() gli eventi in calendario dovranno essere formattati in modo che vi sia un carattere “:” (due punti) che separa il nome dell’attività dalla descrizione della stessa. Per esempio in calendario potrò scrivere:
CLIENTE X: Eseguita attività di consulenza
TAGLIO ERBA: prato del vicino
Gli orari di inizio e fine verranno presi invece dagli orari propri impostati nell’evento.

 

INIZIAMO

Subito dopo aver creato un calendario e recuperato il suo id, sarà necessario creare uno spreadsheet di nome KalGest (oppure nome a piacere), all’interno del quale rinominare un foglio come “Resoconto” e crearne un secondo chiamato “log”. Verranno poi creati dal programma i fogli uno per ogni mese. Nel foglio Resoconto potremo creare a mano, se lo desideriamo, un riepilogo annuale. Fatto questo, possiamo procedere:  “Estensioni” -> “Apps Script” potrete andare ad incollare le seguenti righe di codice andando a sovrascrivere quelle che trovate di default.

 
/** @description
 * Programma KalGest realizzato tramite Google Apps script 
 * @author Massimiliano Brambilla
 * Gestione consuntivo delle attività da Google Calendar verso Spreadsheet
 * See http://b-software.it 
 */

// INDICE

// VARIABILI_GLOBALI
// FUNZIONI_PRINCIPALI
// FUNZIONI_LIBRERIA
// FUNZIONI_VBA


// VARIABILI_GLOBALI
//
// ***IMPORTANTE*** RICORDARSI DI AGGIORNARE ID_PREAD da link spreadsheet
var ID_SPREAD = "1mJf_jJJbL2N3WFqahFdiiL2QnfXlbbn8PEhkQpvW1tc";  // id spreadsheet ricavato dall'url
//
var calendari = ["62d6c2842dc93e838acd13ce831a22563feff237b2e38752986c2948e39aa6ac@group.calendar.google.com","bae89297dc615becf694f770b01d0e02894cfb55f616b8f3df29566623078507@group.calendar.google.com" ];
var collaboratori = ["UTENTE1","UTENTE2"] 
var numero_calendari = calendari.length
var anno
var mese


// FUNZIONI_PRINCIPALI

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Kalendario')
      .addItem('Crea fogli mese', 'crea_fogli')
      .addItem('Contabilizza mese', 'contabilizza')
      .addToUi();
}

function crea_fogli(){
  anno = annoKalendario()
  for(var n=1; n<=12; n=n+1){
    aggiungiFoglio(Right("0" + n ,2)) //n.toString().replace(/(\.\d\d)\d+/, '$1'))
    logga("debug","crea_fogli()","Ultimo giorno del mese " + n + " dell'anno " + anno + " è " + ultimo_gg_mese(anno,n))
    }   
}

function contabilizza(){
  mese = input("mese");
  anno = annoKalendario()
  clearSpread();
  scriviIntestazione();
  for(var n=0;n<numero_calendari;n=n+1){
    contabilizza_calendario(calendari[n],collaboratori[n]);
    }   
}

// FUNZIONI_SECONDARIE

function contabilizza_calendario(calendario,collaboratore){
  var calendar = CalendarApp.getOwnedCalendarById(calendario);
  var inizio = new Date(anno, mese-1, 1);
  var ultimo = ultimo_gg_mese(anno,mese);
  var fine = new Date(anno, mese-1, ultimo,23,59,0);
  var events = calendar.getEvents(inizio, fine);
  for (var i = 0; i < events.length; i++){
    var event = events[i];
    valorizza(event,collaboratore);
  }
  resizeColumnsToFitData()
}

function valorizza(event,collaboratore){
  var objEvent = {};
  objEvent.title = event.getTitle();
  objEvent.sTime = event.getStartTime(); 
  objEvent.eTime = event.getEndTime();
  objEvent.collaboratore = collaboratore;
  objEvent.commessa = objEvent.title.split(":")[0];
  objEvent.descrizione = objEvent.title.split(":")[1].trim();
  scriviRiga(objEvent);
}

function scriviRiga(eventObj){
  var spread = SpreadsheetApp.openById(ID_SPREAD);
  var sheet = spread.getSheetByName(Right("0" + mese ,2));
  sheet.appendRow([eventObj.collaboratore,eventObj.commessa, (eventObj.eTime-eventObj.sTime)/1000/3600,eventObj.sTime,eventObj.eTime,eventObj.descrizione]);
  // Imposta la formattazione delle celle per le date di inizio e fine
  sheet.getRange(sheet.getLastRow(), 4).setNumberFormat("yyyy-MM-dd HH:mm");
  sheet.getRange(sheet.getLastRow(), 5).setNumberFormat("yyyy-MM-dd HH:mm");
  // Centra l'allineamento per la colonna delle ore e sinistra per il resto
  sheet.getRange(sheet.getLastRow(),1,1,6).setHorizontalAlignment("left");
  sheet.getRange(sheet.getLastRow(), 3, 1).setHorizontalAlignment("center"); // Centra solo la colonna delle ore
}


function scriviIntestazione(){
  var spread = SpreadsheetApp.openById(ID_SPREAD);
  var sheet = spread.getSheetByName(Right("0" + mese ,2));
  sheet.appendRow(["Collaboratore","Commessa", "Ore","Inizio","Fine","Descrizione"]);
  sheet.getRange(1,1,1,6).setFontWeight("bold");
}

function clearSpread (){
  var spread = SpreadsheetApp.openById(ID_SPREAD);
  var sheet = spread.getSheetByName(Right("0" + mese ,2));
  sheet.clear();
}

function annoKalendario(){
  var n = nomeSpreadsheet()
  return Number(Right(n,4))
}

// FUNZIONI_LIBRERIA

function aggiungiFoglio(nomeFoglio) {
  var cartella = SpreadsheetApp.getActiveSpreadsheet();
  var nullaosta = false;
  var tmp = nomeFoglio;
  var trovati = 0;
  while( true ) {
    nullaosta = true;
    cartella.getSheets().forEach(function(f) {
      if( f.getName() == tmp ) {
        tmp = nomeFoglio + " (" + (++trovati) + ")";
        nullaosta = false;
      }
    });
    if( nullaosta ) break;
  }
  cartella.insertSheet(tmp);
  return SpreadsheetApp.getActiveSpreadsheet().getSheetByName( tmp );
}


function resizeColumnsToFitData() {
  const sheet = SpreadsheetApp.getActiveSheet();
  sheet.autoResizeColumns(1, sheet.getLastColumn());
}

function nomeSpreadsheet() {
  let ss = SpreadsheetApp.getActive();
  return ss.getName();
}

function input(s) {
    var ui = SpreadsheetApp.getUi();
    var result = ui.prompt(s,ui.ButtonSet.OK_CANCEL);
    var button = result.getSelectedButton();
    var text = result.getResponseText()
    return(text)
}

function ottieni_anno_corrente(){
  var today = new Date();
  var date = today.getDate();
  var month = today.getMonth() + 1;
  var year = today.getFullYear(); 
  return (year)
}

function ultimo_gg_mese(a,m){
 var g = 28;
 var d = new Date(a, m-1, g);
 var m1 = d.getMonth()
 var m2 = d.getMonth()
 var ultimo = 0
 for(var n=28; n<=33; n=n+1){  
  d = new Date(a,m-1,n)
  m2=d.getMonth()  
  if (m1!=m2) {
    return (n-1) ;
    }
}
}

function logga(categoria,funzione,descrizione){
  var spread = SpreadsheetApp.openById(ID_SPREAD);
  var sheet = spread.getSheetByName("log");
  var data_log = new Date();
  sheet.appendRow([Utilities.formatDate(data_log, 'GMT+1', 'yyyy_MM_dd,HH:mm:ss'),categoria, funzione,descrizione]);
}

function Len(v) {
  return CStr(v).length ;
}

function Right(str,optLen) {
  return Mid( str, 1 + Len(str) - fixOptional ( optLen, Len(str) )  );
}

function Mid (str,optStart,optLen) {
  var s = CStr(str);
  var start = IsMissing (optStart) ? 0 : optStart - 1;
  start = start < 0 ? 0 : start;
  var length = IsMissing (optLen) ?  Len(s) - start + 1 : optLen ;
  DebugAssert( s.slice, s + ' is not a valid string for Mid');
  return  s.slice ( start, start + length);
}

function CStr(v) {
  return v===null || IsMissing(v) ? ' ' :  v.toString()  ;
}

function IsMissing (x) {
  return isUndefined(x);
}

function isUndefined ( arg) {
  return typeof arg == 'undefined';
}

function fixOptional (arg, defaultValue) {
  if (isUndefined(arg) ){
    DebugAssert(!isUndefined(defaultValue) ,
      'programming error: no default value for missing argument');
    
    return defaultValue;
  }
  else 
    return arg;
}

function DebugAssert(mustBeTrue, sayWhat) {
  if (!mustBeTrue) 
      throw new VbaAssert(sayWhat);
      
  return mustBeTrue;
}

A questo punto andrà eseguita la funzione onOpen() oppure chiuso e riaperto lo Spreadsheet (in modo che questa funzione venga eseguita automaticamente). La onOpen() andrà a creare una voce di menu chiamata “Kalendario” dalla quale si potranno chiamare prima la funzione “Crea fogli mese” che andrà a creare un foglio per ogni mese dell’anno. In secondo luogo si potrà lanciare la funzione “Kalendario” -> “Contabilizza mese” che accetterà in input il mese da contabilizzare ed andrà a scansionare i calendari indicati per scrivere le varie voci sullo Spreadsheet.

 

CONCLUSIONE

Spero che questo applicativo possa essere di spunto e di stimolo a molti e quindi utile a diffondere l’uso delle Apps Script, inoltre sarò felice dovessero arrivare feedback e/o suggerimenti migliorativi per il programma stesso nel caso qualcuno di voi lo trovasse utile.

 

by Massimiliano Brambilla

Works at B-Software