Advertisement
Guest User

Untitled

a guest
Mar 24th, 2017
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.96 KB | None | 0 0
  1. /*
  2. *
  3. * Advanced ad scheduling
  4. *
  5. * This script will apply ad schedules to campaigns or shopping campaigns and set
  6. * the ad schedule bid modifier and mobile bid modifier at each hour according to
  7. * multiplier timetables in a Google sheet.
  8. *
  9. * This version creates schedules with modifiers for 4 hours, then fills the rest
  10. * of the day and the other days of the week with schedules with no modifier as a
  11. * fail safe.
  12. *
  13. * Version: 3.0
  14. * Updated to allow -100% bids, change mobile adjustments and create fail safes.
  15. * brainlabsdigital.com
  16. *
  17. */
  18.  
  19. function main() {
  20. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  21. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  22. //Options
  23.  
  24. //The Google sheet to use
  25. //The default value is the example sheet
  26. var spreadsheetUrl = "https://docs.google.com/spreadsheets/d/13AV3Tbb19wYMC6Qz_IAWij2X-r1BqdVb7rX2kit-o30/edit#gid=0";
  27.  
  28. //Shopping or regular campaigns
  29. //Use true if you want to run script on shopping campaigns (not regular campaigns).
  30. //Use false for regular campaigns.
  31. var shoppingCampaigns = true;
  32.  
  33. //Use true if you want to set mobile bid adjustments as well as ad schedules.
  34. //Use false to just set ad schedules.
  35. var runMobileBids = false;
  36.  
  37. //Optional parameters for filtering campaign names. The matching is case insensitive.
  38. //Select which campaigns to exclude e.g ["foo", "bar"] will ignore all campaigns
  39. //whose name contains 'foo' or 'bar'. Leave blank [] to not exclude any campaigns.
  40. var excludeCampaignNameContains = [];
  41.  
  42. //Select which campaigns to include e.g ["foo", "bar"] will include only campaigns
  43. //whose name contains 'foo' or 'bar'. Leave blank [] to include all campaigns.
  44. var includeCampaignNameContains = [];
  45.  
  46. //When you want to stop running the ad scheduling for good, set the lastRun
  47. //variable to true to remove all ad schedules.
  48. var lastRun = false;
  49.  
  50. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  51. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
  52.  
  53. //Initialise for use later.
  54. var weekDays = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"];
  55. var adScheduleCodes = [];
  56. var campaignIds = [];
  57.  
  58. //Retrieving up hourly data
  59. var scheduleRange = "B2:H25";
  60. var accountName = AdWordsApp.currentAccount().getName();
  61. var spreadsheet = SpreadsheetApp.openByUrl(spreadsheetUrl);
  62. var sheets = spreadsheet.getSheets();
  63.  
  64. var timeZone = AdWordsApp.currentAccount().getTimeZone();
  65. var date = new Date();
  66. var dayOfWeek = parseInt(Utilities.formatDate(date, timeZone, "uu"), 10) - 1;
  67. var hour = parseInt(Utilities.formatDate(date, timeZone, "HH"), 10);
  68.  
  69. var sheet = sheets[0];
  70. var data = sheet.getRange(scheduleRange).getValues();
  71.  
  72. //This hour's bid multiplier.
  73. var thisHourMultiplier = data[hour][dayOfWeek];
  74. var lastHourCell = "I2";
  75. sheet.getRange(lastHourCell).setValue(thisHourMultiplier);
  76.  
  77. //The next few hours' multipliers
  78. var timesAndModifiers = [];
  79. var otherDays = weekDays.slice(0);
  80. for (var h=0; h<5; h++) {
  81. var newHour = (hour + h)%24;
  82. if (hour + h > 23) {
  83. var newDay = (dayOfWeek + 1)%7;
  84. } else {
  85. var newDay = dayOfWeek;
  86. }
  87. otherDays[newDay] = "-";
  88.  
  89. if (h<4) {
  90. // Use the specified bids for the next 4 hours
  91. var bidModifier = data[newHour][newDay];
  92. if (isNaN(bidModifier) || (bidModifier < -0.9 && bidModifier > -1) || bidModifier > 9) {
  93. Logger.log("Bid modifier '" + bidModifier + "' for " + weekDays[newDay] + " " + newHour + " is not valid.");
  94. timesAndModifiers.push([newHour, newHour+1, weekDays[newDay], 0]);
  95. } else if (bidModifier != -1 && bidModifier.length != 0) {
  96. timesAndModifiers.push([newHour, newHour+1, weekDays[newDay], bidModifier]);
  97. }
  98. } else {
  99. // Fill in the rest of the day with no adjustment (as a back-up incase the script breaks)
  100. timesAndModifiers.push([newHour, 24, weekDays[newDay], 0]);
  101. }
  102. }
  103.  
  104. if (hour>0) {
  105. timesAndModifiers.push([0, hour, weekDays[dayOfWeek], 0]);
  106. }
  107.  
  108. for (var d=0; d<otherDays.length; d++) {
  109. if (otherDays[d] != "-") {
  110. timesAndModifiers.push([0, 24, otherDays[d], 0]);
  111. }
  112. }
  113.  
  114. //Pull a list of all relevant campaign IDs in the account.
  115. var campaignSelector = ConstructIterator(shoppingCampaigns);
  116. for(var i = 0; i < excludeCampaignNameContains.length; i++){
  117. campaignSelector = campaignSelector.withCondition('Name DOES_NOT_CONTAIN_IGNORE_CASE "' + excludeCampaignNameContains[i] + '"');
  118. }
  119. campaignSelector = campaignSelector.withCondition("Status IN [ENABLED,PAUSED]");
  120. var campaignIterator = campaignSelector.get();
  121. while(campaignIterator.hasNext()){
  122. var campaign = campaignIterator.next();
  123. var campaignName = campaign.getName();
  124. var includeCampaign = false;
  125. if(includeCampaignNameContains.length === 0){
  126. includeCampaign = true;
  127. }
  128. for(var i = 0; i < includeCampaignNameContains.length; i++){
  129. var index = campaignName.toLowerCase().indexOf(includeCampaignNameContains[i].toLowerCase());
  130. if(index !== -1){
  131. includeCampaign = true;
  132. break;
  133. }
  134. }
  135. if(includeCampaign){
  136. var campaignId = campaign.getId();
  137. campaignIds.push(campaignId);
  138. }
  139. }
  140.  
  141. //Return if there are no campaigns.
  142. if(campaignIds.length === 0){
  143. Logger.log("There are no campaigns matching your criteria.");
  144. return;
  145. }
  146.  
  147. //Remove all ad scheduling for the last run.
  148. if(lastRun){
  149. checkAndRemoveAdSchedules(campaignIds, []);
  150. return;
  151. }
  152.  
  153. // Change the mobile bid adjustment
  154. if(runMobileBids){
  155. if (sheets.length < 2) {
  156. Logger.log("Mobile ad schedule sheet was not found in the Google spreadsheet.");
  157. } else {
  158. var sheet = sheets[1];
  159. var data = sheet.getRange(scheduleRange).getValues();
  160. var thisHourMultiplier_Mobile = data[hour][dayOfWeek];
  161.  
  162. if (thisHourMultiplier_Mobile.length === 0) {
  163. thisHourMultiplier_Mobile = -1;
  164. }
  165.  
  166. if (isNaN(thisHourMultiplier_Mobile) || (thisHourMultiplier_Mobile < -0.9 && thisHourMultiplier_Mobile > -1) || thisHourMultiplier_Mobile > 3) {
  167. Logger.log("Mobile bid modifier '" + thisHourMultiplier_Mobile + "' for " + weekDays[dayOfWeek] + " " + hour + " is not valid.");
  168. thisHourMultiplier_Mobile = 0;
  169. }
  170.  
  171. var totalMultiplier = ((1+thisHourMultiplier_Mobile)*(1+thisHourMultiplier))-1;
  172. sheet.getRange("I2").setValue(thisHourMultiplier_Mobile);
  173. sheet.getRange("T2").setValue(totalMultiplier);
  174. ModifyMobileBidAdjustment(campaignIds, thisHourMultiplier_Mobile);
  175. }
  176. }
  177.  
  178. // Check the existing ad schedules, removing those no longer necessary
  179. var existingSchedules = checkAndRemoveAdSchedules(campaignIds, timesAndModifiers);
  180.  
  181. // Add in the new ad schedules
  182. AddHourlyAdSchedules(campaignIds, timesAndModifiers, existingSchedules, shoppingCampaigns);
  183.  
  184. }
  185.  
  186. /**
  187. * Function to add ad schedules for the campaigns with the given IDs, unless the schedules are
  188. * referenced in the existingSchedules array. The scheduling will be added as a hour long periods
  189. * as specified in the passed parameter array and will be given the specified bid modifier.
  190. *
  191. * @param array campaignIds array of campaign IDs to add ad schedules to
  192. * @param array timesAndModifiers the array of [hour, day, bid modifier] for which to add ad scheduling
  193. * @param array existingSchedules array of strings identifying already existing schedules.
  194. * @param bool shoppingCampaigns using shopping campaigns?
  195. * @return void
  196. */
  197. function AddHourlyAdSchedules(campaignIds, timesAndModifiers, existingSchedules, shoppingCampaigns){
  198. // times = [[hour,day],[hour,day]]
  199. var campaignIterator = ConstructIterator(shoppingCampaigns)
  200. .withIds(campaignIds)
  201. .get();
  202. while(campaignIterator.hasNext()){
  203. var campaign = campaignIterator.next();
  204. for(var i = 0; i < timesAndModifiers.length; i++){
  205. if (existingSchedules.indexOf(
  206. timesAndModifiers[i][0] + "|" + (timesAndModifiers[i][1]) + "|" + timesAndModifiers[i][2]
  207. + "|" + Utilities.formatString("%.2f",(timesAndModifiers[i][3]+1)) + "|" + campaign.getId())
  208. > -1) {
  209.  
  210. continue;
  211. }
  212.  
  213. campaign.addAdSchedule({
  214. dayOfWeek: timesAndModifiers[i][2],
  215. startHour: timesAndModifiers[i][0],
  216. startMinute: 0,
  217. endHour: timesAndModifiers[i][1],
  218. endMinute: 0,
  219. bidModifier: Math.round(100*(1+timesAndModifiers[i][3]))/100
  220. });
  221. }
  222. }
  223. }
  224.  
  225. /**
  226. * Function to remove ad schedules from all campaigns referenced in the passed array
  227. * which do not correspond to schedules specified in the passed timesAndModifiers array.
  228. *
  229. * @param array campaignIds array of campaign IDs to remove ad scheduling from
  230. * @param array timesAndModifiers array of [hour, day, bid modifier] of the wanted schedules
  231. * @return array existingWantedSchedules array of strings identifying the existing undeleted schedules
  232. */
  233. function checkAndRemoveAdSchedules(campaignIds, timesAndModifiers) {
  234.  
  235. var adScheduleIds = [];
  236.  
  237. var report = AdWordsApp.report(
  238. 'SELECT CampaignId, Id ' +
  239. 'FROM CAMPAIGN_AD_SCHEDULE_TARGET_REPORT ' +
  240. 'WHERE CampaignId IN ["' + campaignIds.join('","') + '"]'
  241. );
  242.  
  243. var rows = report.rows();
  244. while(rows.hasNext()){
  245. var row = rows.next();
  246. var adScheduleId = row['Id'];
  247. var campaignId = row['CampaignId'];
  248. adScheduleIds.push([campaignId,adScheduleId]);
  249. }
  250.  
  251. var chunkedArray = [];
  252. var chunkSize = 10000;
  253.  
  254. for(var i = 0; i < adScheduleIds.length; i += chunkSize){
  255. chunkedArray.push(adScheduleIds.slice(i, i + chunkSize));
  256. }
  257.  
  258. var wantedSchedules = [];
  259. var existingWantedSchedules = [];
  260.  
  261. for (var j=0; j<timesAndModifiers.length; j++) {
  262. wantedSchedules.push(timesAndModifiers[j][0] + "|" + (timesAndModifiers[j][1]) + "|" + timesAndModifiers[j][2] + "|" + Utilities.formatString("%.2f",timesAndModifiers[j][3]+1));
  263. }
  264.  
  265. for(var i = 0; i < chunkedArray.length; i++){
  266. var unwantedSchedules = [];
  267.  
  268. var adScheduleIterator = AdWordsApp.targeting()
  269. .adSchedules()
  270. .withIds(chunkedArray[i])
  271. .get();
  272. while (adScheduleIterator.hasNext()) {
  273. var adSchedule = adScheduleIterator.next();
  274. var key = adSchedule.getStartHour() + "|" + adSchedule.getEndHour() + "|" + adSchedule.getDayOfWeek() + "|" + Utilities.formatString("%.2f",adSchedule.getBidModifier());
  275.  
  276. if (wantedSchedules.indexOf(key) > -1) {
  277. existingWantedSchedules.push(key + "|" + adSchedule.getCampaign().getId());
  278. } else {
  279. unwantedSchedules.push(adSchedule);
  280. }
  281. }
  282.  
  283. for(var j = 0; j < unwantedSchedules.length; j++){
  284. unwantedSchedules[j].remove();
  285. }
  286. }
  287.  
  288. return existingWantedSchedules;
  289. }
  290.  
  291. /**
  292. * Function to construct an iterator for shopping campaigns or regular campaigns.
  293. *
  294. * @param bool shoppingCampaigns Using shopping campaigns?
  295. * @return AdWords iterator Returns the corresponding AdWords iterator
  296. */
  297. function ConstructIterator(shoppingCampaigns){
  298. if(shoppingCampaigns === true){
  299. return AdWordsApp.shoppingCampaigns();
  300. }
  301. else{
  302. return AdWordsApp.campaigns();
  303. }
  304. }
  305.  
  306. /**
  307. * Function to set a mobile bid modifier for a set of campaigns
  308. *
  309. * @param array campaignIds An array of the campaign IDs to be affected
  310. * @param Float bidModifier The multiplicative mobile bid modifier
  311. * @return void
  312. */
  313. function ModifyMobileBidAdjustment(campaignIds, bidModifier){
  314.  
  315. var platformIds = [];
  316. var newBidModifier = Math.round(100*(1+bidModifier))/100;
  317.  
  318. for(var i = 0; i < campaignIds.length; i++){
  319. platformIds.push([campaignIds[i],30001]);
  320. }
  321.  
  322. var platformIterator = AdWordsApp.targeting()
  323. .platforms()
  324. .withIds(platformIds)
  325. .get();
  326. while (platformIterator.hasNext()) {
  327. var platform = platformIterator.next();
  328. platform.setBidModifier(newBidModifier);
  329. }
  330. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement