I have a small script that saves data in Google Sheets. It looks at 3 cells and 'saves' them below. I have it set on a 'Project Trigger' every few hours and I also have a button. I would like to, in Column D, insert into the cell some text that indicates wether is was a trigger, or a manual button click ("Trigger","Manual").
What I get at the moment is:
URL : follower_count : date
I would like:
URL : follower_count : date : trigger_status
Here is the code:
// function to save data
function saveData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var url = sheet.getRange('Sheet1!A3').getValue();
var follower_count = sheet.getRange('Sheet1!B3').getValue();
var date = sheet.getRange('Sheet1!C3').getValue();
sheet.appendRow([url, follower_count, date]);
}
Thank You.
From the Google Apps Script documentation:
Simple triggers and installable triggers let Apps Script run a
function automatically if a certain event occurs. When a trigger
fires, Apps Script passes the function an event object as an argument,
typically called e.
Hence, when your function saveData is called by the trigger it will be invoked with an argument. This way you will be able to tell if its "Manual" or "Trigger". I.e:
function saveData(e) {
var isTrigger = false;
if(e){
isTrigger = true;
}
...
}
Related
I am trying to get a Google Apps Script to trigger on edit of a particular cell. Right now I have achieved this using a run-time trigger option built in Google Sheets. However, I lose the run-time trigger when I make a copy.
Hence, I have managed to find a programmable trigger from various sources and patched something together, but it is not working. I have no programming knowledge, so I am unable to understand where I am going wrong.
My objective is to run the script when user edits the Named Range "monthfilter".
With script trigger creation
function HideColumns() {
//open the current spreadsheet and get the value of the named range and trigger project
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("monthfilter").forSpreadsheet(ss).onedit()
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
My previous function:
function HideColumns() {
//open the current spreadsheet and get the value of the named range
var ss = SpreadsheetApp.getActive();
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
//show or hide February
if (namevalue == "February") {
var sheet = ss.getSheetByName("MIS");
sheet.hideColumns(30);
/** other stuff */
Not sure what is the 'runtime trigger' you mean, there are three kinds of trigger:
Simple trigger
Installable trigger
Time driven trigger
onEdit(e) is just a Simple trigger.
Let's take a look to the following situation:
yourMainSpreadsheet - (You are the owner)
|
binding script functions: - 1. function makeCopy( ) {...}
2. function monthFilter(e) {...}
3. function onEdit(e) { monthFilter(e); }
|
| After copying, new spreadsheet remains all functions and simple trigger.
V
newSpreadsheet- (You are the owner, maybe share with other editors.)
|
binding script functions: - 1. function makeCopy() {...}
2. function monthFilter(e) {...}
3. function onEdit(e) { monthFilter(e); }
No matter what kind of trigger you use or just manually run makeCopy() in yourMainSpreadsheet, onEdit(e) would be copied to new spreadsheet too, and it works when you or other editors make edits, you don't need to do anything:
function makeCopy(){
var mainSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSpreadsheet = mainSpreadsheet.copy('newSheet');
}
function monthFilter(e) {
var thisSheet = e.source.getActiveSheet();
var name = thisSheet.getRangeByName("rangeMonthfilter");
var nameValue = name.getValue();
//...
}
function onEdit(e) {
monthFilter(e);
}
What you have done is set a installable trigger to new spreadsheet.
Highlight of documentation about difference between them.
Installable triggers, however, offer more flexibility than simple
triggers: they can call services that require authorization ...
For example:
function monthFilter(e) {
var thisSheet = e.source.getActiveSheet();
var name = thisSheet.getRangeByName("rangeMonthfilter");
var nameValue = name.getValue();
// If you have operation like:
var onlyYouHavePermission = SpreadsheetApp.openById('xxxxxxxxx');
/* This is not allowed if you just use Simpler trigger onEdit(e) to
drive monthFilter(). */
}
For this, now is the time to use installable trigger:
function makeCopy(){
var mainSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSpreadsheet = mainSpreadsheet.copy('newSheet');
ScriptApp.newTrigger('monthFilter')
.forSpreadsheet(newSpreadsheet)
.onEdit()
.create();
/* The host of this trigger is you, it's like you
deploy a trigger to the newSpreadsheet, and make other editors
available to do something on "onlyYouHavePermission" through it.*/
}
Update
If all you want to do is just in HideColumns() you provide, you just need to add a new function which is a simpler trigger in script editor:
function onEdit(e) {
var ss = SpreadsheetApp.getActive();
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
if (namevalue == "February") {
/* If you copy the spreadsheet, of course including the sheets
inside it, and you share the permission of the new spreadsheet to
other editors, because they own the permission, authorization
is not required here.
*/
var sheet = ss.getSheetByName("MIS");
sheet.hideColumns(30);
}
}
I am having issues getting information from a Google Form into a Google Sheet. I am looking to get the edit url onFormSubmit and then set it to the end of the record in a column where the responses are stored.
Research:
I asked this question, which started as a script bound to the sheet but trying to access the form. It then became a script bound to the form, trying to access the sheet.
I found this question which looks to be related to my question (with a slightly different use case). Similarly to mine, I think it will have issues getting spreadsheet methods while on the form.
Since both required methods that are only available to either the script or the form I keep hitting a wall. Now I am thinking that I may need a hybrid solution that requires some of the code to be bound to the sheet, and some to be bound to the form, with a variable passed between the two scripts that are both executing onFormSubmit.
This is what I think I should keep bound to the form
function onFormSubmit(e)
{
Logger.clear; //if I can use log to pass variable I want to clear out at the beginning of each submission
var form = FormApp.getActiveForm();
var activeFormUrl = form.getEditUrl();//This is the variable I need to pass to the sheet
Logger.log(activeFormUrl); //only to confirm what we are getting unless I can somehow access the log after the fact using sheet script
}//This is the end of onFormSubmit function bound to the Form
This is what I think I should keep bound to the sheet
function onFormSubmit(e)
{
var ss = SpreadsheetApp.getActiveSheet();
var createDateColumn = ss.getMaxColumns(); //CreateDateColumn is currently in AX (Column 50) which is the last/max column position
var urlColumn = createDateColumn-1; //urlColumn is currently in AX (Column 50) Calculating using it's relative position to createDateColumn Position
if (ss.getActiveRange(urlColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite editResponseURL
{
var editResponseURL = setGoogleFormEditUrl(ss, createDateColumn, activeFormUrl);
var createEditResponseUrl = ss.getActiveRange(urlColumn);
createEditResponseUrl.setValue(activeFormUrl);
}
else
{
if (ss.getActiveRange(urlColumn).getValue() != activeFormUrl)
{
Logger.log("Something went wrong - URL doesn't match" + activeFormUrl);
Logger.log(ss.getActiveRange(urlColumn).getValue());
var checkLog2 = Logger.getLog();
}
else {}//do nothing
}
}//This is the end of the onFormSubmit function bound to the Sheet
What I need to know is how to take activeFormUrl from the form script and send it to the sheet script. Can I use the log?
I'm not sure if this would work for you, but you can make an HTTPS GET or POST request to an Apps Script project with UrlFetchApp.fetch(url). So, from the Form project, you can make an HTTPS POST request to a published Web App. The published Web App can actually be published from the project bound to the spreadsheet, if you want to do that.
The way that an Apps Script project detects an HTTPS GET or POST request being sent to it, is with either a doGet() or doPost() function.
var webAppUrl = "https://script.google.com/macros/s/123_My_FileID/exec";
var payload = {
"url":"activeFormUrl"
};
var options = {"method":"post","payload":payload};
UrlFetchApp.fetch(webAppUrl, options);
The above code makes a POST request to another Apps Script project, and sends the payload to the file.
function doPost(e) {
var theUrl = e.parameter.url;
};
I'm assuming that you are trying to have a spreadsheet that is getting data from multiple Forms?
I had to separate the form and the spreadsheet operations as getting the formEditURL using the FormApp method would not work if I was using other SpreadsheetApp methods in the same function and the FormApp method only worked if it was in the onFormSubmit function.
Here is the code snippet which I used successfully
function onFormSubmit(e)
{
var rng = e.range; //Collects active range for event
var ss = SpreadsheetApp.getActiveSpreadsheet();//collects active spreadsheet object
var fUrl = ss.getFormUrl();//gets form url linked with active spreadsheet
var f = FormApp.openByUrl(fUrl);//opens form using form url
var rs = f.getResponses(); //gets responses from active form
var r = rs[rs.length - 1]; // Get last response made on active form
var c = getCellRngByCol(rng, 'formEditURL'); //locates the cell which the form url will be stored by searching header name
c.setValue(r.getEditResponseUrl());// sets form url value into correct cell for active form response
var callSpreadsheetFunctions = spreadsheetFunctions(rng, ss); //method calls other spreadsheet functions. This had to be modularized as you can't get form url if the other functions are occuring in the same function
}//This is the end of the onFormSubmit function
function spreadsheetFunctions (rng, ss)
{
var rowIndex = rng.getRowIndex();//gets row index for current response. This is used by tracking number
var createDateCell = getCellRngByCol(rng, 'CreateDate'); //locates which cell the createdate will be stored in by searching header name
var timestampCell = getCellRngByCol(rng, 'Timestamp'); //locates which cell the autogenerated timestamp is located in by searching header name
var trackingNumberCell = getCellRngByCol(rng, 'Tracking ID#');//locates which cell the tracking ID# will be stored in by searching by header name
var createDate = setCreateDate(rng, createDateCell, timestampCell); //method sets create date. NOTE: Function not included in code snippet but left here to demonstrate type of information used
var trackingNumber = setTrackingNumber(rng, rowIndex, trackingNumberCell, createDateCell); //method sets tracking number. NOTE: Function not included in code snippet but left here to demonstrate type of information used
return;
} //This is the end of the callSpreadsheetFunctions function
function getCellRngByCol(rng, col)//finds the cell associated with the active range and column
{
var aRng = SpreadsheetApp.getActiveSheet().getDataRange();//gets the spreadsheet data range
var hRng = aRng.offset(0, 0, 1, aRng.getNumColumns()).getValues();//finds the header row range by offsetting
var colIndex = hRng[0].indexOf(col);// declares the column index in the header row
return SpreadsheetApp.getActiveSheet().getRange(rng.getRow(), colIndex + 1); //returns the cell range at the position of the active row and column name passed into this method
}//This is the end of the getCellRngByCol function
I want to send the google sheet data to another application, using any trigger like onEdit. i am now able to send the data on a button click event, but now i want to do this without using any button little more automatic. it should be done just on cell edit.i've tried this can anybody please help.
var ss = SpreadsheetApp.openById("#########################");
function createSpreadsheetEditTrigger() {
ScriptApp.newTrigger('cellTracker')
.forSpreadsheet(ss)
.onOpen()
.create();
}
function cellTracker(){
var cell = ss.getActiveCell().getCellValue();
if((ss.getActiveCell().getValue()) > 0 && (ss.getActiveCell().getValue()) == Cell){
var link = app.createAnchor('XXXXX',"http://###############/noetic_Data/AddDataToAras?data="+Cell);
}
}
Instead of calling a spreadsheet by id, you could in this case attach a script to a spreadsheet by opening Tools -> Script editor -> Blank Project. This will open the script editor where you can use the onEdit trigger with this code:
function onEdit(e){
var myRange = e.range; //get the range that's edited
var myValue = myRange.getValue(); //get the value of the first cell in the range
//send data to other application
}
Every time you update a range (mostly just one cell) in your spreadsheet, this trigger wil use the event object containing the range and it's data.
https://developers.google.com/apps-script/guides/triggers/events
To see what's going on you can use the logger. For example:
Logger.log(myValue);
I have created an GUI app that allows a user to submit data and writes the data in a Spreadsheet. This Spreadsheet has the visibility option: "anyone with the link" and "Can edit" permission.
Also, there is a onEdit() trigger that should be executed everytime that a change has been made. There is no problem to write the data to the Spreadsheet but the trigger is not working.
Could you please assist me in this matter?
Many thanks in advance.
onEdit trigger is executed only when you manually change the contents of the table.
You can create general function and call it from UiApp and execute onEdit:
function edit(str){ //general onedit function
//do somethig (For example add " edited")
var cell = SpreadsheetApp.getActiveSheet().getActiveCell();
cell.setValue(str + ' edited');
}
function onEdit(){
var str = SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
edit(str);
}
function gui(){
var app = UiApp.createApplication();
var tb = app.createTextBox().setName('tb');
var hndlr = app.createServerHandler('onSend').addCallbackElement(tb);
var btn = app.createButton('Send').addClickHandler(hndlr);
SpreadsheetApp.getActiveSpreadsheet().show(app.add(tb).add(btn));
}
function onSend(e){
var str = e.parameter.tb;
edit(str);
}
sorry for my bad english =)
I'm trying to write a script that display row value in a SpreadSheet file using UiApp.
And this script will need to update the data whenever there is a change to the SpreadSheet. This would normally be simple with just Trigger onChange.
However, the SpreadSheet itself is also updated by another function, and it seem that onChange is not triggered when the changes made are caused by another AppScript.
Is there an alternative way to achieve what I described?
Code below, thanks
var ss = SpreadsheetApp.openById("tqwJk280I1O_yW5oNX9nLQA");
function doGet() {
var app = UiApp.createApplication();
var masPanel = app.createFlowPanel().setId("panel");
var number = parseInt(ss.getRange("A1").getValue());
masPanel.add(app.createLabel(number));
masPanel.add(app.createFlowPanel().add(app.createFormPanel()
.add(app.createFlowPanel()
.add(app.createSubmitButton("Plus!")))));
app.add(masPanel);
return app;
}
function doPost()
{
var num = parseInt(ss.getRange("A1").getValue()) + 1;
ss.getRange("A1").setValue(num);
}
function onChange()
{
ss.getRange("A2").setValue("hahaha");
var app = UiApp.createApplication();
app.remove(app.getElementById("panel"))
var masPanel = app.createFlowPanel().setId("panel");
var number = parseInt(ss.getRange("A1").getValue());
masPanel.add(app.createLabel(number));
masPanel.add(app.createFlowPanel().add(app.createFormPanel()
.add(app.createFlowPanel()
.add(app.createSubmitButton("Plus!")))));
app.add(masPanel);
return app;
}
The onChange() function will not have reference to the UI. For example, if there are multiple people who have the same UI open, which one should be updated ?
So, the solution to your problem is for your UI code to poll the spreadsheet regularly and update the UI if the spreadsheet has changed. To do so, you can use the undocumented addTimer() method.
See this SO question & answer for a nice example