Oggi voglio sospendere temporaneamente la panoramica sugli Apps Script e iniziare con voi un piccolo progetto. Avevo in mente un Google Apps Script che giornalmente invitasse una email con il riepilogo dei compleanni odierni dei propri contatti e quelli dei prossimi 7 giorni in modo da potersi ricordare per tempo un eventuale regalo.
Procediamo a creare il nostro script come abbiamo imparato a fare in precedenza.
Google riversa automaticamente (prendendoli dai nostri contatti) i compleanni, e altri eventi, in un calendario speciale. Dovremmo quindi puntare questo calendario per recuperarli.
Ci tengo a precisare che per ottenere il riferimento a un calendario ci sono 3 diversi metodi della classe CalendarApp:
In questo progetto useremo 3 file diversi. Oltre a Codice.gs, presente di default in ogni nuovo progetto, dovremmo creare due file HTML (Email.html e Stylesheet.html), dal menù File -> Nuovo -> File HTML
In Codice.gs ci saranno tutte le funzioni che verranno utilizzate nel nostro progetto, in caso di progetti più complessi si potrà suddividerle in più file che dovranno avere estensione .gs
In Stylesheet.html ci sarà il tag <style> con tutto il CSS necessario a dare uno stile alla nostra mail (Gmail supporta i CSS).
In Email.html ci sarà il template che, a partire dai dati recuperati da una funzione, genererà l'HTML che verrà inviato via mail.
Come potrete vedere non mi sono impegnato molto per creare un HTML particolarmente accattivante ma ho cercato di fare del mio meglio per documentare chiaramente il codice.
Procediamo a creare il nostro script come abbiamo imparato a fare in precedenza.
Google riversa automaticamente (prendendoli dai nostri contatti) i compleanni, e altri eventi, in un calendario speciale. Dovremmo quindi puntare questo calendario per recuperarli.
Ci tengo a precisare che per ottenere il riferimento a un calendario ci sono 3 diversi metodi della classe CalendarApp:
- getDefaultCalendar() restituisce il calendario di default quindi non servono parametri e otteniamo un unico calendario
- getCalendarsByName(name) restituisce un array di calendari, di proprietà dell'utente o a lui accessibili, il cui nome combacia con la stringa passata come parametro.
- getCalendarById(id) restituisce il calendario che risponde a uno specifico id (i vari id dei calendari a cui avete accesso li trovate ovviamente in Google Calendar, nelle impostazioni dello specifico calendario)
In questo progetto useremo 3 file diversi. Oltre a Codice.gs, presente di default in ogni nuovo progetto, dovremmo creare due file HTML (Email.html e Stylesheet.html), dal menù File -> Nuovo -> File HTML
In Codice.gs ci saranno tutte le funzioni che verranno utilizzate nel nostro progetto, in caso di progetti più complessi si potrà suddividerle in più file che dovranno avere estensione .gs
In Stylesheet.html ci sarà il tag <style> con tutto il CSS necessario a dare uno stile alla nostra mail (Gmail supporta i CSS).
Come potrete vedere non mi sono impegnato molto per creare un HTML particolarmente accattivante ma ho cercato di fare del mio meglio per documentare chiaramente il codice.
Di seguito il codice commentato di tutti e 3 i file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Retrieve the birthdays in a range and return them in two lists, one for today's and one for the rest of the period | |
* | |
* @name getBirthdays | |
* @function | |
* @param {number} days - number of days to add to today to get the date range (optional) | |
* @returns {object} object with following properties 'today' with today's birthdays 'remaining' with the remaingin birthdays for the date range | |
*/ | |
function getBirthdays(days) { | |
var result = { | |
'today': [] | |
}; | |
var _tmpNow = new Date(); | |
// Preparo un oggetto per avere a disposizione le parti della data odierna che mi serviranno | |
var now = { | |
'year': _tmpNow.getFullYear(), | |
'month': _tmpNow.getMonth(), | |
'day': _tmpNow.getDate() | |
}; | |
var start = new Date(now.year, now.month, now.day, 0, 0, 0, 0); | |
var end = new Date(now.year, now.month, now.day + days, 23, 59, 59, 999); | |
// Recupero i compleanni tra oggi e "days" giorni da oggi | |
var contactsBirthdays=CalendarApp | |
.getCalendarById('#contacts@group.v.calendar.google.com') | |
.getEvents(start, end); | |
// Per ogni compleanno controllo quanti siano in data odierna e li sposto | |
// in result.today | |
contactsBirthdays.forEach(function(bday) { | |
if(bday.getAllDayStartDate().getDate() === now.day){ | |
result.today.push(contactsBirthdays.shift()) | |
} | |
}); | |
// Assegno i restanti compleanni a result.remaining | |
result.remaining = contactsBirthdays; | |
return result; | |
} | |
/** | |
* Send an HTML email with a list of birthdays | |
* | |
* @name sendTodaysBirthday | |
* @function | |
*/ | |
function sendTodaysBirthday() { | |
// Creo un template HTML a partire dal file 'Email.html' | |
var emailTemplate = HtmlService | |
.createTemplateFromFile('Email'); | |
// assegno alla proprità 'birthdays' quanto viene restituito dalla funzione getBirthdays(7) | |
emailTemplate.birthdays = getBirthdays(7); | |
// Genero l'HTML a partire dal template e i dati che gli ho precedentemente passato | |
var email = emailTemplate.evaluate(); | |
// Invio una mail a me stesso con oggetto 'Promemoria' e come corpo l'HTML precedentemente generato | |
MailApp.sendEmail(Session.getActiveUser().getEmail(), "Promemoria", "", {"htmlBody" : email.getContent()}); | |
} | |
/** | |
* Include the partial HTML of a specific file | |
* | |
* @name include | |
* @function | |
* @param {string} filename - name of the file to get the HTML from | |
* @returns {string} partial HTML | |
*/ | |
function include(filename) { | |
return HtmlService | |
.createHtmlOutputFromFile(filename) | |
.getContent(); | |
} | |
/** | |
* Format nicely the date | |
* | |
* @name niceDate | |
* @function | |
* @param {date} date - the date object to format | |
* @returns {string} nicely formatted date | |
*/ | |
function niceDate(date) { | |
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }; | |
return date.toLocaleDateString('it-IT', options); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<!-- | |
Questo template riceve i dati restituiti dalla funzionalità getBirthdays | |
quindi un oggetto | |
{ | |
'taday': CalendarEvent[], | |
'remaining': CalendarEvent[] | |
} | |
--> | |
<html> | |
<head> | |
<base target="_top"> | |
<!-- Utilizzo la funzione 'include' per incorporare i CSS dal file 'Stylesheet.html' --> | |
<?!= include('Stylesheet'); ?> | |
</head> | |
<body> | |
<div class="main"> | |
<h1>I Prossimi Compleanni</h1> | |
<div id="today"> | |
<? if(birthdays.today.length === 0) { ?> | |
Non ci sono compleanni oggi | |
<? } else { | |
var bPrefix = (birthdays.today.length === 1) ? 'c\'è' : 'ci sono'; | |
var bSuffix = (birthdays.today.length === 1) ? 'evento' : 'eventi'; ?> | |
Oggi <?= bPrefix ?> <?= birthdays.today.length ?> <?= bSuffix ?> | |
<? birthdays.today.forEach(function(bday) { ?> | |
<div><?= bday.getDescription() ?></div> | |
<? }); ?> | |
<? } ?> | |
</div> | |
<div id="remaining"> | |
<? birthdays.remaining.forEach(function(bday) { ?> | |
<div><?= niceDate(bday.getStartTime()) ?> | <?= bday.getDescription() ?></div> | |
<? }); ?> | |
</div> | |
</div> | |
</body> | |
</html> | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style> | |
@media screen and (min-width: 500px) { | |
.main { | |
width: 90%; | |
} | |
} | |
.main { | |
margin: 0 auto; | |
} | |
#today { | |
padding: 1% 1% 3%; | |
background-color: lightblue; | |
} | |
#remaining { | |
padding: 1%; | |
background-color: lightgrey; | |
} | |
</style> |
Il progetto nella sua interezza è su Github, la versione relativa a questo articolo è qui ma se voleste contribuire a migliorarlo o semplicemente andare a vedere l'ultima versione disponibile allora recatevi subito qui. Ovviamente qualsiasi contributo all'html e ai css sarà il benvenuto.
Come potrete intuire al momento dovrete configurare manualmente l'esecuzione automatica dello script come avevamo già visto qui. Io vi consiglio di metterlo giornaliero ma se volete potrete modificare velocemente il codice per eseguire lo script una volta a settimana, recuperare gli eventi dei successivi 30 giorni, mostrare i compleanni della settimana in primo piano e i restanti in coda.
Nei prossimi articoli vorrei illustrarvi come scrivere agevolmente il codice offline e caricarlo velocemente (in modo da poter usare strumenti di versioning come git e in modo da poter caricare con più comodità i miei futuri progetti), vorrei trattare la creazione programmatica dei trigger e migliorare questo progetto inserendo anche un modo per filtrare i contatti per cui ricevere il promemoria. Se avete preferenze fatemi sapere.
Come potrete intuire al momento dovrete configurare manualmente l'esecuzione automatica dello script come avevamo già visto qui. Io vi consiglio di metterlo giornaliero ma se volete potrete modificare velocemente il codice per eseguire lo script una volta a settimana, recuperare gli eventi dei successivi 30 giorni, mostrare i compleanni della settimana in primo piano e i restanti in coda.
Nei prossimi articoli vorrei illustrarvi come scrivere agevolmente il codice offline e caricarlo velocemente (in modo da poter usare strumenti di versioning come git e in modo da poter caricare con più comodità i miei futuri progetti), vorrei trattare la creazione programmatica dei trigger e migliorare questo progetto inserendo anche un modo per filtrare i contatti per cui ricevere il promemoria. Se avete preferenze fatemi sapere.
Commenti
Ho difficoltà a capire alcuni punti.
=========================================================
contactsBirthdays.forEach(function(bday) {
if(bday.getAllDayStartDate().getDate() === now.day){
result.today.push(contactsBirthdays.shift())
}
});
// Assegno i restanti compleanni a result.remaining
result.remaining = contactsBirthdays;
return result;
========================================================
Qui penso che la funzione esegua un controllo su tutto l'array delle date comprese tra oggi e days (con foreach che analizza tutto l'array in base ad una funzione)
Appena trova una data in oggi la inserisce nell'oggetto result e rimuove con shift il valore dall'array delle date.
L'array che rimane viene poi caricato sempre nell'oggetto con un parametro nuovo .remaining che dovrebbe essere un array (dico nuovo perchè non c'era nella prima dichiarazione).
A questo punto ho un oggetto che contiene l'array delle date compleanno di oggi (today) e le rimanenti (remaining).
Poi lo script procede mandando la mail che utilizza un template html.
Nel template c'è un punto:
=========================================================================
Oggi
=====================================================================
bday dovrebbe essere l'item dell'array, ovvero ogni data (o meglio è un array o oggetto con i valori del calendario), invece getDescription() dovrebbe essere la funzione del calendario, la descrizione, qindi forse il nome delle persone.
e l'ultima parte dovrebbe fare lo stesso però per quelli che verranno nei prossimi giorni.
Andrea
contactsBirthdays.forEach cicla su ogni elemento dell'array contactsBirthdays (che è stato popolato recuperando gli eventi dal calendario).
Come hai ipotizzato per ogni evento viene effettuato un controllo e se la data dell'evento è uguale al primo giorno del periodo allora l'evento viene rimosso dall'array originale e aggiunto all'array "today" che viene definito nell'oggetto "result". A ciclo ultimato l'array originale, privo degli eventi odierni viene assegnato alla proprietà "remaining" dell'oggetto "result". Il motivo per cui "today" era stato definito inizialmente come array mentre "remaining" no solo perché "today" deve essere un array affinché si possa richiamare il metodo "push".
Per quanto riguarda la seconda curiosità tramite birthdays.today.forEach ciclo su ogni evento dell'array e, ogni volta, questo evento viene passato alla funzione come parametro di nome "bday" e tramite la funzione getDescription() viene recuperata la descrizione dell'evento così come viene mostrata nel calendario.
Spero che questo abbia chiarito i tuoi dubbi.