إنشاء عروض تقديمية في "العروض التقديمية من Google" من Big Data في Node.js

1. نظرة عامة

في هذا الدرس التطبيقي حول الترميز، ستتعلم كيفية استخدام "العروض التقديمية من Google" كأداة عرض تقديمي مخصّصة لتحليل تراخيص البرامج الأكثر شيوعًا. ستُجري طلب بحث عن كل الرموز البرمجية المفتوحة المصدر على GitHub باستخدام BigQuery API وستنشئ عرض شرائح باستخدام Google Slides API لعرض نتائجك. تم إنشاء نموذج التطبيق باستخدام Node.js، ولكن تنطبق المبادئ الأساسية نفسها على أي بنية.

ما ستتعرَّف عليه

  • إنشاء العروض التقديمية باستخدام واجهة برمجة تطبيقات العروض التقديمية
  • استخدام BigQuery لاكتساب رؤى حول مجموعة بيانات كبيرة
  • نسخ ملف باستخدام Google Drive API

المتطلبات

  • تم تثبيت Node.js.
  • الوصول إلى الإنترنت ومتصفّح الويب
  • حساب Google
  • مشروع Google Cloud Platform

2. الحصول على الرمز النموذجي

يمكنك تنزيل نموذج التعليمات البرمجية بالكامل على الكمبيوتر...

...أو استنساخ مستودع جيت هب من سطر الأوامر.

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

يحتوي المستودع على مجموعة من الأدلة التي تمثل كل خطوة على طول العملية، في حال كنت بحاجة إلى الإشارة إلى نسخة صالحة.

وستعمل على النسخة المتوفّرة في دليل start، ولكن يمكنك الرجوع إلى الملفات الأخرى أو نسخ الملفات منها حسب الحاجة.

3- تشغيل نموذج التطبيق

لنبدأ أولاً بتشغيل النص البرمجي الخاص بالعقدة. بعد تنزيل الرمز، اتّبِع التعليمات أدناه لتثبيت تطبيق Node.js وبدء تشغيله:

  1. افتح وحدة طرفية لسطر الأوامر على الكمبيوتر وانتقِل إلى دليل start الخاص بالدرس التطبيقي حول الترميز.
  2. أدخل الأمر التالي لتثبيت تبعيات Node.js.
npm install
  1. أدخِل الأمر التالي لتشغيل النص البرمجي:
node .
  1. يمكنك ملاحظة رسالة الترحيب التي توضح خطوات هذا المشروع.
-- Start generating slides. --
TODO: Get Client Secrets
TODO: Authorize
TODO: Get Data from BigQuery
TODO: Create Slides
TODO: Open Slides
-- Finished generating slides. --

يمكنك الاطّلاع على قائمة المهام في slides.js وlicense.js وauth.js. يُرجى العِلم أنّنا نستخدم وعود JavaScript لسلسلة الخطوات اللازمة لإكمال التطبيق لأنّ كل خطوة تعتمد على الخطوة السابقة التي يتم إكمالها.

وإذا لم تكن على دراية بالوعود، لا داعي للقلق، سنوفّر لك كل الرموز التي ستحتاج إليها. باختصار، تمنحنا الوعود طريقة للتعامل مع المعالجة غير المتزامنة بشكل أكثر تزامنًا.

4. الحصول على أسرار العملاء

لاستخدام واجهات برمجة تطبيقات "العروض التقديمية من Google" وBigquery وDrive، سيتم إنشاء عميل OAuth وحساب خدمة.

إعداد Google Developers Console

  1. استخدِم هذا المعالج لإنشاء مشروع أو اختياره في Google Developers Console وتفعيل واجهة برمجة التطبيقات تلقائيًا. انقر على متابعة، ثم الانتقال إلى بيانات الاعتماد.
  2. في صفحة إضافة بيانات اعتماد إلى مشروعك، انقر على الزر إلغاء.
  3. في أعلى الصفحة، اختَر علامة التبويب شاشة موافقة OAuth. اختَر عنوان بريد إلكتروني، وأدخِل اسم المنتج Slides API Codelab، ثم انقر على الزر حفظ.

تفعيل واجهات برمجة تطبيقات BigQuery وDrive و"العروض التقديمية من Google"

  1. اختَر علامة التبويب لوحة البيانات، ثم انقر على الزر تفعيل واجهة برمجة التطبيقات وفعِّل واجهات برمجة التطبيقات الثلاث التالية:
  2. BigQuery API
  3. Google Drive API
  4. Google Slides API

