I was interested in automating data validation of a Google Spreadsheet against another Google Spreadsheet using Google Scripts. I can't seem to find a way to reference the range of another spreadsheet using the openById method. Any thoughts?
function externalSheetDataValidation() {
var cell = SpreadsheetApp.getActiveRange();
//var dataValidationSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dataValidationRules");
//var dataValidationSheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/10Z2s1bSRIhZzBBrMfPmhEphnPx-kJdV3LLlbv0L59g8/edit#gid=0");
var dataValidationSheet = SpreadsheetApp.openById("10Z2s1bSRIhZzBBrMfPmhEphnPx-kJdV3LLlbv0L59g8");
var sheet = dataValidationSheet.getSheets()[0];
var range = SpreadsheetApp.getActiveSheet().getRange("A3:A4");
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(range, true)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
Logger.log(dataValidationSheet.getName());
}
If I am understanding correctly, the issue you are having is selecting a data range from an external spreadsheet?
Im assuming dataValidationSheet is supposed to be the external sheet. It looks like you were on the right track. It looks like the issue is that you are expecting the sheet to be active once you grab it.
Try this change:
var range = sheet.getRange("A3:A4");
Currently you are grabbing the external sheet, but then getting the range from the sheet that was already active. Hopefully this helps!
Related
I have had a look online and on previosuly asked questions but have not come across anything that is suited.
Essenitally I am looking for a script that populates a changes log of when a cell is edited.
I would like to keep a log of the previous comment, updated comment, user of who changed the cell and a timestamp
Any help / advice would be appreciated
Thank you
previoulsy used below; but it does not like the source
function edit(e) {
var logsheet = SpreadsheetApp.getActive().getSheetByName('Change Log');
var esheet = e.source.getActiveSheet();
var ecell = esheet.getActiveCell();
var ecol = ecell.getColumn();
var erow = ecell.getRow();
var editor = Session.getActiveUser().getEmail();
var newcontents = ecell.getValue();
var origValue = e.oldValue
var Avals = logsheet.getRange('A1:A').getValues();
var Alast = Avals.filter(String).length;
var maxRows = logsheet.getMaxRows();
logsheet.getRange(Alast+1,1).setValue(new Date()).setNumberFormat('dd"/"mm"/"yy hh:MM');
logsheet.getRange(Alast+1,2).setValue(editor);
logsheet.getRange(Alast+1,3).setValue(esheet.getName());
logsheet.getRange(Alast+1,4).setValue(ecell.getA1Notation());
logsheet.getRange(Alast+1,5).setValue(origValue);
logsheet.getRange(Alast+1,6).setValue(newcontents);
}
Your code works just fine(*). Here is example of output on the sheet 'Change log':
All you need is to rename the function from edit(e) to onEdit(e).
And it doesn't work from Script editor of course. It fires only when you edit the table.
(*) Except editor column, perhaps. onEdit() may not be able to get editor names due the restrictions for simply triggers: https://developers.google.com/apps-script/guides/triggers
They may or may not be able to determine the identity of the current user, depending on a complex set of security restrictions.
I have tried to find a solution to this problem already for a while.
This is the code
function CHECK(INPUT) {
var url = "GOOGLE SHEET URL" + INPUT;
var response = UrlFetchApp.fetch(url);
var json = response.getContentText("UTF-8");
var data = JSON.parse(json);
var price = data;
SpreadsheetApp.flush();
return price;
}
This function pulls values from google sheet and, as a result, display it in another google sheet. The problem is that when the source changed the destination value do not change.
All that I need is something which will force to recalculate values in google sheet, or refresh function or something else but will keep values up-to-date.
P.S. =CHECK(INPUT, **DATE**) is not a solution
You want to forcibly recalculate the custom function using Google Apps Script.
If my understanding is correct, how about this sample script? I think that there are several answers for your situation. So please think of this as just one of several answers.
In this sample script, in order to recalculate the function, the function is replaced by the same function.
Sample script 1:
In this sample script, when =CHECK(INPUT) is the cell "A1" of "Sheet1", the function is replaced by the same function.
var sheetName = "Sheet1"; // Please modify this for your situation.
var cell "A1"; // Please modify this for your situation.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheetByName(sheetName).getRange(cell);
var formula = range.getFormula();
range.clearContent();
SpreadsheetApp.flush();
range.setFormula(formula);
Sample script 2:
In this sample script, all functions in "Sheet1" are replaced by the same function.
var sheetName = "Sheet1"; // Please modify this for your situation.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheetByName(sheetName).getDataRange();
var values = range.getValues();
var formulas = range.getFormulas();
var replacedValues = values.map(function(e, i) {return e.map(function(f, j) {return formulas[i][j] || f})});
range.clearContent();
SpreadsheetApp.flush();
range.setValues(replacedValues);
Note:
When the above sample scripts are run, the function is recalculated. For example, one of sample scripts is installed as the time-driven trigger, the function you want to recalculate is forcibly updated by the time-driven trigger.
References:
getFormula()
getFormulas()
setFormula(formula)
setFormulas(formulas)
Time-driven triggers
If I misunderstood your question and this was not the direction you want, I apologize.
This is my first time posting on Stack Overflow, so I apologize in advance if I made any "posting" mistakes.
I am using Google Apps Script for the project I am doing, because I am interacting with Google Docs and Google sheets. Google Script has a deprecated Ui Service, which is very frustrating to me.
So far, I have written a JavaScript program that parses through the "table of contents" part of a Google Doc, and retrieves the items I wanted. (If the table of content is changed, then the items retrieved might be different too.) Now, my intention is to make a checkbox that uses all the items that I retrieved from the table of contents as its options, and after users choosing and clicking "submit" button, a new Google Sheet would be generated. Of course I will operate on that new Google Sheet, but I want to stick with this problem first.
I did look up HTML Service, a Ui recommended by Google Script, but I don't know too much about HTML. What is most difficult is that if I use HTML to make the checkbox, how am I supposed to get all the items I parsed from the JavaScript passed into the HTML file, check which options are checked, and then get checked options passed back to the JavaScript so that I can do further operation? Is it even possible? Or am I having a misconception?
What I have so far:
//custom menu
function onOpen()
{
var ui = DocumentApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Export Checksheets', 'customMenu')
.addToUi();
}
//parse the table of contents
function readFile()
{
var options = [];
var testDoc = DocumentApp.getActiveDocument();
var file = testDoc.getBody();
var tableOfCon = DocumentApp.ElementType.TABLE_OF_CONTENTS;
var searchRes = file.findElement(tableOfCon);
//If the element exists
if (searchRes)
{
var TC = searchRes.getElement().asTableOfContents();
var numChild = TC.getNumChildren();
for (var i=0; i < numChild; i++)
{
var info = {};
var TCItem = TC.getChild(i).asParagraph();
var TCItemText = TCItem.getChild(0).asText();
var TCItemAttrs = TCItemText.getAttributes(); //for future usage
//get all the options
if (!TCItemText.isBold())
{
options.push(TCItem.getText());
}
}
}
Logger.log(options); //which successfully displays the items retrieved
}
//linked to the HTML file
function customMenu()
{
var html = HtmlService.createHtmlOutputFromFile('Index');
html.setHeight(350).setWidth(280);
var ui = DocumentApp.getUi().showModalDialog(html, 'Hello!');
}
My HTML file only has a little code that displays some message when the custom menu is chosen.
Any help would be appreciated!
I'm struggling to get my script to auto-run at 6AM (ish). I have the trigger set up to run this script, "Time-Driven", on a "day timer" between "6-7 am". I'm getting no failure notifications (set up to email to me immediately), but the script isn't running. It works exactly as I want it to when I manually run it, but the whole point was to automate it, so I'm not sure what I am doing wrong here. I looked up other instances, and they seem to have been fixed by deleting and re-adding the triggers, but that doesn't solve the issue for me. Is it something in my script preventing an auto-run?
function getMessagesWithLabel() {
var destArray = new Array();
var label= GmailApp.getUserLabelByName('Personal/Testing');
var threads = label.getThreads(0,2);
for(var n in threads){
var msg = threads[n].getMessages();
var body = msg[0].getPlainBody();
var destArrayRow = new Array();
destArrayRow.push('thread has '+threads[n].getMessageCount()+' messages');
for(var m in msg){
destArrayRow.push(msg[m].getPlainBody());
}
destArray.push(destArrayRow);
}
Logger.log(destArray);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
if(ss.getLastRow()==0){sh.getRange(2,1).setValue('getMessagesWithLabel() RESULTS')};
sh.getRange(2,1,destArray.length,destArray[0].length).setValues(destArray);
}
I'm not 100% sure, but the reason for this could be that during a trigger there's no "ActiveSpreadsheet" if the script isnt directly linked to spreadsheet (As the spreadsheet is closed). So you should try using:
var ss = SpreadsheetApp.openById(id); // id is the id of the spreadsheet
// https://docs.google.com/spreadsheets/d/id_is_here/
var sh = ss.getSheetByName(name); // name of the actual sheet ("Sheet 1" for example)
Otherwise i see nothing wrong with your code (other than you using label.getThreads(0,2) which sets the maximum number of threads to be brought in to 2, but i assume that's intentional)
Also, you're setting 2,1 instead of what i assume needs to be 1,1 in
if(ss.getLastRow()==0){sh.getRange(2,1).setValue('getMessagesWithLabel() RESULTS')};
The problem is due to the use of getActiveSheet as it retrieves the sheet displayed on the UI but when your time-driven trigger runs there isn't a sheet displayed on the UI.
Replace getActiveSheet by getSheetByName or better get the sheet by it's ID (for details see Get Google Sheet by ID?)
Reference:
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#getactivesheet
It seems like in the documentation that Google Spreadsheets and Google Sheets both have methods such as getActiveCell() which leads to confusion what the difference between the "Spreadsheet" object and the "Sheet" object. I assume that a spreadsheet is a collection of sheets. Suppose that I want to open a file with the following code where my Spreadsheet only has 1 sheet (see question in commented lines and assume I want to then do operations on the first sheet of both files):
function OpenFile(FileName) {
var FileToGetIterator = DriveApp.getFilesByName(FileName);
var FileToGet = FileToGetIterator.next();
var SpreadSheets = SpreadsheetApp.open(FileToGet);
var SpreadSheet1 = SpreadSheets.getSheets()[0]; // is this line necessary??
}
Similarly for creating a file
function CreateFile(SpreadSheetName) {
var NewSpreadSheets = SpreadsheetApp.Create(SpreadSheetName);
var NewSpreadSheet = NewSpreadSheets.getSheets()[0]; // is this line necessary?
}
when you create a spreadsheet or you open a spreadsheet it automatically open the first sheet of the spreadsheet,so no it's not necessary. nevertheless the opened sheet is opened as the "activesheet" and you've got less methods than if you specifically open a particular sheet with your method