I'm looking a way to "conditionally protect" some of my sheet's ranges. I know it's not possible without a script... So I would need this:
On open, protect range (A6:A) from being modified if A2="C". Else if, leave unprotected.
This should be repeated on open in 10 sheets with given names (Com1, Com2,...,Com10)
Any help would be appreciated
Check out the Apps Script reference to access and modify protected ranges in sheets. You can build a Protection object which contains permissions for who can edit a given range.
Google Sheets has simple triggers including a function that will run onOpen() which you can put your code inside. The full Apps Script reference for extending Sheets provides a great resource for building functions to edit and manipulate structures and data of Spreadsheets.
You'll want to use .getRange('A6:A') and build a condition which calls the .protect() method if A2 = 'C'.
You can also you use SpreadsheetApp.getActiveSpreadsheet.getSheets() to get all the sheets from the spreadsheet and iterate through the returned array using forEach() to apply your protection to all sheets.
Related
I would like to create a macro in sheets that would open a different spreadsheet and clear data from certain cells. I can do it in the same spreadsheet, but haven't been able to figure out how to do it when I have spreadsheet 'A' open and want to delete information on spreadsheet 'B'
You can use either SpreadsheetApp.openById('{your_sheet_id}') (see documentation) or SpreadsheetApp.openByUrl('{your_sheet_url}') (see documentation).
Then you can chain with the usual getSheet(), getRange() and clear() or clearContent() methods, e.g.:
SpreadsheetApp.openById('xxxxxxxxx').getSheetByName('Sheet1').getRange('A4').clearContent();
I originally asked this on WebApps, thinking that I was just missing a google trick. There I received the answer that it would require a custom function.
https://webapps.stackexchange.com/questions/129068/recalculate-google-sheet-on-demand
Here is my original question:
I have the following formula in a sheet:
=if(E1="HOLD",,query(Cust_Orders!B6:Z5000,"Select Y,G,I,H,K where H>0 "))
With E1 being a drop down with values HOLD and FETCH
The recalculation when I go to FETCH takes about 13 seconds.
But when it is in HOLD, the query doesn't exceute, and everything goes
blank. Not what I want.
I'm trying to avoid recalculating this query every time I make a
change in the Cust_Orders range, but keep the old values, as two
different pivot tables that are viewed by other people are dependent
on it.
Google spreadsheet recalc settings all are 'on change and foo'
Is there a way to do this?
Custom functions only update when one of their parameters changes. So if the function only depends on the HOLD/FETCH cell, but executes the formula is the query cell, I think I win.
My research:
This answer google sheets custom function built-in function is specific in telling the OP how to do what he wants in the script language. I suspect that rewriting the query in appscript would not be a net win.
This link Using Bound Google Scripts to Generate a Query Object
was proposed as a solution, but this in effect is rewriting my built-in function within the script. I want to use scripts as rarely as possible, and as generically as possible, as it makes long term maintenance and modification easier.
The query function above is an example. I am looking for a more general solution that allows me to use an arbitrary formula using the same script.
This discussion on google product forums: https://support.google.com/docs/forum/AAAABuH1jm01F-8MzzCxbY/?hl=en&gpf=%23!topic%2Fdocs%2F1F-8MzzCxbY says you can't call built-ins from scripts. But the question is almost 4 years old.
I have asked this question there too, but generally asking on Google Product Forums is a Hail Mary.
A viable solution:
An example of a script calling a built-in function.
A link to an add-on that allows recalculation of a range to be toggled on/off
A more general method than custom formulas to control recalc.
I do NOT want a script that emulates the desired built-in inside the script.
A more general method than custom formulas to control recalc.
What I'm doing on project for a client is to have the "expensive" formulas saved as variables on the script and have buttons to freeze/unfreeze the certain ranges (those that have a high impact on the recalculation time.
The "unfreeze" button adds the formulas to the spreadsheet
The "freeze" button put the formulas results over the range used by the formulas
There is a document property that stores the frozen/unfrozen spreadsheet state
A sidebar is used to show the buttons the spreadsheet status.
An example of a script calling a built-in function.
Scripts can get values, display values, formulas, and other stuff but they can not call built-in functions.
In the case of my client, they have one array formula (IMPORTRANGE, QUERY, FILTER, ARRAYFORMULA, etc) by sheet, all the formulas that are been included are on A1. The formulas are saved in an array of objects of the following form
var formulas = [
{
name:'Sheet1',
address:'A1',
formula:'=IMPORTRANGE(...)'
}
]
The address property, is included for future improvements.
The key code lines of the "freeze" function are
var dataRange = sheet.getDataRange();
dataRange.copyTo(dataRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Please note that the above lines copy-paste-as-values the whole data range.
The key code lines of the "unfreze" function are
formulas.forEach(function(item){
var sheet = spreadsheet.getSheetByName(item.name);
sheet.clear();
sheet.getRange(item.address).setFormula(item.formula);
});
Please note that the above lines clear the whole sheet.
I'm trying to find an Employee ID from the attendance which spans multiple sheets and pull the timestamp into a single sheet.
The Google sheet has multiple sheets in it. There are individual sheets for every working day:
Each attendance sheet has two columns. In order to know all the times of the employee's login I want to pull in all the occurrences of the employee ID from all the sheets and along with its timestamp to the Consolidation sheet: .
I've done a similar thing in Excel and guess can be done in Google sheet using Google Apps script.
It would be helpful if someone can guide me to a built-in or custom function in google sheets.
I'll help you with a basic outline, some advice, and some resources to help, but don't expect me to write the whole thing.
Step 1 - Create a custom menu option
You'll want to be able to access you script from the spreadsheet. You can accomplish this by creating a custom menu option. Here's an article by google on custom menus.
Although google uses the simple trigger onOpen, I've found installable triggers to be more reliable.
Step 2 - Get user input
It would be nice to be prompted for the id of the employee and have the script work it's magic. Here is an article by google on dialogs and sidebars that discusses how to get user input for a script.
Step 3 - Read and write data from the spreadsheet
You can access spreadsheeet data with the SpreadsheetApp. When calling your script from a custom menu, you can use SpreadsheetApp.getActiveSpreadsheet; however, if you want to test your code in the script editor, there is no "active spreadsheet", so use SpreadsheetApp.openById instead.
With access to the spreadsheet, you can call:
spreadsheet.getSheetByName("name").getRange(1, 1, 2, 2).getValues();
spreadsheet.getSheetByName("name").getRange(1, 1, 2, 2).setValues([[1, 2], [3, 4]]);
Note that getValues and setValues work with 2-dimensional arrays
WARNING - As usual, I/O takes a lot of processing time so avoid superfluously calling getRange().XetValues, this google article about appscript best practices goes into more detail. Also, if you have a LOT of attendance sheets, then you may want to consider condensing them into one.
Step 4 - Get the appropriate sheets
You'll need someway to distinguish which sheets in the spreadsheet are attendance sheets:
function isAttendanceSheet(sheet) {
return /Magical regex/.test(sheet.getName);
}
var sheets = spreadsheet.getSheets().filter(isAttendanceSheet);
Come up with the magical regex that works for you, and this will filter them.
Hope this helps and good luck!
I have a spreadsheet with different sheets in Google sheet, 3 users can edit each one a sheet (protections are set, each user can edit only one sheet). They all can execute a google script function that writes what they edited in a summary sheet. I don't want anyone to be abble to edit the summary sheet, so I set myself as the only available editor.
So my problem is to authorize the 3 users, only through the google script function, to write in the summary sheet. I tried to use the following function :
var unprotected = summarySheet.getRange('G3:G10');
protection.setUnprotectedRanges([unprotected]);
but since the users are not allowed to edit the summary sheet, and since the function is run with the active user, so they can't give themselves the right to unprotect a range in the summary sheet... Do you know how to workaround this problem?
Thanks a lot!
I see two script-based choices, one easy and one quite hard, and one sheet-based choice, that is easiest:
Easy:
You run the "summarize" script instead of them or, you set the summarize script run on a trigger out of your account. Then you actually leave protections alone. You could set the summarize script to run on open with error catching if the user doesn't have the necessary authority to unprotect the summary sheet and/or write to the summary sheet.
Hard:
When they run the "summarize" script it calls a published standalone script that has been given the authorization to make the necessary protection changes. I'll be honest, I wouldn't be able to code this but have seen/heard of similar implementations.
Easiest:
Finally, I want to make sure you've considered having the summary sheet itself contain the necessary formulas, parsing, etc. to summarize data from the other sheets without any need of scripts for this aspect of the sheet. The sheet could call custom functions as needed if the parsing or other summarization functionality is beyond built-in functions' capabilities. The sheet could stay fully protected and update itself in real time as users enter data (no need for users to trigger the summary creation, unless spreadsheet settings have auto-recalculate turned off).
Edited to add: put in A1 of Summary sheet something like:
=summarize()
And have that custom function return a 2-dimensional array of the summarized data.
I have a spreadsheet which extracts and accumulates all the required data from other 6 spreadsheets using Vmerge and Query formulas and all the consolidated data will be converted to pdf and emaild to mail ids using trigger event.
Here begins the problem every time the attachment mail posted consist all the headers and other format, but the data which is extracted does not appear. It seems to appear like - to open the same spreadsheet, after a while all the - (hyphens) are replaced by the data / Hope all the data updates after a while of opening the spreadsheet.
link for sheet
Can anyone direct me to make this issue sorted out.
For this solution should be -> all the data should be updated and then the email script should work; or either it should update before emailing script starts.
Or any other better ideas are appreciated.
Some spreadsheet formulas, like ImportRange and ImportXml (and also Apps Script custom formulas) are only evaluated when there's someone logged in the spreadsheet. It's like these functions need an account to be evaluated from, for example, in importRange the account logged in must have access to the range being imported, if you share this spreadsheet with someone but not the importRange source, when this person is viewing this spreadsheet, the importRange function will not work (well, unless you're also in the spreadsheet and the formulas have already been evaluated).
Bottom line is, you can't have this formulas and use a script triggered on time-driven (or other trigger that does not require someone logged in) and expect the script to be able to read this data.
The workaround though is quite simple. Do what the importRange function does inside your script! e.g.
var source = SpreadsheetApp.openById('source-spreadsheet-key');
var data = source.getSheetByName('List').getRange('I6:AT500').getValues();
//then save it somewhere
var s = Spreadsheet.getActive().getSheetByName('hidden-import');
s.getRange('I6:AT500').setValues(data);
SpreadsheetApp.flush(); //force the data to be written
//so all the other formulas on your spreadsheet get updated with the new data
All your "logic" formulas, like query and vmerge, which are difficult for the script to mimic, can be left on the spreadsheet, but reference this "hidden-import" sheet I just invented instead of nesting importRange directly.
[edit]
To copy only non-empty rows do like this:
var data = SpreadsheetApp.openById('source-spreadsheet-key').
getSheetByName('List').getDataRange().getValues();
Spreadsheet.getActive().getSheetByName('hidden-import').
getRange(1,1,data.length,data[0].length).setValues(data);