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.
Related
I am getting an email every day with 3 attachments (xlsx). I want the last column of the second tab of the 2nd attachment to be appended to an existing google sheet (in the column next to the last one). I have done some research and came up with a code to export the xlsx file to google drive, then convert it to CSV. I am able to update the whole google sheet with the data in the second attachment, but I am not able to copy the last column of the sheet and append it to an existing sheet. The destination sheet has 4 columns as of now, and I want a new column to be added every day along with the latest data from the attachment in my Gmail. Below is the code which I am using:
function importCSVFromGmail() {
var thread = GmailApp.search("Scheduled Reports from RingCentral"); // enter search criteria here
var messages = thread[0].getMessages();
var len = messages.length;
var message=messages[len-1] //get last message
var attachments = message.getAttachments(); // Get attachment of first message
var xlsxBlob = attachments[1]; // Is supposes that attachments[1] is the blob of xlsx file. I get 3 attachments in gmail, and I am using the 2nd one, so using attachment[1]
var convertedSpreadsheetId = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS}, xlsxBlob).id;
var sheet = SpreadsheetApp.openById(convertedSpreadsheetId).getSheets()[2]; // There is the data in 1st tab.
var data = sheet.getDataRange().getValues();
var sheet2 = SpreadsheetApp.openByUrl("link to the existing google sheet").getSheetByName("Test"); //add the URL of the google sheet along with the tab name, in this case, "Test"
sheet2.clearContents();
var copyColumn = sheet.getRange("D1:D21"); //copying the column which I need to paste
var range = sheet2.getRange(5, 5, data.length, data[0].length); //5 is the column where I was testing to paste the data
var lastColumn = sheet2.getLastColumn();
copyColumn.copyValuesToRange(sheet2, lastColumn+1, lastColumn+1, 1, 23); // there are 23 rows
Drive.Files.remove(convertedSpreadsheetId); // Remove the converted file.
}
I am pretty sure I am doing some blunder in this code, also I am getting this exception and the code is not working:
Exception: Target sheet and source range must be on the same spreadsheet. (line 23, file "AP Script")
Please let me know if my requirements are not clear.
try this:
function importCSVFromGmail() {
var thread = GmailApp.search("Scheduled Reports from RingCentral"); // enter search criteria here
var messages = thread[0].getMessages();
var len = messages.length;
var message=messages[len-1] //get last message
var attachments = message.getAttachments(); // Get attachment of first message
var xlsxBlob = attachments[1]; // Is supposes that attachments[1] is the blob of xlsx file. I get 3 attachments in gmail, and I am using the 2nd one, so using attachment[1]
var convertedSpreadsheetId = Drive.Files.insert({mimeType: MimeType.GOOGLE_SHEETS}, xlsxBlob).id;
var sheet = SpreadsheetApp.openById(convertedSpreadsheetId).getSheets()[2]; // There is the data in 1st tab.
var data = sheet.getDataRange().getValues();
var sheet2 = SpreadsheetApp.openByUrl("link to the existing google sheet").getSheetByName("Test"); //add the URL of the google sheet along with the tab name, in this case, "Test"
sheet2.clearContents();
var vs = sheet.getRange("D1:D21").getValues();
var range = sheet2.getRange(5, 5, data.length, data[0].length); //5 is the column where I was testing to paste the data
var lastColumn = sheet2.getLastColumn();
sheet2.getRange(sheet2.getLastRow() + 1,1,vs.length,vs[0].length).setValues(vs);
Drive.Files.remove(convertedSpreadsheetId);
}
I've been using Excel for the longest time but would now need to use Google sheet for the new company that I work for. Seems like it is harder than I expected.
I am creating a form to track how long an employee finishes a certain task for a client.
A simple form that I created where I hide the details on Form!F1:J1 (F1 - Time now, G1 - Name of employee, H1 - Client name, I1 - task done and J1 - Start/End the task
I just want to copy and paste the values to the next empty row on the Tracker sheet.
I tried to search previous answers but doesn't seem to work. I always get an error message:
"ReferenceError: database is not defined"
on the var copyToSheet = database.getSheetByName('Tracker!A1'); part. Here's what I got.
function Done() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var source = ss.getSheetByName('Form');
var dataToCopy = source.getRange('F1:J1');
var copyToSheet = database.getSheetByName('Tracker!A1');
var sourceValues = dataToCopy.getValues();
var lastRow = copyToSheet.getLastRow();
copyToSheet.getRange(lastRow + 1, 1, sourceValues.length, sourceValues[0].length).setValues(sourceValues);
dataToCopy.clear({
contentsOnly: true
});
}
I was able to find the correct code somewhere and just changed it a bit.
function Done() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Form");
var pasteSheet = ss.getSheetByName("Tracker");
var source = copySheet.getRange(1,6,1,5); //from which row number, from which column number, 1, how many cells to copy
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,1,1,5);
source.copyTo(destination,{contentsOnly:true});
//this is to delete what you input on the form after it copies and pastes to the tracker
var ss = SpreadsheetApp.getActive().getSheetByName("Form");
var cell = ss.getRange("C4:C7");
cell.clearContent();
};
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
}
I've been struggling for a bit with this now, and I think it's time to ask for help.
I'm working on a script that will take a range of cells from a google spreadsheet and place it into a google doc.
My particular problem is that the script works with appendTable(), but I need the table to be entered before some data that's already in the doc, not after.
What I have so far is this:
var source = SpreadsheetApp.openById("-----");
var sheet = source.getSheetByName("Sheet1");
var oldfile = DriveApp.getFileById("----");
var number = Sheet.getRange(row, 2, 1, 1).getValues();
var row = sheet.getLastRow();
var sourcedata = sheet.getRange(1, 1, row, 1).getValues();
var newfile = oldfile.makeCopy("Attachment " + number);
var doc = DocumentApp.openById(newfile.getId());
var body = doc.getBody();
body.insertTable(sourcedata);
As I said, the whole thing works without a hitch when appending the table, but when using insertTable as shown above I get a "cannot convert" error.
Can anybody give me a push in the right direction, please?
I'm writing a script to loop through each sheet in one spreadsheet and copy data from specific cells into a corresponding sheet on another spreadsheet. I am getting an error on line 18 of the below code, however, stating that it can't call the getLastRow method of null. I used a couple of Logger.log lines to check my variables and see that targetSheet is coming back as null. Any advice on what I've got wrong?
//Export each sheet's daily data to another sheet *Test Version*
function exportReports() {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
//Open Back Production Record *Test Version*
var targetSS = SpreadsheetApp.openById("1ZJKZi-UXvqyGXW9V7KVx8whxulZmx0HXt7rmgIJpUY4");
var allSourceSheets = sourceSS.getSheets();
//For-Loop to loop through hourly production sheets, running the move data for-loop on each
for(var s in allSourceSheets){
var loopSheet = allSourceSheets[s];
var loopSheetName = loopSheet.getSheetName();
var targetSheet = targetSS.getSheetByName(loopSheetName);
Logger.log(s);
Logger.log(loopSheet);
Logger.log(targetSheet);
Logger.log(loopSheetName);
var targetRow = targetSheet.getLastRow()+1;
var currentDate = Utilities.formatDate(new Date(), "GMT-5", "MM/dd/yy");
targetSheet.getRange(targetRow, 1).setValue(currentDate);
//For-Loop to move data from source to target
for(var i=6;i<=10;i++){
var sourceRange = sourceSheet.getRange(i, 2);
var targetRange = targetSheet.getRange(targetRow, i-4);
var holder = sourceRange.getValue();
targetRange.setValue(holder);
}
}
}
Per the documentation on getSheetByName, if the target sheet name does not exist, then you get null as a return value.
getSheetByName(name)
Returns a sheet with the given name.
If multiple sheets have the same name, the leftmost one is returned. Returns null if there is no sheet with the given name.
So, the desired sheet with name specified by loopSheetName does not exist in the target workbook. Perhaps someone has created a new sheet, or renamed an existing sheet in the source workbook.
You haven't asked about it, but you can improve the performance of your copy code as well, by reading the inputs as a multi-row range array, creating a row array to hold the results, and writing that once:
var sourceData = sourceSheet.getRange(6, 2, 5, 1).getValues(); // (6,2) through (10, 2)
var output = [];
// Transpose row array to column array (could use other functions, but this is easier to understand)
for(var i = 0; i < sourceData.length; ++i) { output.push(sourceData[i][0]); }
targetSheet.getRange(targetRow, 2, 1, output.length).setValues([output]); // i0 = 6 -> 6 - 4 = 2