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?
Related
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);
}
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
}
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.
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.
I have used your extensive catalog of questions and answers innumerable times over the years so thanks for that.
I have been venturing into the world of Google Scripts inside Google Sheets - I'm a solid novice at js and scripts so bear with me.
I currently have a sheet which has a column full of file names within the connected Google Drive (e.g. - Images/ImageTEST123.jpg). I have been tinkering away with a code which takes the file name, searches the Drive and spits out the URL for the image itself - see below.
function searchq() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange(2,14);
var searchTerm = range.getValue();
var r = sheet.getLastRow();
var files = DriveApp.searchFiles("title contains '"+searchTerm.replace("'","\\'")+"'");
var output = [];
while (files.hasNext()) {
var file = files.next();
var url = file.getUrl();
output.push(url);
}
sheet.getRange(2, 15, output.length, 1).setValue(url);
}
I am aware this is far from tidy but it currently works, (taking the search term from [2,14] and sticking the URL into [2,15]). I would like to get this script to go through every row of column 14 and give its respective URL into column 15.
I'm sure there's something simple I can do, but any help would be fantastic.
If you need anything clarifying please ask!
Cheers,
Update: I have spent too much time on this and I feel like I'm getting close.
function searchz() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var r = sheet.getLastRow();
var range = sheet.getRange(2, 14, r - 1);
var searchTerm = range.getValues();
var output = [];
Logger.log(searchTerm)
for (i = 0; i < searchTerm.length; i++) {
var files = DriveApp.searchFiles('title contains'+ searchTerm[i]);
while (files.hasNext()) {
var file = files.next();
var url = file.getUrl();
Logger.log(url)
output.push(url);
Logger.log(output)
}
sheet.getRange(2, 15, output.length).setValue(output);
}
}
The code above gives the error Invalid argument: query (line 14, file"CurrentFiddle"). Which relates to the findNext() section of code.
Other than that, it appears that the rest of the code up to that point appears to work. I have a sneaking suspicion that it has something to do with iterating through the array of search terms within the searchFiles() section.
Any information to bring light to this situation would be hugely appreciated.
For this you could get the last row of the sheet by using this method.
sheet.getLastRow()
and then get the complete range of values for column 14 using this [getRange(row, column, numRows)][2]
So you get the range as 2D array. You can loop through each row and get the file names and add the urls.
So after creating the urls for each file, you can save them in an array and update the sheet(add url to column) only once(If the file name matches to one single file from Drive).
Hope that helps!
Try
for (var i=0;i<searchTerms.length;i++) {
var searchTerm = searchTerms[i].toString();
var files = DriveApp.searchFiles("title contains '"+searchTerm.replace("'","\\'")+"'");
while (files.hasNext()) {
var file = files.next();
var url = file.getUrl();
Logger.log(url);
output.push(url);
}
}