Generowanie prezentacji Google na podstawie big data w Node.js

1. Omówienie

Z tego ćwiczenia w Codelabs dowiesz się, jak używać Prezentacji Google jako własnych narzędzi do prezentacji, aby przeprowadzić analizę najpopularniejszych licencji na oprogramowanie. Będziesz wysyłać zapytania do całego kodu open source w GitHubie za pomocą interfejsu BigQuery API, a także przygotować prezentację za pomocą interfejsu Google Prezentacji API, aby zaprezentować wyniki. Przykładowa aplikacja została skompilowana w środowisku Node.js, ale te same podstawowe zasady mają zastosowanie do każdej architektury.

Czego się nauczysz

  • Tworzenie prezentacji przy użyciu interfejsu API Prezentacji
  • Korzystanie z BigQuery do uzyskiwania wglądu w duży zbiór danych
  • Kopiowanie pliku przy użyciu interfejsu Google Drive API

Czego potrzebujesz

  • Zainstalowano Node.js
  • Dostęp do internetu i przeglądarki
  • konto Google,
  • Projekt Google Cloud Platform

2. Pobieranie przykładowego kodu

Możesz pobrać cały przykładowy kod na swój komputer...

...lub skopiuj repozytorium GitHub z poziomu wiersza poleceń.

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

Repozytorium zawiera zbiór katalogów reprezentujących każdy etap procesu, na wypadek gdyby trzeba było odwoływać się do roboczej wersji.

Będziesz pracować na kopii znajdującej się w katalogu start, ale w razie potrzeby możesz odwołać się do innych plików lub kopiować z nich pliki.

3. Uruchamianie przykładowej aplikacji

Najpierw skonfigurujmy skrypt Node. Po pobraniu kodu wykonaj te instrukcje, aby zainstalować i uruchomić aplikację Node.js:

  1. Otwórz terminal wiersza poleceń na komputerze i przejdź do katalogu start ćwiczenia w Codelabs.
  2. Wpisz podane niżej polecenie, aby zainstalować zależności Node.js.
npm install
  1. Wpisz to polecenie, aby uruchomić skrypt:
node .
  1. Zwróć uwagę na powitanie, które wyświetla kroki dla tego projektu.
-- Start generating slides. --
TODO: Get Client Secrets
TODO: Authorize
TODO: Get Data from BigQuery
TODO: Create Slides
TODO: Open Slides
-- Finished generating slides. --

Listę zadań do wykonania znajdziesz w slides.js, license.js i auth.js. Pamiętaj, że do łączenia kroków wymaganych do ukończenia aplikacji używamy obietnic JavaScriptu, ponieważ każdy z nich zależy od tego, jaki krok został ukończony.

Jeśli nie znasz obietnic, nie martw się – dostarczymy cały potrzebny kod. Krótko mówiąc, obietnice pozwolą nam obsługiwać przetwarzanie asynchroniczne w bardziej synchroniczny sposób.

4. Pobieranie tajnych kluczy klienta

Aby korzystać z interfejsów API Prezentacji, BigQuery i Dysku, utworzymy klienta OAuth i konto usługi.

Skonfiguruj Google Developers Console

  1. Użyj tego kreatora, aby utworzyć lub wybrać projekt w Google Developers Console i automatycznie włączyć interfejs API. Kliknij Dalej, a następnie Przejdź do danych logowania.
  2. Na stronie Dodaj dane logowania do projektu kliknij przycisk Anuluj.
  3. U góry strony wybierz kartę Ekran zgody OAuth. Wybierz Adres e-mail, wpisz nazwę produktu Slides API Codelab i kliknij przycisk Zapisz.

Włączanie interfejsów API BigQuery, Dysku i Prezentacji

  1. Wybierz kartę Panel, kliknij przycisk Włącz API i włącz te 3 interfejsy API:
  2. BigQuery API.
  3. Interfejs Google Drive API
  4. Interfejs API Prezentacji Google

Pobierz tajny klucz klienta OAuth (na potrzeby Prezentacji i Dysku)

  1. Wybierz kartę Dane logowania, kliknij przycisk Utwórz dane logowania i wybierz Identyfikator klienta OAuth.
  2. Wybierz typ aplikacji Other (Inna), wpisz nazwę Google Slides API Codelab i kliknij przycisk Create (Utwórz). Kliknij OK, aby zamknąć okno dialogowe.
  3. Kliknij przycisk file_download (Pobierz plik JSON) po prawej stronie identyfikatora klienta.
  4. Zmień nazwę pliku obiektu tajnego na client_secret.json i skopiuj go do katalogów start/ i finish/.

Pobierz tajny klucz konta usługi (dla BigQuery)

  1. Wybierz kartę Dane logowania, kliknij przycisk Utwórz dane logowania i wybierz Klucz konta usługi.
  2. W menu wybierz New Service Account (Nowe konto usługi). Wybierz nazwę Slides API Codelab Service dla swojej usługi. Następnie kliknij Rola i przewiń do sekcji BigQuery, a potem zaznacz Wyświetlający dane BigQuery i Użytkownik zadań BigQuery.
  3. W polu Typ klucza wybierz JSON.
  4. Kliknij Utwórz. Plik klucza zostanie automatycznie pobrany na Twój komputer. Kliknij Zamknij, aby zamknąć wyświetlone okno.
  5. Zmień nazwę pliku obiektu tajnego na service_account_secret.json i skopiuj go do katalogów start/ i finish/.

Pobieranie tajnych kluczy klienta

W języku start/auth.js wypełnimy metodę getClientSecrets.

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));
    });
  });
}

Klucze tajny klienta zostały wczytane. Dane logowania zostaną przekazane do następnej obietnicy. Uruchom projekt za pomocą narzędzia node ., aby sprawdzić, czy nie ma błędów.

5. Utwórz klienta OAuth2

Aby utworzyć slajdy, dodajmy uwierzytelnianie do interfejsów API Google, dodając poniższy kod do pliku auth.js. W ramach tego uwierzytelniania będzie można prosić o dostęp do Twojego konta Google z możliwością odczytywania i zapisywania plików na Dysku Google, tworzenia prezentacji w Prezentacjach Google oraz wykonywania zapytań tylko do odczytu z Google BigQuery. (Uwaga: getClientSecrets nie został zmieniony)

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. Skonfiguruj BigQuery

Poznaj BigQuery (opcjonalnie)

BigQuery pozwala nam wysyłać zapytania do ogromnych zbiorów danych w kilka sekund. Zanim zaczniesz tworzyć zapytania programowe, użyj interfejsu internetowego. Jeśli dopiero zaczynasz konfigurować BigQuery, wykonaj czynności opisane w tym krótkim wprowadzeniu.

Otwórz konsolę Cloud, aby przeglądać dane GitHub dostępne w BigQuery i uruchamiać własne zapytania. Aby poznać najpopularniejsze licencje na oprogramowanie w GitHubie, napisz to zapytanie i naciśnij przycisk Uruchom.

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

Właśnie przeanalizowaliśmy miliony publicznych repozytoriów w GitHubie i odkryliśmy najpopularniejsze licencje. Super! Teraz skonfigurujmy uruchamianie tego samego zapytania, ale tym razem automatycznie.

Skonfiguruj BigQuery

Zastąp kod w pliku license.js. Funkcja bigquery.query zwróci obietnicę.

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
      });
    });
  });
}

Spróbuj console.log niektóre dane zawarte w wywołaniu zwrotnym Promise, aby zrozumieć strukturę naszych obiektów i zobaczyć, jak działa kod.

7. Tworzenie prezentacji

Teraz zaczyna się najciekawsza część: Możesz utworzyć slajdy, wywołując metody create i batchUpdate interfejsu Prezentacje API. Nasz plik powinien zostać zastąpiony następującym plikiem:

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. Otwórz Prezentacje

Na koniec otwórz prezentację w przeglądarce. Zaktualizuj tę metodę w 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);
}

Uruchom projekt po raz ostatni, aby zobaczyć efekt końcowy.

9. Gratulacje!

Udało Ci się wygenerować Prezentacje Google na podstawie danych przeanalizowanych w BigQuery. Skrypt tworzy prezentację przy użyciu interfejsu API Prezentacji Google i BigQuery, aby zgłosić analizę najpopularniejszych licencji na oprogramowanie.

Możliwe ulepszenia

Oto kilka dodatkowych pomysłów na jeszcze atrakcyjniejszą integrację:

  • Dodaj obrazy do każdego slajdu
  • Udostępnij slajdy w e-mailu, korzystając z interfejsu Gmail API.
  • Dostosuj slajd szablonu jako argument wiersza poleceń

Więcej informacji