Program seems to hang only when NOT debugging in Google Apps Script - 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

Related

Google Apps Script execution time issue

Im currently using Google Apps Script to implement a viewer of a supply chain database.
To synchronize the viewer with the current database (a google spreadsheet) I import the values and all the formatting it into a new sheet, this means the viewer basically is a copy of the current database.
However executing the script always takes something about 1 minute in time. I tried to find the issue with logging some debug messages at various positions in the code.
At first it seemed that the line with Viewer.setFrozenRows(1); (which is strange since I actually only freeze the first row) was the issue, however when commenting out this line the line afterwards (Viewer.setFrozenColumns(Database.getFrozenColumns());) seemed to be the issue.
Unfortuanetly I'm not able to share the database sheet with you, but maybe somebody can already spot the issue from the code.
Some additional Info: The database sheet has 1300 rows and 100 columns, and I added a picture of the log of the current code below.
function LoadViewer(view) {
Logger.log("LoadViewer Start");
if (view == null) {
view = 0;
}
var Database = SpreadsheetApp.openByUrl('[SHEET_URL].getSheetByName('Database');
var Viewer = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var numberOfColms = Database.getLastColumn();
var numberOfRows = Database.getLastRow();
var rules = Database.getConditionalFormatRules();
var headerRowHeight = Database.getRowHeight(1);
var dataRowHeight = Database.getRowHeight(2);
var Values = Database.getRange(1, 1, numberOfRows, numberOfColms).getValues();
Logger.log("Declarations Finished");
Viewer.getRange(1, 1,numberOfRows,numberOfColms).setValues(Values);
if(!Viewer.getRange(1, 1,numberOfRows,numberOfColms).getFilter())
Viewer.getRange(1, 1,numberOfRows,numberOfColms).createFilter();
Viewer.setConditionalFormatRules(rules);
Viewer.getRange(1, 1, 1, numberOfColms).setFontWeight('bold');
Viewer.autoResizeColumns(1, numberOfColms);
Viewer.setRowHeight(1, headerRowHeight);
Logger.log("1st Half of functions finished");
Viewer.setRowHeights(2, numberOfRows-1, dataRowHeight);
Logger.log("Freeze Rows");
//Viewer.setFrozenRows(1);
Logger.log("Freeze Columns");
Viewer.setFrozenColumns(Database.getFrozenColumns());
Logger.log("Loop Start");
for(var i = 1; i<=numberOfColms; i++){
Viewer.setColumnWidth(i, Database.getColumnWidth(i));
}
Logger.log("Loop End");
Viewer.getRange(1, 1,1,numberOfColms).setVerticalAlignment('middle').setWrap(true);
Logger.log("Load Viewer End");
}
Two optimization points I can see for your code:
Requests to the any external service including SpreadsheetApp make your code slow - see Best Practices.
Thus, making calls to a SpreadsheetApp method within a for loop will slow your code down.
You will be able to accelerate your code by replacing multiple setColumnWidth() requests within the loop by a single setColumnWidths(startColumn, numColumns, width) - avoiding iteration.
Log the number of columns and rows in your sheet.
A common problem is that the sheet contains a significant amount of empty rows and columns that increase your detected data range and consequently apply the subsequent calls to a bigger range than necessary.
If that you case - either delete the spare rows and columns manually, or use getNextDataCell() instead of getLastRow() or getLastColumn()

Dynamic Page Breaks in Google Sheets

I have mild general programming knowledge but know basically nothing about google apps scripts specifically.
I am trying to create dynamic page breaks in google sheets, or find another way to keep certain rows grouped together when printing.
On my data sheet I have 100s of rows of information and within each row the data can vary significantly in length (from a single number to many paragraphs of text). I have created a second sheet that both filters the information that I want and displays it in a visually-appealing way, taking the original data from each row and parsing it into 8 total rows (7 with information, and one blank to visually separate one block of info from the next) per one original. The problem is that the varying length of the data means I have to manually move the page breaks every time I change the filter.
Here is a blank section of the second sheet for reference.
I want to be able to print with as many 8-row groupings on a page as I can, but not split up a group onto the next page.
I'm honestly not sure how to get started, though I presume that I can use the blank row to trigger the page breaks somehow. Any help would be greatly appreciated!
Updated
I have been able to write some rudimentary code to (mostly) accomplish what I wanted. However the best that I can tell is that getRowHeight() is not working with my wrapped text, as it properly formats when I have any empty data set, but not otherwise.
Can someone confirm, and tell me what I'm missing?
function dynamicPageBreaks() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var curRow = 2; //start on Row 2 (1 contains the filter selection)
var curTotPix = ss.getRowHeight(curRow);
var pgPix = 1030;
//loop until end of sheet
var endRow = ss.getLastRow();
do {
//find row that goes past page break
do {
curRow++;
curTotPix = curTotPix + ss.getRowHeight(curRow);
} while (curTotPix <= pgPix);
//get value of cell in column B of that row
var curCell = sheet.getRange(curRow,2).getValue();
//back up until we find an empty row
if (curCell == "") {
break;
} else do {
curTotPix = curTotPix - ss.getRowHeight(curRow);
curRow = curRow - 1;
curCell = sheet.getRange(curRow,2).getValue();
} while (curCell != "");
//expand empty row to match necessary pixels
var addHeight = pgPix - curTotPix;
ss.setRowHeight(curRow - 1, ss.getRowHeight(curRow) + addHeight);
//reset for next iteration
curTotPix = ss.getRowHeight(curRow);
} while (curRow < endRow);
}
I would recommend you get started by following an Apps script quickstart like the one about extending sheets here[1]
Then you can define your logic using methods from the Spreadsheet service[2].
You have the method getRowHeight()[3] which could help you in determining the length of the range you are looking to print. And that also depends on the paper size you choose.
[1]https://developers.google.com/apps-script/guides/sheets#get_started
[2]https://developers.google.com/apps-script/reference/spreadsheet
[3]https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrowheightrowposition

Service error: Spreadsheets(line... with .setColumnFilterCriteria

This question is an extension from another.
Apply basic filter to multiple values in a spreadsheet column
I am experiencing an error, specifically Service error: Spreadsheets (line 8, file "Filter") with the following code:
function testFilter() {
var ss = SpreadsheetApp.getActive();
var monthlyDetailSht = ss.getSheetByName("Monthly_Detail");
var filterRange = monthlyDetailSht.getRange(2,12,359,1).getValues(); //Get L column values
var hidden = getHiddenValueArray2(filterRange,["Apple"]); //get values except Apple
var filterCriteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(hidden).build();
var rang = monthlyDetailSht.getDataRange();
var filter = rang.getFilter() || rang.createFilter();// getFilter already available or create a new one
//remove filter and flush
if(monthlyDetailSht.getFilter() != null){monthlyDetailSht.getFilter().remove();}
SpreadsheetApp.flush();
filter.setColumnFilterCriteria(12, filterCriteria);
};
//flattens and strips column L values of all the values in the visible value array
function getHiddenValueArray2(colValueArr,visibleValueArr){
var flatArr = colValueArr.map(function(e){return e[0];}); //Flatten column L
visibleValueArr.forEach(function(e){ //For each value in visible array
var i = flatArr.indexOf(e.toString());
while (i != -1){ //if flatArray has the visible value
flatArr.splice(i,1); //splice(delete) it
i = flatArr.indexOf(e.toString());
}
});
return flatArr;
}
I have used a Logger.log(hidden) to capture the values returned by the function and it is a list of all of the other "fruits" repeated as many times as they are available in column L. I am using fruits as a substitute for the sensitive data.
So here goes my question. Why am I getting that error now when it was working perfectly fine for a couple of days? How can I correct this?
Attempted fixes:
I've tried to add rows to the end of my data. Did not fix.
I tried removing filter, flushing, setting filter. Did not fix. (updated code above with what I did to flush in case anyone else is interested.)
It's working now. A couple of things I want to note for people who stumble upon this with their google searches. First, the issue was in fact an error on Google's side. Using the same code I have above it now works. I did not change it.
Second, I was able to record the filtering through the macro recorder and that code worked when my original code did not. This may help people who are on a time crunch and can't wait for google to get their stuff together. I'm still not sure what specifically in my original code caused the error, but my guess is that it does not matter. I've dedicated a full day to researching this error and it seems sporadic with not a single culprit. My issue may not be the same as yours if it happens in the future.
Hope that helps!

Guild Roster Sheet - Setting value row-by-row in one column

I'm just starting to learn how to code with Google Apps Script. Barely.
I have a guild roster sheet (toon names in range B2:B and Realm (We also add prospects to this list) in range C2:C.
In Column D, I would like to set the value of each cell row-by-row to a function that uses the data in B and C (i.e. "=wowi(B2,C2)")
My intent is to just be able to add as many names as I want in Column B, and run the function manually to update the values for each in column D, and sleeps for a second between each (so as to not abuse the UrlFetch in the "=wowi(toonName, realmName)" function when the names column gets a little long)
Sadly, this is about as far as I got before I gave up:
function rosterUpdate() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destCell = sheet.getRange("D2");
for (x = 0, x //(less than or equal to)// sheet.getRange("B2:B").getNumRows(), x ++) {
if (x != 0 {
NOCLUEWHATTODO-IDKWHYIEVENTRY;
sleep(1000);
}
}
}
I know I need to use a for, but not quite sure on how to go about reading the value and modifying the D cell's value. I'm quite possibly overthinking this, as it's currently 5:30 in the morning and I'm running on about 2 hours of sleep, but would really appreciate the help.
Thanks in advance. Sorry if I'm bad. :[
Hopefully this answers your question in terms of getting and setting values (you should check the documentation: https://developers.google.com/apps-script/reference/spreadsheet/range) and how to sleep for one second between function calls, however it's pretty inefficient as it will iterate over the whole list each time.
Please be aware this is written off the top of my head very quickly so you may need to play with the i values to get it working properly, and there is probably a much, much cleaner way to do it!
var listLength = sheet.getLastRow() - 1;
for(var i = 0; i < listLength; i++){
var toonName = sheet.getRange("B" + (i+2)).getValue();
var realmName = sheet.getRange("C" + (i+2)).getValue();
sheet.getRange("D" + (i+2)).setValue(yourFunction(toonName, realmName));
Utilities.sleep(1000);
}

sheet.appendRow() sporadically fails silently and results in an empty row

Using google-apps-script from google-spreadsheet, I'm trying to fetch some hundreds of rows of data using fetchUrl and paste them into my sheet using sheet.appendRow.
Fetching data and splitting to arrays are OK. But appending them to a sheet row by row ocasionally fails without any errors and resulting in empty rows.
Here's quite simple example to recreate the problem.
function appendTest() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
sheet = SpreadsheetApp.setActiveSheet(sSheet.getSheets()[2]);
for (var i = 0; i <= 100; i++) {
sheet.appendRow([i, 1, 2, 3, 4, 5]);
}
}
About 5 or 10 rows, differs time to time, become empty.
Inserting Utilities.sleep() seems to be not working, either.
Does anybody have any idea to get rid of this situation?
Thank you in advance.
You could try to use batch writing to the sheet like this
function appendTest() {
var target = new Array()
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
sheet = SpreadsheetApp.setActiveSheet(sSheet.getSheets()[2]);
for (var i = 0; i <= 100; i++) {
target.push(['some data','in some columns','bla bla']);
}
sheet.getRange(sSheet.getLastRow()+1,1,target.length,target[0].length).setValues(target);
}
Note that if you think the sheet will be too 'short' you could append empty rows in the loop as well.
EDIT : following Michael pertinent answer, it would be a much better idea to add the necessary rows using sheet.insertRowsAfter(sheet.getMaxRows(), target.length)
Using appendRow inside a loop is flaky and not best practice. Please see:
https://productforums.google.com/forum/#!topic/docs/y0E9PB_VTJw
for a discussion of this issue.
Just a small comment:
I use appendRow in a loop, appending maybe up to 30 rows at a time. The script has been used daily for about a week now.
The interesting part is that during testing of the script, when I set it to append about 100 rows, then I too saw rows missing. But after a simple manual refresh of the destination spreadsheet, I saw no missing rows. So I took it that the rows were in fact appended, but that the bug? lies in their display.