Consigli per utilizzare Google Apps Script nel trattamento di dati

Di - 16 May 2012 - in

Come ogni suite per ufficio che si rispetti, Google Docs ha il suo foglio di calcolo, Spreadsheets. E come ogni foglio di calcolo che si rispetti, Spreadsheets permette un comodo trattamento di grandi moli di dati per generare report e rendere il tutto facilmente leggibile.
Lo strumento che Google ci offre è Apps Script, un insieme di funzioni javascript che rendono possibile la scrittura di procedure per organizzare dati. Vediamo qui un paio di tecniche base, dalle quali si può prender spunto.

Caricare le impostazioni da un foglio di configurazione

Immaginiamo di avere uno script che genera dei report, spesso a partire da dati diversi. La prima cosa da fare sarà avere dei parametri di configurazione facilmente modificabili per impostare cosa lo script debba fare e come. Queste impostazioni possono essere elencate su un foglio di calcolo Spreadsheets, in modo che lo script le legga al momento dell’esecuzione. Immaginiamo che la prima colonna contenga il nome della variabile, la seconda contenga il tipo e la terza il valore:

Con un array accessibile globalmente, chiamato globals, si possono caricare le impostazioni dal foglio di configurazione (che qui è il foglio SHT_CONFIG) in un qualunque entrypoint dello script.

// Variabili globalmente accessibili
var SHT_CONFIG = 'Config';
var globals = new Array();

function entryPoint() {
  globals = (globals.length == 0) ? LoadGlobals(
    SpreadsheetApp.getActive(), SHT_CONFIG)
    : globals;
  // Il tuo codice va qui
}

In questo frammento di codice utilizziamo la funzione LoadGlobals. Tale funzione dovrà, a partire dal nome del file e del foglio di lavoro, leggere i dati di configurazione ed elaborarli in base al tipo. Ovviamente è possibile aggiungere facilmente altri tipi oltre a quelli definiti, ed è possibile aggiungere eventualmente altre colonne.

// Genera le variabili globali da caricare nell'array globals
function LoadGlobals_(wb, configSheet) {
  var configsheet = wb.getSheetByName(configSheet);
  var tGlobals = new Array();

  // I dati di configurazione sono strutturati come VARIABILE, IS_ARRAY, VALOR[EI]
  // e includono ciò come riga di intestazione
  var cfgdata = configsheet.getDataRange().getValues();
  for (i = 1; i < cfgdata.length; i++) {
    switch (cfgdata[i][1]) {
      case 'ARRAY':
        // tratta il dato come un array - javascript mette un valore
        // nullo nell'array se si esegue split() su una
        //stringa vuota...
        if (cfgdata[i][2].length == 0) {
          tGlobals[cfgdata[i][0]] = new Array();
        } else {
          tGlobals[cfgdata[i][0]] = cfgdata[i][2].split(',');
        }
        break;
      // Definisci il tuo TUODATATYPE personalizzato utilizzando una
      // funzione customTreatement da te creata o eseguendo
      // direttamente qui il trattamento
      case 'YOURDATATYPE':
        tGlobals[cfgdata[i][0]] = customTreatment(cfgdata[i][2]);
        break;
      default: // tratta come dato generico (stringa)
        tGlobals[cfgdata[i][0]] = cfgdata[i][2];
    }
  }
  return tGlobals
}

Una volta caricati i valori di configurazione globali durante l’esecuzione dello script, ci si può riferire ad un qualunque valore con, ad esempio, globals.toList:

function getToList() {
  return globals.toList.join(‘,’);
  // or
  return globals[‘toList’].join(‘,’);
}

Smettere di preoccuparsi dei numeri di colonna

I dati che dobbiamo poi elaborare potrebbero venire da persone o applicazioni diverse, ed ogni persona o applicazione spesso necessita di tirar fuori i dati in un certo ordine, che può essere scomodo modificare. Sarebbe quindi utile poter leggere i dati disinteressandoci dell’ordine delle colonne e mettendoli in un array associativo nome_colonna -> dato ed avere cosí bisogno solo di rendere i nomi delle colonne uguali per tutti.

