Google-Präsentationen aus Big Data in Node.js erstellen

1. Übersicht

In diesem Codelab lernen Sie, wie Sie Google Präsentationen als benutzerdefiniertes Präsentationstool für eine Analyse der gängigsten Softwarelizenzen verwenden. Sie fragen sämtlichen Open-Source-Code auf GitHub mithilfe der BigQuery API ab und stellen die Ergebnisse mit der Google Präsentationen API in einer Präsentation zusammen. Die Beispielanwendung wird mit Node.js erstellt, doch die Grundprinzipien gelten für alle Architekturen.

Lerninhalte

  • Präsentationen mithilfe der Google Slides API erstellen
  • Daten mit BigQuery aus einem großen Dataset abrufen
  • Dateien mithilfe der Google Drive API kopieren

Voraussetzungen

  • Node.js ist installiert
  • Zugriff auf das Internet und einen Webbrowser
  • Ein Google-Konto
  • Google Cloud Platform-Projekt

2. Beispielcode abrufen

Sie können entweder den gesamten Beispielcode auf Ihren Computer herunterladen…

Sie können das GitHub-Repository auch über die Befehlszeile klonen.

git clone https://github.com/googleworkspace/slides-api.git

Das Repository enthält Verzeichnisse für die einzelnen Schritte im Prozess, sodass Sie stets Zugriff auf funktionierende Versionen haben.

Sie arbeiten mit der Kopie im Verzeichnis start, können bei Bedarf aber auch auf die anderen Verzeichnisse zugreifen oder Dateien daraus kopieren.

3. Beispiel-App ausführen

Starten Sie zuerst das Node-Skript. Nachdem Sie den Code heruntergeladen haben, gehen Sie folgendermaßen vor, um die Node.js-Anwendung zu installieren und zu starten:

  1. Öffnen Sie ein Befehlszeilenterminal auf Ihrem Computer und wechseln Sie zum Verzeichnis start des Codelabs.
  2. Geben Sie den folgenden Befehl ein, um die Node.js-Abhängigkeiten zu installieren.
npm install
  1. Geben Sie den folgenden Befehl ein, um das Script auszuführen:
node .
  1. Sehen Sie sich die Begrüßung an, in der die Schritte für dieses Projekt angezeigt werden.
-- Start generating slides. --
TODO: Get Client Secrets
TODO: Authorize
TODO: Get Data from BigQuery
TODO: Create Slides
TODO: Open Slides
-- Finished generating slides. --

Eine Liste der TODOs finden Sie unter slides.js, license.js und auth.js. Beachten Sie, dass wir JavaScript Promises verwenden, um die Schritte zu verketten, die zum Ausführen der Anwendung erforderlich sind. Grund dafür ist, dass jeder einzelne Schritt nur dann ausgeführt werden kann, wenn der vorherige Schritt abgeschlossen wurde.

Falls Sie mit Promises nicht vertraut sind, machen Sie sich keine Sorgen: Der Code für dieses Lab wird Ihnen in vollem Umfang bereitgestellt. Kurz gesagt: Promises können eine asynchrone Verarbeitung von Code in etwas synchronere Bahnen lenken.

4. Clientschlüssel abrufen

Um die Google Slides API, Google BigQuery API und Google Drive API nutzen zu können, erstellen wir einen OAuth-Client und ein Dienstkonto.

Google Developers Console einrichten

  1. Verwenden Sie diesen Assistenten, um in der Google Developers Console ein Projekt zu erstellen oder auszuwählen und die API automatisch zu aktivieren. Klicken Sie auf Weiter und dann auf Zu den Anmeldedaten.
  2. Klicken Sie auf der Seite Anmeldedaten zu Projekt hinzufügen auf Abbrechen.
  3. Klicken Sie oben auf der Seite auf den Tab OAuth-Zustimmungsbildschirm. Wählen Sie eine E-Mail-Adresse aus, geben Sie den Produktnamen Slides API Codelab ein und klicken Sie auf die Schaltfläche Speichern.

