Copy last row on change - simultaneous entries - google-apps-script

I have a google script that copies the last row on that spreadsheet to another spreadsheet sheet every time an entry is made. This entry is not linked to a Google form at all - so it is not bound to an onFormSubmit trigger - this submission is made from a JotForm form and inserted into the spreadsheet automatically. What I would like to know if my code will always work even if say two users make their submissions at the same time.
The code works fine - there seems to be no problem with that. I use the On change trigger. But I'd like to make sure it would always work. Many thanks for your help in advance.
function copyLastRow() {
var target = SpreadsheetApp.openById('xxxxx').getSheetByName('Records');
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastrow = sheet.getLastRow();
var sourceData = sheet.getRange(lastrow, 1, 1, 10).getValues();
target.appendRow(sourceData[0]);
}

This is kind of the template I imagine you could use in completing this objective.
function updateSpreadsheet() {
//Open Form Responses Spreadsheet and get the Sheet you are reading data from
// You will need to update ID and SheetName parameters for your own sheets.
var formResponses = SpreadsheetApp.openById(ID).getSheetByName(SheetName);
//Open destinationSheet that you are writing to and get the sheet that you are writing to
// You will need to update ID and SheetName parameters for your own sheets.
var destinationSheet = SpreadsheetApp.openById(ID).getSheetByName(SheetName);
//Getting last rows from formResponse and destinationSheet
//It is not totally necessary to create everything as a variable, but I wanted to make it explicit what I am doing here
var formResponsesLastRow = formResponses.getLastRow();
var destinationSheetLastRow = destinationSheet.getLastRow();
//We are going to calculate the difference between the formResponse and destinationSheet
//This lets us know how many more rows have been added since we last updated destinationSheet
var numberNewRows = formResponsesLastRow - destinationSheetLastRow;
//Now get the new data to Write
var dataToWrite = formResponses.getRange((formResponsesLastRow - numberNewRows) + 1 , 1, numberNewRows, 3).getValues();
//Write to destinationSheet
destinationSheet.getRange(destinationSheetLastRow + 1, 1, numberNewRows, 3).setValues(dataToWrite);
}

Related

Copying last form response to new last row other sheet

