Deleting specific data from multiple Google Sheets - google-apps-script

I am trying to clear data from multiple sheets. I have a code that works for the current active sheet and data ranges I need, but I can not for the life of me figure out how to make it clear the same data from multiple sheets. Here is the code:
// This section adds the top menu
function onOpen() {
SpreadsheetApp.getUi().createMenu('Reset')
.addItem("Formulas Only", "ResetFormulas")
.addItem("Entire Page", "ResetPage")
.addToUi()
}
// This section makes a button to reset the whole page copies the entire page
function ResetPage() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Data1'); // Enter tab name of what you’re copying from
sheet.getRange('A1:U39').copyTo(ss.getRange('A1:U39')) //enter entire tab range
}
// This section copies only the cells you specify
function ResetFormulas() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet12'); // Enter tab name here of what you’re copying
from
//Header
sheet.getRange('B6:E20').copyTo(ss.getRange('B6:E20')) //first range you’re copying
sheet.getRange('N6:Q20').copyTo(ss.getRange('N6:Q20')) //next range
sheet.getRange('B25:E39').copyTo(ss.getRange('B25:E39')) //etc
sheet.getRange('N25:Q39').copyTo(ss.getRange('N25:Q39')) //you get it
}
What specifically would I change to make it clear multiple sheets rather than the active one?

The line sheet.getRange('A1:U39').copyTo(ss.getRange('A1:U39')) specifies that data shall be copied to the range 'A1:U39' in the active spreadsheet
It doe not specify in which sheet the range is located, this is why Apps Script assumes that you want to copy into the active sheet
If you want to chose instead a different sheet - you need to specify the sheet
Sample:
function ResetPage() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Data1'); // Enter tab name of what you’re copying from
var sheet2 = ss.getSheetByName('Data2'); // Enter tab name of what you’re copying from
sheet.getRange('A1:U39').copyTo(sheet2.getRange('A1:U39')) //enter entire tab range
}

Related

Trigger when change is happening in specific sheet

I have a sheet with data that is imported through IMPORTRANGE to another sheet. When I make changes to Spreadsheet 1 >>> Spreadsheet 2 I have a script that copy the data from Copy to Paste. It is working all fine however I want to make sure that the script only runs when changes are made in the 'Copy' sheet and not any other. At the moment it runs independent on what sheet I make changes in.
Spreadsheet 1
Spreadsheet2
I tried onChange trigger two different ways...
This one makes the change but triggers when changes are made in any of the sheets
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
if (sheet.getName() === 'Copy') {
copyInfo();
}
}
AND
(this does not work)
function Trigger2(e) {
var sheet = e.source.getActiveSheet();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
if( sheet.getActiveSheet().getName() !== "Copy" ) return;{
copyInfo();
}}
The copy code looks like this...
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
// get source range
var source = copySheet.getRange(2,1,12,8);
// get destination range
var destination = pasteSheet.getRange(2,1,12,8);
// copy values to destination range
source.copyTo(destination);
}
Copy Changes from one Sheet To Another after Imported Range Changes
Spreadsheet 2 Imports a range to Sheet1 from Spreadsheet 1 Sheet1 and when a change occurs in Spreadsheet 2 Sheet1 we copy data from Spreadsheet2 Sheet1 to Spreadsheet2 Sheet 2.
function onMyChange(e) {
//Logger.log(JSON.stringify(e));//useful during setup
//e.source.toast("Entry");//useful during setup
const sh = e.source.getSheetByName("Sheet1")
Logger.log(sh.getName());
if(e.changeType == "OTHER") {
let tsh = e.source.getSheetByName("Sheet2");
sh.getRange(2,1,12,8).copyTo(tsh.getRange(2,1));//Only need the upper left hand corner of the range. Thus you change change the size of the source data without having to change the target range.
}
}
One might be inclined to attempt to use e.source.getActiveSheet() unfortunately this does not necessary get the sheet that you have written unless in is SpreadsheetApp.getActive().getSheets()[0]. onChange event object always returns the most left sheet in the spreadsheet in this situation so it can't be used to identify the sheet that is being written to from the importRange function.
With this function you no longer require another function.
A simple if should work, but you're trying to compare the name to the wrong field. According to Google's documentation, the source field in the Event Object e returns the Spreadsheet object, which is the entire document.
Instead you can use the e.range field, which contains the exact range where the edit was made, and the Range class has a getSheet() method to get the parent Sheet.
So you can modify your sample to the following:
function Trigger2(e) {
var sheet = e.range.getSheet();
if (sheet.getName() === 'Copy') {//Edit: this will only work if sheet is the most left sheet in the spreadsheet
copyInfo();
}
}

