Remove Location Targets in Bulk - Google Ads Scripts - google-apps-script

After exhausting all options, I'm hoping for some help. I am trying to adjust my script to remove all targeted locations for a specific campaign. The problem is, the only way I've found to do it is to iterate through each targeted location and remove them one by one. If a campaign has 2000 location targets, this takes far too long and the script times out.
Below is an example of the standard script used to remove them one by one. Any suggestions on how to speed this up? Is it possible to pass an array of campaign specific locationIDs in and have it remove them all in one shot? Or, is there a function available to remove ALL locations at once? Either of these methods would work for me but I haven't been able to find a solution that accomplishes this.
Thanks in advance for your help
function removeTargetedLocations(campaign) {
var campaignIterator = AdWordsApp.campaigns()
.withCondition('Name = ' + campaign + '')
.get();
if (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
var locationIterator = campaign.targeting().targetedLocations().get();
while (locationIterator.hasNext()) {
var loc = locationIterator.next();
loc.remove();
}
}
}

Related

Range.SetValues() does not insert data on one sheet, on the other works. What is the reason?

I have a GoogleSheet with basically two sheets, which are very similar in terms of data collected.
I need to calculate same values for both sheets, but source data is in different columns.
Therefore I created three files in AppsScript:
Common.gs - with common function definitions
sheet1.gs
sheet2.gs - both sheet1 and sheet2 have only definitions of proper ranges in particular columns and one function to run script, which essentially calls functions defined in Common.gs, like so in sheet1.gs:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheet1")
var createdColumn = sheet.getRange("E2:E").getValues()
var ackColumn = sheet.getRange("G2:G").getValues()
var resColumn = sheet.getRange("I2:I").getValues()
var timeToAckColumn = sheet.getRange(2,14,ackColumn.length,1)
var timeToResColumn = sheet.getRange(2,15,resColumn.length,1)
var yearAndWeekRange = sheet.getRange(2,16,createdColumn.length,2)
function calculateMetricsSheet1() {
calculateTimeDiff(createdColumn, ackColumn, timeToAckColumn)
calculateTimeDiff(ackColumn, resColumn, timeToResColumn)
calculateWeek(createdColumn, yearAndWeekRange)
}
example function implementation (they are basically very similar with minor differences):
function calculateWeek(createdColumn, yearAndWeekRange) {
var arrData = []
for(var i=0;i<createdColumn.length;i++) {
if(createdColumn[i][0].toString()=="") {
arrData.push(["",""])
continue
}
var createdDate = new Date(createdColumn[i][0])
var year = createdDate.getFullYear()
var week = Utilities.formatDate(createdDate, "GMT+1", "w")
arrData.push([year, week])
}
yearAndWeekRange.setValues(arrData)
}
the sheet2.gs is basically different column definitions, the functions called within calculateMetricsSheet2() are the same.
So what is the problem?
The script works perfectly fine for sheet2.gs, but for sheet1.gs it does collect proper data, calculates proper data, but the data does not appear in proper columns after Range.setValues() call.
No exceptions or errors appear in the console.
Documentation does not provide any kind of information what could be the problem.
I have really ran out of ideas what could be the cause of the issue.
Does anyone have any idea what is going on?
edit: It may be useful to put emphasis on the fact that each script runs function calling 3 other functions -> all of them end with Range.setValues({values}). And for one sheet all of them work, and for the other - none.
That's the reason I assume there is something wrong with the sheet itself, maybe some permissions/protection? But I couldn't find anything :(
edit2: I modified my code to iterate through the sheet 10 rows at a time, because I thought maybe when I get a whole column, something bad happens with data and breaks setValues() function.
Unfortunately - even if my code iterated 1 row at a time, it still did not work on sheet1, but worked on sheet2. So not a data problem.
The code you show always puts values in yearAndWeekRange which is always in the 'sheet1' sheet. To put the data into another sheet, you need to change the target range appropriately.
The dimensions of the range must match the dimensions of the array you put there. Use this pattern:
yearAndWeekRange.offset(0, 0, arrData.length, arrData[0].length).setValues(arrData);
I found out what is the problem.
Two scripts were pretty identical, even with naming of variables - ie ackColumn, resColumn etc.
Those were stored as a global variables, so even if I was running script1.gs, it used global variables from script2.gs, effectively writing proper data to wrong sheet.
separating global variables names fixed the issue.
Perhaps a rookie mistake, but I missed the fact, that if I have a variable defined outside any function, it becomes global and could be overwritten from other file

Creating a UrlFetchApp script to replace the Google Sheet importHTML function

I used the following formula for about a year now and suddenly it stopped working/importing the table.
=IMPORTHTML("https://tradingeconomics.com/matrix";"table";1)
It gives me a "Could not fetch url: https://tradingeconomics.com/matrix" error.
I tried various things and one of the interesting findings was that the importHTML works for the cached version, but only in a new sheet under a different Google account. Furthermore, the cached version breaks randomly too.
Thus, it seems I won't get around using a script for this purpose.
Ideally, this script would be flexible enough, where it would have a dedicated function e.g. importHTMLtable where the user can input the URL and the table no. and it works. So it would work for the following functions I currently use e.g.
=importHTMLtable("https://tradingeconomics.com/matrix";"table";1)
OR
=importHTMLtable("https://tradingeconomics.com/country-list/business-confidence?continent=world";"table";1)
OR
=importHTMLtable("https://tradingeconomics.com/country-list/ease-of-doing-business";"table";1)
etc...
Not sure if this Github code solves this problem. It seems to only parse text?
As I would assume this is a fairly common problem users of Google Sheets have and would think there might already be an AppScript out there that does exactly this and might be faster in terms of importing speed too.
I can't program, so I tried copying and posting codes to see if I can get some code to work. No luck :(
Can anyone provide a code or maybe an existing app script (I'm not aware of) that does exactly this that?
Try this way
=importTableHTML(A1,1)
with
function importTableHTML(url,n){
var html = UrlFetchApp.fetch(url,{followRedirects : true,muteHttpExceptions: true}).getContentText().replace(/(\r\n|\n|\r|\t| )/gm,"")
const tables = [...html.matchAll(/<table[\s\S\w]+?<\/table>/g)];
var trs = [...tables[n-1][0].matchAll(/<tr[\s\S\w]+?<\/tr>/g)];
var data = [];
for (var i=0;i<trs.length;i++){
console.log(trs[i][0])
var tds = [...trs[i][0].matchAll(/<(td|th)[\s\S\w]+?<\/(td|th)>/g)];
var prov = [];
for (var j=0;j<tds.length;j++){
donnee=tds[j][0].match(/(?<=\>).*(?=\<\/)/g)[0];
prov.push(stripTags(donnee));
}
data.push(prov);
}
return(data)
}
function stripTags(body) {
var regex = /(<([^>]+)>)/ig;
return body.replace(regex,"").replace(/ /g,' ').trim();
}
url-fetch-app#advanced-parameters
matchAll

Create Multiple Choice Question with data from Google Sheet

I am creating a database for my study program's inventory which can be accessed for anyone who want to request using the item.
For weeks, I am stuck with making the multiple choice question from the data in my Google Sheet Database.
These codes are the closest that I can do...Instead of creating all the choices, the program only puts different statements inside 1 choice... Could anyone help me making the proper multiple choice please?
function myFunction() {
var ss = SpreadsheetApp.openById('1lSB55vGZeuaJC0OS3zjpuRHEDCXpUTDYC7MPUVr0tMU');
var form = FormApp.openById('1sH-qxqiiyh3Qqq28KuYoaNftnY3RUe4FhGq6qck2eMg');
var ss_mediaandchemical = ss.getSheetByName('Stock - Media & Chemical').getRange('K2:K').getValues();
var form_mediaandchemical_question = form.getItemById('447046025').asMultipleChoiceItem();
for (var i in ss_mediaandchemical){
form_mediaandchemical_question.createChoice(ss_mediaandchemical[i]).isCorrectAnswer;
form_mediaandchemical_question.setChoiceValues(ss_mediaandchemical[i]);
}
}
Issues:
setChoiceValues(values) sets all the provided values in the array as choices of the item, removing the choices that were previously set.
Since you are iterating through the different choices, you are replacing each choice you set with the next value in the source array.
Also, getValues() returns a 2D array, and you want to provide a simple array. You can use flat() to fix that. Also, the source data contains several empty cells, I'd suggest removing them from the array (for example, using .filter(String).
Finally, createChoice doesn't add the choice to the item, and it should be used in combination with setChoices. It doesn't make sense to use it if you're then using setChoiceValues.
Solution:
Retrieve a simple array with values, removing empty values, and set the choices directly using setChoiceValues, without iterating through the array:
var ss_mediaandchemical = ss.getSheetByName('Stock - Media & Chemical').getRange('K2:K').getValues().flat().filter(String);
var form_mediaandchemical_question = form.getItemById('447046025').asMultipleChoiceItem();
form_mediaandchemical_question.setChoiceValues(ss_mediaandchemical);
If you want to create the choices via createChoice (it could allow more customization, like setting a choice as correct or adding a navigation item), you can do this instead:
var ss_mediaandchemical = ss.getSheetByName('Stock - Media & Chemical').getRange('K2:K').getValues().flat().filter(String);
var form_mediaandchemical_question = form.getItemById('447046025').asMultipleChoiceItem();
var choices = ss_mediaandchemical.map(value => form_mediaandchemical_question.createChoice(value));
form_mediaandchemical_question.setChoices(choices);

How to fix the "Service invoked too many times for one day: urlfetch" error?

I am getting the following error in my Google sheet:
Service invoked too many times for one day: urlfetch
I know for a fact I am not making 100k calls, but I do have quite a few custom functions in my sheet. I tried to make a new sheet and copy/paste the script into that one, but I still get the same error. I then switched my account, made a new sheet, added the code, and I still got the error.
Is this just because I am on the same computer? Is Google smart enough to realize I am the same person trying to do it? I highly doubt that, so I am wondering why it would be throwing this error, even after switching accounts and making a new sheet.
In addition to that, is there any way to make sure I don't go over the limit in the future? This error sets me back at least a day with what I was working on. I do plan to write a script to just copy/paste the imported HTML as values into another sheet, but until I get that working, I need a temporary fix.
Sample code:
function tbaTeamsAtEvent(eventcode){
return ImportJSON("https://www.thebluealliance.com/api/v3/event/" + eventcode + "/teams?X-TBA-Auth-Key=" + auth_key);
}
function ImportJSONForTeamEvents(url, query, options){
var includeFunc = includeXPath_;
var transformFunc = defaultTransform_;
var jsondata = UrlFetchApp.fetch(url);
var object = JSON.parse(jsondata.getContentText());
var newObject = [];
for(var i = 0; i < object.length; i++){
var teamObject = {};
teamObject.playoff = object[i].alliances
newObject.push(teamObject);
}
return parseJSONObject_(object, query, "", includeFunc, transformFunc);
}
That is one "set" of code that is used for a specific function. I am pulling two different functions multiple times. I have about 600 of one function, and 4 of another. That would only be just over a thousand calls if all were run simultaneously.
I should note that I also have another sheet in my drive that automatically updates every hour with a UrlFetch. I do no believe this should affect this though, due to the very low pull rate.
I had a similar issue even though I was only calling two fetch calls in my functions and each function per data row. It exponentially grew, and with my data changing, every recalculate call also called those functioned, which VERY quickly hit the max.
My solution? I started using the Cache Service to temporarily store the results of the fetch calls, even if only for a few seconds, to allow for all the cells triggered by the same recalculation event to propagate using only the single call. This simple addition saved me thousands of fetch calls each time I accessed my sheets.
For reference:
https://developers.google.com/apps-script/reference/cache?hl=en

.geteditors and Groups

I"m trying to run a script against DocsList that will gather the list of viewers and editors and in turn perform some "work" against them. (Specifically, I'm trying to strip rights from the file in question).
It's works perfectly for users. For groups though, it returns the group name and not the email.
I can't find any way using apps scripts to retrieve the group email address from that information.
If I run fileObject.removeEditor(Group Name) is tells me it's an invalid email (which is entirely true).
I'm open to suggestions... I'm completely stuck here.
Alternatively, I'm open to a way I haven't thought of to remove all sharing rights to a bunch of files in Google Docs.
function getDocs(){
var myFolders = DocsList.getAllFolders();
var myFiles = DocsList.getAllFiles(0,10);
var mySharing = new Array();
for(x in myFiles){
mySharing[x] = [myFiles[x].getId(), myFiles[x].getEditors(), myFiles[x].getViewers()];
for(y in mySharing[x][1]){
if(mySharing[x][1][y].toString() != "doc.owner#deltahotels.com"){
myFiles[x].removeEditor(mySharing[x][1][y]);
}
}
for(y in mySharing[x][2]){
if(mySharing[x][2][y].toString() != "doc.owner#deltahotels.com"){
myFiles[x].removeEditor(mySharing[x][1][y]);
}
}
}
}
Thanks for bringing this up. This is a problem with the way the getEditors/getViewers methods handle groups. I've raised this issue for you in the issue tracker. Please star it to be updated on the progress.