Automação Google Ads: Campanhas de Display e Shopping

Nessa série especial, reunimos alguns scripts úteis para você automatizar e aumentar o retorno de suas campanhas no Google Ads. Confira abaixo como automatizar o Google Ads para “Trabalhar com Campanhas de Display e Shopping”.

 

1. Campanhas de Shopping – Por Google Ads. Gerencie suas campanhas do Google Shopping sem a necessidade de utilizar a plataforma do Google Ads. Esse grupo de scripts permitem criar anúncios de produtos, atualizar lances, etc.

Recuperar todas as campanhas de compras

function getAllShoppingCampaigns() {
var retval = [];
var campaignIterator = AdWordsApp.shoppingCampaigns().get();
while (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();

// Optional: Comment out if you don’t need to print details.
Logger.log(‘Campaign Name: %s’, campaign.getName());

retval.push(campaign);
}
return retval;
}

 

Recuperar uma campanha de compras pelo nome dela

function getShoppingCampaignByName(campaignName) {
  var campaignIterator = AdWordsApp.shoppingCampaigns()
      .withCondition("CampaignName = '" + campaignName + "'")
      .get();
  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    Logger.log('Campaign Name: %s', campaign.getName());
  }
}


Recuperar um grupo de anúncios de compras pelo nome dele

function getShoppingAdGroup() {
var campaignName = ‘INSERT_CAMPAIGN_NAME_HERE’;
var adGroupName = ‘INSERT_ADGROUP_NAME_HERE’;

var adGroupIterator = AdWordsApp.shoppingAdGroups()
.withCondition(“CampaignName = ‘” + campaignName +
“‘ and AdGroupName = ‘” + adGroupName + “‘”)
.get();
while (adGroupIterator.hasNext()) {
var adGroup = adGroupIterator.next();
Logger.log(
‘AdGroup Name: %s, CPC = %s, Mobile Bid ‘ + ‘Modifier = %s’,
adGroup.getName(),
adGroup.bidding().getCpc(),
adGroup.devices().getMobileBidModifier()
);
}
}

 

Crie um grupo de anúncios do Shopping

function createShoppingAdGroup() {
var campaignName = ‘INSERT_CAMPAIGN_NAME_HERE’;

var shoppingCampaign = AdWordsApp.shoppingCampaigns()
.withCondition(“CampaignName = ‘” + campaignName + “‘”)
.get()
.next();
var adGroupOperation = shoppingCampaign.newAdGroupBuilder().build();
var adGroup = adGroupOperation.getResult();
Logger.log(adGroup);
}

 

Criar uma hierarquia de grupo de produtos de compras

function createTree() {
var campaignName = ‘INSERT_CAMPAIGN_NAME_HERE’;
var adGroupName = ‘INSERT_ADGROUP_NAME_HERE’;

var shoppingAdGroup = AdWordsApp.shoppingAdGroups()
.withCondition(“CampaignName = ‘” + campaignName +
“‘ and AdGroupName = ‘” + adGroupName + “‘”)
.get()
.next();

var root = shoppingAdGroup.rootProductGroup();

// The structure created is
// – root
// – cardcow brand
// – New
// – Refurbished
// – Other conditions
// – Other brands

// Add a brand product group for “cardcow” under root product group.
var brandNode = root.newChild()
.brandBuilder()
.withName(‘cardcow’)
.withBid(1.2)
.build()
.getResult();

// Add new conditions for New and Refurbished cardcow brand items.
var newItems = brandNode.newChild()
.conditionBuilder()
.withCondition(‘NEW’)
.build()
.getResult();

var refurbishedItems = brandNode.newChild()
.conditionBuilder()
.withCondition(‘REFURBISHED’)
.withBid(0.9)
.build()
.getResult();
}

 

Atravessa a hierarquia do grupo de produtos

function walkProductPartitionTree() {
var campaignName = ‘INSERT_CAMPAIGN_NAME_HERE’;
var adGroupName = ‘INSERT_ADGROUP_NAME_HERE’;

var shoppingAdGroup = AdWordsApp.shoppingAdGroups()
.withCondition(“CampaignName = ‘” + campaignName +
“‘ and AdGroupName = ‘” + adGroupName + “‘”)
.get()
.next();
var root = shoppingAdGroup.rootProductGroup();
walkHierarchy(root, 0);
}

function walkHierarchy(productGroup, level) {
var description = ”;

if (productGroup.isOtherCase()) {
description = ‘Other’;
} else if (productGroup.getDimension() == ‘CATEGORY’) {
// Shows how to process a product group differently based on its type.
description = productGroup.asCategory().getName();
} else {
description = productGroup.getValue();
}

var padding = new Array(level + 1).join(‘-‘);

// Note: Child product groups may not have a max cpc if it has been excluded.
Logger.log(
‘%s %s, %s, %s, %s, %s’,
padding,
description,
productGroup.getDimension(),
productGroup.getMaxCpc(),
productGroup.isOtherCase(),
productGroup.getId().toFixed()
);
var childProductGroups = productGroup.children().get();
while (childProductGroups.hasNext()) {
var childProductGroup = childProductGroups.next();
walkHierarchy(childProductGroup, level + 1);
}
}

 

Obtém o grupo de produtos “Tudo mais”

function getEverythingElseProductGroup() {
var campaignName = ‘INSERT_CAMPAIGN_NAME_HERE’;
var adGroupName = ‘INSERT_ADGROUP_NAME_HERE’;

var shoppingAdGroup = AdWordsApp.shoppingAdGroups()
.withCondition(“CampaignName = ‘” + campaignName +
“‘ and AdGroupName = ‘” + adGroupName + “‘”)
.get()
.next();

var rootProductGroup = shoppingAdGroup.rootProductGroup();
var childProductGroups = rootProductGroup.children().get();
while (childProductGroups.hasNext()) {
var childProductGroup = childProductGroups.next();
if (childProductGroup.isOtherCase()) {
// Note: Child product groups may not have a max cpc if it has been
// excluded.
Logger.log(
‘”Everything else” product group found. Type of the product ‘ +
‘group is %s and bid is %s.’,
childProductGroup.getDimension(),
childProductGroup.getMaxCpc());
return;
}
}
Logger.log(‘”Everything else” product group not found under root ‘ +
‘product group.’);
}

 

Atualizar lances para grupos de produtos

function updateProductGroupBid() {
var productGroups = AdWordsApp.productGroups()
.withCondition(‘Clicks > 5’)
.withCondition(‘Ctr > 0.01’)
.forDateRange(‘LAST_MONTH’)
.get();
while (productGroups.hasNext()) {
var productGroup = productGroups.next();
productGroup.setMaxCpc(productGroup.getMaxCpc() + 0.01);
}
}

 

Recuperar anúncios de produtos

function getProductAds() {
var adGroupName = ‘INSERT_ADGROUP_NAME_HERE’;

var shoppingAdGroup = AdWordsApp.shoppingAdGroups()
.withCondition(“AdGroupName = ‘” + adGroupName + “‘”)
.get()
.next();

var productAds = shoppingAdGroup.ads().get();
while (productAds.hasNext()) {
var productAd = productAds.next();
Logger.log(
“Ad with ID = %s was found.”,
productAd.getId().toFixed(0));
}
}

 

Crie anúncios de produtos

function createProductAd() {
var adGroupName = ‘INSERT_ADGROUP_NAME_HERE’;

var shoppingAdGroup = AdWordsApp.shoppingAdGroups()
.withCondition(“AdGroupName = ‘” + adGroupName + “‘”)
.get()
.next();

var adOperation = shoppingAdGroup.newAdBuilder()
.withMobilePreferred(true)
.build();
var productAd = adOperation.getResult();
Logger.log(
“Ad with ID = %s was created.”,
productAd.getId().toFixed(0));

}

 

