Membuat presentasi Google Slide dari Big Data di Node.js

1. Ringkasan

Dalam codelab ini, Anda akan mempelajari cara menggunakan Google Slide sebagai alat presentasi kustom untuk analisis lisensi software yang paling umum. Anda akan membuat kueri untuk semua kode open source di GitHub menggunakan BigQuery API dan membuat slide presentasi menggunakan Google Slides API untuk mempresentasikan hasilnya. Aplikasi contoh dibuat menggunakan Node.js, tetapi prinsip dasar yang sama berlaku untuk arsitektur apa pun.

Yang akan Anda pelajari

  • Membuat presentasi menggunakan Slides API
  • Menggunakan BigQuery untuk mendapatkan insight tentang set data yang besar
  • Menyalin file menggunakan Google Drive API

Yang Anda butuhkan

  • Node.js terinstal
  • Akses ke internet dan browser web
  • Akun Google
  • Project Google Cloud Platform

2. Mendapatkan kode contoh

Anda bisa mendownload semua kode contoh ke komputer Anda...

...atau membuat clone repositori GitHub dari command line.

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

Repositori berisi kumpulan direktori yang mewakili setiap langkah sepanjang proses, jika Anda perlu mereferensikan versi yang berfungsi.

Anda akan mengerjakan salinan yang terletak di direktori start, tetapi Anda dapat merujuk ke, atau menyalin file dari, file lainnya sesuai kebutuhan.

3. Menjalankan aplikasi contoh

Pertama, mari kita siapkan dan jalankan skrip Node. Setelah kode didownload, ikuti petunjuk di bawah ini untuk menginstal dan memulai aplikasi Node.js:

  1. Buka terminal command line di komputer Anda dan buka direktori start codelab.
  2. Masukkan perintah berikut untuk menginstal dependensi Node.js.
npm install
  1. Masukkan perintah berikut untuk menjalankan skrip:
node .
  1. Amati salam yang menunjukkan langkah-langkah untuk proyek ini.
-- Start generating slides. --
TODO: Get Client Secrets
TODO: Authorize
TODO: Get Data from BigQuery
TODO: Create Slides
TODO: Open Slides
-- Finished generating slides. --

Anda dapat melihat daftar TODO dalam slides.js, license.js, dan auth.js. Perlu diperhatikan bahwa kita menggunakan Promise JavaScript untuk merantai langkah-langkah yang diperlukan untuk menyelesaikan aplikasi karena setiap langkah bergantung pada langkah yang telah diselesaikan sebelumnya.

Jika Anda tidak terbiasa dengan promise, jangan khawatir, kami akan menyediakan semua kode yang Anda perlukan. Singkatnya, promise memberi kita cara untuk menangani pemrosesan asinkron secara lebih sinkron.

4. Mendapatkan Rahasia Klien

Untuk menggunakan Slide, BigQuery, dan Drive API, kita akan membuat Klien OAuth dan Akun Layanan.

Siapkan Google Developers Console

  1. Gunakan wizard ini untuk membuat atau memilih project di Google Developers Console dan mengaktifkan API secara otomatis. Klik Lanjutkan, lalu Buka kredensial.
  2. Di halaman Add credentials to your project, klik tombol Cancel.
  3. Di bagian atas halaman, pilih tab OAuth consent screen. Pilih Alamat email, masukkan Nama produk Slides API Codelab, lalu klik tombol Simpan.

Mengaktifkan BigQuery, Drive, dan Slides API

  1. Pilih tab Dashboard, klik tombol Enable API, lalu aktifkan 3 API berikut:
  2. BigQuery API
  3. API Google Drive
  4. API Google Slide

