1. نظرة عامة
في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية استخدام "العروض التقديمية من Google" كأداة عرض تقديمي مخصّصة لتحليل تراخيص البرامج الأكثر شيوعًا. ستستعلم عن جميع الرموز البرمجية المفتوحة المصدر على GitHub باستخدام BigQuery API، وستنشئ عرضًا تقديميًا باستخدام 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 وبدء استخدامه:
- افتح نافذة طرفية لسطر الأوامر على الكمبيوتر وانتقِل إلى دليل
startالخاص ببرنامج التدريب العملي. - أدخِل الأمر التالي لتثبيت حِزم Node.js التابعة.
npm install
- أدخِل الأمر التالي لتشغيل النص البرمجي:
node .
- لاحظ الترحيب الذي يعرض خطوات هذا المشروع.
-- 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 Promises لتسلسل الخطوات اللازمة لإكمال التطبيق، لأنّ كل خطوة تعتمد على إكمال الخطوة السابقة.
إذا لم تكن على دراية بالوعود، لا تقلق، سنوفّر لك كل الرموز التي تحتاج إليها. باختصار، تمنحنا الوعود طريقة للتعامل مع المعالجة غير المتزامنة بطريقة أكثر تزامنًا.
4. الحصول على أسرار العميل
لاستخدام واجهات برمجة التطبيقات Slides وBigquery وDrive، سننشئ عميل OAuth وحساب خدمة.
إعداد Google Developers Console
- استخدِم هذا المعالج لإنشاء مشروع أو اختياره في Google Developers Console وتفعيل واجهة برمجة التطبيقات تلقائيًا. انقر على متابعة، ثم على الانتقال إلى بيانات الاعتماد.
- في صفحة إضافة بيانات الاعتماد إلى مشروعك، انقر على الزر إلغاء.
- في أعلى الصفحة، انقر على علامة التبويب شاشة طلب الموافقة المتعلّقة ببروتوكول OAuth. اختَر عنوان بريد إلكتروني، وأدخِل اسم المنتج
Slides API Codelab، ثم انقر على الزر حفظ.
تفعيل واجهات برمجة التطبيقات BigQuery وDrive وSlides
- اختَر علامة التبويب لوحة البيانات، ثم انقر على الزر تفعيل واجهة برمجة التطبيقات وفعِّل واجهات برمجة التطبيقات الثلاث التالية:
- BigQuery API
- Google Drive API
- Google Slides API
تنزيل سر عميل OAuth (لبرنامجي "العروض التقديمية من Google" وGoogle Drive)
- انقر على علامة التبويب بيانات الاعتماد، ثم على الزر إنشاء بيانات اعتماد واختَر معرّف عميل OAuth.
- اختَر نوع التطبيق غير ذلك، وأدخِل الاسم
Google Slides API Codelab، ثم انقر على الزر إنشاء.انقر على حسنًا لإغلاق مربّع الحوار الناتج. - انقر على الزر file_download (تنزيل JSON) على يسار معرّف العميل.
- أعِد تسمية ملفك السري إلى
client_secret.jsonوانسخه إلى الدليلَين start/ وfinish/.
تنزيل سرّ حساب الخدمة (لـ BigQuery)
- انقر على علامة التبويب بيانات الاعتماد، ثم على الزر إنشاء بيانات اعتماد واختَر مفتاح حساب الخدمة.
- في القائمة المنسدلة، اختَر حساب خدمة جديد. اختَر الاسم
Slides API Codelab Serviceلخدمتك. بعد ذلك، انقر على الدور وانتقِل إلى BigQuery واختَر كلاً من مُشاهد بيانات BigQuery ومستخدم وظائف BigQuery. - بالنسبة إلى نوع المفتاح، اختَر JSON.
- انقر على إنشاء. سيتم تنزيل ملف المفتاح تلقائيًا على جهاز الكمبيوتر. انقر على إغلاق للخروج من مربّع الحوار الذي يظهر.
- أعِد تسمية ملفك السري إلى
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 من خلال إضافة الرمز التالي إلى ملف 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 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 API
- تخصيص شريحة النموذج كمعلَمة في سطر الأوامر
مزيد من المعلومات
- اطّلِع على مستندات المطوّرين حول Google Slides API.
- يمكنك نشر الأسئلة والعثور على الإجابات على Stack Overflow ضمن العلامة google-slides-api.