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 presentasi slide menggunakan Google Slides API untuk menyajikan 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 besar
  • Menyalin file menggunakan Google Drive API

Yang Anda butuhkan

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

2. Mendapatkan kode contoh

Anda dapat mendownload semua kode contoh ke komputer...

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

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

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

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

3. Menjalankan aplikasi contoh

Pertama, mari kita siapkan dan jalankan skrip Node. Setelah kode didownload, ikuti petunjuk di bawah 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 ucapan yang menunjukkan langkah-langkah untuk project 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 kami di slides.js, license.js, dan auth.js. Perhatikan bahwa kita menggunakan Promise JavaScript untuk merantai langkah-langkah yang diperlukan untuk menyelesaikan aplikasi karena setiap langkah bergantung pada langkah sebelumnya yang telah selesai.

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 dengan cara yang lebih sinkron.

4. Mendapatkan Rahasia Klien

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

Menyiapkan Google Developers Console

  1. Gunakan wizard ini untuk membuat atau memilih project di Konsol Google Developers dan secara otomatis mengaktifkan API. Klik Lanjutkan, lalu Buka kredensial.
  2. Di halaman Tambahkan kredensial ke project Anda, klik tombol Batal.
  3. Di bagian atas halaman, pilih tab Layar persetujuan OAuth. Pilih Alamat email, masukkan Nama produk Slides API Codelab, lalu klik tombol Simpan.

Mengaktifkan BigQuery, Drive, dan Slides API

  1. Pilih tab Dasbor, klik tombol Aktifkan API, lalu aktifkan 3 API berikut:
  2. BigQuery API
  3. Google Drive API
  4. Google Slides API

Mendownload Rahasia Klien OAuth (untuk Slide dan Drive)

  1. Pilih tab Credentials, klik tombol Create credentials, lalu pilih OAuth client ID.
  2. Pilih jenis aplikasi Lainnya, masukkan nama Google Slides API Codelab, lalu klik tombol Buat.Klik Oke 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, lalu salin ke direktori start/ dan finish/.

Mendownload Secret Akun Layanan (untuk BigQuery)

  1. Pilih tab Kredensial, klik tombol Buat kredensial, lalu pilih Kunci akun layanan.
  2. Di menu dropdown, pilih Akun Layanan Baru. Pilih nama Slides API Codelab Service untuk layanan Anda. Kemudian, 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, lalu 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 rahasia klien. Kredensial 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. 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 membuat kueri set data 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 untuk menjalankan kueri yang sama, tetapi kali ini secara terprogram.

Menyiapkan BigQuery

Ganti kode di 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
      });
    });
  });
}

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

7. Buat Slide

Sekarang saatnya 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 browser. 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 project 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:

  • Menambahkan gambar ke setiap slide
  • Membagikan slide melalui email menggunakan Gmail API
  • Menyesuaikan slide template sebagai argumen command line

Pelajari Lebih Lanjut