Google Sheet ConditionalFormatRuleBuilder issue with .setrange - google-apps-script

New user here and not versed in code much. Im working on a COVID-19 form for our company and looking for some help with conditional formatting issues.
SOW: Current we have a google form with simple questions that employees will fill out each day as they arrive, this data is populated on a google sheet.
Each night a google trigger runs that deletes 200 rows of the google sheet for the next days entries, when that script run its messes up my manual conditional formatting, so im trying to run the delete script followed by a script that will apply the conditional formatting to the new sheet each night so everything is ready for the next day.
This is currently what im running that deletes rows each night:
***function deleteResponses() {
var ss = SpreadsheetApp.openById('Sheet_ID');
var sheet = ss.getSheets()[0];
sheet.deleteRows(2, 200);
};***
This is the conditional formatting script ive added to it and trying to run and getting the following error im hoping someone can help me with.
**function deleteResponses() {
var ss = SpreadsheetApp.openById('Sheet_ID');
var sheet = ss.getSheets()[0];
sheet.deleteRows(2, 200);
};
var sheet = SpreadsheetApp.openById('Sheet_ID');
var range = sheet.getRange('C2:C1010');
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenTextContains('No')
.setBackground('#FF0000')
*.setRanges('C2:C1010')*
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);**
Im receiving this error.
"Message details
Exception: The parameters (String) don't match the method signature for SpreadsheetApp.ConditionalFormatRuleBuilder.setRanges. (line 12, file "Code")"
Anyone that could help me, I would greatly appreciate it!

Conditional rule ranges, are plural and the setRanges takes a list of them. It should be a list of ranges and not a list of A1 notations.
So you have to build the range from the A1 notation first, and then pass it to the rule builder like:
var range = sheets.getRange("A1:A1010");
var ranges = [range]; // You can add more ranges to the same rule
var rule = SpreadsheetApp.newConditionalFormatRule().setRanges(ranges)...
Note: Notice you are passing in a list of range objects.
References:
setRanges()

I just wanted to be clear. I think #Aerial is correct but I wanted to point out where I think your problem was:
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenTextContains('No')
.setBackground('#FF0000')
*.setRanges('C2:C1010')* <= Problem is here
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);**
As shown in this example in the documentation. Set Ranges requires an array of Ranges not an array of A1 notations or just one A1 notation. It could take one range such as [range1].
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenNumberBetween(1, 10)
.setBackground("#FF0000")
.setRanges([rangeOne, rangeTwo]) <= Notice the array of ranges
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
reference to example
I'd say that many problems here at SO suggest that many programmers rely on their memory of the documentation too much. I find it better to refer to the documentation regularly so that when I get to the end of writing code for a while I can spend less time debugging it.
Pay particular attention to return types and parameter types.

Related

Google sheets appscript to copy tabs to new sheets