Can I protect the name of a Google sheet? Or reference the sheet by position rather than name?

I manage a Google spreadsheet that has multiple sheets. I use scripts to automate some processes on the first sheet. Recently, a user accidentally renamed the first sheet -so the script stopped working until I found out and corrected the name of the sheet.
My question is: how can I protect against this happening again? Is there a way to protect the sheet name from being changed by users other than myself? Or, in the script, is there a different way to reference the first sheet, regardless of its name? I.e., instead of sheet.getSheetName is there something like sheet.getFirst that returns the first sheet, regardless of its name?
Here's a sample of one script that is being affected.
function onOpen(){
//Sheet where this script should run
var SHEETNAME = "Scan IN Check OUT"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if (sheet.getSheetName() == SHEETNAME ) { //Check we are on the correct sheet
var sheet = SpreadsheetApp.getActiveSheet();
//Find the last cell with data in that specific column (A in this case)
var lastCell = sheet.getRange('A1').getNextDataCell(SpreadsheetApp.Direction.DOWN);
//Activate the next cell
lastCell.offset(1, 0).activate();
}
}
SpreadsheetApp.getActive().getSheets()[0]; will always be the first sheet on the left which may not always be the same sheet since users can change their position. You can use the first function below to see how the indexes change as you move sheets around. WARNING: don't do this in a spreadsheet that contains important information because it first clears the sheet and then writes the name, the id's and the indexes for all sheets.
So you can also use the first function to get the id of the sheet that you want not to change.
The second function will get the sheet by id. If you will select the id of your sheet and replace the string 'My Sheet ID' with it and also put the correct name of your sheet where it says my sheet name, then when you runTwo() it will move you sheets all the way to left (ie index 1) and rename to whatever you want.
function runOne() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var sht=ss.getSheets();
sh.clear();
sh.appendRow(['Name','id','index']);
sht.forEach(function(s){
sh.appendRow([s.getName(),s.getSheetId(),s.getIndex()]);
});
}
function getSheetById(id) {
var id=id||'My Sheet ID';
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
for(var i=0;i<shts.length;i++) {
if(shts[i].getSheetId()==id) {
return shts[i];
}
}
}
function runTwo() {
var ss=SpreadsheetApp.getActive();
var sh=getSheetById().activate();
ss.moveActiveSheet(1);
sh.setName('My Sheet Name');
}
Of course the real problem here is that if the user can change the name of your sheet they can also go in and change your script.

Unwanted multiple row entries / copies (Google Form --> Google Sheet file, sheet1 --> Google Sheet file, sheet 2)

