Gere apresentações Google usando Big Data no Node.js

1. Visão geral

Neste codelab, você vai aprender a usar o app Apresentações Google como uma ferramenta de apresentação personalizada para uma análise das licenças de software mais comuns. Você vai consultar todo o código aberto no GitHub usando a API BigQuery e criar uma apresentação de slides com a API Google Slides para apresentar os resultados. O aplicativo de amostra é criado com o Node.js, mas os mesmos princípios básicos se aplicam a qualquer arquitetura.

O que você vai aprender

  • Criar apresentações usando a API Slides
  • Usar o BigQuery para extrair informações de um conjunto de dados grande
  • Copiar um arquivo usando a API Google Drive

O que é necessário

  • Node.js instalado
  • Acesso à Internet e a um navegador da Web
  • Uma Conta do Google
  • Um projeto do Google Cloud Platform

2. Fazer o download do exemplo de código

Você pode fazer o download de todo o exemplo de código para seu computador...

...ou clonar o repositório do GitHub pela linha de comando.

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

O repositório contém um conjunto de diretórios que representam cada etapa do processo, caso você precise fazer referência a uma versão em funcionamento.

Você trabalhará com a cópia localizada no diretório start, mas poderá consultar ou copiar arquivos dos outros conforme necessário.

3. Executar o app de amostra

Primeiro, vamos executar o script do Node. Depois de fazer o download do código, siga as instruções abaixo para instalar e iniciar o aplicativo Node.js:

  1. Abra um terminal de linha de comando no computador e navegue até o diretório start do codelab.
  2. Digite o comando abaixo para instalar as dependências do Node.js.
npm install 
  1. Digite este comando para executar o script:
node . 
  1. Observe a saudação que mostra as etapas deste projeto.
-- Start generating slides. -- TODO: Get Client Secrets TODO: Authorize TODO: Get Data from BigQuery TODO: Create Slides TODO: Open Slides -- Finished generating slides. -- 

Confira nossa lista de O que fazer em slides.js, license.js e auth.js. Usamos promessas em JavaScript para encadear as etapas necessárias para concluir o app, já que cada uma delas depende da conclusão da anterior.

Se você não conhece as promessas, não se preocupe. Forneceremos todo o código necessário. Em resumo, as promessas nos dão uma maneira de lidar com o processamento assíncrono de maneira mais síncrona.

4. Crie as chaves secretas do cliente

Para usar as APIs Slides, BigQuery e Drive, criaremos um cliente OAuth e uma conta de serviço.

Configure o Google Developers Console

  1. Use este assistente para criar ou selecionar um projeto no Google Developers Console e ativar a API automaticamente. Clique em Continuar e em Acessar credenciais.
  2. Na página Add credentials to your project, clique no botão Cancel.
  3. Na parte superior da página, selecione a guia OAuth consent screen. Selecione um Endereço de e-mail, digite o nome do produto Slides API Codelab e clique no botão Salvar.

Ative as APIs BigQuery, Drive e Slides

  1. Selecione a guia Painel, clique no botão Ativar API e ative as três APIs a seguir:
  2. API BigQuery
  3. API Google Drive
  4. API Google Slides

Baixar a chave secreta do cliente OAuth (para Apresentações e Drive)

  1. Selecione a guia Credenciais, clique no botão Criar credenciais e selecione ID do cliente do OAuth.
  2. Selecione o tipo de aplicativo Outro, insira o nome Google Slides API Codelab e clique no botão Criar.Clique em OK para fechar a caixa de diálogo que aparece.
  3. Clique no botão "file_download" (Fazer o download do JSON) à direita do ID do cliente.
  4. Renomeie seu arquivo secreto como client_secret.json e copie-o nos diretórios start/ e finish/.

Fazer o download da chave secreta da conta de serviço (para BigQuery)

  1. Selecione a guia Credenciais, clique no botão Criar credenciais e selecione Chave da conta de serviço.
  2. No menu suspenso, selecione Nova conta de serviço. Escolha o nome Slides API Codelab Service para o serviço. Em seguida, clique em Papel, role até BigQuery e selecione Leitor de dados do BigQuery e Usuário de jobs do BigQuery.
  3. Em Tipo de chave, selecione JSON.
  4. Clique em Criar. O arquivo da chave será baixado automaticamente para o computador. Clique em Fechar para fechar a caixa de diálogo que aparece.
  5. Renomeie seu arquivo secreto como service_account_secret.json e copie-o nos diretórios start/ e finish/.

Crie as chaves secretas do cliente

Em start/auth.js, vamos preencher o método 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));     });   }); } 

Agora carregamos as chaves secretas do cliente. As credenciais serão transmitidas para a próxima promessa. Execute o projeto com node . para garantir que não haja erros.

5. Crie um cliente OAuth2

Para criar slides, vamos adicionar a autenticação às APIs do Google adicionando o código a seguir ao nosso arquivo auth.js. Essa autenticação solicitará acesso à sua Conta do Google para ler e gravar arquivos no Google Drive, criar apresentações no Apresentações Google e executar consultas somente leitura no Google BigQuery. Observação: não mudamos 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. Configure o BigQuery

conhecer o BigQuery (opcional)

Com o BigQuery, é possível consultar conjuntos de dados enormes em segundos. Vamos usar a interface da Web antes de fazer consultas de forma programática. Se você nunca configurou o BigQuery, siga as etapas deste guia de início rápido.

Abra o console do Cloud para navegar pelos dados do GitHub disponíveis no BigQuery e executar suas próprias consultas. Vamos descobrir as licenças de software mais populares no GitHub escrevendo esta consulta e pressionando o botão 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 

Acabamos de analisar milhões de repositórios públicos no GitHub e descobrimos as licenças mais populares. Legal! Agora vamos configurar essa mesma consulta programaticamente.

Configure o BigQuery

Substitua o código no arquivo license.js. A função bigquery.query vai retornar uma promessa.

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

Tente usar console.log em alguns dos dados no callback da promessa para entender a estrutura dos objetos e ver o código em ação.

7. Criar slides

A diversão começa agora! Vamos criar slides chamando os métodos create e batchUpdate da API Slides. Nosso arquivo será substituído pelo seguinte:

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. Abra os slides

Por fim, vamos abrir a apresentação no navegador. Atualize o método a seguir em 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); } 

Execute o projeto uma última vez para mostrar o resultado final.

9. Parabéns!

Você gerou slides no Apresentações Google usando dados analisados com o BigQuery. Seu script cria uma apresentação usando a API Google Slides e o BigQuery para mostrar uma análise das licenças de software mais comuns.

Possíveis melhorias

Veja estas outras ideias para fazer uma integração ainda mais interessante:

  • Adicionar imagens a cada slide
  • Compartilhar seus slides por e-mail usando a API Gmail
  • Personalizar o modelo de slide como um argumento de linha de comando

Saiba mais