So, I made a Google Spreadsheet for a group of people to use to keep track of weekly "counts" for a large group of people on a Reddit sub. The things I'm trying to automate are two things. The one I'm having problems with is the one I thought would be the easiest, just copying the values from one set (G2:G200) to overwrite the values in another (E2:E200). I'm having some other issues as well, but I'd be more interested in an explanation for what I'm doing wrong there than just an answer. The biggest one is that this is supposed to be making a custom menu on the sheet, and I can't seem to get that working, even though I basically copied the script from the Google Tutorial for that. I've tried the script for this two ways, one using the same script as Excel printed out when recording the same basic thing:
function UpdateLore_() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'Only do this once per week, at end of updates.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
Range("G2:G200").Select;
Selection.Copy;
Range("E2:E200").Select;
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False;
} else {
// User clicked "No" or X in the title bar.
ui.alert('No Changes Made.');
}
}
This returns an arror on the "Selection.PasteSpecial" line. The other way I tried it was using what I could find online for this:
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValue = inputRange.getValue();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200");
}
The top part of the code looks like this:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Weekly Update')
.addItem('Update for Lore', 'UpdateLore')
.addItem('Update for XP Master', 'UpdateMaster')
}
I feel like I'm missing something very obvious, especially with the whole "doesn't seem to change the sheet in anyway" part. Thanks for any help
Got some answers and now it works, thanks for all the help:
Got it, thanks for all the help. New code looks like this:
function UpdateLore() {
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.alert(
'Please confirm',
'Only do this once per week, at end of updates.',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
copyFunction ();
}
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValues = inputRange.getValues();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200").setValues(inputValues);
}
if (result ==ui.Button.NO) {
// User clicked "No" or X in the title bar.
ui.alert('No Changes Made.');
}
}
To add data to a sheet you need to use:
setValue()
setValues()
appendRow()
You've got a function inside of the if body:
if (result == ui.Button.YES) {
// User clicked "Yes".
function copyFunction () {
. . . .
}
}
If you want to call another function at that point, you could use:
if (result == ui.Button.YES) {
// User clicked "Yes".
copyFunction ();
};
function copyFunction () {
. . .
};
You need to set the values from the inputRange to the outputRange. Use the .setValues() on your outputRange to do this.
function copyFunction () {
var inputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("G2:G200");
var inputValues = inputRange.getValues();
var outputRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("E2:E200").setValues(inputValues);
}
None of this is valid apps script code:
Range("G2:G200").Select;
Selection.Copy;
Range("E2:E200").Select;
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _ :=False, Transpose:=False;
Related
I'm looking at a cooldown to a function I've created so that once the function has been ran and the "else' tag has been ran that it wouldn't attempt to run again for 15 minutes.
Is there a simple way of doing this. My code is below and I've inserted a add here section for where I would want it to run.
function Cancel() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
// first prompt
var presult = ui.prompt(
"Please Enter the Password!",
ui.ButtonSet.OK_CANCEL);
var password = "Money2022";
var pbutton = presult.getSelectedButton();
var ptext = presult.getResponseText();
// User clicked "OK" on first prompt
if (pbutton == ui.Button.CANCEL) {
ui.alert('Wrong Answer Buddy!');
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else if (pbutton == ui.Button.CLOSE) {
ui.alert('Wrong Answer Buddy!');
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else if (ptext != password) {
Password();
} else {
**!!INSERT FUNCTION HERE!!**
}
}
function Password() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var response = ui.alert("Wrong Answer Buddy!",
ui.ButtonSet.OK_CANCEL);
if (response == ui.Button.CANCEL) {
ui.alert("BYE BYE!");
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else if (response == ui.Button.CLOSE) {
ui.alert("BYE BYE!");
SpreadsheetApp.getActive().getSheetByName('2022').hideSheet();
} else {
Cancel();
}
}
Seems like Cooper's suggestion of using Utilities.sleep() already worked for you but I'd like to add a little more information and another way to do this for anyone else with a similar question.
Since you mentioned that you wanted to cooldown for 15 minutes you should know that the maximum sleep time for Utilities.sleep() is 300000 milliseconds or 5 minutes. You can find this documented here.
As an alternative to bypass this limit you can consider using the Properties service. This allows you to save properties for either the users or the entire script, so you can set a property with a time x minutes from now and the script can check if it's passed this time before running. This allows you to set the cooldown as long as you want, and choose between having different cooldowns for each user or lock the entire document.
Here's an example of how you could do it:
function yourProcess(){
var properties = PropertiesService.getUserProperties() //gets all properties
var cooldownprop = properties.getProperty("cooldown") //gets cooldown property
var now = new Date()
//if cooldown has not been set it just sets the current time so the user can go through
var cooldown = cooldownprop == null ? now : new Date(cooldownprop)
if (now >= cooldown){
//run your code
//this sets the cooldown if needed, add the time in milliseconds
properties.setProperty("cooldown", new Date(now.getTime()+900000))
} else {
//tell the user they're on cooldown
}
}
For more information and samples on how to use the properties you can check out the documentation.
Sources:
Utilities.sleep()
Properties Service
Kinda new to Google Apps Script. I'm trying to create Google Calendar events from GoogleSheet. It won't be revealing that I'm using someone's answers where some answers are here from stackoverflow. Unfortunately, I have not found a complete answer to my problem. Or if there was I couldn't recreate it :)
GoogleSheet
That's the Google Sheet that I'm using (trying to make a shipping calendar where someone can track deliveries). Columns marked as 0 are not used to make calendar events, only these marked as 1. For example what should be in Calendar Event:
Title: L-H-19/22/Description: Driver 1 Mobile: 1234567890/Date:2022-05-16
Note: Date probably will be changed to start/end date with hours. (2022-05-16 06:00 - 2022-5-16 07:00). Also, I mentioned "Description" which I'm not using but I'll give it a go, while I change ".createAllDayEvent" to ".createEvent", that's why also there is commented "DATA_2".
For now, it creates an event but I cannot figure out how to update it without making duplicates.. Tried to use .deleteEventSeries() and update it, also tried to use trigger onEdit() but still without luck.
Here is code which I use to add events to calendar:
function initMenu() {
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('SPEDYCJA')
menu.addItem('DODAJ DO KALENDARZA','DodanieSpedycjiDoKalendarza')
menu.addItem('AKTUALIZUJ KALENDARZ','AktualizacjaWydarzenia')
menu.addToUi();
}
function onOpen() {
initMenu();
}
function DodanieSpedycjiDoKalendarza() {
var ui = SpreadsheetApp.getUi();
var WcisnietyPrzycisk = ui.alert("Czy na pewno chcesz uruchomić skrypt??",ui.ButtonSet.YES_NO);
if (WcisnietyPrzycisk == ui.Button.YES) {
let AktywnyArkusz = SpreadsheetApp.getActiveSheet();
let KalendarzSpedycja = CalendarApp.getCalendarById("cbvdt6iek0qbgujgbhq68et950#group.calendar.google.com");
let TabelaDanych = AktywnyArkusz.getRange(12,8,8,9).getValues();
for (let x=0; x<TabelaDanych.length; x++) {
const ZMIANA = TabelaDanych[x];
const TYTUŁ = ZMIANA[0];
const DATA_1 = ZMIANA[7];
//const DATA_2 = ZMIANA[14];
const ID_Wydarzenia = ZMIANA[8];
if (ID_Wydarzenia == "") {
const NoweWydarzenie = KalendarzSpedycja.createAllDayEvent(TYTUŁ,DATA_1);
const ID_NoweWydarzenie = NoweWydarzenie.getId();
AktywnyArkusz.getRange(8+x,8).setValue(ID_NoweWydarzenie);
}
try {
var event = KalendarzSpedycja.getEventSeriesById(ID_Wydarzenia);
event.deleteEventSeries();
//entry[9] = '';
} catch(e) {
//nie rób nic
}
var newEvent = KalendarzSpedycja.createAllDayEvent(TYTUŁ,DATA_1);
// entry[9] = newEvent;
debugger;
}
ui.alert("Dodano spedycje do kalendarza!")
} else if (WcisnietyPrzycisk == ui.Button.NO) {
ui.alert("Nie uruchomiłeś skryptu.");
}
}
Also, sample code where I tried to use trigger onEdit() while then in the main function I did not used "try { .deleteEventSeries() }", there was only loop for but still failed..
function AktualizacjaWydarzenia(e) {
var ZaktualizowaneWiersze = e.range.getRange();
var ZaktualizowaneDane = e.source.getActiveSheet().getRange(ZaktualizowaneWiersze, 8, 8, 9).getValues()[4];
var ID_ZaktualizowaneWydarzenie = ZaktualizowaneDane[9]
try {
var Wydarzenie = CalendarApp.getEventById(ID_ZaktualizowaneWydarzenie);
ID_ZaktualizowaneWydarzenie.setTitle(ZaktualizowaneDane[1]);
ID_ZaktualizowaneWydarzenie.setDate(ZaktualizowaneDane[8]);
} catch(err) {
console.log("Wystąpił błąd podczas aktualizowania wydarzeń do kalendarza. Konkretne wydarzenie może jeszcze nie istnieć.");
}
}
I will be very grateful where someone will paste answer with code and point me/critique in existing one what I did wrong.
Thank you, have a nice day.
To prevent duplicates,
you need to save in one column the event id when you create that event. Then, if you want to update, you have to take account of this id.
reference
setTime(startTime, endTime)
try
function AktualizacjaWydarzenia(ID_Wydarzenia,TYTUŁ,DATA_1,DATA_2) {
var Wydarzenie = CalendarApp.getEventById(ID_ZaktualizowaneWydarzenie);
Wydarzenie.getEventById(ID_Wydarzenia).setTitle(TYTUŁ)
Wydarzenie.getEventById(ID_Wydarzenia).setTime(DATA_1,DATA_2)
}
Gory title but I couldn't find a way of being clearer.
I have no experience with coding and I was wondering if doing something like what I'm about to explain would be possible.
This is my example sheet:
What I'm looking to do is to have automated emails sent out to the person assigned to the task if the task status is set to urgent, while referencing people by names and having an auxiliary sheet with all the names and corresponding emails.
I've browsed around and found some similar questions which I unfortunately had no success in adapting. The one thing I got is that I need to setup an onEdit trigger, which I've done, but I'm completely clueless from here on out.
Can someone point me in the right direction? I don't have a clue where to start.
Looking forward to hearing your advice.
Thanks and stay safe in these crazy times!
It was a funny exercise. I tried to make the script as clean and reusable as possible for others to be able to adapt it to their needs.
Usage
Open spreadsheet you want to add script to.
Open Script Editor: Tools / Script editor.
Add the code. It can be configured by adjusting variables in the top:
var trackerSheetName = 'Tracker 1'
var trackerSheetStatusColumnIndex = 2
var trackerSheetNameColumnIndex = 4
var triggeringStatusValue = 'Urgent'
var peopleSheetName = 'AUX'
var peopleSheetNameColumnIndex = 1
var peopleSheetEmailColumnIndex = 2
var emailSubject = 'We need your attention'
var emailBody = 'It is urgent'
function checkStatusUpdate(e) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
var activeSheet = spreadsheet.getActiveSheet()
// skip if different sheet edited
if (activeSheet.getName() !== trackerSheetName) {
return
}
var editedRange = e.range
// skip if not a single cell edit
if (editedRange.columnStart !== editedRange.columnEnd || editedRange.rowStart !== editedRange.rowEnd) {
return
}
// skip if edited cell is not from Status column
if (editedRange.columnStart !== trackerSheetStatusColumnIndex) {
return
}
// skip if Status changed to something other than we're looking for
if (e.value !== triggeringStatusValue) {
return
}
var assigneeName = activeSheet.getRange(editedRange.rowStart, trackerSheetNameColumnIndex, 1, 1).getValue()
var peopleSheet = spreadsheet.getSheetByName(peopleSheetName)
var people = peopleSheet.getRange(2, 1, peopleSheet.getMaxRows(), peopleSheet.getMaxColumns()).getValues()
// filter out empty rows
people.filter(function (person) {
return person[peopleSheetNameColumnIndex - 1] && person[peopleSheetEmailColumnIndex - 1]
}).forEach(function (person) {
if (person[peopleSheetNameColumnIndex - 1] === assigneeName) {
var email = person[peopleSheetEmailColumnIndex - 1]
MailApp.sendEmail(email, emailSubject, emailBody)
}
})
}
Save the code in editor.
Open Installable Triggers page: Edit / Current project's triggers.
Create a new trigger. Set Event Type to On edit. Keep other options default.
Save the Trigger and confirm granting the script permissions to access spreadsheets and send email on your behalf.
Go back to your spreadsheet and try changing status in Tracker 1 tab for any of the rows. Corresponding recipient should receive an email shortly.
This should get you started:
You will need to create an installable trigger for onMyEdit function. The dialog will help you to design you email by giving you an html format to display it. When you're ready just comment out the dialog and remove the // from in front of the GmailApp.sendEdmail() line.
function onMyEdit(e) {
//e.source.toast('Entry');
const sh=e.range.getSheet();
if(sh.getName()=="Tracker") {
if(e.range.columnStart==2 && e.value=='Urgent') {
//e.source.toast('flag1');
const title=e.range.offset(0,-1).getValue();
const desc=e.range.offset(0,1).getValue();
const comm=e.range.offset(0,3).getValue();
if(title && desc) {
var html=Utilities.formatString('<br />Task Title:%s<br />Desc:%s<br />Comments:%s',title,desc,comm?comm:"No Additional Comments");
//GmailApp.sendEmail(e.range.offset(0,2).getValue(), "Urgent Message from Tracker", '',{htmlBody:html});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html).setWidth(600), 'Tracker Message');
e.source.toast('Email Sent');
}else{
e.source.toast('Missing Inputs');
}
}
}
}
GmailApp.sendEmail()
so I have created a small script here for my google sheets. Since google sheets doesn't allow you to use password protection on individual sheets, I was wondering if there was a way to protect my script with a password so that only certain people can use it. Here is my code.
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Custom Menu')
.addItem('Record', 'Record')
.addItem('Cancelation', 'Cancel')
.addToUi();
}
function Record() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Nightly Stats'),
row = sheet.getLastRow()
range = sheet.getRange("A3:G3");
sheet.insertRowAfter(row);
range.copyTo(sheet.getRange(row + 1, 1), {contentsOnly:true});
}
I would greatly appreciate any suggestions that you can provide.
Ok so I actually figured out how to do a password system via prompting. Here was what I did in case anyone needs this in the future.
function Cancel() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
// first prompt
var presult = ui.prompt(
"Please Enter the Password to Use this Feature.",
ui.ButtonSet.OK_CANCEL);
var password = "Test";
var pbutton = presult.getSelectedButton();
var ptext = presult.getResponseText();
// User clicked "OK" on first prompt
if (pbutton == ui.Button.CANCEL) {
ui.alert('The Process Was Ended.');
} else if (pbutton == ui.Button.CLOSE) {
ui.alert('The Process Was Ended.');
} else if (ptext != password) {
Password();
} else {
"Insert whatever action you would want them to do after the password works here"
}
}
function Password() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var response = ui.alert("The Password is Incorrect. Retry?",
ui.ButtonSet.OK_CANCEL);
if (response == ui.Button.CANCEL) {
ui.alert("The Process Was Ended.");
} else if (response == ui.Button.CLOSE) {
ui.alert("The Process Was ended.");
} else {
Cancel();
}
}
I only gave a piece of the code so sorry if it looks a little weird. I just didn't want to give the whole code and make you search for everything. Hope that helps :)
It is a trick but it's useful to free account user!
Use onOpen() function and little code below.
var inputPassword;
function checkPassword(){
var refAddress = 'https://script.google.com/d/' + ScriptApp.getScriptId() + '/edit';
var curAddress = refAddress;
Logger.log(curAddress);
var referPassword = 1234;
if( refAddress == curAddress){
while(referPassword != inputPassword){
inputPassword = Browser.inputBox('[Password Check]', '[Input Password!]', Browser.Buttons.OK_CANCEL);
}
}
}
Refer to my blog for more information.
So you're trying to protect the script itself?
Host the script in a different location and use a library:
https://developers.google.com/apps-script/guides/libraries
I have to check weather a certain property exists before I can run the function listed in the add-on menu for a google spreadsheet. Rather than creating a copy of the same check for each function I would like to create a single function that I can pass the function to run as a parameter. How can I do this?
Below is my non functioning test code, but you may get the idea.
function testRun(){
//in practicality this would be an add-on menu
test1Check('test1()');
}
function test1(){
Logger.log("Function Ran");
}
function test1Check(functionToRun){
var labels = PropertiesService.getDocumentProperties().getProperty('labels');
var ui = SpreadsheetApp.getUi(); // Same variations.
if (!labels) {
var result = ui.alert(
'You have not yet set up the page for gClassFolders',
'Would you like to do this now?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
setupGCF();
}
}
else {
functionToRun;
}
}
I had to remove the () from the parameter sent, and add () to the variable in the function.
function testRun(){
test1Check(test1);
}
function test1(){
Logger.log("Function Ran");
}
function test1Check(functionToRun){
var labels = PropertiesService.getDocumentProperties().getProperty('labels');
var ui = SpreadsheetApp.getUi(); // Same variations.
if (!labels) {
var result = ui.alert(
'You have not yet set up the page for gClassFolders',
'Would you like to do this now?',
ui.ButtonSet.YES_NO);
// Process the user's response.
if (result == ui.Button.YES) {
// User clicked "Yes".
setupGCF();
}
}
else {
functionToRun();
}
}