Hesap Özeti Raporu, bir Google Ads hesabının tamamının performansını gösteren bir genel bakış raporudur. Ayrıca, mevcut hesap istatistiklerini içeren HTML biçiminde günlük bir e-posta gönderir. Hesap yöneticileri, profesyonel olarak biçimlendirilmiş raporları doğrudan müşterilere e-postayla göndermek için benzer bir teknik kullanabilir.
Planlama
Komut dosyası, önceki günün istatistiklerini kullanır. Google Ads istatistiklerinde 3 saate kadar gecikmeler yaşanabileceğinden doğruluğu garanti etmek için çözümü her gün saat 03:00'te veya daha geç bir saatte planlayın.
İşleyiş şekli
Komut dosyası, dünün istatistiklerini getirir ve e-tabloyu günceller. Grafikler, istatistikleri gösterecek şekilde önceden yapılandırılmıştır. E-posta adresi belirtilirse komut dosyası HTML biçiminde bir e-posta gönderir.
Kurulum
Google Ads hesabınızda e-tablo tabanlı komut dosyasını oluşturmak için aşağıdaki düğmeyi tıklayın.
Şablon e-tablosunun kopyasını oluşturmak için aşağıdaki düğmeyi tıklayın.
Komut dosyanızda
spreadsheet_url
değerini güncelleyin.Komut dosyasını Her gün, 03:00'da çalışacak şekilde planlayın.
Raporu genişletme (isteğe bağlı)
Rapor, customer
kaynağındaki ek metrikler ekleyerek genişletilebilir:
E-tablonuza, kullanılabilir bir sonraki sütuna bir başlık (ör. Arama Gösterim Payı) ekleyin. Bu başlık, temel şablonda Tıklamalar'ın yanında yer alır.
Komut dosyasının üst kısmındaki
REPORT_FIELDS
tanımını düzenleyin. Örneğin:var REPORT_FIELDS = [ {columnName: 'metrics.cost_micros', displayName: 'Cost'}, {columnName: 'metrics.average_cpc', displayName: 'Average CPC'}, {columnName: 'metrics.ctr', displayName: 'CTR'}, {columnName: 'metrics.impression', displayName: 'Impressions'}, {columnName: 'metrics.clicks', displayName: 'Clicks'}, {columnName: 'metrics.search_impression-share', displayName: 'Search Imp. Share'} ];
Bu işlem, rapora Arama Gösterim Payı'nı ekler. displayName için herhangi bir değer seçin. Bu değer, e-posta başlığında kullanılır.
Yeni alan için grafik oluşturmak üzere:
- E-tablonuzdaki mevcut grafiklerden birini tıklayın ve grafiğin sağ üst kısmındaki üç nokta menüsünden Grafiği Kopyala'yı seçin. Ardından sağ tıklayıp Yapıştır'ı tıklayın.
- E-tabloda yeni bir grafik oluşturulur. Yeni grafiğin menüsünde Grafiği Düzenle'yi seçin.
- Seri sütunu referansını yeni verileri içeren sütuna değiştirin. Örneğin,
G
değeriniH
olarak değiştirin. - Grafik başlığını çift tıklayın ve yeni metrik adıyla değiştirin.
Kaynak kodu
// Copyright 2015, Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @name Account Summary Report * * @overview The Account Summary Report script generates an at-a-glance report * showing the performance of an entire Google Ads account. See * https://developers.google.com/google-ads/scripts/docs/solutions/account-summary * for more details. * * @author Google Ads Scripts Team [[email protected]] * * @version 2.1 * * @changelog * - version 2.1 * - Split into info, config, and code. * - version 2.0 * - Updated to use new Google Ads Scripts features. * - version 1.1 * - Add user-updateable fields, and ensure report row ordering. * - version 1.0.4 * - Improved code readability and comments. * - version 1.0.3 * - Added validation for external spreadsheet setup. * - version 1.0.2 * - Fixes date formatting bug in certain timezones. * - version 1.0.1 * - Improvements to time zone handling. * - version 1.0 * - Released initial version. */ /** * Configuration to be used for the Account Summary Report. */ CONFIG = { // URL of the report spreadsheet. This should be a copy of // https://docs.google.com/spreadsheets/d/1gYLXtDK93lWoTe3OBKvTlfcc7L_qHJFgWU9N6HwhZtU/copy 'spreadsheet_url': 'YOUR_SPREADSHEET_URL', // More reporting options can be found at // https://developers.google.com/google-ads/scripts/docs/reference/adsapp/adsapp#report_2 'reporting_options': { // Comment out the following line to default to the latest reporting // version. 'apiVersion': 'v10' }, /** * To add additional fields to the report, follow the instructions at * https://developers.google.com/google-ads/scripts/docs/solutions/account-summary#extending_the_report_optional */ 'report_fields': [ {'columnName': 'metrics.cost_micros', 'displayName': 'Cost'}, {'columnName': 'metrics.average_cpc', 'displayName': 'Avg. CPC'}, {'columnName': 'metrics.ctr', 'displayName': 'CTR'}, {'columnName': 'metrics.search_impression_share', 'displayName': 'Search Impr. share'}, {'columnName': 'metrics.impressions', 'displayName': 'Impressions'}, {'columnName': 'metrics.clicks', 'displayName': 'Clicks'} ] }; const SPREADSHEET_URL = CONFIG.spreadsheet_url; const REPORTING_OPTIONS = CONFIG.reporting_options; const REPORT_FIELDS = CONFIG.report_fields; /** The spreadsheet is updated to showcase the performance of the account. * An email is sent to the email ID mentioned in the spreadsheet */ function main() { Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL); const spreadsheet = validateAndGetSpreadsheet(); spreadsheet.setSpreadsheetTimeZone(AdsApp.currentAccount().getTimeZone()); spreadsheet.getRangeByName('account_id_report').setValue(AdsApp.currentAccount().getCustomerId()); const yesterday = getYesterday(); const date = getFirstDayToCheck(spreadsheet, yesterday); const rows = []; const existingDates = getExistingDates(); while (date.getTime() <= yesterday.getTime()) { if (!existingDates[date]) { let row = getReportRowForDate(date); rows.push([new Date(date)].concat(REPORT_FIELDS.map(function(field) { row[field.columnName] = format(field.columnName, row[field.columnName]); return row[field.columnName]; }))); spreadsheet.getRangeByName('last_check').setValue(date); } date.setDate(date.getDate() + 1); } if (rows.length > 0) { writeToSpreadsheet(rows); const email = spreadsheet.getRangeByName('email').getValue(); if (email) { sendEmail(email); } } } /**Converts the metrics.cost_micros by dividing it by a million to match the * output with version v1.1.1 of the file. * * @param {string} value that needs to be converted. * @return {string} A value that is of type float. */ function formatMicros(value) { const micros = parseFloat(value / 1000000).toFixed(2); return `${micros}`; } /** * Formats decimal number into a percentage. * * @param {string} value The decimal number to format. * @return {string} The decimal number formatted as a percentage. */ function formatPercentage(value) { value=parseFloat(value*100).toFixed(2)+'%'; return value; } /** * Formats Impression Share values. * * @param {string} value The Impression Share in Google Ads API format. * @return {string} The Impression Share formatted for the spreadsheet. */ function formatImpressionShare(value) { if (value <= 0.0999) { value='<10%'; } else if (value>0.0999) { value=parseFloat(value*100)+'%'; } else { value='--'; } return value; } /** * Formats clicks, impressions, ctr, average_cpc, cost_micros field values. * * @param {string} column The name of the field. * @param {string} value The value of the field. * @return {string} The formatted value of the field. */ function format(column, value) { switch (column) { case 'metrics.clicks': case 'metrics.impressions': return value; case 'metrics.ctr': return formatPercentage(value); case 'metrics.average_cpc': case 'metrics.cost_micros': return formatMicros(value); case 'metrics.search_impression_share': return formatImpressionShare(value); default: throw new Error(`Unknown field ${column}`); } } /** * Retrieves a lookup of dates for which rows already exist in the spreadsheet. * * @return {!Object} A lookup of existing dates. */ function getExistingDates() { const spreadsheet = validateAndGetSpreadsheet(); const sheet = spreadsheet.getSheetByName('Report'); const data = sheet.getDataRange().getValues(); const existingDates = {}; data.slice(5).forEach(function(row) { existingDates[row[1]] = true; }); return existingDates; } /** * Sorts the data in the spreadsheet into ascending date order. */ function sortReportRows() { const spreadsheet = validateAndGetSpreadsheet(); const sheet = spreadsheet.getSheetByName('Report'); const data = sheet.getDataRange().getValues(); const reportRows = data.slice(5); if (reportRows.length) { reportRows.sort(function(rowA, rowB) { if (!rowA || !rowA.length) { return -1; } else if (!rowB || !rowB.length) { return 1; } else if (rowA[1] < rowB[1]) { return -1; } else if (rowA[1] > rowB[1]) { return 1; } return 0; }); sheet.getRange(6, 1, reportRows.length, reportRows[0].length) .setValues(reportRows); } } /** * Append the data rows to the spreadsheet. * * @param {!Array<!Array<string>>} rows The data rows. */ function writeToSpreadsheet(rows) { const access = new SpreadsheetAccess(SPREADSHEET_URL, 'Report'); let emptyRow = access.findEmptyRow(6, 2); if (emptyRow < 0) { access.addRows(rows.length); emptyRow = access.findEmptyRow(6, 2); } access.writeRows(rows, emptyRow, 2); sortReportRows(); } /** * Sends mail to specified email address in spreadsheet * * @param {string} email address */ function sendEmail(email) { const day = getYesterday(); const yesterdayRow = getReportRowForDate(day); day.setDate(day.getDate() - 1); const twoDaysAgoRow = getReportRowForDate(day); day.setDate(day.getDate() - 5); const weekAgoRow = getReportRowForDate(day); const html = []; html.push( '<html>', '<body>', '<table width=800 cellpadding=0 border=0 cellspacing=0>', '<tr>', '<td colspan=2 align=right>', "<div style='font: italic normal 10pt Times New Roman, serif; " + "margin: 0; color: #666; padding-right: 5px;'>" + 'Powered by Google Ads Scripts</div>', '</td>', '</tr>', "<tr bgcolor='#3c78d8'>", '<td width=500>', "<div style='font: normal 18pt verdana, sans-serif; " + "padding: 3px 10px; color: white'>Account Summary report</div>", '</td>', '<td align=right>', "<div style='font: normal 18pt verdana, sans-serif; " + "padding: 3px 10px; color: white'>", AdsApp.currentAccount().getCustomerId(), '</h1>', '</td>', '</tr>', '</table>', '<table width=800 cellpadding=0 border=0 cellspacing=0>', "<tr bgcolor='#ddd'>", '<td></td>', "<td style='font: 12pt verdana, sans-serif; " + 'padding: 5px 0px 5px 5px; background-color: #ddd; ' + "text-align: left'>Yesterday</td>", "<td style='font: 12pt verdana, sans-serif; " + 'padding: 5px 0px 5px 5px; background-color: #ddd; ' + "text-align: left'>Two Days Ago</td>", "<td style='font: 12pt verdana, sans-serif; " + 'padding: 5px 0px 5x 5px; background-color: #ddd; ' + "text-align: left'>A week ago</td>", '</tr>'); REPORT_FIELDS.forEach(function(field) { html.push(emailRow( field.displayName,field.columnName, yesterdayRow, twoDaysAgoRow, weekAgoRow)); }); html.push('</table>', '</body>', '</html>'); MailApp.sendEmail(email, 'Google Ads Account ' + AdsApp.currentAccount().getCustomerId() + ' Summary Report', '', {htmlBody: html.join('\n')}); } /** * Generates html summary report with the column names todays ,twodays ago, week ago * * @param {string} title of the report * @param {string} column field names * @param {!Object} yesterdayRow holds Object containing fields yesterday data * @param {!Object} twoDaysAgoRow holds Object containing fields twodays ago data * @param {!Object} weekAgoRow holds Object containing fields week ago data * @return {!html} html format */ function emailRow(title, column, yesterdayRow, twoDaysAgoRow, weekAgoRow) { yesterdayRow[column] = format(column,yesterdayRow[column]); twoDaysAgoRow[column] = format(column,twoDaysAgoRow[column]); weekAgoRow[column] = format(column,weekAgoRow[column]); const html = []; html.push('<tr>', "<td style='padding: 5px 10px'>" + title + '</td>', "<td style='padding: 0px 10px'>" + yesterdayRow[column]+ '</td>', "<td style='padding: 0px 10px'>" + twoDaysAgoRow[column] + formatChangeString(yesterdayRow[column], twoDaysAgoRow[column]) + '</td>', "<td style='padding: 0px 10px'>" + weekAgoRow[column] + formatChangeString(yesterdayRow[column], weekAgoRow[column]) + '</td>', '</tr>'); return html.join('\n'); } /** * Retrieves Dates in the format yyyyMMdd * * @param {!Date} date value * @return {!Object} dateStrings */ function getReportRowForDate(date) { const timeZone = AdsApp.currentAccount().getTimeZone(); const dateString = Utilities.formatDate(date, timeZone, 'yyyyMMdd'); return getReportRowForDuring(dateString + ' AND ' + dateString); } /** Retrieves data from the select query * * @param {string} during on which dates to get the reports * @return {!Object} rows returns field values as an object */ function getReportRowForDuring(during) { const fields = REPORT_FIELDS.map(function(field) {return field.columnName;}).join(', '); const query = `SELECT ${fields} FROM customer WHERE segments.date BETWEEN ${during}`; const report = AdsApp.report(query,REPORTING_OPTIONS); return report.rows().next(); } /** * Extracts the percentage value from a percentage string. E.g. given "12.3%" returns 12.3. * * @param {string} value A field value containing a percentage sign at the end. * @return {string} The percentage value. */ function extractPercentageValue(value){ const index = value.indexOf('%'); value = value.substring(0, index); return value; } /** * Formats a change between two values. * * @param {string} newValue The new value of a field. * @param {string} oldValue The old value of a field. * @return {string} A string representing the change in the field. */ function formatChangeString(newValue,oldValue) { const isPercentage = newValue.indexOf('%') >= 0; if(isPercentage){ newValue = extractPercentageValue(newValue); oldValue = extractPercentageValue(oldValue); } const change = parseFloat(newValue - oldValue).toFixed(2); let changeString = change; if (isPercentage) { changeString = change + '%'; } if (change >= 0) { return "<span style='color: #38761d; font-size: 8pt'> (+" + changeString + ')</span>'; } else { return "<span style='color: #cc0000; font-size: 8pt'> (" + changeString + ')</span>'; } } /** * The spreadsheet is accessed and updated. * * @param {string} spreadsheetUrl takes spreadsheets url * @param {string} sheetName is a sheet name */ function SpreadsheetAccess(spreadsheetUrl, sheetName) { this.spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl); this.sheet = this.spreadsheet.getSheetByName(sheetName); // what column should we be looking at to check whether the row is empty? this.findEmptyRow = function(minRow, column) { const values = this.sheet.getRange(minRow, column, this.sheet.getMaxRows(), 1).getValues(); for (let i = 0; i < values.length; i++) { if (!values[i][0]) { return i + minRow; } } return -1; }; this.addRows = function(howMany) { this.sheet.insertRowsAfter(this.sheet.getMaxRows(), howMany); }; this.writeRows = function(rows, startRow, startColumn) { this.sheet.getRange(startRow, startColumn, rows.length, rows[0].length). setValues(rows); }; } /** * Gets a date object that is 00:00 yesterday. * * @return {!Date} A date object that is equivalent to 00:00 yesterday in the * account's time zone. */ function getYesterday() { const yesterday = new Date(Date.now() - 24 * 3600 * 1000); return new Date(getDateStringInTimeZone('MMM dd, yyyy 00:00:00 Z', yesterday)); } /** * Returned the last checked date + 1 day, or yesterday if there isn't * a specified last checked date. * * @param {!Spreadsheet} spreadsheet The export spreadsheet. * @param {!Date} yesterday The yesterday date. * @return {!Date} The date corresponding to the first day to check. */ function getFirstDayToCheck(spreadsheet, yesterday) { const last_check = spreadsheet.getRangeByName('last_check').getValue(); let date; if (last_check.length == 0) { date = new Date(yesterday); } else { date = new Date(last_check); date.setDate(date.getDate() + 1); } return date; } /** * Produces a formatted string representing a given date in a given time zone. * * @param {string} format A format specifier for the string to be produced. * @param {!Date} date A date object. Defaults to the current date. * @param {string} timeZone A time zone. Defaults to the account's time zone. * @return {string} A formatted string of the given date in the given time zone. */ function getDateStringInTimeZone(format, date, timeZone) { date = date || new Date(); timeZone = timeZone || AdsApp.currentAccount().getTimeZone(); return Utilities.formatDate(date, timeZone, format); } /** * Validates the provided spreadsheet URL to make sure that it's set up * properly. Throws a descriptive error message if validation fails. * * @return {!Spreadsheet} The spreadsheet object itself, fetched from the URL. */ function validateAndGetSpreadsheet() { if ('YOUR_SPREADSHEET_URL' == SPREADSHEET_URL) { throw new Error('Please specify a valid Spreadsheet URL. You can find' + ' a link to a template in the associated guide for this script.'); } const spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL); const email = spreadsheet.getRangeByName('email').getValue(); if ('[email protected]' == email) { throw new Error('Please either set a custom email address in the' + ' spreadsheet, or set the email field in the spreadsheet to blank' + ' to send no email.'); } return spreadsheet; }