2. Conteúdo do Shopping – Por Google Ads. Assim como o script de Campanhas de Shopping, é possível inserir produtos, extrair informações da conta do merchant, listar todos os produtos e mais, sem a necessidade da plataforma do Google Ads.

Inserir um produto

function insertProduct() {
var merchantId = ‘INSERT_MERCHANT_ID_HERE’;

// Create a product resource. See
// https://developers.google.com/shopping-content/v2/reference/v2/products
// for the full list of fields supported by product resource.
var productResource = {
‘offerId’: ‘book123’,
‘title’: ‘A Tale of Two Cities’,
‘description’: ‘A classic novel about the French Revolution’,
‘link’: ‘http://my-book-shop.com/tale-of-two-cities.html’,
‘imageLink’: ‘http://my-book-shop.com/tale-of-two-cities.jpg’,
‘contentLanguage’: ‘en’,
‘targetCountry’: ‘US’,
‘channel’: ‘online’,
‘availability’: ‘in stock’,
‘condition’: ‘new’,
‘googleProductCategory’: ‘Media > Books’,
‘productType’: ‘Media > Books’,
‘gtin’: ‘9780007350896’,
‘price’: {
‘value’: ‘2.50’,
‘currency’: ‘USD’
},
‘shipping’: [{
‘country’: ‘US’,
‘service’: ‘Standard shipping’,
‘price’: {
‘value’: ‘0.99’,
‘currency’: ‘USD’
}
}],
‘shippingWeight’: {
‘value’: ‘2’,
‘unit’: ‘pounds’
}
};

ShoppingContent.Products.insert(productResource, merchantId);
}

 

Listar todos os produtos

function listProducts() {
var merchantId = ‘INSERT_MERCHANT_ID_HERE’;

// List all the products for a given merchant.
var products = ShoppingContent.Products.list(merchantId);
if (products.resources) {
for (var i = 0; i < products.resources.length; i++) {
Logger.log(products.resources[i]);
}
}
}

 

Inserir produtos usando a API custombatch

function custombatch() {
var merchantId = ‘INSERT_MERCHANT_ID_HERE’;

// Create your product resources. See
// https://developers.google.com/shopping-content/v2/reference/v2/products
// for the full list of fields supported by product resource. See the
// insertProduct() snippet for a code example that shows how to construct
// a product resource.
var productResource1 = {
// FILL THIS OUT.
};

var productResource2 = {
// FILL THIS OUT.
};

var productResource3 = {
// FILL THIS OUT.
};

var custombatchResource = {
‘entries’: [
{
‘batchId’: 1,
‘merchantId’: merchantId,
‘method’: ‘insert’,
‘productId’: ‘book124’,
‘product’: productResource1
},
{
‘batchId’: 2,
‘merchantId’: merchantId,
‘method’: ‘insert’,
‘productId’: ‘book125’,
‘product’: productResource2
},
{
‘batchId’: 3,
‘merchantId’: merchantId,
‘method’: ‘insert’,
‘productId’: ‘book126’,
‘product’: productResource3
},

]
};
var response = ShoppingContent.Products.custombatch(custombatchResource);
Logger.log(response);
}

 

Obter informações da conta do comerciante

function getAccountInfo() {
var merchantId = ‘INSERT_MERCHANT_ID_HERE’;
var accountId = ‘INSERT_ACCOUNT_ID_HERE’;

// See https://developers.google.com/shopping-content/v2/reference/v2/accounts
// for the list of fields supported by Account type.
var accounts = ShoppingContent.Accounts.get(merchantId, accountId);
Logger.log(accounts);

// See https://developers.google.com/shopping-content/v2/reference/v2/accountstatuses
// for the list of account status fields supported by Shopping Content API.
var accountstatuses = ShoppingContent.Accountstatuses.get(merchantId,
accountId);
Logger.log(accountstatuses);

// See https://developers.google.com/shopping-content/v2/reference/v2/accountshipping
// for various Account shipping settings fields supported by Shopping
// Content API..
var accountshipping = ShoppingContent.Accountshipping.get(merchantId,
accountId);
Logger.log(accountshipping);

// See https://developers.google.com/shopping-content/v2/reference/v2/accounttax
// for various Account tax fields supported by Shopping Content API.
var accounttax = ShoppingContent.Accounttax.get(merchantId, accountId);
Logger.log(accounttax);
}

3. Rede de Display – Por Google Ads. Esse script permite gerenciar alguns recursos da rede de display sem que você utilize a plataforma.

Adicionar um canal a um grupo de anúncios existente

