Avoiding Exceeded maximum execution time while trying to copy files - google-apps-script

Hey every one this is my first time posting here, usually I find answers here but this time I had to ask.
So I'm trying to create 12 folders with months names and create 31 files in each one of them , the files are a copy of existing spreadsheet file.
I'v succeeded in writing script that works but after creating 3-4 months it stops and throws "Exceeded maximum execution time", now I did some reading and understand that there is time limit of something like 5 minutes - and as you can see in the code below my way of doing things isn't the most efficient maybe , now the only idea I got is to save the original file data inside blob and then read from that blob while creating the new files - that way avoiding large number of calls and making things faster, but when I tried to createFile(blob) I get PDF as output , which isn't my intention.
function create_months(month_name)
{
var testingfolder = DocsList.getFolder("testing");
var targetFolder = testingfolder.createFolder(month_name);
var mainDoc = DocsList.getFileById('original file id');
for(var i=1;i<32;i++)
{
mainDoc.makeCopy(i).addToFolder(targetFolder);
var root = DocsList.getRootFolder();
var file = root.find(i);
file[0].removeFromFolder(root);
}
}
//array of months in hebrew
year = ['ינואר','פברואר','מרץ','אפריל','מאי','יוני','יולי','אוגוסט','ספטמבר','אוקטובר','נובמבר','דצמבר'];
function create(){
for(var i=0;i<=12;i++)
{
create_months(year[i]);
}
}
Thanks in advance :)