I have a google sheet with around 190 tabs on that i need to split into 190 different files
The files need to be named the same as the tab, the contents of the tab need to be copied as values but i also need to bring the formatting accross (just not the formulas).
I have looked around, and through a combination of previous questions and answers plus using the function list help have formed the following code. It actually works for the first few tabs but then throws up an error about being unable to delete the only sheet.
function copySheetsToSS() {
var ss = SpreadsheetApp.getActive();
for(var n in ss.getSheets()){
var sheet = ss.getSheets()[n];// look at every sheet in spreadsheet
var name = sheet.getName();//get name
if(name != 'master' && name != 'test'){ // exclude some names
var alreadyExist = DriveApp.getFilesByName(name);// check if already there
while(alreadyExist.hasNext()){
alreadyExist.next().setTrashed(true);// delete all files with this name
}
var copy = SpreadsheetApp.create(name);// create the copy
sheet.copyTo(copy);
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
copy.getSheets()[0].setName(name);// rename first sheet to same name as SS
var target_sheet = copy.getSheetByName(name);
var source_range = sheet.getRange("A1:M50");
var target_range = target_sheet.getRange("A1:M50");
var values = source_range.getValues();
target_range.setValues(values);
}
}
}
I am hoping someone can tell me what i have done wrong as I cannot figure it out at this point. I am also open to better solutions though please be aware I am very much a beginner on google appscript, nothing too complex please.
thankyou
In principle your script correctly adds a new sheet to the new spreadsheet before removing the preexisting one
However, mind that calls to service such as SpreadsheetApp are asynchronous.
And this becomes the more noticeable, the longer your script runs.
In your case it apparently leads to behavior that the only sheet is being deleted before the new sheet is being created.
To avoid this, you can force the execution to be synchronous by implementing calls to SpreadsheetApp.flush().
This will ensure that the old sheet won't be deleted before the new one gets inserted.
Sample:
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
SpreadsheetApp.flush();
copy.getSheets()[0].setName(name);
You might want to introduce call toflush()` also at other positions where it is important for the code to run synchronously.

Google Sheets Macro not completing

I'm trying to parse a CSV file using Google Sheets macros. I've recorded all the steps as individual macros, and one by one they work fine, but when I combine into one macro, it doesn't run properly. The point at which it stops working is after the PODdateformatting part has completed and it's run through the first three lines of Daystodeliverformula. Cell H2 is populated with the formula, but the formula doesn't then autofill down the rest of the column. Any ideas? Or indeed, am I going about this all wrong and need a good talking to? :-)
function TheWholeShebang() {
var spreadsheet = SpreadsheetApp.getActive(); // start of DeletedUnwantedColumns
spreadsheet.getRange('AA:DE').activate();
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('W:X').activate();
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('R:U').activate();
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('H:P').activate();
spreadsheet.setCurrentCell(spreadsheet.getRange('P1'));
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('A:E').activate();
spreadsheet.setCurrentCell(spreadsheet.getRange('E1'));
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns()); // end of DeletedUnwantedColumns
var spreadsheet = SpreadsheetApp.getActive(); // start of Addcolumnsandheaderlabels
spreadsheet.getRange('A:F').activate();
spreadsheet.getActiveSheet().insertColumnsAfter(spreadsheet.getActiveRange().getLastColumn(), 6);
spreadsheet.getActiveRange().offset(0, spreadsheet.getActiveRange().getNumColumns(), spreadsheet.getActiveRange().getNumRows(), 6).activate();
spreadsheet.getRange('G1').activate();
spreadsheet.getCurrentCell().setValue('POD Date (formatted)');
spreadsheet.getRange('H1').activate();
spreadsheet.getCurrentCell().setValue('Days to Deliver');
spreadsheet.getRange('G2').activate(); // end of Addcolumnsandheaderlabels
var spreadsheet = SpreadsheetApp.getActive(); // start of PODdateformatting
spreadsheet.getRange('G2').activate()
.setFormula('=DATE(LEFT(D2,4),mid(D2,5,2),right(D2,2))');
spreadsheet.getActiveRange().autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES); // end of PODdateformatting
var spreadsheet = SpreadsheetApp.getActive(); //start of Daystodeliverformula
spreadsheet.getRange('H2').activate()
.setFormula('=NETWORKDAYS(E2,G2,Instructions!$B$15:$B$40)-1');
spreadsheet.getActiveRange().autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES); // end of Daystodeliverformula
};
After messing about with range.autoFillToNeighbor() and failing (see comments above) I was still suspicious about that method.
When I used this the range.autoFill() method instead it all worked fine. See code below.
// start of PODdateformatting
//var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('G2').activate().setFormula('=DATE(LEFT(D2,4),mid(D2,5,2),right(D2,2))');
var sourceRange = spreadsheet.getRange("G2:G2");
var destination = spreadsheet.getRange("G2:G369");
sourceRange.autoFill(destination,SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
//start of Daystodeliverformula
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange('H2').activate().setFormula('=NETWORKDAYS(E2,G2,Instructions!$B$15:$B$40)-1');
var sourceRange = spreadsheet.getRange("H2:H2");
var destination = spreadsheet.getRange("H2:H369");
sourceRange.autoFill(destination,SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
In hindsight I now believe that range.autoFillToNeighbor() was never right for your use case in the first place!
range.autoFillToNeighbor() expects to build autofill formulas based on data that is contained in neighbouring columns. It tries to do this intelligently, but neighbouring columns do not contain anything useful! Its amazing it ever worked... maybe sometimes defaulting to range.autoFill()!
range.autoFill() on the other hand, just duplicates the formula (data) above or below ( NOT looking to neighbouring cells for help).
You can copy and paste these over the corresponding functions in function TheWholeShebang() and should all work.
Note I assume a fixed range of 369 as per your data, but you can calculate this based on actual size if you prefer, in case no of rows is changed.

Issue applying script to second sheet

I have no idea how to code. I use to make website on HTML, so my knowledge is limited. I piece together and alter existing codes. I got decent on Excel VBA, but then needed to start using google sheets. So, that said...
I have a spreadsheet with two sheets, 'MIS' and 'Admin'. I have a bunch of code a formula that assigns a value (1,2,3...) based on how many of two drop-down criteria that each line matches (1 if it matches criteria #1, 2 if matches criteria #2, and 3 if it matches both). The code then hides everything and unhides only those with numbers matching the criteria. I need this to be clean and quick, it's for people who can barely use computers.
The problem is, the code only works on the first page. I tried using the same code, tried amending the code, and I tried inserting 'Admin' in about half a million places. Please help. The admin function is my latest attempt. This is where I inserted 'Admin' in a dozen places. Also, if you see anything I'm using that is slowing down the code, I could use some help with that too. There are 6 functions, which basically do the same thing using the same code but corresponding to different number combinations. The one in question is below.
function Admin(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets('Admin')[0];
var range = sheet.getRange(1, 1, sheet.getLastRow());
sheet.hideRows(8,sheet.getLastRow());
var values = range.getValues();
for (var i=0; i<values.length; i++){
if(values[i][0] === 3){
sheet.showRows(i+1);
}
}
}
This line is your 'only the first sheet' problem, you're sort of combining two methods of defining a sheet:
var sheet = ss.getSheets('Admin')[0];
You can get a specific sheet by name by using the .getSheetByName() method i.e.
var adm_sheet = ss.getSheetByName('Admin');
var mis_sheet = ss.getSheetByName('MIS');
Or you can get a sheet by index
var adm_sheet = ss.getSheets()[0]; //returns first sheet
var mis_sheet = ss.getSheets()[1]; //returns second sheet
Lastly, you can get all of the sheets in your spreadsheet and list them in an array like this:
var sheets = ss.getSheets();
And then use the index of the sheet you need in this array like so:
sheets[0] //the first sheet
sheets[1] //the second sheet

Formatting rows (color, line-through etc.) based on date in Google Sheets with script

I've made a list of events in Google Sheets to help me and my band to get an overview of when we are to play. We have had a lot of jobs lately, and it is getting a little hard to quickly figure out where the next job is...
If it's possible, I would like to automatically make the rows containing previous jobs in italics, line-through and the font-color gray if the dates i column C is before the today/current date.
I have been searching a lot for it, but haven't really quite found the right piece of code..
My skills in scripting is OK, but this is the first time for me to use Google apps script, so I would appreciate if you could explain some of the harder steps about the more Google Sheets specific parts of the code :)
Thanks a lot!
You don't need a script to do that : in your spreadsheet, format>conditional formating.
EDIT :
Since you seem to prefer using a script to customize it more personally (which I understand ;-)
here is a script to begin with : (it does not need a lot of explanations, see comments in code)
function formatOnDate() {
var sh = SpreadsheetApp.getActive().getActiveSheet();
var range = sh.getDataRange();
var data = range.getValues();
var color = '#CCC';// value you want
var style = 'italic';// value you want
var line = 'line-through';// value you want
var fontColors = range.getFontColors();// get all font colors
var fontLines = range.getFontLines();// lines
var fontStyles = range.getFontStyles();//style
//var today = new Date();// include today in sheet
var today = new Date(new Date().setDate(new Date().getDate()-1));// exclude today... uncomment the one you use
for(var n=1 ; n<data.length ; n++){ // start on row 2 so that headers are not changed
if(data[n][2] < today){
for(var c in data[0]){
fontColors[n][c]=color;//set format
fontLines[n][c]=line;//set format
fontStyles[n][c]=style;//set format
}
}
}
sh.getRange(1,1,data.length,data[0].length).clear();
// now update sheet with new data and style
sh.getRange(1,1,data.length,data[0].length).setValues(data).setFontColors(fontColors).setFontLines(fontLines).setFontStyles(fontStyles);
}
Test sheet here

Google Script to Clear multiple ranges.

I have many spreadsheets that get filled out weekly and closed at the start of the new week. I've written a script to go through and clear out all kinds of ranges in a lot of different sheets. See code below. My question is there a better way to do this instead of having to clear ranges one section at a time and have a million clearContent functions? I cannot figure out how to write a function to clearContent where I can list many ranges all in the same function.
function startWeek() {
var confirm = Browser.msgBox('Did you **Close the Week** first?','Pressing YES will clear your week', Browser.Buttons.YES_NO);
if(confirm=='no'){Logger.log('The user clicked "NO."')};
if(confirm=='yes'){
var sheet = SpreadsheetApp.getActive().getSheetByName('INVOICE LOG');
sheet.getRange('A3:M47').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('DAILY INVENTORY');
sheet.getRange('C5:C8').clearContent();
sheet.getRange('D6:I8').clearContent();
sheet.getRange('C10:I10').clearContent();
sheet.getRange('C13:C16').clearContent();
sheet.getRange('D14:I16').clearContent();
sheet.getRange('C18:I18').clearContent();
sheet.getRange('C21:C24').clearContent();
sheet.getRange('D22:I24').clearContent();
sheet.getRange('C26:I26').clearContent();
sheet.getRange('C29:C32').clearContent();
sheet.getRange('D30:I32').clearContent();
sheet.getRange('C34:I34').clearContent();
sheet.getRange('C37:C40').clearContent();
sheet.getRange('D38:I40').clearContent();
sheet.getRange('C42:I42').clearContent();
sheet.getRange('C45:C48').clearContent();
sheet.getRange('D46:I48').clearContent();
sheet.getRange('C50:I50').clearContent();
sheet.getRange('C55:C58').clearContent();
sheet.getRange('D56:I58').clearContent();
sheet.getRange('C60:I60').clearContent();
sheet.getRange('C63:C66').clearContent();
sheet.getRange('D64:I66').clearContent();
sheet.getRange('C68:I68').clearContent();
sheet.getRange('C71:C74').clearContent();
sheet.getRange('D72:I74').clearContent();
sheet.getRange('C76:I76').clearContent();
sheet.getRange('C79:C82').clearContent();
sheet.getRange('D80:I82').clearContent();
sheet.getRange('C84:I84').clearContent();
sheet.getRange('C87:C90').clearContent();
sheet.getRange('D88:I90').clearContent();
sheet.getRange('C92:I92').clearContent();
sheet.getRange('C95:C98').clearContent();
sheet.getRange('D96:I98').clearContent();
sheet.getRange('C100:I100').clearContent();
sheet.getRange('C105:C108').clearContent();
sheet.getRange('D106:I108').clearContent();
sheet.getRange('C110:I110').clearContent();
sheet.getRange('C113:C116').clearContent();
sheet.getRange('D114:I116').clearContent();
sheet.getRange('C118:I118').clearContent();
sheet.getRange('C121:C124').clearContent();
sheet.getRange('D122:I124').clearContent();
sheet.getRange('C126:I126').clearContent();
sheet.getRange('C129:C132').clearContent();
sheet.getRange('D130:I132').clearContent();
sheet.getRange('C134:I134').clearContent();
sheet.getRange('C137:C140').clearContent();
sheet.getRange('D138:I140').clearContent();
sheet.getRange('C142:I142').clearContent();
sheet.getRange('C145:C148').clearContent();
sheet.getRange('D146:I148').clearContent();
sheet.getRange('C150:I150').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('FOOD INVENTORY');
sheet.getRange('D5:F615').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('LIQUOR INVENTORY');
sheet.getRange('D6:G361').clearContent();
sheet.getRange('E365:G520').clearContent();
sheet.getRange('D524:G573').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('DAILY SALES SHEET');
sheet.getRange('B4:H10').clearContent();
sheet.getRange('B12:H12').clearContent();
sheet.getRange('B14:H20').clearContent();
sheet.getRange('B22:H27').clearContent();
sheet.getRange('B29:H30').clearContent();
sheet.getRange('B33:H34').clearContent();
sheet.getRange('B36:H38').clearContent();
sheet.getRange('B43:H44').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('LAST WEEK INVENTORY');
sheet.getRange('E3:E9').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('SAFE AUDIT');
sheet.getRange('C3:P11').clearContent();
sheet.getRange('C14:P18').clearContent();
sheet.getRange('C22:P22').clearContent();
var destination = SpreadsheetApp.getActiveSpreadsheet();
var name = Browser.inputBox('New Week', 'Enter Pub Name & WE Date (ex. SandwichWE02-02-14)', Browser.Buttons.OK);
destination.rename(name)
};
}
Unfortunately there is not currently a Apps Script method to clear multiple ranges with a single function call.
What you might find a little easier to manage is to instead define one or more data structures (even a simple array) containing the Ranges in question. Then you can clear them all by simply looping over the data structure and calling clearContent() on each Range. This would separate the work of keeping track of your Ranges from the work of clearing them. The data structure might also be useful in other areas of your code.
For better organization, you might also make use of the Spreadsheet.setNamedRange() and Spreadsheet.getNamedRange() functions to assign simple IDs to your Ranges. Note that you cannot give more than one Range the same name.