BigQuery, Drive und Slides APIs aktivieren

  1. Wählen Sie den Tab Dashboard aus, klicken Sie auf die Schaltfläche API aktivieren und aktivieren Sie die folgenden drei APIs:
  2. BigQuery API
  3. Google Drive API
  4. Google Präsentationen API

OAuth-Clientschlüssel herunterladen (für Google Präsentationen und Google Drive)

  1. Wählen Sie den Tab Anmeldedaten aus. Klicken Sie dann auf die Schaltfläche Anmeldedaten erstellen und wählen Sie OAuth-Client-ID aus.
  2. Wählen Sie den Anwendungstyp Andere aus, geben Sie den Namen Google Slides API Codelab ein und klicken Sie auf die Schaltfläche Erstellen.Klicken Sie auf OK, um das angezeigte Dialogfeld zu schließen.
  3. Klicken Sie rechts neben der Client-ID auf die Schaltfläche „file_download“ (JSON herunterladen).
  4. Benennen Sie die geheime Datei in client_secret.json um und kopieren Sie sie in die Verzeichnisse start/ und finish/.

Dienstkontoschlüssel (für BigQuery) herunterladen

  1. Wählen Sie den Tab Anmeldedaten aus, klicken Sie auf die Schaltfläche Anmeldedaten erstellen und wählen Sie Dienstkontoschlüssel aus.
  2. Wählen Sie im Drop-down-Menü Neues Dienstkonto aus. Wählen Sie den Namen Slides API Codelab Service für Ihren Dienst aus. Klicken Sie dann auf Rolle, scrollen Sie zu BigQuery und wählen Sie BigQuery-Datenbetrachter und BigQuery-Jobnutzer aus.
  3. Wählen Sie als Schlüsseltyp JSON aus.
  4. Klicken Sie auf Erstellen. Die Schlüsseldatei wird automatisch auf Ihren Computer heruntergeladen. Klicken Sie auf Schließen, um das angezeigte Dialogfeld zu verlassen.
  5. Benennen Sie die geheime Datei in service_account_secret.json um und kopieren Sie sie in die Verzeichnisse start/ und finish/.

Clientschlüssel abrufen

Füllen Sie in start/auth.js die Methode getClientSecrets aus.

auth.js

const fs = require('fs');

/**
 * Loads client secrets from a local file.
 * @return {Promise} A promise to return the secrets.
 */
module.exports.getClientSecrets = () => {
  return new Promise((resolve, reject) => {
    fs.readFile('client_secret.json', (err, content) => {
      if (err) return reject('Error loading client secret file: ' + err);
      console.log('loaded secrets...');
      resolve(JSON.parse(content));
    });
  });
}

Die Clientschlüssel wurden jetzt geladen. Die Anmeldedaten werden an das nächste Promise weitergegeben. Führen Sie das Projekt mit node . aus, um sicherzugehen, dass keine Fehler vorliegen.

5. OAuth2-Client erstellen

Wenn Sie eine Präsentation erstellen möchten, müssen Sie in Google APIs eine Authentifizierung hinzufügen. Fügen Sie dazu den folgenden Code in die Datei „auth.js“ ein. Bei dieser Authentifizierung wird der Zugriff auf Ihr Google-Konto angefordert, damit Google APIs Dateien in Google Drive lesen und schreiben, Präsentationen in Google Präsentationen erstellen und schreibgeschützte Abfragen von Google BigQuery ausführen kann. Hinweis: getClientSecrets wurde nicht geändert.

auth.js

const fs = require('fs');
const readline = require('readline');
const openurl = require('openurl');
const googleAuth = require('google-auth-library');
const TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
      process.env.USERPROFILE) + '/.credentials/';
const TOKEN_PATH = TOKEN_DIR + 'slides.googleapis.com-nodejs-quickstart.json';

// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/slides.googleapis.com-nodejs-quickstart.json
const SCOPES = [
  'https://www.googleapis.com/auth/presentations', // needed to create slides
  'https://www.googleapis.com/auth/drive', // read and write files
  'https://www.googleapis.com/auth/bigquery.readonly' // needed for bigquery
];

/**
 * Loads client secrets from a local file.
 * @return {Promise} A promise to return the secrets.
 */
module.exports.getClientSecrets = () => {
  return new Promise((resolve, reject) => {
    fs.readFile('client_secret.json', (err, content) => {
      if (err) return reject('Error loading client secret file: ' + err);
      console.log('loaded secrets...');
      resolve(JSON.parse(content));
    });
  });
}

/**
 * Create an OAuth2 client promise with the given credentials.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback for the authorized client.
 * @return {Promise} A promise to return the OAuth client.
 */
module.exports.authorize = (credentials) => {
  return new Promise((resolve, reject) => {
    console.log('authorizing...');
    const clientSecret = credentials.installed.client_secret;
    const clientId = credentials.installed.client_id;
    const redirectUrl = credentials.installed.redirect_uris[0];
    const auth = new googleAuth();
    const oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

    // Check if we have previously stored a token.
    fs.readFile(TOKEN_PATH, (err, token) => {
      if (err) {
        getNewToken(oauth2Client).then(() => {
          resolve(oauth2Client);
        });
      } else {
        oauth2Client.credentials = JSON.parse(token);
        resolve(oauth2Client);
      }
    });
  });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * fulfills the promise. Modifies the `oauth2Client` object.
 * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
 * @return {Promise} A promise to modify the oauth2Client credentials.
 */
function getNewToken(oauth2Client) {
  console.log('getting new auth token...');
  openurl.open(oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES
  }));

  console.log(''); // \n
  return new Promise((resolve, reject) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });
    rl.question('Enter the code from that page here: ', (code) => {
      rl.close();
      oauth2Client.getToken(code, (err, token) => {
        if (err) return reject(err);
        oauth2Client.credentials = token;
        let storeTokenErr = storeToken(token);
        if (storeTokenErr) return reject(storeTokenErr);
        resolve();
      });
    });
  });
}

/**
 * Store token to disk be used in later program executions.
 * @param {Object} token The token to store to disk.
 * @return {Error?} Returns an error or undefined if there is no error.
 */
function storeToken(token) {
  try {
    fs.mkdirSync(TOKEN_DIR);
    fs.writeFileSync(TOKEN_PATH, JSON.stringify(token));
  } catch (err) {
    if (err.code != 'EEXIST') return err;
  }
  console.log('Token stored to ' + TOKEN_PATH);
}

6. BigQuery einrichten

BigQuery kennenlernen (optional)

Mit BigQuery lassen sich sehr große Datensätze innerhalb von Sekunden abfragen. Verwenden Sie zuerst die Weboberfläche von BigQuery und führen Sie die Abfrage dann programmatisch aus. Wenn Sie BigQuery noch nie eingerichtet haben, folgen Sie der Anleitung in dieser Kurzanleitung.

Öffnen Sie die Cloud Console, um die in BigQuery verfügbaren GitHub-Daten zu durchsuchen und eigene Abfragen auszuführen. Sehen wir uns die beliebtesten Softwarelizenzen auf GitHub an. Geben Sie dazu diese Abfrage ein und klicken Sie auf die Schaltfläche Ausführen.

bigquery.sql

WITH AllLicenses AS (
  SELECT * FROM `bigquery-public-data.github_repos.licenses`
)
SELECT
  license,
  COUNT(*) AS count,
  ROUND((COUNT(*) / (SELECT COUNT(*) FROM AllLicenses)) * 100, 2) AS percent
FROM `bigquery-public-data.github_repos.licenses`
GROUP BY license
ORDER BY count DESC
LIMIT 10

Wir haben gerade Millionen öffentlicher Repositories auf GitHub analysiert und die beliebtesten Lizenzen gefunden. Nicht schlecht, oder? Jetzt führen Sie dieselbe Abfrage noch einmal programmatisch aus.