I've written an apps script in GSheet to:
Copy the last row of the Google Form sheet in Google Sheet file if a new entry arrived (trigger) by Google Form
Paste this row to another sheet in the same GSheet file to the last row
Unfortunately it copies the last row of the Form sheet more than one time to the appropriate sheet. Sometimes two times, sometimes four times. I cannot see my mistake in the code.
I couldn't find a solution so far. I included a pause before appendRow with Utilities.sleep(5000) but w/o effect.
function myFunction() {
// Source sheet: Form is the source sheet of the Google Form
var source =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form");
// Get last row in Form
var lastrow = source.getLastRow();
// Get just the date of the Form input
var formdate = source.getRange(lastrow,7,1,7).getValue();
// Change month number to string (e.g. April)
var currentD2 = Utilities.formatDate(formdate,
Session.getScriptTimeZone(), "MMMMM");
// Identify target sheet in same Google sheet file
var target =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(currentD2);
// Identify the appropriate range of the last row of the Form sheet
// (source)
var sourceData = source.getRange(lastrow, 2,1,8).getValues();
// Append the last row of the Form sheet to the last row in the target
// sheet
target.appendRow(sourceData[0])
}
I expect that the function copies just one time the last row of the Form sheet and not multiple times.
Hello I guess that you are using an onEdit trigger to start your function.
That means that when you change the "Google Form sheet" it triggers your function which changes another sheet in the same spreadsheet which starts your trigger again.
I recommend to rename your function onEdit(e)
ref(https://developers.google.com/apps-script/guides/triggers/events)
and then condition your action with: if e.range belongs to your "google form sheet" then do something otherwise not.
function onEdit(e) {
if (e.range.getSheet().getName()='your google form sheet') {
// insert here the contents of your function
}
}
you can use either this :
function appendEntry(){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var ws = ss.getSheetByName("data") // this is the sheet where new entry arrive
var ws2 = ss.getSheetByName("target") // this is the sheet where the entries will go
var data = ws.getDataRange().getValues()
var data2 = ws2.getDataRange().getValues()
for(var i=0;i<data.length;i++){
var row = data[i]
for(var k=0;k<data2.length;k++){
var row2 = data[k]
if(row2[0] != row[0]){
} // This will loop through both sheet
// If entry is already mentioned in other sheet the data will not append
}
}
ws2.appendRow(data[k]) //other wise entry will append to the next sheet
}

Google Apps Script to Create a Copy of, Rename, and then Reset a Spreadsheet

I've designed a spreadsheet for tracking status of systems in an enterprise.
It gets updated every 12 hours and then manually renamed based on the date and report number, then it gets archived.
I have created a script that allows the user to create a copy of the current spreadsheet by pressing on a drawing
function copyDocs() {
for(i=0; i<1; i++){
var drive=DriveApp.getFileById('190X72ZGwHtKaUa5MQerlW2k1ops-zYTnr1UXwuweJxGY');
drive.makeCopy();
}
}
I have created a script which allows users to reset all the data fields to empty by pressing on an drawing.
function reset() {
var s = SpreadsheetApp.getActive().getSheetByName('STATUS');
s.getRange("a2:b4").clearContent();
s.getRange("c3:d4").clearContent();
s.getRange("e2:f4").clearContent();
s.getRange("b5").clearContent();
s.getRange("e5:f5").clearContent();
s.getRange("c6:d6").clearContent();
s.getRange("f6").clearContent();
s.getRange("c7:f10").clearContent();
s.getRange("a12:f18").clearContent();
s.getRange("b20:f25").clearContent();
s.getRange("b27:f31").clearContent();
s.getRange("b33:f37").clearContent();
s.getRange("b39:f39").clearContent();
s.getRange("b41:f46").clearContent();
s.getRange("b48:f52").clearContent();
s.getRange("b54:f58").clearContent();
s.getRange("b60:f60").clearContent();
s.getRange("b62:f79").clearContent();
s.getRange("b81:f81").clearContent();
s.getRange("d83:f91").clearContent();
s.getRange("d94:f96").clearContent();
s.getRange("d98:f105").clearContent();
s.getRange("d107:f109").clearContent();
s.getRange("c112:f123").clearContent();
s.getRange("b113:f113").clearContent();
s.getRange("b115:f115").clearContent();
s.getRange("b117:f117").clearContent();
s.getRange("b119:f119").clearContent();
s.getRange("b121:f121").clearContent();
s.getRange("b123:f123").clearContent();
s.getRange("d124:f124").clearContent();
}
What I would like to do is to have one "button" for the user that they press and it runs four scripts in succession. I have tried to think through the best way to make this happen and here is what I need help with.
Rename the Spreadsheet they are currently working on to the data in merged cells (A2:B4) it will be a number like "17-087"
Make a copy of the whole spreadsheet that will automatically save in the same folder
Clear data from the specific cells in the "reset" script above
Rename the current spreadsheet from the data in cell 'J2'
In theory the user will press a button, it will rename the spreadsheet (not the sheet, but the whole file) to the data in the merged cell ie. "status 17-087", then it will make a copy of the whole Spreadsheet (file), then clear all the designated cells, then rename the spreadsheet back to the data in 'J2' which is "STATUS TEMPLATE".
Can anyone help me work through this multi-step script?
Thanks in advance.
Could try something like this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
function onOpen() {
// Add a menu item based on properties (doesn't work in AuthMode.NONE).
var items = [
{name: 'Backup', functionName: 'backup'},
];
ss.addMenu('Backup', items);
}
function backup() {
rename();
copyDocs();
reset();
rename2();
}
function rename(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("Sheet1");
// renames the google sheet to the data that is in 2nd Row, 3rd Column
ss.rename(first.getRange(2, 3).getValue());
}
function copyDocs() {
for(i=0; i<1; i++){
var drive=DriveApp.getFileById('190X72ZGwHtKaUa5MQerlW2k1ops-zYTnr1UXwuweJxGY');
drive.makeCopy();
}
}
function reset() {
var s = SpreadsheetApp.getActive().getSheetByName('STATUS');
s.getRange("a2:b4").clearContent();
s.getRange("c3:d4").clearContent();
s.getRange("e2:f4").clearContent();
s.getRange("b5").clearContent();
s.getRange("e5:f5").clearContent();
s.getRange("c6:d6").clearContent();
s.getRange("f6").clearContent();
s.getRange("c7:f10").clearContent();
s.getRange("a12:f18").clearContent();
s.getRange("b20:f25").clearContent();
s.getRange("b27:f31").clearContent();
s.getRange("b33:f37").clearContent();
s.getRange("b39:f39").clearContent();
s.getRange("b41:f46").clearContent();
s.getRange("b48:f52").clearContent();
s.getRange("b54:f58").clearContent();
s.getRange("b60:f60").clearContent();
s.getRange("b62:f79").clearContent();
s.getRange("b81:f81").clearContent();
s.getRange("d83:f91").clearContent();
s.getRange("d94:f96").clearContent();
s.getRange("d98:f105").clearContent();
s.getRange("d107:f109").clearContent();
s.getRange("c112:f123").clearContent();
s.getRange("b113:f113").clearContent();
s.getRange("b115:f115").clearContent();
s.getRange("b117:f117").clearContent();
s.getRange("b119:f119").clearContent();
s.getRange("b121:f121").clearContent();
s.getRange("b123:f123").clearContent();
s.getRange("d124:f124").clearContent();
}
function rename2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("Sheet1");
// renames the google sheet to the data that is in J2
ss.rename(first.getRange(2, 10).getValue());
}