I am very new to coding with apps script but I am trying to set up a code that transfer the last row of a new google forms spreadsheet submission to another sheet in the same workbook. The reason I am trying to do this is so I can make simple calculations with the data and also edit the data (you can't edit the data with Query functions).
I have one sheet called (Form Responses 1) and one sheet called (Clients). When the form is filled out all the answers are ofcourse automatically transferred to the Form Response 1 sheet. I would like to automatically copy the last row of the Form Response 1 sheet to the Clients sheet so the information can be edited and used in calculations without losing the original submission which will be in Form Responses 1 (not a query).
There are two of the codes I tried with my very basic knowledge and they both do not work. The codes probably suck but any help and advice is greatly appreciated!
Code 1 that I tried
function onFormSubmit(e){
var ss = SpreadsheetApp.getSheetByName("Form responses 1")();
var copyFromRange = 'Form responses 1!A2:M999';
var copyToRangeStart = 'Clients!A2:M999';
copyValuesOnly(copyFromRange, copyToRangeStart);
}
Code 2 that I tried
function onFormSubmit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Form responses 1');
var r = s.getRange(s.getLastRow(),s.getLastColumn());
var data = r.getValues();
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Clients");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
targetSheet.getRange(targetSheet.getLastRow()+1,1,1,20).setValues(data);
I have been browsing the website while trying to figure this out and learn more on scripting, you have all been a great help!
function onFormSubmit(e){
var ss=SpreadsheetApp.getActive();
ss.getSheetByName('Clients').appendRow(e.values);
}
onFormSubmit Event Object
You don't want to get the last row because if submissions come to fast the last row may not be the one from the currrent onFormSubmit trigger.

Google app script trigger not writing data to other sheets in same spreadsheet

I have the following app script associated with a Google Spreadsheet that is accepting data from a Google Form:
function writePatientData() {
var spreadsheet = SpreadsheetApp.openById("<spreadsheet id>");
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//get last row in active/main sheet
var numRows = sheet.getLastRow();
//get last row of data
var last_row = sheet.getSheetValues(numRows, 1, 1, 23);
//get patientID (column V) in last row of sheet
var lastPatientID = sheet.getRange(numRows,3).getValue();
//find patient sheet based on patientID and make it active, then write to it
var patientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
var activePatientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
activePatientSheet.getRange(activePatientSheet.getLastRow()+1, 1,1,23).setValues(last_row);
}
What this script is doing is writing data (a row) to another sheet within this spreadsheet based on the the patientID (column V). This works as it should when I manually run the script. However, when I set a trigger to run this script (either onSubmit or edit) nothing happens. I created another function that just writes a message to the logs and set a trigger for that function and it works, so I think there is something in the script that is causing it to fail. Any ideas appreciated.
There are a few issues with your code. I tried to fix it while commenting each line I changed. Hopefully that is clear enough, please comment if you have any questions and I'll try to clarify.
function writePatientData() {
var spreadsheet = SpreadsheetApp.getActive(); //no need for id if the script is on the same spreadsheet
//var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//setActiveSheet will not work from a trigger like on-form-submit (what if no-one has the sheet open, or multiple have)
var sheet = spreadsheet.getSheets()[0]; //if you want the first sheet, just get it, no need to "activate"
var numRows = sheet.getLastRow();
var last_row = sheet.getSheetValues(numRows, 1, 1, 23)[0]; //added [0] since it is just one row
//var lastPatientID = sheet.getRange(numRows,3).getValue(); //you already have this in memory
var lastPatientID = last_row[2]; //arrays are zero based, that's why 2 instead of 3
//btw, you mention column V, but this is actually C
//var patientSheet = SpreadsheetApp.getActive().getSheetByName(lastPatientID);
//you already have the spreadsheet, no need to get it again
var patientSheet = spreadsheet.getSheetByName(lastPatientID);
//var activePatientSheet = spreadsheet.getSheetByName(lastPatientID); //this is the exact same as above, why?
patientSheet.appendRow(last_row); //appendRow is just simpler than getRange(getLastRow).setValues
}

Improving Apps Script flexibility by using a column of sheet data instead of hard-coded IDs

Background: My coworkers originally each had a worksheet within the same Google Sheets file that makes a lot of calculations (and was getting unusable). Now, everyone has their own (known) Google Sheets file. To run the same calculations, we need to consolidate all that data into a master sheet (image ref below). We tried =importrange(...), but it's too heavy and breaks often (i.e., Loading... and other unfilled cells).
I've written some code to do this import, but right now its only manual: manually repeating the code and manually add the sheet IDs and changing the destrange.getRange(Cell range) each time. We have 80+ analysts, and fairly high turnover rates, so this would take an absurd amount of time. I'm new to Sheets and Apps Script, and know how to make the script use a cell as reference for a valid range or a valid ID, but I need something that can move a cell down and reference the new info.
Example:
Sheet 1 has a column of everyone Sheet ID
Script Pseudocode
get first row's id(Row 1), get sheet tab, get range, copies to active sheet's corresponding row(Row 1).
gets second row's id(Row 2), get sheet tab, get range, copies to active sheet's corresponding row (Row 2)
etc.
My script understanding is way to low to know how to process this. I have no idea what to read and learn to make it work properly.
function getdata() {
var confirm = Browser.msgBox('Preparing to draw data','Draw the data like your french girls?', Browser.Buttons.YES_NO);
if(confirm == 'yes'){
// I eventually want this to draw the ID from Column A:A, not hard-coded
var sourcess = SpreadsheetApp.openById('1B9sA5J-Jx0kBLuzP5vZ3LZcSw4CN9sS6A_mSbR9b26g');
var sourcesheet = sourcess.getSheetByName('Data Draw'); // source sheet name
var sourcerange = sourcesheet.getRange('E4:DU4'); // range
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet(); //
var destsheet = ss.getSheetByName('Master Totals'); //
// This range needs to somehow move one down after each time it pastes a row in.
var destrange = destsheet.getRange('E4:DU4');
destrange.setValues(sourcevalues); // Data into destsheet
}
}
Any suggestions are greatly appreciated!
Thanks to tehhowch for pointing me in the right direction!
function getdata() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Master Totals');
var confirm = Browser.msgBox('Drawing Data','Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO);
if(confirm =='yes'){
var lr = ss.getLastRow();
for (var i = 4; i<=lr; i++) {
var currentID = ss.getRange(i, 1).getValue();
var sourcess = SpreadsheetApp.openByUrl(currentID);
var sourcesheet = sourcess.getSheetByName('Data Draw');
var sourcerange = sourcesheet.getRange('E4:DU4');
var sourcevalues = sourcerange.getValues();
var destrange = destsheet.getRange('E' +i+':'+ 'DU'+ i);
destrange.setValues(sourcevalues);
I just had to learn how to use a variable loop.
Edit: thanks also to Phil for making my question more presentable!
Now that you've figured out one way to do it, I'll offer an alternative that uses batch methods (i.e. is much more time- and resource-efficient):
function getData() {
var wb = SpreadsheetApp.getActive();
var ss = wb.getActiveSheet();
var dest = wb.getSheetByName('Master Totals');
if (!dest || "yes" !== Browser.msgBox('Drawing Data', 'Would you like to update the sheet? It may take 2 to 5 minutes.', Browser.Buttons.YES_NO))
return;
// Batch-read the first column into an array of arrays of values.
var ssids = ss.getSheetValues(4, 1, ss.getLastRow() - 4, 1);
var output = [];
for (var row = 0; row < ssids.length; ++row) {
var targetID = ssids[row][0];
// Open the remote sheet (consider using try-catch
// and adding error handling).
var remote = SpreadsheetApp.openById(targetID);
var source = remote.getSheetByName("Data Draw");
var toImport = source.getRange("E4:DU4").getValues();
// Add this 2D array to the end of our 2D output.
output = [].concat(output, toImport);
}
// Write collected data, if any, anchored from E4.
if(output.length > 0 && output[0].length > 0)
dest.getRange(4, 5, output.length, output[0].length).setValues(output);
}
Each call to getRange and setValues adds measurable time to the execution time - i.e. on the order of hundreds of milliseconds. Minimizing use of the Google interface classes and sticking to JavaScript wherever possible will dramatically improve your scripts' responsiveness.

Run script on last line of google form responses

I'm new to Google Apps Script and I'm trying without luck to automate a previously manual process. The script is within a sheet which takes form responses, converts them to a document and then emails the respondent with a copy. Here's the code in question:
function createDocFromSheet(){
var templateid = "1E7zzpvDF0U66aNqJdkUqjONx4wQRarkcWDy28NVqafU"; // get template file id
var folder = DriveApp.getFolderById("1-8lae1z_Z-Sy1IczUyB2JvCqCBV8zB5D")// folder name of where to put completed quotes
// get the data from sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var data = sheet.getRange(2, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues();
var username = Session.getActiveUser(); // get their email
var dateNow = new Date(); // get date, clumsily
var dateD = dateNow.getDate();
var dateM = dateNow.getMonth();
var dateY = dateNow.getYear();
// for each row, fill in the template with response data
for (var i in data){
var row = data[i];
// make copy and set active
var docid = DriveApp.getFileById(templateid).makeCopy(row[7]+" - Postal Quote",folder).getId();
var doc = DocumentApp.openById(docid);
var body = doc.getActiveSection();
// date - working
body.replaceText("%D%", dateD);
body.replaceText("%M%", dateM+1);
body.replaceText("%Y%", dateY);
// address - working
body.replaceText("%FNAME%", row[2]);
body.replaceText("%SNAME%", row[3]);
body.replaceText("%ADDL1%", row[4]);
body.replaceText("%ADDL2%", row[5]);
This is setup to trigger on form submit but instead of running the script on the last row it runs for all previous responses as well. This was fine previously when responses were copied to a separate sheet and processed in bulk but I'm lost with how to make it run on only the new response.
Can anyone point me in the right direction?
Line 8; var data = sheet.getRange(2, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues(); in this line you are getting the range A2:whatever the last row and column is, that's why it's going over the entire range.
Changing it to var data = sheet.getRange(sheet.getLastRow()-1, 1, 1, sheet.getLastColumn()).getValues(); should work.
or you can use the e parameter of the onFormSubmit event.
var getSubmission = e.range.getValues();
Have a look at the documentation for the e parameter, it's can be difficult to use in the beginning when debugging but it's very useful when you get the hang of it.

Bills sheet scripts

I am trying to create a spreadsheet to create bills. I have previously done this in Excel, and now I want to use Google Sheets, however I am having some problems.
I tried to create a function to be activated each time that a person clicks a specific range
I have to save the sheet with a specific name (for example the value of a A1 Row + A2 ROW) and save it into my Google drive account
I used importRange() to import the data of a Google Form, however if I make a copy of the sheet I have to give permission to display the data. (How can I do that but without the permission?)
Each of these is almost a question on it own. However, Here's an answer for your questions:
1) I tried to create a function that have to be activated each time that a person click a specific range
Use the onEdit function to perform whatever function you are wishing to perform. However, values will have to be changed in order to perform this function. There's no onClick() fucntion in GAS (As mentioned here). However, Here's a simple example for onEdit:
function onEdit()
{
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var actRow = cell.getRow();
var actCol = cell.getColumn();
//Assuming you want the range to be between rows 5-11 and column 2-9
if (actRow >= 11 && actRow<=5 && actCol >=2 && actCol<=9)
{
//Implement your function
}
}
2) I have to save the sheet with a specific name ( for example the value of a A1 Row + A2 ROW) and save it into my google drive account
Here's a simple sample. You will have to modify it according to your use case but this should give you a good idea regarding how to do it:
function moveRows(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var numRows = sheet.getLastRow();
var sRange = sheet.getRange(2, 1, numRows, 1); //Leaving out header rows
var data = sRange.getValues();
var destFolder = DriveApp.getFolderById('*****');
var newSpSheet = SpreadsheetApp.create("Billing Target");
var newRows = "A"+(1+numRows);
var newRange = "A2:"+newRows; //Leaving out header rows
newSpSheet.getRange(newRange).setValues(data);
//If you need the file to be in a specific folder
DriveApp.getFileById(newSpSheet.getId()).makeCopy("Billing Target", destFolder);
}
3) I used import range to import the data of a google form however if a make a copy of the sheet y have to give permision to display the data (How can I do that but without the permision)
As far as I know, You can't do that without a permission.