function addPlacementToAdGroup() {
var adGroup = AdWordsApp.adGroups()
.withCondition(“Name = ‘INSERT_ADGROUP_NAME_HERE'”)
.withCondition(‘CampaignName = “INSERT_CAMPAIGN_NAME_HERE”‘)
.get()
.next();

// Other display criteria can be built in a similar manner using the
// corresponding builder method in the AdWordsApp.Display,
// AdWordsApp.CampaignDisplay or AdWordsApp.AdGroupDisplay class.
var placementOperation = adGroup.display()
.newPlacementBuilder()
.withUrl(‘http://www.site.com’) // required
.withCpc(0.50) // optional
.build();
var placement = placementOperation.getResult();
Logger.log(‘Placement with id = %s and url = %s was created.’,
placement.getId(), placement.getUrl());
}

 

Recuperar todos os tópicos em um grupo de anúncios existente

function getAllTopics() {
var adGroup = AdWordsApp.adGroups()
.withCondition(‘Name = “INSERT_ADGROUP_NAME_HERE”‘)
.withCondition(‘CampaignName = “INSERT_CAMPAIGN_NAME_HERE”‘)
.get()
.next();

// Other display criteria can be retrieved in a similar manner using
// the corresponding selector methods in the AdWordsApp.Display,
// AdWordsApp.CampaignDisplay or AdWordsApp.AdGroupDisplay class.
var topicIterator = AdWordsApp.display()
.topics()
.withCondition(‘Impressions > 100’)
.forDateRange(‘LAST_MONTH’)
.orderBy(‘Clicks DESC’)
.get();

while (topicIterator.hasNext()) {
var topic = topicIterator.next();

// The list of all topic IDs can be found on
// https://developers.google.com/adwords/api/docs/appendix/verticals
Logger.log(‘Topic with criterion id = %s and topic id = %s was ‘ +
‘found.’, topic.getId().toFixed(0),
topic.getTopicId().toFixed(0));
}
}

 

Obter estatísticas de todos os públicos-alvo de um grupo de anúncios existente

function getAudienceStats() {
var adGroup = AdWordsApp.adGroups()
.withCondition(‘Name = “INSERT_ADGROUP_NAME_HERE”‘)
.withCondition(‘CampaignName = “INSERT_CAMPAIGN_NAME_HERE”‘)
.get()
.next();

// Other display criteria can be retrieved in a similar manner using
// the corresponding selector methods in the AdWordsApp.Display,
// AdWordsApp.CampaignDisplay or AdWordsApp.AdGroupDisplay class.
var audienceIterator = adGroup.display()
.audiences()
.get();

Logger.log(‘ID, Audience ID, Clicks, Impressions, Cost’);

while (audienceIterator.hasNext()) {
var audience = audienceIterator.next();
var stats = audience.getStatsFor(‘LAST_MONTH’);

// User List IDs (List IDs) are available on the details page of
// a User List (found under the Audiences section of the Shared
// Library)
Logger.log(‘%s, %s, %s, %s, %s’, audience.getId().toFixed(0),
audience.getAudienceId(), stats.getClicks(),
stats.getImpressions(), stats.getCost());
}
}

4. Encontre oportunidades nos canais na Rede de Display – Por Derek Martin. O script destaca canais que estão com a performance baixa com base em algumas condições. A rede de display gera uma grande quantidade de canais, com esse script é possível economizar tempo já que ele faz essa análise automaticamente.

// This script reviews your GDN placements for the following conditions:
// 1) Placements that are converting at less than $40
// 2) Placements that have cost more than $50 but haven’t converted
// 3) Placements that have more than 5K impressions and less than .10 CTR

function main() {

var body = “<h2>Google Display Network Alert</h2>”;
body += “<h3>Placements that are converting at less than $40:</h3> ” ;
body += “<ul>”;

var list = runLowCostAndConvertingReport();

for (i=0; i < list.length; i++) {
body += “<li><strong>” + list[i].placement + “</strong> – ” + list[i].adgroup + ‘ – $’ + list2[i].cost + “</li>”;

}
body += “</ul>”;

body += “<h3>Placements that have cost more than $50 but haven’t converted:</h3> ” ;
body += “<ul>”;

var list2 = runHighCostNoConversionsReport();

for (i=0; i < list2.length; i++) {
body += “<li><strong>” + list2[i].placement + “</strong> – ” + list2[i].adgroup + ‘ – $’ + list2[i].cost + “</li>”;

}
body += “</ul>”;

body += “<h3>Placements that have more than 5K impressions and less than .10 CTR:</h3> ” ;
body += “<ul>”;

var list3 = runLowCtrAndHighImpressionsReport();

for (i=0; i < list3.length; i++) {
body += “<li><strong>” + list3[i].placement + “</strong> – ” + list3[i].adgroup +” – ” + parseFloat(list3[i].clicks/list3[i].impressions).toFixed(4) + “% – ” + list3[i].clicks + ” clicks – ” + list3[i].impressions + ‘ impressions ‘ + “</li>”;

}
body += “</ul>”;

MailApp.sendEmail(‘[email protected]’,’Display Network Alerts ‘, body,{htmlBody: body});
}

function runLowCostAndConvertingReport()
{
list = [];

// Any placement detail (individual page) that has converted 2 or more times with CPA below $40
var report = AdWordsApp.report(
‘SELECT Url, CampaignName, AdGroupName, Clicks, Impressions, Conversions, Cost ‘ +
‘FROM URL_PERFORMANCE_REPORT ‘ +
‘WHERE Cost < 40000000 ‘ +
‘AND Conversions >= 2 ‘ +
‘DURING LAST_30_DAYS’);

var rows = report.rows();

while (rows.hasNext()) {

var row = rows.next();

var anonymous = row[‘Url’].match(/anonymous\.google/g);
if (anonymous == null) {
var placement = row[‘Url’];

var campaign = row[‘CampaignName’];
var adgroup = row[‘AdGroupName’];
var clicks = row[‘Clicks’];
var impressions = row[‘Impressions’];
var conversions = row[‘Conversions’];
var cost = row[‘Cost’];

var placementDetail = new placementObject(placement, campaign, adgroup, clicks, impressions, conversions, cost);

list.push(placementDetail);
}
}
return list;
}
function runLowCtrAndHighImpressionsReport()
{
list = [];

// Any placement detail (individual page) that has converted 2 or more times with CPA below $40
var report = AdWordsApp.report(
‘SELECT Url, CampaignName, AdGroupName, Clicks, Impressions, Conversions, Cost ‘ +
‘FROM URL_PERFORMANCE_REPORT ‘ +
‘WHERE Impressions > 5000 ‘ +
‘AND Ctr < 0.1 ‘ +
‘DURING LAST_30_DAYS’);

var rows = report.rows();

while (rows.hasNext()) {

var row = rows.next();

var anonymous = row[‘Url’].match(/anonymous\.google/g);
if (anonymous == null) {
var placement = row[‘Url’];

var campaign = row[‘CampaignName’];
var adgroup = row[‘AdGroupName’];
var clicks = row[‘Clicks’];
var impressions = row[‘Impressions’];
var conversions = row[‘Conversions’];
var cost = row[‘Cost’];

var placementDetail = new placementObject(placement, campaign, adgroup, clicks, impressions, conversions, cost);

list.push(placementDetail);
}
}
return list;
}

function runHighCostNoConversionsReport()
{
list = [];

// Any placement detail (individual page) that has converted 2 or more times with CPA below $40
var report = AdWordsApp.report(
‘SELECT Url, CampaignName, AdGroupName, Clicks, Impressions, Conversions, Cost ‘ +
‘FROM URL_PERFORMANCE_REPORT ‘ +
‘WHERE Cost > 50000000 ‘ +
‘AND Conversions = 0 ‘ +
‘DURING LAST_30_DAYS’);

var rows = report.rows();

while (rows.hasNext()) {

var row = rows.next();

var anonymous = row[‘Url’].match(/anonymous\.google/g);
if (anonymous == null) {
var placement = row[‘Url’];

var campaign = row[‘CampaignName’];
var adgroup = row[‘AdGroupName’];
var clicks = row[‘Clicks’];
var impressions = row[‘Impressions’];
var conversions = row[‘Conversions’];
var cost = row[‘Cost’];

var placementDetail = new placementObject(placement, campaign, adgroup, clicks, impressions, conversions, cost);

list.push(placementDetail);
}
}
return list;
}

function placementObject(placement, campaign, adgroup, clicks, impressions, conversions, cost) {
this.placement = placement;
this.campaign = campaign;
this.adgroup = adgroup;
this.clicks = clicks;
this.impressions = impressions;
this.conversions = conversions;
this.cost = cost;

}

// Helpers
function warn(msg) {
Logger.log(‘WARNING: ‘+msg);
}

function info(msg) {
Logger.log(msg);
}

 

5. Identifique mudanças bruscas nos produtos do Google Shopping – Por Derek Martin. Receba um e-mail com um alerta de mudanças nas suas campanhas do Google Shopping. Ele analisa o custo e o CPC médio de seus produtos.

/***************************************************************************************
* AdWords Account Management — Review Google Shopping Products for sharp changes in
* Product level Cost and Product level Avg CPC (200%+ or -200%)
* Will e-mail any offending products for review.
* Version 1.1
* Created By: Derek Martin
* DerekMartinLA.com
****************************************************************************************/
var EMAIL_ADDRESS = “[email protected]

function main() {
var clientName = AdWordsApp.currentAccount().getName().split(“-“)[0];
var offendingProducts = []; // will hold list of product items that need review
var products = [];

products = runShoppingReport();
offendingProducts = analyzeShoppingResults(products);

if (offendingProducts.length > 0) {
var file = createSpreadsheet(clientName,offendingProducts);
sendAnEmail(clientName[0], offendingProducts.toString(), file);
} // end of if statement

} // end of main function

function runShoppingReport() {

// var reportFormat = _.str.quote(camp.getName());
var listOfProducts = [];

var report = AdWordsApp.report(
‘SELECT Date, Brand, OfferId, AverageCpc, Cost ‘ +
‘FROM SHOPPING_PERFORMANCE_REPORT ‘ +
‘DURING LAST_7_DAYS’);

var rows = report.rows();

while (rows.hasNext()) {
var row = rows.next();

var brand = row[‘Brand’];
var offerId= row[‘OfferId’];
var date = row[‘Date’];
var averageCpc = row[‘AverageCpc’];
var cost = row[‘Cost’];

var productResult = new productData(brand, offerId, date, averageCpc, cost);

listOfProducts.push(productResult);

} // end of report run

return listOfProducts;

}

function productData(brand, offerId, date, averageCpc, totalCost) {
this.brand = brand;
this.offerId = offerId;
this.date = date;
this.averageCpc = averageCpc;
this.totalCost = totalCost;
} // end of productData

function analyzeShoppingResults (products) {
var listOfProducts = products;
var listOfResults = [];

listOfProducts = _.uniq(listOfProducts);
listOfProducts.sort(); // sort list to keep things clean

var i = 0;
for each (offerId in listOfProducts) {

var currentOffer = _.where(listOfProducts, {offerId: listOfProducts[i].offerId});
if (currentOffer.length > 1) { // check if there are multiple dates at play, this way we can calculate min and max-

var oldestAvgCpc = parseFloat(currentOffer[0].averageCpc);
var oldestCost = parseFloat(currentOffer[0].totalCost);
var newestAvgCpc = parseFloat(currentOffer[currentOffer.length – 1].averageCpc);
var newestCost = parseFloat(currentOffer[currentOffer.length – 1].totalCost);

var cpcChange = parseFloat((newestAvgCpc – oldestAvgCpc) / oldestAvgCpc).toFixed(2);
var costChange = parseFloat((newestCost – oldestCost) / oldestCost).toFixed(2);

var offerResult = new productResult(listOfProducts[i].brand, listOfProducts[i].offerId, oldestAvgCpc, newestAvgCpc, cpcChange, oldestCost, newestCost, costChange);

listOfResults.push(offerResult);
} // end of length if statement
i++;
} // end of for each

listOfResults = _.uniq(listOfResults);

listOfResults.sort();

var uniqueList = _.uniq(listOfResults, function(item, key, productId) {
return item.productId;
} );

uniqueList.sort();

var offendingProducts = _.filter (uniqueList, function(product) {
return product.cpcDelta <= -2 || product.cpcDelta >=2 || product.costDelta >= 2 || product.costDelta <= -2 ;
});

return offendingProducts;

} // end of analyzeShoppingResults

function productResult (brand, id, oldCpc, newCpc, cpcDelta, oldCost, newCost, costDelta) {
this.brand = brand;
this.productId = id;
this.oldCpc = oldCpc;
this.newCpc = newCpc
this.cpcDelta = cpcDelta;
this.oldCost = oldCost;
this.newCost = newCost;
this.costDelta = costDelta;

} // end of productResult

function createSpreadsheet(client,results) {

var productResults = results;
var clientName = client;
var spreadsheetName = clientName + ‘-shoppingreport’;

var newSS = SpreadsheetApp.create(spreadsheetName, results.length, 26);

var sheet = newSS.getActiveSheet();

var columnNames = [“ProductId”, “Brand”, “Old AvgCpc”, “New AvgCpc”, “AvgCpc Delta”, “Old Daily Cost”, “New Daily Cost”, “Cost Delta”];

var headersRange = sheet.getRange(1, 1, 1, columnNames.length);

for (i = 0; i < productResults.length; i++) {

headersRange.setValues([columnNames]);

var product;
product = productResults[i].productId;
var productBrand = productResults[i].brand;
var oldAvgCpc = productResults[i].oldCpc;
var newAvgCpc = productResults[i].newCpc;
var cpcDelta = productResults[i].cpcDelta;
var oldCost = productResults[i].oldCost;
var newCost = productResults[i].newCost
var costDelta = productResults[i].costDelta;

sheet.appendRow([product, productBrand, oldAvgCpc, newAvgCpc, cpcDelta, oldCost, newCost, costDelta]);

// Sets the first column to a width which fits the text
sheet.setColumnWidth(1, 300);

var range = sheet.getRange(sheet.getMaxColumns(), sheet.getMaxRows());

range.setFontFamily(“Helvetica”);
range.setFontSize(30);

}

return newSS.getUrl();

}

function sendAnEmail (clientName, results, fileUrl) {

var data = Utilities.parseCsv(results, ‘\t’);
var today = new Date();
today = today.getMonth() + today.getDate() + today.getFullYear();

var filename = clientName + ‘search-results’ + today;

// Send an email with Search list attachment
var blob = Utilities.newBlob(results, ‘text/html’, ”);

MailApp.sendEmail(EMAIL_ADDRESS, clientName + ‘ – Google Shopping Alert Results ‘, ‘There are Google Shopping Products that need your attention. You can find the results at the following URL:\n\n’ + fileUrl, {
name: ‘Google Shopping Alert’
});

} // end of sendAnEmail function
/* HELPER FUNCTIONS */

function warn(msg) {
Logger.log(‘WARNING: ‘+msg);
}

function info(msg) {
Logger.log(msg);
}

/* UNDERSCORE LIBRARIES */

// Underscore.js 1.6.0
// http://underscorejs.org
// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};”undefined”!=typeof exports?(“undefined”!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION=”1.6.0″;var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O=”Reduce of empty array with no initial value”;j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[–i]:–i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),”value”)};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])<u?i=o+1:a=o}return i},j.toArray=function(n){return n?j.isArray(n)?o.call(n):n.length===+n.length?j.map(n,j.identity):j.values(n):[]},j.size=function(n){return null==n?0:n.length===+n.length?n.length:j.keys(n).length},j.first=j.head=j.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:0>t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,”length”).concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,””+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if(“number”!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u–;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r<arguments.length;)e.push(arguments[r++]);return n.apply(this,e)}},j.bindAll=function(n){var t=o.call(arguments,1);if(0===t.length)throw new Error(“bindAll must be passed function names”);return A(t,function(t){n[t]=j.bind(n[t],n)}),n},j.memoize=function(n,t){var r={};return t||(t=j.identity),function(){var e=t.apply(this,arguments);return j.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},j.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},j.defer=function(n){return j.delay.apply(j,[n,1].concat(o.call(arguments,1)))},j.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var c=function(){o=r.leading===!1?0:j.now(),a=null,i=n.apply(e,u),e=u=null};return function(){var l=j.now();o||r.leading!==!1||(o=l);var f=t-(l-o);return e=this,u=arguments,0>=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r–)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return–n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case”[object String]”:return n==String(t);case”[object Number]”:return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case”[object Date]”:case”[object Boolean]”:return+n==+t;case”[object RegExp]”:return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if(“object”!=typeof n||”object”!=typeof t)return!1;for(var i=r.length;i–;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&”constructor”in n&&”constructor”in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if(“[object Array]”==u){if(c=n.length,f=c==t.length)for(;c–&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c–)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return”[object Array]”==l.call(n)},j.isObject=function(n){return n===Object(n)},A([“Arguments”,”Function”,”String”,”Number”,”Date”,”RegExp”],function(n){j[“is”+n]=function(t){return l.call(t)==”[object “+n+”]”}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,”callee”))}),”function”!=typeof/./&&(j.isFunction=function(n){return”function”==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||”[object Boolean]”==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{“&”:”&amp;”,”<“:”&lt;”,”>”:”&gt;”,'”‘:”&quot;”,”‘”:”&#x27;”}};T.unescape=j.invert(T.escape);var I={escape:new RegExp(“[“+j.keys(T.escape).join(“”)+”]”,”g”),unescape:new RegExp(“(“+j.keys(T.unescape).join(“|”)+”)”,”g”)};j.each([“escape”,”unescape”],function(n){j[n]=function(t){return null==t?””:(“”+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+””;return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={“‘”:”‘”,”\\”:”\\”,”\r”:”r”,”\n”:”n”,” “:”t”,”\u2028″:”u2028″,”\u2029″:”u2029″},D=/\\|’|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join(“|”)+”|$”,”g”),i=0,a=”__p+='”;n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return”\\”+B[n]}),r&&(a+=”‘+\n((__t=(“+r+”))==null?”:_.escape(__t))+\n'”),e&&(a+=”‘+\n((__t=(“+e+”))==null?”:__t)+\n'”),u&&(a+=”‘;\n”+u+”\n__p+='”),i=o+t.length,t}),a+=”‘;\n”,r.variable||(a=”with(obj||{}){\n”+a+”}\n”),a=”var __t,__p=”,__j=Array.prototype.join,”+”print=function(){__p+=__j.call(arguments,”);};\n”+a+”return __p;\n”;try{e=new Function(r.variable||”obj”,”_”,a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source=”function(“+(r.variable||”obj”)+”){\n”+a+”}”,c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A([“pop”,”push”,”reverse”,”shift”,”sort”,”splice”,”unshift”],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),”shift”!=n&&”splice”!=n||0!==r.length||delete r[0],z.call(this,r)}}),A([“concat”,”join”,”slice”],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),”function”==typeof define&&define.amd&&define(“underscore”,[],function(){return j})}).call(this);
//# sourceMappingURL=underscore-min.map

// Underscore.string
!function(e,t){“use strict”;var n=t.prototype.trim,r=t.prototype.trimRight,i=t.prototype.trimLeft,s=function(e){return e*1||0},o=function(e,t){if(t<1)return””;var n=””;while(t>0)t&1&&(n+=e),t>>=1,e+=e;return n},u=[].slice,a=function(e){return e==null?”\\s”:e.source?e.source:”[“+p.escapeRegExp(e)+”]”},f={lt:”<“,gt:”>”,quot:'”‘,apos:”‘”,amp:”&”},l={};for(var c in f)l[f[c]]=c;var h=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=o,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var s=1,o=r.length,u=””,a,f=[],l,c,p,d,v,m;for(l=0;l<o;l++){u=e(r[l]);if(u===”string”)f.push(r[l]);else if(u===”array”){p=r[l];if(p[2]){a=i[s];for(c=0;c<p[2].length;c++){if(!a.hasOwnProperty(p[2][c]))throw new Error(h(‘[_.sprintf] property “%s” does not exist’,p[2][c]));a=a[p[2][c]]}}else p[1]?a=i[p[1]]:a=i[s++];if(/[^s]/.test(p[8])&&e(a)!=”number”)throw new Error(h(“[_.sprintf] expecting number but found %s”,e(a)));switch(p[8]){case”b”:a=a.toString(2);break;case”c”:a=t.fromCharCode(a);break;case”d”:a=parseInt(a,10);break;case”e”:a=p[7]?a.toExponential(p[7]):a.toExponential();break;case”f”:a=p[7]?parseFloat(a).toFixed(p[7]):parseFloat(a);break;case”o”:a=a.toString(8);break;case”s”:a=(a=t(a))&&p[7]?a.substring(0,p[7]):a;break;case”u”:a=Math.abs(a);break;case”x”:a=a.toString(16);break;case”X”:a=a.toString(16).toUpperCase()}a=/[def]/.test(p[8])&&p[3]&&a>=0?”+”+a:a,v=p[4]?p[4]==”0″?”0″:p[4].charAt(1):” “,m=p[6]-t(a).length,d=p[6]?n(v,m):””,f.push(p[5]?a+d:d+a)}}return f.join(“”)},r.cache={},r.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push(“%”);else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw new Error(“[_.sprintf] huh?”);if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw new Error(“[_.sprintf] huh?”);s.push(u[1]);while((o=o.substring(u[0].length))!==””)if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw new Error(“[_.sprintf] huh?”);s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw new Error(“[_.sprintf] mixing positional and named placeholders is not (yet) supported”);r.push(n)}t=t.substring(n[0].length)}return r},r}(),p={VERSION:”2.3.0″,isBlank:function(e){return e==null&&(e=””),/^\s*$/.test(e)},stripTags:function(e){return e==null?””:t(e).replace(/<\/?[^>]+>/g,””)},capitalize:function(e){return e=e==null?””:t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return e==null?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(“.{1,”+n+”}”,”g”)):[e])},clean:function(e){return p.strip(e).replace(/\s+/g,” “)},count:function(e,n){return e==null||n==null?0:t(e).split(n).length-1},chars:function(e){return e==null?[]:t(e).split(“”)},swapCase:function(e){return e==null?””:t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return e==null?””:t(e).replace(/[&<>”‘]/g,function(e){return”&”+l[e]+”;”})},unescapeHTML:function(e){return e==null?””:t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in f?f[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return e==null?””:t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,”\\$1″)},splice:function(e,t,n,r){var i=p.chars(e);return i.splice(~~t,~~n,r),i.join(“”)},insert:function(e,t,n){return p.splice(e,t,0,n)},include:function(e,n){return n===””?!0:e==null?!1:t(e).indexOf(n)!==-1},join:function(){var e=u.call(arguments),t=e.shift();return t==null&&(t=””),e.join(t)},lines:function(e){return e==null?[]:t(e).split(“\n”)},reverse:function(e){return p.chars(e).reverse().join(“”)},startsWith:function(e,n){return n===””?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return n===””?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return e==null?””:(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return e==null?””:t(e).replace(/(?:^|\s)\S/g,function(e){return e.toUpperCase()})},camelize:function(e){return p.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t.toUpperCase()})},underscored:function(e){return p.trim(e).replace(/([a-z\d])([A-Z]+)/g,”$1_$2″).replace(/[-\s]+/g,”_”).toLowerCase()},dasherize:function(e){return p.trim(e).replace(/([A-Z])/g,”-$1″).replace(/[-_\s]+/g,”-“).toLowerCase()},classify:function(e){return p.titleize(t(e).replace(/_/g,” “)).replace(/\s/g,””)},humanize:function(e){return p.capitalize(p.underscored(e).replace(/_id$/,””).replace(/_/g,” “))},trim:function(e,r){return e==null?””:!r&&n?n.call(e):(r=a(r),t(e).replace(new RegExp(“^”+r+”+|”+r+”+$”,”g”),””))},ltrim:function(e,n){return e==null?””:!n&&i?i.call(e):(n=a(n),t(e).replace(new RegExp(“^”+n+”+”),””))},rtrim:function(e,n){return e==null?””:!n&&r?r.call(e):(n=a(n),t(e).replace(new RegExp(n+”+$”),””))},truncate:function(e,n,r){return e==null?””:(e=t(e),r=r||”…”,n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(e==null)return””;e=t(e),n=~~n,r=r!=null?t(r):”…”;if(e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?”A”:” “},s=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,””):s=p.rtrim(s.slice(0,s.length-1)),(s+r).length>e.length?e:e.slice(0,s.length)+r},words:function(e,t){return p.isBlank(e)?[]:p.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=e==null?””:t(e),n=~~n;var s=0;r?r.length>1&&(r=r.charAt(0)):r=” “;switch(i){case”right”:return s=n-e.length,e+o(r,s);case”both”:return s=n-e.length,o(r,Math.ceil(s/2))+e+o(r,Math.floor(s/2));default:return s=n-e.length,o(r,s)+e}},lpad:function(e,t,n){return p.pad(e,t,n)},rpad:function(e,t,n){return p.pad(e,t,n,”right”)},lrpad:function(e,t,n){return p.pad(e,t,n,”both”)},sprintf:h,vsprintf:function(e,t){return t.unshift(e),h.apply(null,t)},toNumber:function(e,n){if(e==null||e==””)return 0;e=t(e);var r=s(s(e).toFixed(~~n));return r===0&&!e.match(/^0+$/)?Number.NaN:r},numberFormat:function(e,t,n,r){if(isNaN(e)||e==null)return””;e=e.toFixed(~~t),r=r||”,”;var i=e.split(“.”),s=i[0],o=i[1]?(n||”.”)+i[1]:””;return s.replace(/(\d)(?=(?:\d{3})+$)/g,”$1″+r)+o},strRight:function(e,n){if(e==null)return””;e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(e==null)return””;e=t(e),n=n!=null?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(e==null)return””;e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(e==null)return””;e+=””,t=t!=null?””+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||”, “,n=n||” and “;var i=e.slice(),s=i.pop();return e.length>2&&r&&(n=p.rtrim(t)+n),i.length?i.join(t)+n+s:s},toSentenceSerial:function(){var e=u.call(arguments);return e[3]=!0,p.toSentence.apply(p,e)},slugify:function(e){if(e==null)return””;var n=”ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź”,r=”aaaaaaaaceeeeeiiiilnoooooouuuunczz”,i=new RegExp(a(n),”g”);return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||”-“}),p.dasherize(e.replace(/[^\w\s-]/g,””))},surround:function(e,t){return[t,e,t].join(“”)},quote:function(e){return p.surround(e,'”‘)},exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t.match(/^(?:include|contains|reverse)$/))continue;e[t]=this[t]}return e},repeat:function(e,n,r){if(e==null)return””;n=~~n;if(r==null)return o(t(e),n);for(var i=[];n>0;i[–n]=e);return i.join(r)},levenshtein:function(e,n){if(e==null&&n==null)return 0;if(e==null)return t(n).length;if(n==null)return t(e).length;e=t(e),n=t(n);var r=[],i,s;for(var o=0;o<=n.length;o++)for(var u=0;u<=e.length;u++)o&&u?e.charAt(u-1)===n.charAt(o-1)?s=i:s=Math.min(r[u],r[u-1],i)+1:s=o+u,i=r[u],r[u]=s;return r.pop()}};p.strip=p.trim,p.lstrip=p.ltrim,p.rstrip=p.rtrim,p.center=p.lrpad,p.rjust=p.lpad,p.ljust=p.rpad,p.contains=p.include,p.q=p.quote,typeof exports!=”undefined”?(typeof module!=”undefined”&&module.exports&&(module.exports=p),exports._s=p):typeof define==”function”&&define.amd?define(“underscore.string”,[],function(){return p}):(e._=e._||{},e._.string=e._.str=p)}(this,String);

 