Ecco come fare. Prima di tutto, otteniamo un oggetto “chiave-valore” per i nomi delle colonne e la loro posizione nel foglio.

// Ritorna l'oggetto "chiave-valore" per le intestazioni delle colonne e il loro numero.
// Notare che sono ottenuti basandosi sull'indice dell'array, che parte da 0
// mentre le colonne partono da 1...
// è necessario passargli la riga di intestazione dei dati (array) e un array dei nomi di variabile/colonna:
// ad esempio: BUG_COL_ARRAY['id'] = 'Id';
function ColNumbers(hArray, colArray) {
  for (oname in colArray) {
    this[oname] = getColIndex(hArray, colArray[oname]);
  }
}

// -----------------------------------------------------------------------------
function getColIndex(arr, val) {
  for ( var i = 0; i < arr.length; i++) {
    if (arr[i].toLowerCase() == val.toLowerCase()) {
      return i;
    }
  }
  return -1;
}

Definendo un array associativo con i nomi delle colonne, è ora possibile disinteressarsi completamente del loro ordine. Ecco un esempio:

// colonne chiave nel feature sheet
var COLS_KEYAPPCOLS = new Array();
COLS_KEYAPPCOLS[‘feature’] = ‘Feature Title’;
COLS_KEYAPPCOLS[‘desc’] = ‘Description’;
COLS_KEYAPPCOLS[‘visible’] = ‘Visible’;
COLS_KEYAPPCOLS[‘corp’] = ‘Corp Date’;
COLS_KEYAPPCOLS[‘prod’] = ‘Prod Date’;

Per utilizzarlo basterà crearsi un nuovo oggetto ColNumbers di questo tipo:

var curFeatures = curSheet.getDataRange().getValues();
var curCols = new ColNumbers(curFeatures[0], COLS_KEYAPPCOLS);

La prima riga ottiene i dati nel foglio di calcolo, mentre la seconda passa la prima riga del foglio e il nostro array associativo al costruttore di ColNumbers e crea il nostro oggetto.

A questo punto, possiamo riferirci ad esempio alla colonna “Description” semplicemente utilizzando qualcosa come

curCols.desc

senza dover conoscere la sua posizione nel foglio dei dati.

Inoltre, in un foglio di configurazione come quello visto all’inizio, posso definire le colonne da usare in un report e il loro ordine. Posso ad esempio fare:

// Itera sulle righe di dati, iniziando dalla 1 (la 0 è l'header)
for ( var fnum = 1; fnum < curFeatures.length; fnum++) {
  // Itera sui campi definiti in reportcols, nel foglio di configurazione
  for ( var cnum = 0; cnum < globals.reportcols.length; cnum++) {
    outputvalue = curFeatures[fnum][curCols[globals.reportcols[cnum]]];
    // outputvalue è ciò che si vuole mettere nel report.
  }
}

In questo modo posso mettere, ad esempio, “feature, desc, corp” nel file di configurazione ed ottenere facilmente le colonne specificate.

Insomma, grazie ad Apps Script si possono rendere assai semplici trattamenti di dati che non lo sembrano affatto, evitando cosí di dover ricorrere ad una struttura piú formale come una base di dati vera e propria quando in realtà ci serve molto meno.

Via | Google Apps Developer Blog

Leave a Reply

Lorenzo Breda Articolo scritto da

Studente di Informatica a Roma, si occupa di programmazione web sopratutto lato server, e di accessibilità del web. Utilizza e ama Debian GNU/Linux, e si interessa di fisica, fumetto, trekking e fotografia (gli ultimi due possibilmente abbinati). Collabora con Googlab da aprile 2012.

Contatta l'autore

Previous post:

Next post: