I have created a script to run as a time based trigger.
I want to disable that trigger during weekends off and holidays.
I ideally want this to be part of a function I already have that
deletes previous form submissions meaning that the user only has one button to press (deleting previous submissions AND disabling the time trigger) at the start of each holiday.
It would then be brilliant if the
trigger was re-enabled the next time a pupil submitted the form
(indicating holidays were over).
Not sure how to use script to disable / enable triggers in this way - grateful for any advice!
//Menu allowing user to choose to delete previous form submissions
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Clear Out Responses')
.addItem('Clear Form & Responses', 'showAlert')
.addToUi();
}
//Alert box deleting previous form submissions on yes, doing nothing if not
function showAlert() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'This will clear all previously registered data',
'Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
var ui = SpreadsheetApp.getUi();
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lastRow = source.getLastRow()
source.deleteRows(2, lastRow)}
var form, urlForm = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
if (urlForm) {}
form = FormApp.openByUrl(urlForm);
if (form) form.deleteAllResponses();
}
//Trigger running record-keeping function each night
function dailyTrigger() {
ScriptApp.newTrigger('dailyRecord')
.timeBased()
.atHour(22)
.everyDays(1)
.create();
}
//Record-keeping function, copy/value-pasting record into next clear column
function dailyRecord() {
var ss = SpreadsheetApp.getActive()
.getSheetByName('History'),
lastColumn = ss.getLastColumn(),
colE = ss.getRange("C:C")
.getValues();
ss.getRange(1, lastColumn + 1, colE.length, 1)
.setValues(colE);
}
Have you looked at getUserTriggers() and deleteTrigger ?
With getUserTriggers() you can identify whether the trigger to call dailyRecord is already in existence. It returns a list of triggers, and you can check the function being called by each trigger by using getHandlerFunction().
With deleteTrigger() you can delete a given trigger. e.g. if your trigger to call dailyRecord is installed (and returned in the list from getUserTriggers(), you can then delete it using this function.
To enable the trigger again, having checked that it doesn't already exist, you can use the trigger builder as per the code you already have, to recreate the trigger.
Armed with these methods, you should be able to achieve the effect you are after.
An existing trigger can not be retrieved and get re-defined. For example, a time based trigger that has already run, can not be retrieved and given a new time to run using code.
Related
[UPDATE]
I had a look at add-ons and I am afraid this won't work. So let me take a step back and describe what I am trying to achieve.
I have a spreadsheet A, with a list of individual events. Each event is a line item in the spreadsheet. The spreadsheet is very long for one, and has many fields that I don't need to expose to event owners (different events different owners). Which means if I allow all these different people edit access to the sheet, it becomes really chaotic.
The solution I came up with is to generate unique IDs programmatically for each event, which I've done. Then for each event, I create an individual form and a pre-filled link, with pre-filled answers that is pulled from the cell values. I intend to give the pre-filled links to event owners when they need to make any updates.
The issue is now I have 100+ forms, and I don't want to have 100+ corresponding tabs set as destinations of these forms. These 100+ forms need to submit responses to one same sheet (tab). Instead I wrote a function for submitted responses to find the right event (the event unique ID is the title of the form) and updates the right cell. This is what you see below processSubmission().
I have tried to write the processSubmission() in the spreadsheet where the events are listed. If I don't set this spreadsheet as destination of these 100+ forms then the spreadsheet doesn't know there is a "submission" event. Therefore the setting the trigger onFormSubmit() in the spreadsheet doesn't work.
Then I moved onFormSubmit() -> processSubmission() and it doesn't set the trigger because as you all pointed out, it's an installable trigger.
What I did manage to to write an onOpen() -> create the onFormSubmission() trigger. That means I had to manually open 100 forms and close them to create that trigger. The triggers are created alright. But turned out for the trigger to actually run I need to manually grant permission!
When I looked at add-on triggers, it says "Add-ons can only create triggers for the file in which the add-on is used. That is, an add-on that is used in Google Doc A cannot create a trigger to monitor when Google Doc B is opened." So I think that also rules out the add-on triggers. So now I am out of ideas.
[ORIGINAL]
I made a custom function for the processing of submission responses. I use the form title as a key, and the response answers are written to the corresponding headers in the row with the right key.
My first try was something like this. But it simply didn't execute when the form was submitted:
function onFormSubmit(e){
var form = FormApp.getActiveForm();
var key = form.getTitle();
var responses = e.response;
var ss= SpreadsheetApp.openById(ss_id);
var sheet = spreadsheet.getSheetByName('Launch list');
var frozenRow = sheet.getFrozenRows();
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var headers = sheet.getRange(1, 1, 1, lastColumn).getValues()[0];
var keyCol = headers.indexOf(key_header) + 1;
var header1Col = headers.indexOf(header_1) + 1;
var header2Col = headers.indexOf(header_2) + 1;
var header3Col = headers.indexOf(header_3) + 1;
var keysRange = sheet.getRange(frozenRow+1, keyCol , lastRow - frozenRow, 1);
var allKys = keysRange.getValues();
for (i=0; i<allKys.length; i++){
var keyValue = allKys[i][0];
if (keyValue === key){
var rowNum = l + frozenRow + 1;
break;
}
else {
continue;
}
}
var dataRow = sheet.getRange(rowNum, 1, 1, lastColumn).getValues()[0];
var lookUp = {};
lookUp[item_title_1] = header1Col ;
lookUp[item_title_2] = header2Col ;
lookUp[item_title_3] = header3Col ;
var items = form.getItems();
var cnt = 0;
var changes = [];
for (i=0; i< items.length; i++){
var item = items[i];
var title = item.getTitle();
var itemResponse = responses.getResponseForItem(item);
var existingValue = dataRow[lookUp[title] -1];
if ((itemResponse.getResponse() !=='' || itemResponse.getResponse() !== undefined) && itemResponse.getResponse() != existingValue){
cnt++;
var cell = sheet.getRange(rowNum, lookUp[title], 1, 1);
cell.setValue(itemResponse.getResponse());
changes.push(title);
}
else {
continue;
}
}
Logger.log('Made ',cnt,'changes for launch ',featureID,': ',changes);
}
I also tried a slightly different approach but also didn't work:
function onFormSubmit(){
processSubmission();
}
// Processing form submission
function processSubmission() {
var form = FormApp.getActiveForm();
var key = form.getTitle();
var responses = form.getResponses()[form.getResponses().length-1];
// The rest is the same.
}
Manually running the function in the second approach proved my function processSubmission() works. Manually add a onFormSubmit() trigger via the Apps Script Dashboard is not going to be possible because I am generating hundreds of forms (one for each key) programmatically so I chose to have onFormSubmit(e) in the template and every new form is a copy of the template which should also have copies of these functions. But it just doesn't work! Any insight?
The onFormSubmit trigger is an installable trigger which means that it requires to be set up before being able to use it.
It's also important to keep in mind the following, according to the installable triggers documentation:
Script executions and API requests do not cause triggers to run. For example, calling FormResponse.submit() to submit a new form response does not cause the form's submit trigger to run.
What you can do instead is to create the trigger programmatically, something similar to this:
function createTrigger() {
ScriptApp.newTrigger('onFormSubmit')
.forForm('FORM_KEY')
.onFormSubmit()
.create();
}
Reference
Apps Script Installable Triggers;
Apps Script FormTriggerBuilder Class.
In google sheets, I am trying to get one data to copy from one sheet to another.
I have this code which is working however I would like it to run onEdit when changing cell E4 in Googlesheet1. I am new at this and doesn't seem to get it to quite work with the solutions I found online.
function ExportRange() {
var destination = SpreadsheetApp.openById('googlesheet1');
var destinationSheet = destination.getActiveSheet();
var destinationCell = destinationSheet.getRange("AC3");
var cellData = '=IMPORTRANGE("https://docs.google.com/spreadsheets/googlesheet2", "AE10:AE9697")';
destinationCell.setValue(cellData);
}
Chose between a simple and installable onEdit trigger, depending on your requirements
For most applciaitons a simple onEdit trigger is sufficient, to use it you just need to rename your function ExportRange() to onEdit()
Take advantage of event objetcs that give you informaiton about the event that fired the trigger
So, the trigger onEdit can give you among others information about the event range - that is the range that has been edited
Now you can implement an if statement to specify that the rest of the funciton shall only be run if the event range and the corresponding sheet are as required
Sample:
function onEdit(event) {
var range = event.range;
var sheet = range.getSheet();
if(range.getA1Notation() == "E4" && sheet.getName() == "Googlesheet1"){
var destination = SpreadsheetApp.openById('googlesheet1');
var destinationSheet = destination.getActiveSheet();
var destinationCell = destinationSheet.getRange("AC3");
var cellData = '=IMPORTRANGE("https://docs.google.com/spreadsheets/googlesheet2", "AE10:AE9697")';
destinationCell.setValue(cellData);
}
}
Please note that this function can only be fired by the trigger in
case of an edit. If you try to run it manually, it will give you an
error because event (and thus event.range) will be undefined if
the funciton was not called by an edit event.
I have a script associated with a google sheet that it's call after a Google form is submitted.
Actually I can get all rows or the last but in case one form answer is updated I need to find this row updated to make my treatment.
Is there a way to do that?
This is my two case:
// Getting last row data
function getSpreadSheetLastRowData() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var row = sheet.getLastRow();
return sheet.getRange('A'+row+':BG'+row).getValues();
}
// Getting all rows data
function getSpreadSheetAllData() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var row = sheet.getLastRow();
return sheet.getRange('A2:BG'+row).getValues();
}
You will need to write an event handler for the onFormSubmit event object and attach it to your spreadsheet.
From the Spreadsheet linked to your Form go to Tools > Script Editor.
Here write a function to install the trigger:
function installTriggerToSpreadsheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ScriptApp.newTrigger('sumbissionHandler')
.forSpreadsheet(ss)
.onFormSubmit()
.create();
}
Now your trigger will handle the form submission calling the submissionHandler function inside the Spreadsheet context.
This means you can read the affected row by using a function like this:
function submissionHandler(e) {
var affectedRow = e.range.getRow();
// ... Apply your treatment.
}
References:
Triggers
Event Objects
If you have a script that is triggered by a form submission then take the event object lets call it e. Then e.range is the range that was edited in the linked sheet. So e.range.getRow() is the row and e.range.getSheet().getName() is the name of the linked sheet.
form submit object for spreadsheet
I want people to jump to the current date row when opening the sheet. However, this should be the case for everyone viewing the sheet and irrespective of their edit-rights. For example, I want that people edit the current date row in the sheet every day over the link. In this case, onOpen() does not work. Is there any alternative or modification to the function?
I am informed about onOpen trigger, however, this would not work if somebody is editing the sheet only over the link with edit rights.
This is e.g. the code I would like to work for everyone:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("B:B");
var values = range.getValues();
var day = 24*3600*1000;
var today = parseInt((new Date().setHours(0,0,0,0))/day);
var ssdate;
for (var i=0; i<values.length; i++) {
try {
ssdate = values[i][0].getTime()/day;
}
catch(e) {
}
if (ssdate && Math.floor(ssdate) == today) {
sheet.setActiveRange(range.offset(i,0,1,1));
break;
}
}
}
I found the options: Edit Triggers
To manually create an installable trigger through a dialog in the script editor, follow these steps:
From the script editor, choose Edit > Current project's triggers.
Click the link that says: No triggers set up. Click here to add one now.
Under Run, select the name of function you want to trigger.
Under Events, select either Time-driven or the Google App that the script is bound to (for example, From spreadsheet).
Select and configure the type of trigger you want to create (for example, an Hour timer that runs Every hour or an On open trigger).
Optionally, click Notifications to configure how and when you are contacted by email if your triggered function fails.
Click Save.
Google Explanation to Edit Triggers
I'm pretty new to this, so I'm sure the fix is something small and simple. I'm trying to create a code that allows a user to click a button, press a confirmation, and then delete all rows that have data in them minus the top row. Code is as follows:
Code.gs
//adds custom menu to activate function from.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Delete Responses')
.addItem('Delete Current Responses', 'deleteAll')
.addToUi();
}
//Trying to delete all rows that contain data minus the first one here.
function deleteAll() {
var ui = SpreadsheetApp.getUi();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var response = ui.prompt('Are you sure you want to delete all responses?', ui.ButtonSet.YES_NO);
var lastRow = sheet.getLastRow();
if (response.getSelectedButton() == ui.Button.YES) {
sheet.deleteRows(2, lastRow)
}
}
Currently, when calling lastRow in the last bit of sheet.deleteRows Google returns an error saying "Those rows are out of bounds." If I change it to an integer like 10, just to see if it works, it does nothing upon completion of the script. Am I missing something simple?
(x-posted from Code Review, where I deleted my original post. Sorry, I thought I'd successfully posted a question like this there before, but looks like it was just posted on the general site).
The second parameter is "number of rows to delete" not "last row to delete". Changing your script to reflect this and checking for an empty sheet things works.
if (response.getSelectedButton() == ui.Button.YES && lastRow-1>0) {
sheet.deleteRows(2, lastRow-1)
}