6. Criador de grupos de anúncios do Shopping em massa – Por Google Ads. Crie grupos de anúncios e produtos em massa com esse script do Google. Para utilizá-lo é necessário que você utilize uma planilha do Google.

// 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 Bulk Shopping AdGroup Creator
*
* @overview The Bulk Shopping AdGroup Creator provides a way to bulk create ad
* groups and product groups in existing Shopping Campaigns. See
* https://developers.google.com/adwords/scripts/docs/solutions/bulk-shopping-ad-group-creator
* for more details.
*
* @author AdWords Scripts Team [[email protected]]
*
* @version 1.0.2
*
* @changelog
* – version 1.0.2
* – Removed use of ProductAdBuilder.withPromotionLine as it is deprecated.
* – version 1.0.1
* – Added validation for external spreadsheet setup.
* – version 1.0
* – Released initial version.
*/

/**
* SPREADSHEET_URL: URL for spreadsheet to read
* SHEET_NAME: Name of sheet in spreadsheet to read
*/
var SPREADSHEET_URL = ‘YOUR_SPREADSHEET_URL’;
var SHEET_NAME = ‘YOUR_SHEET_NAME’;

/**
* Column header specification
* These are the actual text the script looks for in the spreadsheet header.
*/
var CAMPAIGN_COLUMN = ‘Campaign’;
var AD_GROUP_COLUMN = ‘Ad Group’;
var MAX_CPC_COLUMN = ‘Max CPC’;

var PRODUCT_GROUP_COLUMN = ‘Product Group’;
var AD_GROUP_STATUS_COLUMN = ‘AdGroup Status’;

var REQUIRED_COLUMN_NAMES = {};
REQUIRED_COLUMN_NAMES[CAMPAIGN_COLUMN] = true;
REQUIRED_COLUMN_NAMES[AD_GROUP_COLUMN] = true;
REQUIRED_COLUMN_NAMES[MAX_CPC_COLUMN] = true;
REQUIRED_COLUMN_NAMES[PRODUCT_GROUP_COLUMN] = true;
REQUIRED_COLUMN_NAMES[AD_GROUP_STATUS_COLUMN] = true;
/** End of column header specification */

/**
* Reads campaign and bid data from spreadsheet and writes it to AdWords.
*/
function main() {
Logger.log(‘Using spreadsheet – %s.’, SPREADSHEET_URL);
var sheet = validateAndGetSpreadsheet(SPREADSHEET_URL, SHEET_NAME);
parseSheetAndConstructProductGroups(sheet);

Logger.log(‘Parsed spreadsheet and completed shopping campaign ‘ +
‘construction.’);
}

//Stores results of each row, along with formatting details
var resultsByRow = [‘Result’, ‘Notes’];
var resultColors = [‘Black’, ‘Black’];

/**
* Validates header of sheet to make sure that header matches expected format.
* Throws exception if problems are found. Saves the column number of each
* expected column to global variable.
*
* @param {!Array.<string>} headerRow A list of column header names.
* @return {!Object.<number>} A mapping from column name to column index.
* @throws If required column is missing.
*/
function validateHeader(headerRow) {
var result = {};

var missingColumns = Object.keys(REQUIRED_COLUMN_NAMES);

// Grab the column # for each expected input column.
for (var columnIndex = 0; columnIndex < headerRow.length; columnIndex++) {
var columnName = headerRow[columnIndex];
if (columnName in REQUIRED_COLUMN_NAMES) {
result[columnName] = columnIndex;
var index = missingColumns.indexOf(columnName);
if (index >= 0) {
missingColumns.splice(index, 1);
}
}
}

if (missingColumns.length > 0) {
throw ‘Bid sheet data format doesn\’t match expected format. ‘ +
‘Missing columns: ‘ + missingColumns.join();
}
return result;
}

/**
* Converts a spreadsheet row into map representation.
*
* @param {!Array.<!Object>} row The spreadsheet row.
* @param {!Object.<number>} headers Mapping from column name to column index.
* @return {?Object} The row in object form, or null for a parsing error.
*/
function parseRow(row, headers) {
var parsedRow = {};

for (var header in headers) {
var colNum = headers[header];
var val = row[colNum].toString().trim();
if (!val) {
continue;
}
// AWEditor will add double quotes (“) around string if it contains
// commas or double quotes, so we need to strip those out.
if (val.charAt(0) == ‘”‘ && val.charAt(val.length – 1) == ‘”‘) {
val = val.substring(1, val.length – 1);
}
// AWEditor escapes double quotes (“) with another double quote (“”).
val = val.replace(/””/g, ‘”‘);
if (header == PRODUCT_GROUP_COLUMN) {
var productGroups = [];
var parsedProductGroups =
parseAWEditorFormat(val);
if (parsedProductGroups.error) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = parsedProductGroups.error;
resultColors[0] = ‘Red’;
return null;
}

for (var x = 0; x < parsedProductGroups.length; x++) {
// Product group type and value indices are level-1
// (e.g. for L1, x=0).
productGroups[x] = {
type: parsedProductGroups[x][0],
value: parsedProductGroups[x][1]
};
}
parsedRow[header] = productGroups;
} else {
parsedRow[header] = val;
}
}

// Ignore rows that don’t have any useful information.
var testRow = [];
for (var k in parsedRow) {
// Remove campaign, ad group columns and test if the rest is empty.
if (k == CAMPAIGN_COLUMN || k == AD_GROUP_COLUMN) {
continue;
}
testRow.push(parsedRow[k]);
}
if (testRow.toString().replace(/[,]+/g, ”) == ”) {
resultsByRow[0] = ‘SKIPPED’;
resultsByRow[1] = ‘Superfluous row’;
return null;
}
return parsedRow;
}

/**
* Parses spreadsheet and constructs ad groups and product groups in AdWords.
*
* @param {!Sheet} sheet The sheet to parse.
*/
function parseSheetAndConstructProductGroups(sheet) {
var headerRow = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];

var headers = validateHeader(headerRow);

var values = sheet.getRange(2, 1, sheet.getLastRow() – 1,
sheet.getLastColumn()).getValues();

var campaigns = {};

var outputColumn = sheet.getLastColumn() + 1;
var resultHeaderRange = sheet.getRange(1, outputColumn, 1, 2);
resultHeaderRange.setValues([resultsByRow]);
resultHeaderRange.setFontColors([resultColors]);
resultHeaderRange.setFontWeights([[‘Bold’, ‘Bold’]]);

// Iterate through rows.
for (var r = 0; r < values.length; r++) {
resultsByRow = [”, ”];
resultColors = [‘Black’, ‘Black’];
var row = values[r];

var parsedRow = parseRow(row, headers);

if (parsedRow) {
var bid = parsedRow[MAX_CPC_COLUMN];
if (bid) {
// For European locale, decimal points are commas.
bid = bid.toString().toLowerCase().replace(/,/g, ‘.’);
if (bid != ‘excluded’ && (isNaN(parseFloat(bid)) || !isFinite(bid))) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Invalid bid’;
}
}
var campaignName = parsedRow[CAMPAIGN_COLUMN];
campaigns[campaignName] =
fetchCampaignIfNecessary(campaigns, campaignName);
if (campaigns[campaignName]) {
var adGroupName = parsedRow[AD_GROUP_COLUMN];
campaigns[campaignName].createdAdGroups[adGroupName] =
buildAdGroupIfNecessary(parsedRow, campaigns[campaignName],
adGroupName, bid);
if (campaigns[campaignName].createdAdGroups[adGroupName]) {
if (!campaigns[campaignName].createdAdGroups[adGroupName].skip) {
buildProductGroups(parsedRow,
campaigns[campaignName].createdAdGroups[adGroupName], bid);
} else {
resultsByRow[0] = ‘SKIPPED’;
resultsByRow[1] =
‘Ad group already exists with product groups’;
}
}
}

if (!resultsByRow[0]) {
resultsByRow[0] = ‘SUCCESS’;
}
}
switch (resultsByRow[0]) {
case ‘ERROR’:
resultColors[0] = ‘Red’;
break;
case ‘SUCCESS’:
resultColors[0] = ‘Green’;
break;
case ‘WARNING’:
resultColors[0] = ‘Yellow’;
}
var resultRange = sheet.getRange(r + 2, outputColumn, 1, 2);
resultRange.setValues([resultsByRow]);
resultRange.setFontColors([resultColors]);
resultRange.setFontWeights([[‘Bold’, ‘Normal’]]);
}
}

/**
* Fetches campaign from AdWords if it hasn’t already been done.
*
* @param {!Object.<ShoppingCampaign>} campaigns A cache of campaigns.
* @param {string} campaignName The name of the campaign to fetch.
* @return {?ShoppingCampaign} The campaign, or null if not found.
*/
function fetchCampaignIfNecessary(campaigns, campaignName) {
//Find the campaign
if (!campaignName) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Missing campaign name’;
return null;
}
var campaign = campaigns[campaignName];
if (!campaign) {
campaign = findCampaignByName(campaignName);
campaign.createdAdGroups = {};
if (!campaign) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Could not find campaign’;
}
}
return campaign;
}

/**
* Builds ad group if necessary, otherwise returns existing ad group.
*
* @param {!Array.<Object>} row A spreadsheet row.
* @param {!ShoppingCampaign} campaign
* @param {string} adGroupName The ad group to build or fetch.
* @param {number} bid
* @return {?ShoppingAdGroup} The ad group of null if there is an error.
*/
function buildAdGroupIfNecessary(row, campaign, adGroupName, bid) {
if (!adGroupName) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Missing ad group name’;
return null;
}
// See if we already fetched/created the ad group.
var adGroup = campaign.createdAdGroups[adGroupName];
if (!adGroup) {
// Only use the bid on this line for the ad group default bid if there are
// no product groups specified for it. Ad group default bid must be
// specified.
if (row[PRODUCT_GROUP_COLUMN]) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Ad Group is missing a default bid.’;
return null;
}
// Set default status to enabled.
var status = ‘ENABLED’;
// If ad group status is specified, make sure it’s “active”, “enabled”, or
// “paused”, and set status. Ad group status must be set on the first row
// that the ad group appears in.
if (row[AD_GROUP_STATUS_COLUMN]) {
status = row[AD_GROUP_STATUS_COLUMN].toUpperCase();
if (status == ‘ACTIVE’) {
status = ‘ENABLED’;
}
}
adGroup = createAdGroup(adGroupName, status, bid, campaign);
if (adGroup) {
adGroup.rootProductGroup = adGroup.rootProductGroup();
adGroup.rootProductGroup.childMap = {};
}
}
return adGroup;
}

/**
* Builds product groups from row.
*
* @param {!Array.<Object>} row A spreadsheet row.
* @param {!ShoppingAdGroup} adGroup The ad group to operate on.
* @param {number} bid The product group bid.
*/
function buildProductGroups(row, adGroup, bid) {
if (!row[PRODUCT_GROUP_COLUMN]) {
return;
}
// Iterate through product groups in row.
var maxLevel = -1;
var productGroupsToAdd = row[PRODUCT_GROUP_COLUMN];
var productGroup = adGroup.rootProductGroup;
for (var i = 0; i < productGroupsToAdd.length; i++) {
var type =
productGroupsToAdd[i].type.toString().toLowerCase()
.replace(/[ ]+/g, ”);
var val = productGroupsToAdd[i].value.toString().trim();
if (type) {
//For each Group level n, row must contain values for 1…n-1
if (i – maxLevel > 1) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Every level of the product ‘ +
‘group type must have all higher ‘ +
‘level values. L’ + i + ‘ is filled but missing L’ +
(maxLevel + 1);
return;
}
maxLevel = i;

// Each row must have matching # of bidding attribute type and value.
if (!val) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] =
‘Every product group type must have an associated value. L’ +
i + ‘ has a type but no value’;
return;
}

// Build product groups.
if (!productGroup.childMap[val.toLowerCase()]) {
if (val == ‘*’) {
if (Object.keys(productGroup.childMap).length == 0) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] =
‘”Everything else” product group must come after all others’;
return;
} else {
var child = productGroup.childMap[val];
if (!child) {
var children = productGroup.children().get();
while (children.hasNext()) {
child = children.next();
if (child.isOtherCase()) {
break;
}
}
child.childMap = {};
}
productGroup.childMap[val] = child;
productGroup = child;
productGroup.setMaxCpc(adGroup.getMaxCpc());
//Only assign the bid to the lowest level product group
//on that row
if (i + 1 == productGroupsToAdd.length) {
if (bid != ‘excluded’) {
productGroup.setMaxCpc(bid);
} else {
productGroup.exclude();
}
}
}
} else {
var productGroupBuilder = productGroup.newChild();
// Verify that bidding attribute type is valid, construct
// productGroupBuilder.
switch (type) {
case ‘producttype’:
val = val.toLowerCase();
productGroupBuilder = productGroupBuilder.productTypeBuilder()
.withValue(val);
break;
case ‘brand’:
val = val.toLowerCase();
productGroupBuilder = productGroupBuilder.brandBuilder()
.withName(val);
break;
case ‘category’:
productGroupBuilder = productGroupBuilder.categoryBuilder()
.withName(val);
break;
case ‘condition’:
val = val.toUpperCase();
productGroupBuilder = productGroupBuilder.conditionBuilder()
.withCondition(val);
break;
case ‘itemid’:
val = val.toLowerCase();
productGroupBuilder = productGroupBuilder.itemIdBuilder()
.withValue(val);
break;
default:
if (type.match(/^custom((\\s)?(label|attribute))?/)) {
val = val.toLowerCase();
//make sure there’s a number at the end that’s between 0-4
if (type.match(/[0-4]$/)) {
productGroupBuilder =
productGroupBuilder.customLabelBuilder()
.withType(‘CUSTOM_LABEL_’ +
type.substring(type.length – 1))
.withValue(val);
} else {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] =
‘Invalid custom attribute type: ‘ +
productGroupsToAdd[i].type;
return;
}
} else {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] =
‘Invalid bidding attribute type: ‘ +
productGroupsToAdd[i].type;
return;
}
}

var productGroupOp = productGroupBuilder.build();

if (!productGroupOp.isSuccessful()) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Error creating product group ‘ +
‘level ‘ + (i + 1) + ‘: ‘ + productGroupOp.getErrors();
return;
}

var result = productGroupOp.getResult();
// Only assign the bid to the lowest level product group on that row.
if (i + 1 == productGroupsToAdd.length) {
if (bid == ‘excluded’) {
result.exclude();
} else if (bid) {
result.setMaxCpc(bid);
}
}

result.childMap = {};
productGroup.childMap[val.toLowerCase()] = result;

// Set current product group to the newly created product group.
productGroup = result;
}
} else {
// Set current product group to the last read product group.
productGroup = productGroup.childMap[val.toLowerCase()];
}
}
}
}

/**
* Parses AdWords Editor product group format
* (e.g. * / Condition=’New’ / Custom label 2=’furniture’ /
* Product type=’bar carts’).
*
* @param {string} productGroupPath The product group path.
* @return {!Array.<!Array.<string>>} A list of product group component name/
* value pairs.
*/
function parseAWEditorFormat(productGroupPath) {
// Ignore * / case which is the root product group.
if (productGroupPath == ‘* /’ || !productGroupPath) {
return [];
}

var regexVals = productGroupPath.match(new RegExp(/'(.*?)’/g));

if (regexVals) {
for (var i = 0; i < regexVals.length; i++) {
productGroupPath = productGroupPath.replace(regexVals[i], ‘$’ + i);
}
}

var result = [];
var productGroup = productGroupPath.split(‘/’);

// AW Editor format starts with ‘* /’ so we ignore first one.
for (var x = 1; x < productGroup.length; x++) {
if (!productGroup[x]) {
continue;
}
// AW Editor format looks like: Brand=’nike’.
var pair = productGroup[x].trim().split(‘=’);
if (pair.length != 2) {
return {error: ‘Product group string malformed. Should have 1 “=”, ‘ +
‘but has ‘ + (pair.length – 1)};
}
var val = pair[1];
if (val.charAt(0) != ‘$’ && val.charAt(0) != ‘*’) {
return {error: ‘Product group string malformed. Please ensure you are ‘ +
‘using AdWords Editor format’};
}
// ‘*’ value doesn’t have single quotes around it.
if (val != ‘*’) {
var values = pair[1].split(‘$’);
// Skip 0 because it’s always blank. String always starts with $.
for (var i = 1; i < values.length; i++) {
val = val.replace(‘$’ + values[i], regexVals[values[i]]);
}
val = val.substring(1, val.length – 1).replace(/”/g, ‘\”);
}

result.push([pair[0], val]);
}
return result;
}

/**
* Fetches campaign from AdWords by name.
*
* @param {string} name The campaign name.
* @return {?ShoppingCampaign} The found campaign, or null if not found.
*/
function findCampaignByName(name) {
var campaignName = name.replace(/’/g, ‘\\\”);
var shoppingCampaignSelector = AdWordsApp
.shoppingCampaigns()
.withCondition(‘Name = \” + campaignName + ‘\”);

var campaign = null;

var shoppingCampaignIterator = shoppingCampaignSelector.get();
if (shoppingCampaignIterator.hasNext()) {
campaign = shoppingCampaignIterator.next();
}

return campaign;
}

/**
* Fetches ad group from AdWords given ad group name and campaign.
*
* @param {string} agName The name of the ad group.
* @param {!ShoppingCampaign} campaign The campaign within which to search.
* @return {?ShoppingAdGroup} The ad group or null if not found.
*/
function findAdGroupByName(agName, campaign) {
var adGroupName = agName.replace(/’/g, ‘\\\”);
var adGroupSelector = campaign
.adGroups()
.withCondition(‘Name = \” + adGroupName + ‘\”);

var adGroup = null;

var adGroupIterator = adGroupSelector.get();
if (adGroupIterator.hasNext()) {
adGroup = adGroupIterator.next();
}

return adGroup;
}

/**
* Creates ad group in AdWords if it doesn’t already exist, along with ad group
* ad.
*
* @param {string} name The name of the ad group.
* @param {string} status The desired status of the ad group.
* @param {number} defaultBid The max CPC for the ad group.
* @param {!ShoppingCampaign} campaign The campaign to create the ad group for.
* @return {?ShoppingAdGroup} The created ad group or null if there is an error.
*/
function createAdGroup(name, status, defaultBid, campaign) {
// See if ad group exists. If so, fetch it.
var adGroup = findAdGroupByName(name, campaign);
if (adGroup != null) {
if (adGroup.rootProductGroup()) {
// If root product group exists and not delete, then skip ad group.
adGroup.skip = true;
} else {
adGroup.createRootProductGroup();
}
return adGroup;
}

// Build ad group.
var adGroupOp = campaign.newAdGroupBuilder()
.withName(name)
.withStatus(status)
.withMaxCpc(defaultBid)
.build();

// Check for errors.
if (!adGroupOp.isSuccessful()) {
resultsByRow[0] = ‘ERROR’;
resultsByRow[1] = ‘Error creating ad group: ‘ +
adGroupOp.getErrors();
return null;
}

var adGroupResult = adGroupOp.getResult();

adGroupResult.createRootProductGroup();

Logger.log(‘Successfully created ad group [‘ + adGroupResult.getName() + ‘]’);

// Build ad group ad.
var adGroupAdOp = adGroupResult.newAdBuilder().build();

// Check for errors.
if (!adGroupAdOp.isSuccessful()) {
resultsByRow[0] = ‘WARNING’;
resultsByRow[1] = ‘Error creating ad group ad: ‘ +
adGroupAdOp.getErrors();
}

return adGroupResult;
}

/**
* DO NOT EDIT ANYTHING BELOW THIS LINE.
* Please modify your spreadsheet URL and email addresses at the top of the file
* only.
*/

/**
* Validates the provided spreadsheet URL and email address
* to make sure that they’re set up properly. Throws a descriptive error message
* if validation fails.
*
* @param {string} spreadsheeturl The URL of the spreadsheet to open.
* @param {string} sheetname The name of the sheet within the spreadsheet that
* should be fetched.
* @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
* @throws {Error} If the spreadsheet URL or email hasn’t been set
*/
function validateAndGetSpreadsheet(spreadsheeturl, sheetname) {
if (spreadsheeturl == ‘YOUR_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.’);
}
if (sheetname == ‘YOUR_SHEET_NAME’) {
throw new Error(‘Please specify the name of the sheet you want to use on’ +
‘ your spreadsheet.’);
}
var spreadsheet = SpreadsheetApp.openByUrl(spreadsheeturl);
var sheet = spreadsheet.getSheetByName(sheetname);
return sheet;
}

 

Como Configurar