BigQuery einrichten

Ersetzen Sie den Code in der Datei license.js. Die Funktion bigquery.query gibt ein Versprechen zurück.

license**.js**

const google = require('googleapis');
const read = require('read-file');
const BigQuery = require('@google-cloud/bigquery');
const bigquery = BigQuery({
  credentials: require('./service_account_secret.json')
});

// See codelab for other queries.
const query = `
WITH AllLicenses AS (
  SELECT * FROM \`bigquery-public-data.github_repos.licenses\`
)
SELECT
  license,
  COUNT(*) AS count,
  ROUND((COUNT(*) / (SELECT COUNT(*) FROM AllLicenses)) * 100, 2) AS percent
FROM \`bigquery-public-data.github_repos.licenses\`
GROUP BY license
ORDER BY count DESC
LIMIT 10
`;

/**
 * Get the license data from BigQuery and our license data.
 * @return {Promise} A promise to return an object of licenses keyed by name.
 */
module.exports.getLicenseData = (auth) => {
  console.log('querying BigQuery...');
  return bigquery.query({
    query,
    useLegacySql: false,
    useQueryCache: true,
  }).then(bqData => Promise.all(bqData[0].map(getLicenseText)))
    .then(licenseData => new Promise((resolve, reject) => {
      resolve([auth, licenseData]);
    }))
    .catch((err) => console.error('BigQuery error:', err));
}

/**
 * Gets a promise to get the license text about a license
 * @param {object} licenseDatum An object with the license's
 *   `license`, `count`, and `percent`
 * @return {Promise} A promise to return license data with license text.
 */
function getLicenseText(licenseDatum) {
  const licenseName = licenseDatum.license;
  return new Promise((resolve, reject) => {
    read(`licenses/${licenseName}.txt`, 'utf8', (err, buffer) => {
      if (err) return reject(err);
      resolve({
        licenseName,
        count: licenseDatum.count,
        percent: licenseDatum.percent,
        license: buffer.substring(0, 1200) // first 1200 characters
      });
    });
  });
}

Versuchen Sie, einige der Daten im Rückruf des Promises in console.log zu protokollieren, um die Struktur der Objekte zu verstehen und den Code in Aktion zu sehen.

7. Folien erstellen

Jetzt kommen wir zur Darstellung der Ergebnisse in einer Präsentation. Erstellen Sie Folien, indem Sie die Methoden create und batchUpdate der Slides API aufrufen. Unsere Datei sollte durch Folgendes ersetzt werden:

slides.js

const google = require('googleapis');
const slides = google.slides('v1');
const drive = google.drive('v3');
const openurl = require('openurl');
const commaNumber = require('comma-number');

const SLIDE_TITLE_TEXT = 'Open Source Licenses Analysis';

/**
 * Get a single slide json request
 * @param {object} licenseData data about the license
 * @param {object} index the slide index
 * @return {object} The json for the Slides API
 * @example licenseData: {
 *            "licenseName": "mit",
 *            "percent": "12.5",
 *            "count": "1667029"
 *            license:"<body>"
 *          }
 * @example index: 3
 */