Download Rahasia Klien OAuth (untuk Slide dan Drive)

  1. Pilih tab Credentials, klik tombol Create credentials, lalu pilih OAuth client ID.
  2. Pilih jenis aplikasi Other, masukkan nama Google Slides API Codelab, dan klik tombol Create.Klik OK untuk menutup dialog yang muncul.
  3. Klik tombol file_download (Download JSON) di sebelah kanan client ID.
  4. Ganti nama file rahasia Anda menjadi client_secret.json dan salin ke direktori start/ dan finish/.

Download Rahasia Akun Layanan (untuk BigQuery)

  1. Pilih tab Credentials, klik tombol Create credentials, lalu pilih Service account key.
  2. Di menu dropdown, pilih New Service Account. Pilih nama Slides API Codelab Service untuk layanan Anda. Selanjutnya, klik Role dan scroll ke BigQuery, lalu pilih BigQuery Data Viewer dan BigQuery Job User.
  3. Untuk Jenis kunci, pilih JSON.
  4. Klik Buat. File kunci akan otomatis didownload ke komputer Anda. Klik Tutup untuk keluar dari dialog yang muncul.
  5. Ganti nama file rahasia Anda menjadi service_account_secret.json dan salin ke direktori start/ dan finish/.

Mendapatkan Rahasia Klien

Di start/auth.js, mari kita isi metode 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));
    });
  });
}

Sekarang kita telah memuat secret klien. Kredensial tersebut akan diteruskan ke promise berikutnya. Jalankan project dengan node . untuk memastikan tidak ada error.

5. Membuat Klien OAuth2

Untuk membuat slide, mari kita tambahkan autentikasi ke Google API dengan menambahkan kode berikut ke file auth.js kita. Autentikasi ini akan meminta akses ke Akun Google Anda untuk membaca dan menulis file di Google Drive, membuat presentasi di Google Slide, dan menjalankan kueri hanya baca dari Google BigQuery. (Catatan: Kami tidak mengubah getClientSecrets)

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

Menjelajahi BigQuery (Opsional)

BigQuery memungkinkan kita untuk melakukan kueri {i>dataset<i} yang sangat besar dalam hitungan detik. Mari kita gunakan antarmuka web sebelum membuat kueri secara terprogram. Jika Anda belum pernah menyiapkan BigQuery sebelumnya, ikuti langkah-langkah dalam panduan memulai ini.

Buka Konsol Cloud untuk menjelajahi data GitHub yang tersedia di BigQuery dan menjalankan kueri Anda sendiri. Mari kita cari tahu lisensi software yang paling populer di GitHub dengan menulis kueri ini dan menekan tombol Run.

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

Kami baru saja menganalisis jutaan repo publik di GitHub dan menemukan lisensi yang paling populer. Keren! Sekarang, mari kita siapkan dan menjalankan kueri yang sama, tetapi kali ini secara terprogram.

Menyiapkan BigQuery

Ganti kode dalam file license.js. Fungsi bigquery.query akan menampilkan promise.

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

Cobalah untuk melakukan console.log pada beberapa data di dalam callback Promise untuk memahami struktur objek dan melihat cara kerja kode.

7. Buat Slide

Sekarang, bagian yang menyenangkan! Mari kita buat slide dengan memanggil metode create dan batchUpdate Slides API. File kita harus diganti dengan kode berikut:

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. Buka Slide

Terakhir, mari kita buka presentasi di {i>browser<i}. Perbarui metode berikut di 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);
}

Jalankan proyek Anda untuk terakhir kalinya guna menampilkan hasil akhir.

9. Selamat!

Anda telah berhasil membuat Google Slide dari data yang dianalisis menggunakan BigQuery. Skrip Anda membuat presentasi menggunakan Google Slides API dan BigQuery untuk melaporkan analisis lisensi software yang paling umum.

Kemungkinan Peningkatan

Berikut beberapa ide tambahan untuk membuat integrasi yang lebih menarik:

  • Tambahkan gambar ke setiap slide
  • Bagikan slide Anda melalui email menggunakan Gmail API
  • Menyesuaikan slide template sebagai argumen command line

Pelajari Lebih Lanjut