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

1. نظرة عامة

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

المُعطيات

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

المتطلبات

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

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

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

أو يمكنك استنساخ مستودع GitHub من سطر الأوامر.

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

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

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

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

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

  1. افتح وحدة طرفية لسطر الأوامر على جهاز الكمبيوتر وانتقِل إلى الدليل start في ملف codelab.
  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. الحصول على مفاتيح سرّ العملاء

لاستخدام واجهات برمجة التطبيقات Slides وBigquery وDrive، سننشئ عميل OAuth وحساب خدمة.

إعداد Google Developers Console

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

تفعيل واجهات برمجة التطبيقات BigQuery وDrive وSlides

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

تنزيل "سر عميل OAuth" (لتطبيق "العروض التقديمية من Google" و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 لتصفّح بيانات 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 API. يجب استبدال ملفنا بما يلي:

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
  • تخصيص شريحة النموذج كوسيطة سطر أوامر

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