Artikel top billede

(Foto: Computerworld)

Lav dine egne udvidelser til Google Apps

Der er masser af udvidelser til Googles tjenester som Google Docs og Sheets. Men hvad med at lave dine egne?.

Af Kenneth Geisshirt, Alt om Data

Denne artikel er oprindeligt bragt på Alt om Data. Computerworld overtog i november 2022 Alt om Data. Du kan læse mere om overtagelsen her.

For et par numre siden i Alt om DATA nr. 2 2016 præsenterede vi en række udvidelser til Google Docs og Sheets. Det naturlige spørgsmål er, hvordan man selv udvikler udvidelser til Google App? I denne artikel vil vi se nærmere på, hvad der skal til.

Dine egne udvidelser
Der findes en del udvidelser til både Docs og Sheets, men når sandheden skal frem, er der ikke mange rigtig gode og relevante udvidelser. Det er virkelig en skam, eftersom Google stiller en god og funktionsrig ramme til rådighed.
Du kan vælge at skrive dine udvidelser kun til dig selv. Det skal forstås på den måde, at en udvidelse kan bygges ind i dit dokument eller regneark og ingen andre kan få adgang til dem. Der er dog også mulighed for at offentliggøre en udvidelse, så andre kan få glæde af den.

JavaScript
Inden vi kommer for godt i gang, er det nok en god idé at give dig et indtryk af, hvilket programmeringssprog og ramme, som udvidelserne skrives i.
Programmeringssproget er baseret på JavaScript, men kaldes for Google Script. Det er et logisk valg, da JavaScript er udbredt i web-kredse. JavaScript er et sjovt sprog: alt er objekter. Med alt, menes alt - selv funktioner er objekter. Selv om JavaScript er et objekt-orienteret programmeringssprog, benytter det sig ikke af klasser, men kan tilføje og fjerne objekters attributter ved runtime.
Du finder JavaScript mange steder. Udover at være populært til udvikling af web-applikationer, har JavaScript på servere i de senere år været meget udbredt i form af node.js (https://nodejs.org/en/). En af de voldsomt hypede teknologier lige nu er JavaScript til udvikling af apps til smartphones (primært Android og iOS), f.eks. i form af Facebooks React Native (https://facebook.github.io/react-native/).
Med et sprog, hvor funktioner er objekter som alle andre objekter, kan du bruge JavaScript som et funktionsprogrammeringssprog. Dog er Googles API (Application Programmering Interface - snitfladen, du skal bruge til at få adgang til Googles funktionalitet) mere traditionelt i dets opbygning.

Script editor
Udviklingen af dine udvidelser og funktioner sker i din webbrowser. Både i Docs og Sheets finder du menupunktet “Script Editor” i menuen “Tools”. Når du vælger menupunktet, åbnes en ny fane. I denne fane finder du en editor.

Se Script Editoren på billede 1

Billede 1: Skal du skrive udvidelser til Docs eller Sheets, finder du “Script Editor” i menuen “Tools”. Billede 1: Skal du skrive udvidelser til Docs eller Sheets, finder du “Script Editor” i menuen “Tools”.



Men det er mere end blot en editor. Ude til venstre kan du se dine forskellige projekter. Google har valgt at lade filnavnene ende på “gs” for at vise, at der er tale om et Google Script.
Begynder du at skrive på dit Google Script, vil du hurtigt opdage, at editoren benytter syntax highlighting, dvs. editoren viser med forskellige farver, de forskellige elementer, som din kode består af. For eksempel er variabler blå, mens konstanter er grønne.
Endvidere hjælper editoren dig med at holde styr på parenteser, idet både højre- og venstre-parentes lyser op, når cursoren står ved den ene af dem. Googles interne standarder foreskriver, at indrykning ved blokke er på 2 mellemrum. Personligt foretrækker jeg fire mellemrum.
Modsat Google Docs og Sheet, gemmes dit script ikke automatisk. Det vil sige, at du skal huske at gemme i ny og næ. Når du gemmer din kode, bliver den tjekket. Med andre ord, når du gemmer, vil du få at vide, om der er syntaksfejl og lignende.

Sheets
Lad os begynde med at se på, hvad du kan skrive af ekstra funktionalitet til dine regneark. I infoboks “calcPi.gs” finder du et lille eksempel på et script. Eller rettere, der er tale om én funktion. Når vi kommer til at udføre eller køre dine script, vælger du hvilken funktion, som skal udføres.
Funktionen calcPi estimerer værdien af π vha. en Monte Carlo-simulering (se infoboksen ovenfor ved navn “Udregn π” for lidt baggrund). Med kaldet get-ActiveSheet() får du adgang til det regneark, som er det aktive. Der kan jo være flere ark i samme fil, men med dette kald får du udvalgt det ark, som du vil arbejde med.
Det er let nok at læse en celle i et ark. getRange() er en tilbagevendende funktion, og her henter du værdien af én celle (her B1). Eftersom indholdet af en celle kan være enten tal, datoer eller tekst, er det praktisk at sætte valideringsregler op. Der er ingen grund til at forsøge at bruge indholdet af en celle med en streng, hvis du forventer et tal. Som det også fremgår af eksemplet, kan du også bruge kolonnenr./rækkenr. til at indeksere, når du kalder getRange(). Det har den klare fordel, at du kan adressere cellerne programmatisk.
Det meste af eksemplet er egentlig almindelig JavaScript. Men mod slutningen kan du se, at du med stor lethed kan indsætte en graf. Med newChart() opretter du en graf vha. et klassisk builder pattern.

Docs
Du kan også skrive udvidelser og funktioner til Google Docs. I infoboksen “Tjek wikiside” på næste opslag finder du et eksempel på, hvordan en lille udvidelse til Docs kan se ud. I dette eksempel er funktionen TjekWIkiSide() hovedfunktionen. Det er den, som du kan udføre. Den kalder to andre funktioner - og er egentlig blot driveren. Hovedideen i scriptet er, at en bestemt Wikipedia-side hentes og først indsættes i dokumentet. Derefter optælles, hvor mange mellemrum siden består af. Resultatet af optællingen indsættes til slut i dokumentet i form af en tabel.
Som med Sheets kan du få adgang til det aktive dokument. Funktionen getActiveDocument() er den logiske følgesvend til Sheets’ getActiveSheet(). Du kan se, at getActiveDocument() bruges i begge de to arbejdsfunktioner HenWikiSide() og Tegn().
Lad os lige se nærmere på, hvordan du kan hente en Wikipedia-side. Funktionen UrlFetchApp.fetch() bruges til at downloade data/sider fra internettet. Den er ikke bundet til Google Docs, og du kan godt bruge den i andre applikationer, herunder Sheets. At få behandlet de rå data til et bedre format ligger udenfor denne artikel, men det er bestemt muligt.
Som du kan se, kan du indsætte tekst i dit dokument med funktionen insertText(). Den første parameter styrer, hvor i dokumentet, du indsætter teksten. I eksemplet vises, hvordan du indsætter direkte i brødteksten, men du kan også finde præcis det afsnit/tekstelement, hvor du ønsker at indsætte teksten.
I den anden funktion, Tegn(), tælles hvor mange mellemrum der er i teksten. Det er i virkeligheden “bare” JavaScript-kode. Det interessante er appendTable(). Funktionen tilføjer en tabel til dokumentet. Selve indholdet af tabellen er defineret vha. et array (af array) lige ovenfor. Funktionen appendTable() viser, hvor let det er at arbejde med Google Docs. Det kræver ikke megen fantasi at forestille sig, hvordan du kan hente data fra en server med UrlFetchApp.fetch() og indsætte data som en tabel med appendTable() og derved automatisere en del arbejdsgange.

Kør et script
Har du skrevet et script, er det også meningen, at du skal køre det. I “Script Editor” finder du en “play”-knap (trekant/pil). Ved at klikke på denne “play”-knap, går dit script i gang. Eller rettere, den funktion som er valgt i drop-down-menuen til højre vil blive udført.
Men inden du kommer i gang med at udføre det første script/funktion, skal du give tilladelse til det. Det sikrer, at der ikke udføres kode bag om ryggen på dig. Du skal give tilladelse for hver applikation, dvs. både for Docs og Sheets.

Se tilladelser for scripts billede 2 og 3

AOD07_googleapps04

Billede 2 og 3: Du skal tillade, at der kan udføres script. Billede 2 og 3: Du skal tillade, at der kan udføres script.



Nogle scripts skal køres med jævne mellemrum. Google App har konceptet triggers til at klare den del af opgaven. Med en trigger kan du starte et script på bestemte tidspunkter. Det kan være en gang i døgnet eller en gang i minuttet. Det er også muligt at få besked ved at tilføje en eller flere notifications.

Se trigger på billede 4 og 5

AOD07_googleapps08

AOD07_googleapps09 Billede 4 og 5: Med triggers kan du får udført den samme opgave igen og igen.


Debugging
En del af JavaScripts generelle API har en log-funktion, som du bruger til at skrive informationer til en log. Der findes også log-funktion i Google App’s API. I eksemplet i boks “calcPi.gs” finder du linjen Logger.log(‘Pi = ‘ + pi). Den skriver en linje i loggen. Tricket med at bruge +-operatoren er, at du tvinger variablen pi til at blive konverteret til en streng.
Selve logfilen finder du som menupunktet “Logs” i menuen “View”. Åbner du en log, kan du se de enkelte linjer med præcise tidsstempler. Loggen kan være praktisk, men den kan ikke erstatte en egentlig debugger.

Se billede 6 hvordan du kan skrive beskeder i loggen.

Billede 6: Du kan skrive beskeder i loggen, så du lettere kan følge med i, hvad dine scripts udfører. Billede 6: Du kan skrive beskeder i loggen, så du lettere kan følge med i, hvad dine scripts udfører.



Der er heldigvis en indbygget debugger i “Script Editor”. Du starter den ved at klikke på den lille bille ved siden af “Play”-knappen. Du kan sætte breakpoints ved at klikke i margin i dit script - en breakpoint vises med en lille, rød prik. Under debugging vil dit script stoppe udførelsen, når det kommer til et breakpoint. Og med debuggeren er der rig mulighed for at undersøge værdierne af de forskellige variabler. Det er også muligt at udføre en linje ad gangen (“single step”).

Se debuggeren på billede 7

Billede 7: Med debuggeren får du let indsigt i, hvad dit script gør, og hvad værdierne af forskellige variabler er. Billede 7: Med debuggeren får du let indsigt i, hvad dit script gør, og hvad værdierne af forskellige variabler er.


Afslutning
Rigdommen i Googles API’er er stor, og denne artikel er ikke andet end en smagsprøve.
Der findes i dag mange åbne datakilder. Mange danske myndigheder giver dig mulighed for at hente rådata direkte fra dem. Det er ikke svært at forestille sig, hvordan Sheets kan bruges til at downloade rådata fra for eksempel Folketinget og præsentere, hvordan hvert medlem stemmer. Google Apps gør datajournalistikken meget lettere.
Mange virksomheder bruger Google Apps (gmail, kalender, etc.). Ved at skrive små funktioner til disse applikationer, er det relativ let at få automatiseret flere arbejdsgange.

Få mere at vide

Monte Carlo metoden: https://en.wikipedia.org/wiki/Monte_Carlo_method
Introduktion til JavaScript af K. Geisshirt. Libris, 2010.
Builder pattern: https://en.wikipedia.org/wiki/Builder_pattern

Udregn π

Monte Carlo er hjemstedet for Monacos kasino og er nok et af verdens mest kendte steder for hasardspil. Et kasino bygger på, at tallene vælges tilfældigt - eller stokastisk, hvis du vil sige det matematisk. I statistisk termodynamik (en gren af fysikken) bruges tilfældige tal til at udregne egenskaber ved fysiske systemer. Teknikken går tilbage til 1940’ernes simuleringer af atomare processer (atombomber)- og inspireret af high society’s kasino, hedder metoden i dag for Monte Carlo-metoden.

Du kan bruge Monte Carlo-metoden til at estimere værdien af π. Som du måske husker, er π lig med 3,1415926535 (der er uendelig mange decimaler), og du bruger π til at udregne arealet af en cirkel som A = πr2 hvor r er cirklens radius.

Omvendt kan du udregne π som A/r2. Ser du på en enhedscirkel (en cirkel med radius 1) i et koordinatsystem (se figur “Enhedscirkel”). I første kvadrant (øverst til højre) vælger du et punkt  (vælg to tilfældige tal mellem 0 og 1). Hvis punktet ligger indenfor cirklen, noterer du dette. Cirklens areal er proportionalt med, hvor mange gange, du tilfældig vælger et punkt indenfor cirklen. Du kan nu estimere π som 4n/N, hvor n er antallet af punkter indenfor cirklen, mens N er det totale antal punkter (faktoren 4 kommer fra, at vi ser på en kvartcirkel).

 

calcPi.gs
function calcPi() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var rule = SpreadsheetApp.newDataValidation().requireNumberGreaterThan(500);
 
  var cell = sheet.getRange(’B1’);
  cell.setDataValidation(rule);
  var iterations = cell.getValue();
 
  var hits = 0;
  for (var i = 0; i < iterations; i++) {
    var x = Math.random();
    var y = Math.random();
    var d = x*x + y*y;
    if (d < 1.0) {
      hits++;
    }
    if ((i % 100) == 0) {
      var D = sheet.getRange(1 + i/100, 4);
      D.setValue(4.0*hits/(i+1));
    }
  }
 
  var pi = 4.0*hits/iterations;
  Logger.log(’Pi = ’ + pi);
  var output = sheet.getRange(’B2’);
  output.setValue(pi);
 
  var chart = sheet.newChart()
    .setChartType(Charts.ChartType.LINE)
    .addRange(sheet.getRange(’D1:D’+ (iterations / 100)))
    .setPosition(7, 7, 0, 0)
    .build();
  sheet.insertChart(chart);
}

Tjek wikiside
function HentWikiSide() {
  var response = UrlFetchApp.fetch(”https://da.wikipedia.org/w/index.php?title=Audio_Media&printable=yes”);
  var body = DocumentApp.getActiveDocument().getBody();
  var text = body.editAsText();
  text.insertText(0, response.getContentText());
}

function Tegn() {
  var body = DocumentApp.getActiveDocument().getBody();
  var text = body.getText();
  var tegn = text.length;
  var mellemrum = 0;
  for (var i = 0; i < tegn; i++) {
    if (text[i] == ’ ’) {
      mellemrum++;
    }
  }
 
  var celler = [
    [ ‘Tegn’, ‘Mellemrum’ ],
    [ tegn, mellemrum ]
  ];
  body.appendTable(celler);
}

function TjekWikiSide() {
  HentWikiSide();
  Tegn();
}