function createSlideJSON(licenseData, index) {
  // Then update the slides.
  const ID_TITLE_SLIDE = 'id_title_slide';
  const ID_TITLE_SLIDE_TITLE = 'id_title_slide_title';
  const ID_TITLE_SLIDE_BODY = 'id_title_slide_body';

  return [{
    // Creates a "TITLE_AND_BODY" slide with objectId references
    createSlide: {
      objectId: `${ID_TITLE_SLIDE}_${index}`,
      slideLayoutReference: {
        predefinedLayout: 'TITLE_AND_BODY'
      },
      placeholderIdMappings: [{
        layoutPlaceholder: {
          type: 'TITLE'
        },
        objectId: `${ID_TITLE_SLIDE_TITLE}_${index}`
      }, {
        layoutPlaceholder: {
          type: 'BODY'
        },
        objectId: `${ID_TITLE_SLIDE_BODY}_${index}`
      }]
    }
  }, {
    // Inserts the license name, percent, and count in the title
    insertText: {
      objectId: `${ID_TITLE_SLIDE_TITLE}_${index}`,
      text: `#${index + 1} ${licenseData.licenseName}  — ~${licenseData.percent}% (${commaNumber(licenseData.count)} repos)`
    }
  }, {
    // Inserts the license in the text body paragraph
    insertText: {
      objectId: `${ID_TITLE_SLIDE_BODY}_${index}`,
      text: licenseData.license
    }
  }, {
    // Formats the slide paragraph's font
    updateParagraphStyle: {
      objectId: `${ID_TITLE_SLIDE_BODY}_${index}`,
      fields: '*',
      style: {
        lineSpacing: 10,
        spaceAbove: {magnitude: 0, unit: 'PT'},
        spaceBelow: {magnitude: 0, unit: 'PT'},
      }
    }
  }, {
    // Formats the slide text style
    updateTextStyle: {
      objectId: `${ID_TITLE_SLIDE_BODY}_${index}`,
      style: {
        bold: true,
        italic: true,
        fontSize: {
          magnitude: 10,
          unit: 'PT'
        }
      },
      fields: '*',
    }
  }];
}

/**
 * Creates slides for our presentation.
 * @param {authAndGHData} An array with our Auth object and the GitHub data.
 * @return {Promise} A promise to return a new presentation.
 * @see https://developers.google.com/apis-explorer/#p/slides/v1/
 */
module.exports.createSlides = (authAndGHData) => new Promise((resolve, reject) => {
  console.log('creating slides...');
  const [auth, ghData] = authAndGHData;

  // First copy the template slide from drive.
  drive.files.copy({
    auth: auth,
    fileId: '1toV2zL0PrXJOfFJU-NYDKbPx9W0C4I-I8iT85TS0fik',
    fields: 'id,name,webViewLink',
    resource: {
      name: SLIDE_TITLE_TEXT
    }
  }, (err, presentation) => {
    if (err) return reject(err);

    const allSlides = ghData.map((data, index) => createSlideJSON(data, index));
    slideRequests = [].concat.apply([], allSlides); // flatten the slide requests
    slideRequests.push({
      replaceAllText: {
        replaceText: SLIDE_TITLE_TEXT,
        containsText: { text: '{{TITLE}}' }
      }
    })

    // Execute the requests
    slides.presentations.batchUpdate({
      auth: auth,
      presentationId: presentation.id,
      resource: {
        requests: slideRequests
      }
    }, (err, res) => {
      if (err) {
        reject(err);
      } else {
        resolve(presentation);
      }
    });
  });
});

8. Folien öffnen

Öffnen Sie als Nächstes die Präsentation im Browser. Aktualisieren Sie die folgende Methode in slides.js.

slides.js

/**
 * Opens a presentation in a browser.
 * @param {String} presentation The presentation object.
 */
module.exports.openSlidesInBrowser = (presentation) => {
  console.log('Presentation URL:', presentation.webViewLink);
  openurl.open(presentation.webViewLink);
}

Führen Sie Ihr Projekt noch einmal aus, um das Endergebnis zu sehen.

9. Glückwunsch!

Sie haben aus Daten, die Sie vorher mit BigQuery analysiert haben, eine Google-Präsentation generiert. Das Skript nutzt die Google Slides API zur Erstellung einer Präsentation und BigQuery zur Analyse der beliebtesten Softwarelizenzen.

Mögliche Verbesserungen

Die Integration lässt sich natürlich noch weiter verbessern. Unter anderem wäre Folgendes denkbar:

  • Jede Folie mit Bildern versehen
  • Folien per E-Mail mit der Gmail API teilen
  • Vorlagenfolie als Befehlszeilenargument anpassen

Weitere Informationen