How do I deselect an active cell when clicking on an image to run a script?

I have a spreadsheet-bound script that is invoked by clicking an image in the spreadsheet. I've found that the script can be blocked if a cell that it needs to modify is active or being edited by the user.
The code could be anything, even as something as simple as this:
function newf() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("R4");
range.clear();
}
When the cell R4 is open, the function will ignore the cell.
How do I deselect an open cell when clicking on an image to run a script?
The easy (manual) solution would be to click off the open cell prior to clicking on the image, but I am trying to make the script fool-proof and this can be a costly concern. Is there any way that the function can forcefully deselect an active cell?
Edit: I do not mean active cell. Definitely "open cell." Here are pictures of the error.
Open cell with data being entered
Clicked on Picture, program ran
You can use Sheet.setActive() to move the attached user's cursor to a different location in the spreadsheet. You need to remember that there is a relationship between the User Interface and scripts invoked from it, which is what allows the script to determine and change the currently active cell. This does not extend to the multiple user case, though - if User1 is editing the guarded cell when User2 clicks to run the script, the script will have no idea about it.
function newf() {
var safePlace = "A1"; // Some safe cell
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var activeCell = ss.getActiveCell();
if (activeCell.getSheet() == sheet && activeCell.getA1Notation() == "R4")
sheet.setActiveSelection(safePlace);
var range = sheet.getRange("R4");
var activeCell = ss.getActiveCell();
if (activeCell.getA1Notation() == range.getA1Notation() && activeCell.getSheet().getName() == range.getSheet().getName()) {
// Move user from target cell
sheet.setActiveSelection(safePlace);
range.clear();
}
I had the same problems, both the initial one and with the solution proposed by Mogsdad.
I resolved it the following way:
Before running any of the script, the first three lines get a different sheet opened and the spreadsheet gets flushed:
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('DIFFERENT SHEET NAME'), true);
SpreadsheetApp.flush();
The rest of the script follows.
Then at the end of the script, I reopen the initial sheet with a simple:
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('ORIGINAL SHEET NAME'), true);
Another "solution" to this is not to use a button, but use a check mark as if it we a button, and use the onEdit function.
function onEdit(e)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
checkbox1 = ss.getRange("saveCheckbox_001");
if (checkbox1.isChecked())
{
submit1(); //do all the fun stuff associated with this checkbox
checkbox1.uncheck();
}
//more checkbox handling and calling other functions can go here if you need more than one "button"
}