Your code seems fine, aside from the "error by 1" on your loop in the create function (it should be I i < 12, not <=. If your script ever got to the end you would have a undefined month as your month_name on create_months.
If you said 3-4 months work, I recommend you do only 3 months at time. If you're running this manually. The easiest solution is just to wait, then run again, then again, etc. e.g.
function create1stQuarter() {
for( var i = 0; i < 3; ++i )
create_months(year[i]);
}
function create2ndQuarter() {
for( var i = 3; i < 6; ++i )
create_months(year[i]);
}
function create3rdQuarter() {
for( var i = 6; i < 9; ++i )
create_months(year[i]);
}
function create4thQuarter() {
for( var i = 9; i < 12; ++i )
create_months(year[i]);
}
Now, if you're not running this from the script editor yourself, or you find it difficult for your users to click 4 itens in a spreadsheet menu or elsewhere. You can enhance this by setting a up a trigger automatically and removing it automatically when you finish. You'd also have to keep track inside the script on which quarter you have to create next. A good place for something simple like this is ScriptProperties.
If you're using the trigger solution, I also recommend that you do not set the time interval lower than 10 minutes as you'll risk your functions running concurrently. If you do so, it's advised that you use the LockService to guarantee that does not happen.

Do what you can outside of the loop and get rid of the find since you already have the file.
var root = DocsList.getRootFolder();
for(var i=1;i<32;i++)
{
var daily = mainDoc.makeCopy(i);
daily.addToFolder(targetFolder);
daily.removeFromFolder(root);
}
That should speed things up.

Related

Speed up adding Items to Forms with Apps Script

I'm wondering if there is a more efficient way to add Items to Google Forms with App Script.
My script is taking a long time and I'm wondering if there's anyway to add items more efficiently using an array or something rather than one by one. Here's the code I'm running...
function addFormItems() {
var startTime = new Date(); var form = FormApp.create("Test") form.setIsQuiz(true)
for (var i = 1; i < 100; i++) {
form.addTextItem().setTitle("Question number " + i).setHelpText("Hint: answer should not be more than a couple of words")
}
var endTime = new Date();
//Get runTime in seconds var runTime = (endTime - startTime)/1000;
Logger.log("runtime is: " + runTime)
}
Currently it takes quite a long time a minute to a minute and a half (odd thing is every time I execute I get a very different runtime not sure why that happens) Any thoughts on how to speed this up is much appreciated.
I searched Documentation and couldn't find anything about adding multiple items with one call.

Program seems to hang only when NOT debugging in Google Apps Script

In the script I've created, when I run it normally, seems to hang or get stuck or end up in an infinite loop. I placed a couple of breakpoints throughout my code to step through it to try find where the problem is, but when I run it in the debugger, there aren't any issues!
I've dug around online about this a little, but can't find anything. Probably more of an issue of not putting in the correct search criteria, but in any event, I'm stuck.
Ok, so I think I've found the region that the problem lies in. Here is the code:
function inductDaycareIntoSystem(){
var ss = SpreadsheetApp.getActive();
var controlRoomSheet = ss.getActiveSheet(); // alias for master doc sheet
var daycareNumber = chooseADaycare(); // Holds the number entry for the daycare chosen to work on (the starting entry, when doing multiple daycares) col. A
Logger.log("Line 5");
var finalDaycareNumber = daycareNumber; // Holds the last daycare slot number to be processed (same number as 'daycareNumber' when only one is being processed)
//Needs to be adjusted to get a final number as well
for(var i = daycareNumber; i >= finalDaycareNumber; i ++){
**code to execute in loop**
}
The program still seems to hang with all the code inside the for loop commented out.
Think about what you are doing
Let's assume daycareNumber = 2
Since var finalDaycareNumber = daycareNumber; - finalDaycareNumber will also be 2
For your loop it means:
for(var i = 2; i >= 2; i ++){
In words: Start with i =2 and increase iin every iteration as long asiis not smaller than2`
But i will never be smaller than 2, since 2 is the start value and from there on i will only increase in each iteration

Script to set formula every 25th row increasing date by 1 day

I need to set a formula into ColM every 25th row, starting with M4. The formula I'm setting needs to increase the date by one day each time.
The script I've previously used successfully on a different sheet, doesn't work after I've adapted it for this new sheet. I'm sure it's something simple I've missed, buy my dyslexia makes it very hard to see what.
I want to set formulas like this...
=FILTER(ZapUPDATE!T2:Z, ZapUPDATE!Q2:Q=date(2016,10,1), ZapUPDATE!R2:R="SUP") // Into M4
=FILTER(ZapUPDATE!T2:Z, ZapUPDATE!Q2:Q=date(2016,10,1)+1, ZapUPDATE!R2:R="SUP") // Into M29
=FILTER(ZapUPDATE!T2:Z, ZapUPDATE!Q2:Q=date(2016,10,1)+2, ZapUPDATE!R2:R="SUP") // Into M54
Etc and every 25th row down to M754. Here's the script...
function setFormulas() {
var sheet = SpreadsheetApp.getActiveSheet();
for (var k = 0; k < 775; k++) {
sheet.getRange(4 + 25*k, 13).setFormula('=FILTER(ZapUPDATE!T2:Z, ZapUPDATE!Q2:Q=date(2016,10,1)+' + k + ', ZapUPDATE!R2:R="SUP")');
}
}
Link to the sheet is below. Any help gratefully received, thanks.
Copy of sheet for Stack Overflow
Try this:
function setFormulas() {
var sheet = SpreadsheetApp.getActiveSpreadsheetSheet().getSheetByName('SUP10');
var i = 0;
for (var k = 4; k < 775; k = k + 25) {
sheet.getRange(k, 13).setFormula('=FILTER(ZapUPDATE!T2:Z, ZapUPDATE!Q2:Q=date(2016,10,1)+' + i + ', ZapUPDATE!R2:R="SUP")');
i++;
}
}
If you do Logger.log(SpreadsheetApp.getActiveSheet().getName()) you'll see that GAS considers another sheet (AVL) active — who knows why but in your case, I think, it can easily be overcome by using getSheetByName().
Another thing is that iterating through every k value until it reaches 774 is really slow (and it creates 7k additional rows in your spreadsheet) — it hits the time limit and still doesn't finish the loop. Changing the loop's settings to (var k = 4; k < 775; k = k + 25) makes it run in 0.075 seconds.
And the last thing: you don't need to create a separate project for every single function you have :) You may write them all in one file or create a bunch of new .gs files inside a project — it'll be more convenient to work in such a fashion.
Hope it helps.

Clock Trigger Builder Not calling function when scheduled - Google sheets app Script

I am using the app script provided by Google to access their prediction API through sheets. I am trying to predict thousands of rows at once, however, after 6 minutes the maximum execution time is reached at the code stops.
I implemented a solution that I found using clock trigger builder. Once I run the function it goes for 5 mins, then it stops sets a trigger to recall the function within 2 mins.
The major problem is that the function is not called when scheduled. I see it in the current triggers list, but it never gets called again. Can you please explain why this is occurring.
My intention is to predict as many lines as possible in 5 min then stop set a trigger to call the predict function again within a few minutes start where it left off and continue until ever element has been predicted.
I also need to know how would I store then values in cache so that it would know all the information that it needs when the function is called again.
//This is the function that is used to predict a selection of data
function predict() {
try {
clearOutput();
var startTime= (new Date()).getTime();
var sheet = SpreadsheetApp.getActiveSheet();
var selection = sheet.getActiveSelection();
var instances = selection.getValues();
var project_number = getProjectNumber();
var model_name = getModelName();
var startRow = stRow;
var MAX_RUNNING_TIME = 300000;
var REASONABLE_TIME_TO_WAIT = 60000;
for (var i = startRow; i < instances.length; ++i) {
var currTime = (new Date()).getTime();
if(currTime - startTime >= MAX_RUNNING_TIME) {
var builder = ScriptApp.newTrigger('predict').timeBased().after(REASONABLE_TIME_TO_WAIT);
builder.create();
break;
} else {
var result = predictSingleRow(project_number, model_name, instances[i]);
selection.getCell(i + 1, 1).setValue(result);
}
}
} catch(e) {
Browser.msgBox('ERROR:' + e, Browser.Buttons.OK);
}
}
Few things as to why your code is not functioning as intended:
1) Since you mentioned,"I see it in the current triggers list, but it never gets called again" and looking at your code, I am unsure whether you intended to call the function again after it's execution has completed. If you do, this is because your for loop runs for a while until the length of the instances is obtained. Nothing in the script suggests that the function needs to be run again once it has finished iterating through instances. Refer to this link to see how to Manage Trigger Programmatically.
2) var builder = ScriptApp.newTrigger('predict').timeBased().after(REASONABLE_TIME_TO_WAIT);
This line of your code falls under an if condition which stops the execution for 1 minute (value is 60000). Hence, adding 1 minute to the time since execution started. Nowhere are you resetting the startTime counter to the time after the waiting time since once the value of currTime - startTime has exceeded MAX_RUNNING_TIME, the function will keep calling the if loop for all iterations of the for loop after that. Simply put, if startTime was 9:35 and currTime was 9:40, after waiting for 1 minute the currTime is 9:41 which is still more than the MAX_RUNNING_TIME(5 minutes) because value of startTime still remains 9:35. Resetting it to 9:41 at this point should resolve your problem.
3) Loosing the break in the if loop would probably help fix that as well.
EDIT:
Add a function as shown in the link I mentioned above:
function callTrigger(){
ScriptApp.newTrigger('predict')
.timeBased()
.everyMinutes(30)
.create();
}
Run the function callTrigger once from your editor and you should be good to go. Remember, for minutes you can only pass values 1,5,15 or 30.

If statement in for loop referencing incremented variable

as quite an novice when it comes to coding, I ran into this problem with my code:
I use Google App Script [Edit: Corrected Google App Engine to Google App Script] to go through a list of timestamps and filter for stamps that equal the current month. Therefor I load the spreadsheet, the according sheet and get the data from all the rows as an object.
In the next step I go though all the elements of the object and check whether they contain the current date.
/* Initial data */
var email = "name#domain.com";
var spreadsheet = SpreadsheetApp.openById("1bN7PTOa6PwryVvcGxzDxuNVkeZMRwYKAGFnQvxJ_0nU");
var tasklist = spreadsheet.getSheets()[0].getDataRange();
var tasks = tasklist.getValues();
var tasksnum = tasklist.getNumRows();
Logger.log(tasks[7][2]); //Console returns "01.12.2014"
Logger.log(tasks[7][2].indexOf(month)); //Console returns "12.2014"
/* Filter tasks by month */
for (var i = 1; i < 9; i++) {
if (tasks[i][2].indexOf(month) >= 0) {
Logger.log(tasks[i]);
}
else {
return;
}
}
What drives me crazy is the following: As stated above, the for loop doesn't work. But if I alter it like this
if (tasks[7][2].indexOf(month) >= o) {
it works like a charm. And that's what I don't get. i should be incremented til 9 so should be seven at some point. At least then, the condition should be true and the loop should return a log.
What am I missing?
Thank you all in advance.
ps: If I am just following the wrong path of how to implement the function, please let me know.
ps2: I think my question's title is a bit cryptic. If you have a better one in mind, I'd love to change it.
The reason your original code doesn't work is due to the return statement in your else block. Return exits the function immediately, so your loop never gets past the first "false" of your IF.
remove the "return" (or the else block entirely) and your code should work.
I'm not sure what's supposed to be in the month variable but I suggested building a string to match your date format and doing string comparisons instead of the indexOf() strategy you're trying. This code is tested and does just that:
function test() {
/* Initial data */
var spreadsheet = SpreadsheetApp.openById("");
var tasklist = spreadsheet.getSheets()[0].getDataRange();
var tasks = tasklist.getValues();
var tasksnum = tasklist.getNumRows();
/* Filter tasks by month */
var today = Utilities.formatDate(
new Date(),
spreadsheet.getSpreadsheetTimeZone(),
"dd.MM.yyyy"
);
for(var i=1; i<9; i++){
if (tasks[i][2] == today){
Logger.log(tasks[i]);
}
}
}