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

1. Übersicht

In diesem Codelab erfahren Sie, wie Sie Google Präsentationen als benutzerdefiniertes Präsentationstool für eine Analyse der gängigsten Softwarelizenzen verwenden. Sie fragen den gesamten Open-Source-Code auf GitHub mit der BigQuery API ab und erstellen eine Präsentation mit der Google Slides API, um Ihre Ergebnisse zu präsentieren. Die Beispielanwendung wird mit Node.js erstellt, aber die gleichen 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 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...

Oder Sie klonen das GitHub-Repository über die Befehlszeile.

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

Das Repository enthält eine Reihe von Verzeichnissen, die die einzelnen Schritte des Prozesses darstellen, für den Fall, dass Sie auf eine funktionierende Version verweisen müssen.

Sie arbeiten mit der Kopie im Verzeichnis start, aber Sie können bei Bedarf auf die anderen Dateien verweisen oder Dateien aus den anderen kopieren.

3. Beispiel-App ausführen

Lassen Sie uns zuerst das Node-Skript starten und ausführen. Nachdem Sie den Code heruntergeladen haben, folgen Sie der Anleitung unten, um die Node.js-Anwendung zu installieren und zu starten:

  1. Öffnen Sie ein Befehlszeilenterminal auf Ihrem Computer und rufen Sie das Verzeichnis start des Codelab auf.
  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 Skript auszuführen:
node .
  1. Halten Sie sich an die Begrüßung, die die Schritte für dieses Projekt zeigt.
-- Start generating slides. --
TODO: Get Client Secrets
TODO: Authorize
TODO: Get Data from BigQuery
TODO: Create Slides
TODO: Open Slides
-- Finished generating slides. --

Unsere Liste der Aufgaben findest du in slides.js, license.js und auth.js. Beachten Sie, dass wir JavaScript Promises verwenden, um die Schritte, die zum Ausführen der App erforderlich sind, verketten, da jeder Schritt davon abhängt, ob der vorherige Schritt ausgeführt wurde.

Keine Sorge, wenn du mit Versprechen nicht vertraut bist, erhältst du den gesamten Code, den du benötigst. Kurz gesagt bieten uns Versprechen die Möglichkeit, die asynchrone Verarbeitung synchroner abzuwickeln.

4. Clientschlüssel abrufen

Für die Verwendung der Präsentationen, BigQuery und Drive APIs erstellen wir einen OAuth-Client und ein Dienstkonto.

Google Developers Console einrichten

  1. Verwenden Sie diesen Assistenten, um ein Projekt in der Google Developers Console 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 Slides API

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

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

Dienstkonto-Secret herunterladen (für BigQuery)

  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 sowohl BigQuery-Datenbetrachter als auch 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 Ihre Secret-Datei in service_account_secret.json um und kopieren Sie sie in die Verzeichnisse start/ und Finish/.

Clientschlüssel abrufen

In start/auth.js füllen wir 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 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

Fügen Sie zum Erstellen von Folien der Authentifizierung für Google APIs hinzu, indem Sie der Datei auth.js den folgenden Code hinzufügen. Bei dieser Authentifizierung wird Zugriff auf Ihr Google-Konto angefordert, um Dateien in Google Drive zu lesen und zu schreiben, Präsentationen in Google Präsentationen zu erstellen und schreibgeschützte Abfragen in Google BigQuery auszuführen. (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 können wir riesige Datensätze in Sekundenschnelle abfragen. Lassen Sie uns die Weboberfläche verwenden, bevor wir eine programmatische Abfrage ausführen. Wenn Sie BigQuery noch nie eingerichtet haben, führen Sie die Schritte in dieser Kurzanleitung aus.

Öffnen Sie die Cloud Console, um die in BigQuery verfügbaren GitHub-Daten zu durchsuchen und eigene Abfragen auszuführen. Schreiben Sie die Abfrage und klicken Sie auf die Schaltfläche Ausführen, um die beliebtesten Softwarelizenzen auf GitHub herauszufinden.

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 von öffentlichen Repositories auf GitHub analysiert und die beliebtesten Lizenzen ermittelt. Cool! 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 eine Zusage 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 Callback unseres Promise mit console.log zu versehen, um die Struktur unserer 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. Ersetzen Sie unsere Datei wie folgt:

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

Zum Schluss öffnen wir 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 ein letztes Mal aus, um das Endergebnis anzuzeigen.

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:

  • Jeder Folie Bilder hinzufügen
  • Folien mithilfe der Gmail API per E-Mail teilen
  • Vorlagenfolie als Befehlszeilenargument anpassen

Weitere Informationen