How to create a button which clears data in spreadsheets - google-apps-script

Currently I have a google spreadsheet document in which users are inputting data based on conditional formatting the cells are changing colors. I would like to create a button with which I can easy restore all the cells into their first state.
I have this script at the moment:
function clearRange() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
sheet.getRange('B13:B30').clearContent();
}
Which works like a charm but the thing is that I have default data in the cells and when this scripts get applied it clears the default data.
Any ideas how to make such a script which will clear only the inputted data by the user.

It's not the solution you want but apart from what Scott suggested, which is the most straightforward approach, you could make a copy of your sheet in the unedited state. In this copy have a script which will just copy and paste the info into the sheet that the user fills in.
Then if you make changes you just need to update the template sheet and not the script.
This is a very basic script but should you'll get the idea.
function myFunction() {
var app = SpreadsheetApp;
var tempSheet = app.getActiveSpreadsheet().getSheetByName('Sheet1');
var inputSheet = app.openById('-----ID OF INPUT SHEET----').getSheetByName('Sheet1');
var tempData = tempSheet.getDataRange().getValues();
inputSheet.getRange(1, 1, tempData.length,
tempData[1].length).setValues(tempData);
}

Related

Google sheets appscript to copy tabs to new sheets

I have a google sheet with around 190 tabs on that i need to split into 190 different files
The files need to be named the same as the tab, the contents of the tab need to be copied as values but i also need to bring the formatting accross (just not the formulas).
I have looked around, and through a combination of previous questions and answers plus using the function list help have formed the following code. It actually works for the first few tabs but then throws up an error about being unable to delete the only sheet.
function copySheetsToSS() {
var ss = SpreadsheetApp.getActive();
for(var n in ss.getSheets()){
var sheet = ss.getSheets()[n];// look at every sheet in spreadsheet
var name = sheet.getName();//get name
if(name != 'master' && name != 'test'){ // exclude some names
var alreadyExist = DriveApp.getFilesByName(name);// check if already there
while(alreadyExist.hasNext()){
alreadyExist.next().setTrashed(true);// delete all files with this name
}
var copy = SpreadsheetApp.create(name);// create the copy
sheet.copyTo(copy);
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
copy.getSheets()[0].setName(name);// rename first sheet to same name as SS
var target_sheet = copy.getSheetByName(name);
var source_range = sheet.getRange("A1:M50");
var target_range = target_sheet.getRange("A1:M50");
var values = source_range.getValues();
target_range.setValues(values);
}
}
}
I am hoping someone can tell me what i have done wrong as I cannot figure it out at this point. I am also open to better solutions though please be aware I am very much a beginner on google appscript, nothing too complex please.
thankyou
In principle your script correctly adds a new sheet to the new spreadsheet before removing the preexisting one
However, mind that calls to service such as SpreadsheetApp are asynchronous.
And this becomes the more noticeable, the longer your script runs.
In your case it apparently leads to behavior that the only sheet is being deleted before the new sheet is being created.
To avoid this, you can force the execution to be synchronous by implementing calls to SpreadsheetApp.flush().
This will ensure that the old sheet won't be deleted before the new one gets inserted.
Sample:
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
SpreadsheetApp.flush();
copy.getSheets()[0].setName(name);
You might want to introduce call toflush()` also at other positions where it is important for the code to run synchronously.

Programmatically delete a script

I was wondering if someone could offer me some advice.
I have a master spreadsheet, acting as a template. I have written a script which can be run from the menu (using addToUi command), which makes a copy of template spreadsheet.
The problem is that the script gets copied into the new spreadsheet also which I don't want.
Could anyone suggest a possible way around this please?
I did think a possible way was to get the script to open the copied template and delete the script but not sure if this is possible.
Here is the function which does the copying....
function createCopy() {
var myValue = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange("B8").getValue();
var destinationFolder = DriveApp.getFolderById("xxxxxxxxxxxxxxxx");
DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()).makeCopy(myValue,destinationFolder);
}
(Cell reference B8 holds the value of what I called the copied spreadsheet).
Rayden, I use a function like that to just copy one sheet to a new spreadsheet and it doesn't drag the script with it. gDrive is the id for the Spreadsheet, tabName the individual sheet you want copied, Filename the name of the copy and destination the destination directory.
//******************************************************************************
//- This function takes a tab and makes it its own file
function tabToSheet(gDrive,tabName,fileName,destination){
var sh = SpreadsheetApp.openById(gDrive);
var ss = sh.getSheetByName(tabName);
//create a new document in the location given
var newSheet = SpreadsheetApp.create("TEMPDELETEME");
//copy the tab from current document to new document
ss.copyTo(newSheet);
var id = newSheet.getId();
newSheet.deleteSheet(newSheet.getSheetByName("Sheet1"));
os = newSheet.getSheets()[0];
os.setName(tabName);
var file = DriveApp.getFileById(id);
var folder = DriveApp.getFolderById(destination);
var finalId = file.makeCopy(fileName, folder).getId();
file.setTrashed(true);
return finalId;
}//*****************************************************************************
The difference is that i'm making a new sheet, then copying the tab rather than copying the entire file. You could add another tab and remove the variables if you want to copy multiple tabs.
I was having trouble implementing J.G.'s approach of moving individual sheets, so the approach I took was to add a conditional in front of the script to only run if the spreadsheet id was equal to the id of the original spreadsheet. My use case was trying to suppress a custom menu on the original workbook from reappearing on the copy, so it worked for that.
var bookId = spreadsheet.getId();
if (bookId === 'original spreadsheet id') {
*Function*
}

Google Sheets as Database

I'm trying to enter some data on a sheet in google sheets create a button that submits data onto another sheet. The other sheet will be like my database.
I have little to no JS experience. I'm able to create the button and link it my script but after that, I'm lost. This code worked well to get data to my database sheet. The problem is that the data stays on the same row and when I run the script the old data is erased.
function transfer() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
var range1 = source.getRange("B2");
range2.copyValuesToRange(destination,3,3,2,2);
range2.clearContent();
var range2 = source.getRange("C2");
range2.copyValuesToRange(destination,4,4,2,2);
range2.clearContent();
var range3 = source.getRange("D2");
range3.copyValuesToRange(destination,5,5,2,2);
range3.clearContent();
var range4 = source.getRange("C2");
range4.copyValuesToRange(destination,4,4,2,2);
range4.clearContent();
}
The other issue is I don't want the cells to be empty so I tried to set an alert.
var range1 = source.getRange("A2");
range1.copyValuesToRange(destination,2,2,2,2);
if (range1 ==!"");
Browser.msgBox("Please Enter A Date");
It prompted the msg box but still copied the data over.
Last I would like range1 to be like a unique ID. So if I put a value in A2 on my source sheet then it will auto-fill the other cells.
Here's the link if that helps.
https://docs.google.com/spreadsheets/d/1Zi7Oc0f5AlxcRRoFMM1Q1VkkM1Co6Yo8yYBY1TvkQMc/edit?usp=sharing
I recommend that you spend more time with the documentation. Personally, I've never considered putting buttons directly on the spreadsheet. I'd rather use the menu or a dialog or a sidebar. Also you generally pick up a lot of performance to replace the use of getValue() with getValues() where appropriate.
But here's another way to approach the problem your working on. Perhaps it will give something to think about. Have fun. Your buttons do look nice though. Very nice work.
function onOpen(){//This will put a post button on a menu
SpreadsheetApp.getUi().createMenu('My Menu')
.addItem('Post Data', 'postData')
.addToUi();
}
function postData() {//this will append data fromm 'DataEntry' to 'DB' with a timestamp spliced into it.
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('DataEntry');
var sh2=ss.getSheetByName('DB');
var rg1=sh1.getRange(sh1.getLastRow(),1,1,sh1.getLastColumn());
var vA=rg1.getValues();
vA[0].splice(0,0,Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"E MMM dd, yyyy HH:mm:ss"));
sh2.appendRow(vA[0]);
}
This is what my DateEntry tab looks like:
This is what my DB tab looks like:
The post just adds the last row in DataEntry to the row after the last row in DB.

Can I add a formula to a google form response using Apps Script?

Apologies as I am a beginner to coding. I am interested in using Google Apps Script to automate the analysis of a Google Form response.
The simple example I have is for the spreadsheet of responses for a form asking people:
1) how many players there were?
2) where they finished [1st, 2nd, etc,]
On submission of the form I want to run a script that calculates how may points they received and inserts this value in the next available column (column E in this example).
I have tried writing my first Apps Script to automate this process, but without success.
SAMPLE CODE:
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form Responses Master");
var players = e.values[2];
var place = e.values[3];
var positionPoints = e.values[4];
var positionPoints = (players - place + 1);
return positionPoints;
}
I know there are workarounds available by creating duplicate pages, but I was hoping someone might be able to advise me on how to code a solution in App Scripts, in the hope I might get a better understanding of the coding process.
You can write your appscript in the response collector spreadsheet itself instead of writing your code in form's script editor.
So, go to your response sheet and paste this code.
function myFunction()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses Master");
var rowNo = sheet.getLastRow();
var colNo = sheet.getLastColumn();
sheet.getRange(rowNo, colNo).setValue("=(C"+rowNo+"-D"+rowNo+")+1");
}
Now, go to Resources -> Current project triggers. Click on add new and set these values in drop downs: myFunction - From Spreadsheet - On form submit.
And you are done. Whenever a new response is submitted, position points will be calculated automatically.
Here,
variable sheet gets your active spreadsheet for different sheet operations which you can perform.
rowNo and colNo as seen in the code, simply fetches the value of last row/column respectively of spreadsheet in which something is written.
And, to set formula in column 'E', you can use setValue. Hence, "=(C"+rowNo+"-D"+rowNo+")+1" will be converted to (C2-D2)+1 in case of second row and so on for next rows.
getRange is simply used to tell the script that write formula inside particular cell.

Replace entire sheet with another in Google Apps Scripts

What I'd like to do is warehouse information from a particular sheet within a spreadsheet and copy it to a second spreadsheet at the end of every day. The second spreadsheet will run complex pivots and reports against the copied information that don't need to run throughout the day.
I can set up a time-driven trigger which will run the job every day within an hour block.
I'm working on the following script which uses SpreadsheetApp.getActiveSpreadsheet to get the current Spreadsheet. Then gets the individual sheet to backup with spreadsheet.getSheetByName. And then uses the sheet.copyTo method to add the current sheet to a new spreadsheet. I'm getting the new spreadsheet by looking up the id with SpreadsheetApp.openById all like this:
function startBackupJob() {
var currentSpreadSheet = SpreadsheetApp.getActiveSpreadsheet()
var masterSheet = currentSpreadSheet.getSheetByName("Sheet1")
var backupSpreadSheetId = "#######################################";
var backupSpreadSheet = SpreadsheetApp.openById(backupSpreadSheetId);
// var backupSheet = backupSpreadSheet.getSheetByName("Sheet1");
// backupSpreadSheet.deleteSheet(backupSheet);
masterSheet.copyTo(backupSpreadSheet).setName("Sheet1");
}
The issue I'm having is that copyTo will create a new worksheet rather than overwrite the existing spreadsheet. The point of moving to the new workbook is to run pivot tables off the data and not re-wire them to point to a new sheet.
I can delete the previous sheet to make room for the new one, but this kills the references on the PivotTable as well, so it doesn't help much.
Is there an easy way to transfer the entire contents of one worksheet to another?
This is similar to (but different from) the following questions:
How do I script making a backup copy of a spreadsheet to an archive folder? - However, I don't want to move the whole file, but a specific sheet within the spreadsheet.
How can copy specifics sheet to another spreadsheet using google script & copy one spreadsheet to another spreadsheet with formatting - However copying produces a new sheet, whereas I need to replace the contents of an existing sheet
Scripts, copy cell from one sheet to another sheet EVERYDAY at a specific time - However, I do want to replace the entire sheet, rather than just specific cells within the sheet.
Update
I might be able to do this by calling getRange on each sheet and then using getValues and setValues like this:
var currentValues = masterSheet.getRange(1, 1, 50, 50).getValues()
backupSheet.getRange(1, 1, 50, 50).setValues(currentValues)
But I'm worried about edge cases where the master sheet has a different available range than the backup sheet. I also don't want to hardcode in the range, but for it to encompass the entire sheet. If I call .getRange("A:E") then the two worksheets have to have the exact same number of rows which is not likely.
Your update has you about 90% of the way there. The trick is to explicitly check the size of the destination sheet before you copy data into it. For example, if I did something like this:
var cromulentDocument = SpreadsheetApp.getActiveSpreadsheet();
var masterSheet = cromulentDocument.getSheetByName('master');
var logSheet = cromulentDocument.getSheetByName('log');
var hugeData = masterSheet.getDataRange().getValues();
var rowsInHugeData = hugeData.length;
var colsInHugeData = hugeData[0].length;
/* cross fingers */
logSheet.getRange(1, 1, rowsInHugeData, colsInHugeData).setValues(hugeData);
...then my success would totally depend upon whether logSheet was at least as big as masterSheet. That's obvious, but what's less so is that if logSheet is bigger then there will be some old junk left over around the edges. Ungood.
Let's try something else. As before, we'll grab the master data, but we'll also resize logSheet. If we don't care about logSheet being too big we could probably just clear() the data in it, but let's be tidy.
var cromulentDocument = SpreadsheetApp.getActiveSpreadsheet();
var masterSheet = cromulentDocument.getSheetByName('master');
var logSheet = cromulentDocument.getSheetByName('log');
var hugeData = masterSheet.getDataRange().getValues();
var rowsInHugeData = hugeData.length;
var colsInHugeData = hugeData[0].length;
/* no finger crossing necessary */
var rowsInLogSheet = logSheet.getMaxRows();
var colsInLogSheet = logSheet.getMaxColumns();
/* adjust logSheet length, but only if we need to... */
if (rowsInLogSheet < rowsInHugeData) {
logSheet.insertRowsAfter(rowsInLogSheet, rowsInHugeData - rowsInLogSheet);
} else if (rowsInLogSheet > rowsInHugeData) {
logSheet.deleteRows(rowsInHugeData, rowsInLogSheet - rowsInHugeData);
}
/* likewise, adjust width */
if (colsInLogSheet < colsInHugeData) {
logSheet.insertColumnsAfter(colsInLogSheet, colsInHugeData - colsInLogSheet);
} else if (colsInLogSheet > colsInHugeData) {
logSheet.deleteColumns(colsInHugeData, colsInLogSheet - colsInHugeData);
}
/* barring typos, insert data with confidence */
logSheet.getRange(1, 1, rowsInHugeData, colsInHugeData).setValues(hugeData);
What's going on here is pretty straightforward. We figure out how big the log needs to be, and then adjust the destination sheet's size to match that data.