تنزيل سر عميل OAuth (لالعروض التقديمية وDrive)

  1. اختَر علامة التبويب بيانات الاعتماد، وانقر على الزر إنشاء بيانات اعتماد واختَر معرِّف عميل OAuth.
  2. اختَر نوع التطبيق غير ذلك، وأدخِل الاسم Google Slides API Codelab، ثم انقر على الزر إنشاء.انقر على حسنًا لإغلاق مربّع الحوار الناتج.
  3. انقر على الزر file_download (تنزيل JSON) على يسار معرِّف العميل.
  4. أعِد تسمية الملف السري إلى client_secret.json وانسخه إلى كل من الدليل start/ وfinish/.

تنزيل سر حساب الخدمة (لأداة BigQuery)

  1. اختَر علامة التبويب بيانات الاعتماد، ثم انقر على الزر إنشاء بيانات اعتماد واختَر مفتاح حساب الخدمة.
  2. في القائمة المنسدلة، اختَر حساب الخدمة الجديد. اختَر اسمًا للخدمة Slides API Codelab Service. بعد ذلك، انقر على الدور وانتقِل إلى BigQuery واختَر كلّ من عارِض بيانات BigQuery ومستخدم وظائف BigQuery.
  3. بالنسبة إلى نوع المفتاح، اختَر JSON.
  4. انقر على إنشاء. سيتم تنزيل ملف المفتاح تلقائيًا على جهاز الكمبيوتر. انقر على إغلاق للخروج من مربّع الحوار الذي يظهر.
  5. أعِد تسمية الملف السري إلى service_account_secret.json وانسخه إلى كل من الدليل start/ وfinish/.

الحصول على أسرار العملاء

في start/auth.js، لنملأ الطريقة 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));
    });
  });
}

لقد حمّلنا الآن أسرار العميل. سيتم تمرير بيانات الاعتماد إلى الوعد التالي. يمكنك تنفيذ المشروع باستخدام "node ." للتأكّد من عدم حدوث أخطاء.

5- إنشاء عميل OAuth2

لإنشاء شرائح، يمكننا إضافة المصادقة إلى واجهات Google APIs من خلال إضافة الرمز التالي إلى ملف auth.js. ستطلب هذه المصادقة الوصول إلى حسابك على Google لقراءة الملفات وكتابتها في Google Drive وإنشاء عروض تقديمية في "العروض التقديمية من Google" وتنفيذ طلبات بحث للقراءة فقط من Google BigQuery. (ملاحظة: لم يتم تغيير 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- إعداد BigQuery

استكشاف BigQuery (اختياري)

يتيح لنا BigQuery الاستعلام عن مجموعات البيانات الضخمة في ثوانٍ. دعونا نستخدم واجهة الويب قبل إجراء الاستعلام آليًا. إذا لم يسبق لك إعداد BigQuery، اتّبع الخطوات الواردة في دليل البدء السريع هذا.

افتح Cloud Console لتصفّح بيانات GitHub المتاحة في BigQuery وتشغيل طلبات البحث الخاصة بك. لنتعرَّف على تراخيص البرامج الأكثر شيوعًا على GitHub من خلال كتابة هذا الاستعلام والضغط على الزر تشغيل.

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

لقد حللنا للتو ملايين المستودعات العامة على GitHub واكتشفنا التراخيص الأكثر رواجًا. رائع! الآن لنقم بإعداد نفس الاستعلام، ولكن هذه المرة آليًا.

إعداد BigQuery

استبدِل الرمز في الملف license.js. ستعرض الدالة bigquery.query وعدًا.

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

يُرجى محاولة console.log بعض البيانات الواردة في استدعاء Promise لفهم بنية العناصر والاطّلاع على عمل الرمز.

7. إنشاء شرائح

الآن نصل إلى الجانب الشيق! لننشئ شرائح من خلال استدعاء الطريقتَين create وbatchUpdate في واجهة برمجة التطبيقات للعروض التقديمية. ويجب استبدال الملف بما يلي:

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. فتح "العروض التقديمية من Google"

وأخيرًا، لنفتح العرض التقديمي في المتصفح. عدِّل الطريقة التالية في 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);
}

قم بتشغيل مشروعك مرة أخيرة لإظهار النتيجة النهائية.

9. تهانينا

لقد أنشأت بنجاح "العروض التقديمية من Google" من البيانات التي تم تحليلها باستخدام BigQuery. ينشئ النص البرمجي عرضًا تقديميًا باستخدام Google Slides API وBigQuery للإبلاغ عن تحليل لتراخيص البرامج الأكثر شيوعًا.

التحسينات المحتملة

في ما يلي بعض الأفكار الإضافية لإجراء عملية دمج أكثر جاذبية:

  • إضافة صور إلى كل شريحة
  • مشاركة الشرائح عبر البريد الإلكتروني باستخدام Gmail API
  • تخصيص شريحة النموذج كوسيطة سطر أوامر

مزيد من المعلومات