i'm writing a script that will take data from a spreadsheet and replace text keys on a document, adding one duplicated page to the document for each row of data on the spreadsheet.
the script was working properly when i created some test arrays with dummy data but it's not working when i try to pull the data from the spreadsheet.
function requestGen3() {
var templateDocID = ScriptProperties.getProperty("backRxRequestDocID");
// go back to variable below after testing
//var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sheet = sheets[3];
var activeSheetName = sheet.getName();
var range = sheet.getRange(2, 1, sheet.getMaxRows() - 1, sheet.getMaxColumns());
var user = Session.getUser().getEmail();
///////////////////////////////////////////////////////////////////////////////////
// the problem seems to be somewhere between here....
///////////////////////////////////////////////////////////////////////////////////
// For every row of employee data, generate an employee object.
var ptObjects = getRowsData(sheet, range);
// Get document template, copy it as a new temp doc, and save the Doc’s id
var docID = DocsList.getFileById(templateDocID).makeCopy().getId();
var doc = DocumentApp.openById(docID);
var body = doc.getActiveSection();
var pars = doc.getParagraphs();
for( var i in pars ) //loop to keep a copy of the original paragraphs
pars[i] = pars[i].copy();
// Create an array for every row object
for (var i = 0; i < ptObjects.length; ++i) {
// Get a row object
var rowData = ptObjects[i];
for (var i = 0; i < rowData.length; ++i) {
// Replace place holder keys,
body.replaceText('%PHYS_NAME%', rowData.physName[i]);
body.replaceText('%PHYS_ADDR1%', rowData.physAddr1[i]);
body.replaceText('%PHYS_ADDR2%', rowData.physAddr2[i]);
body.replaceText('%PHYS_CITY%', rowData.physCity[i]);
body.replaceText('%PHYS_STATE%', rowData.physState[i]);
body.replaceText('%PHYS_ZIP%', rowData.physZip[i]);
body.replaceText('%PHYS_PHONE%', rowData.physPhone[i]);
body.replaceText('%PT_NAME%', rowData.firstName[i]);
body.replaceText('%PT_DOB%', rowData.ptDOB[i]);
if( i != physName.length-1 ) { //has next?
doc.appendPageBreak();
for( var j in pars )
doc.appendParagraph(pars[j].copy());
}
}
}
///////////////////////////////////////////////////////////////////////////////////
// ...... and here
///////////////////////////////////////////////////////////////////////////////////
// Save and close the document
doc.saveAndClose();
}
the other functions referenced are from the "reading data from a spreadsheet" tutorial. when i debug the script it seems like the ptObjects and rowData variables contain the right information but it is not replacing the keys (which are formatted %KEY_NAME%) in the document properly. i'm fairly new to this so there may be some obvious mistakes.
any help would be very much appreciated
This looks like it could a simple problem assuming that the getRowsData function is the one described in the Reading Spreadsheet Data tutorial from the Apps Script Articles site.
That being the case, rowData.physName[i] should simply be rowData.physName.
Related
To explain the larger context: there are several forms which generate different sheets. I'm looking for a way to conditionally copy some of the responses sheet to a seperate "Overview" document. Code-wise, I had some ideas for the Overview document, but stranded near the start.
My method was going to be to build functions for all the information I want to retrieve, such as date of birth (example in code block below), date of submission and phone number, when I click on a button. The information may only be copied if the first and surname match the ones in the Overview. The order of the sheets in different docs are not the same and the column length is continually in flux. Furthermore, the amount of rows in the Overview doc is different than the form submission sheets.
In other words: if Anne Annenson would be the twenty-first respondent to a form, I want that information in the overview sheet where they are the first person.
function getDobs() {
var targetSpreadsheet = SpreadsheetApp.getActive();
var targetSheet = targetSpreadsheet.getSheetByName("Overview");
var targetFirstNameCheck = targetSpreadsheet.getRange("A4:A");
var targetSurnameCheck = targetSpreadsheet.getRange("B4:B");
var sourceSpreadsheetDob = SpreadsheetApp.openById("...");
var sourceDob = sourceSpreadsheetDob.getSheetByName("Form responses 1");
var sourceFirstNameCheckDob = sourceSheetDob.getRange("C2:C");
var sourceSurnameCheckDob = sourceSheetDob.getRange("D2:D");
var sourceRangeDob = sourceSheetDobConsent.getRange("E2:E");
if (sourceFirstNameCheckDob==targetFirstNameCheck && sourceSurnameCheckDob==targetSurnameCheck){ //then I want to copy the data
var sourceData = sourceRangePronouns.getValues();
var targetRangeDob = targetSheet.getRange("C4:C");
}
else (//I want it to leave the cells alone, so any text or formatting that might have been put in manually is still there.){
}
}
I would like for the responses to remain in the form response sheets as well.
Any thoughts?
Cooper already explained all the things you need in the comments. And below is what your code would look like following Cooper's comments.
Code
function getDobs() {
var targetSpreadsheet = SpreadsheetApp.getActive();
var targetSheet = targetSpreadsheet.getSheetByName("Overview");
var targetLastRow = targetSheet.getLastRow();
// range equivalent to A4:B
var targetNamesCheck = targetSheet.getRange(4, 1, targetLastRow - 3, 2).getValues();
// tested in same spreadsheet, change "targetSpreadsheet" to openById on your actual script
var sourceSpreadsheetDob = targetSpreadsheet;
var sourceDob = sourceSpreadsheetDob.getSheetByName("Form responses 1");
var sourceLastRow = sourceDob.getLastRow();
// range equivalent to C2:D
var sourceNamesCheckDob = sourceDob.getRange(2, 3, sourceLastRow - 1, 2).getValues();
// range for data to be copied (E2:G in my sample data)
var sourceRangeDob = sourceDob.getRange(2, 5, sourceLastRow - 1, 3).getValues();
var output = [];
targetNamesCheck.forEach(function (targetNames) {
// search sourceNamesCheckDob for targetNames
var index = searchForArray(sourceNamesCheckDob, targetNames);
// if targetNames is in sourceNamesCheckDob, save the data on that row for later
if (index > -1)
output.push(sourceRangeDob[index]);
// append blank cells if data is not found
else
output.push(new Array(sourceRangeDob[0].length));
});
// if there were names that were found, write the data beside the targetNames
if (output.length > 0) {
targetSheet.getRange(4, 3, output.length, output[0].length).setValues(output);
}
}
// function to search the array for the object
function searchForArray(haystack, needle) {
var i, j, current;
for(i = 0; i < haystack.length; ++i) {
if(needle.length === haystack[i].length) {
current = haystack[i];
for(j = 0; j < needle.length && needle[j] === current[j]; ++j);
if(j === needle.length)
return i;
}
}
return -1;
}
Overview:
Form responses 1:
Overview after running getDobs:
EDIT:
Since there are no methods that includes the apostrophe when the cell value is being fetched, easiest way is to have the sheets identify the phone number as text so it won't remove the 0 in the beginning. I've thought of 3 ways to have the 0 included in the output:
Add the apostrophe manually on the specific output column via script
Add dashes on the number so it is treated as text (09395398314 -> 093-9539-8314) (just an example, not sure if that is the right format)
Format the output column into number -> plain text instead of number -> automatic
I prefer formatting the output column as that will be the fastest and easiest thing to do.
Format:
Output:
Note:
This function will fill up rows where names in Overview are present in Form responses 1.
References:
Check whether an array exists in an array of arrays?
javascript create empty array of a given size
I'm using the code below to concatenate certain sheets in my file into a sheet named "Master".
Currently, my script duplicates the header row, but I would like to prevent this. The data in all the sheets is in exactly the same format with the same header.
Each sheet has 58 columns and one of the sheets has 5000 rows.
Ideally I'd like the script to overwrite the "Master" sheet each time the script is run.
How can I modify my script to accomplish these goals?
function concatAllSheets()
{
var includedSheet = ['Virtue data - Norway - NOK', 'Virtue data - Sweden - SKK', 'Virtue data - Denmark - DKK', 'Virtue Data - GBP', 'Virtue data - EUR markets', 'Virtue data - Arabia - USD'];
var ss = SpreadsheetApp.getActive();
var allSheets = ss.getSheets();
var sheetName = 'Master'
var mother = ss.insertSheet(sheetName);
for(var i = 0; i < allSheets.length; i++)
{
var sht = allSheets[i];
if(includedSheet.indexOf(sht.getName()) > -1)
{
var rng = sht.getDataRange();
var rngA = rng.getValues();
for(var j = 0; j < rngA.length; j++)
{
var row = rngA[j];
mother.appendRow(row);
}
}
}
}
Rather than calling appendRow() for each row of data, which will really slow your script down, use a batch operation like setValues().
To do so, create an allData array to hold what will be the content of your "Master" sheet. As you iterate through the sheets, append their data into allData, and finally print it to the sheet.
As you're iterating, you can do a simple check of allData's length, to see if the header row is already there. If allData is empty, then there is obviously no header row.
function concatAllSheets()
{
var includedSheet=['Virtue data - Norway - NOK','Virtue data - Sweden - SKK','Virtue data - Denmark - DKK','Virtue Data - GBP','Virtue data - EUR markets','Virtue data - Arabia - USD'];
var ss=SpreadsheetApp.getActive();
var allSheets=ss.getSheets();
var sheetName='Master'
var mother=ss.insertSheet(sheetName);
var allData = [];
for(var i=0;i<allSheets.length;i++)
{
var sht=allSheets[i];
if(includedSheet.indexOf(sht.getName())>-1)
{
var rng=sht.getDataRange();
var rngA=rng.getValues();
if (allData.length == 0) // This will only ever be true on the first sheet copied
{
allData = rngA;
} else {
rngA.shift(); // Remove the first row
allData = allData.concat(rngA);
}
}
}
mother.getRange(1, 1, allData.length, allData[0].length).setValues(allData); // Use a batch operation to insert the data
}
Given the size of the dataset, you need to minimize reads and writes by using the batch methods Range#getValues and Range#setValues. You can also avoid iterating over the unnecessary sheets by only binding the ones in your array of names, using Array#forEach. This pattern also ensures that you collect all the data you intend - if there is a typo in your names array, or the sheet's name is changed unintentionally, this will throw an exception rather than silently not including the data.
function concatenateSheets() {
var sheetNames = ["name1", "name2", ...];
var ss = SpreadsheetApp.getActive();
var dest = ss.getSheetByName("someName");
var output = [], header = [];
// Assemble a single paste output from all the sheets.
sheetNames.forEach(function (name) {
var sheet = ss.getSheetByName(name);
if(!sheet)
throw new Error("Incorrect sheet name '" + name + "'");
var vals = sheet.getDataRange().getValues();
// Remove the header row.
header = vals.splice(0, 1);
// Append to existing output array.
output = [].concat(output, vals);
});
// Serialize output data array.
if(dest && output.length && output[0].length) {
// Remove all existing data values on the destination sheet.
// (Only necessary if the number of rows or columns can decrease.)
dest.getDataRange().clearContent();
// Prepend the header on the output data array.
output.unshift(header);
dest.getRange(1, 1, output.length, output[0].length).setValues(output);
}
}
I'm working in Google Sheets and I'm trying to create a script that will make a set number of copies of the current file, giving each copy the next name from a list of names in a range, and place those files in folders that were created by a previous script. I was able to get it all working, but only for the first file (out of 6, and possible far more) and can't figure out what I'm doing wrong.
Here's a copy of the sheet. I also have another version of this that works to just create copies of the document, but I'm trying to streamline the process for my end users who may be creating dozens of copies and was hoping that doing the organization for them would help.
Thanks for your help!
Here's the script:
function createcopies2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the range of cells that store necessary data.
var CoachNames = ss.getRangeByName("CoachNames");
var CoachObjects = CoachNames.getValues();
var schoolNames = ss.getRangeByName("SchoolNames");
var SchoolObjects = schoolNames.getValues();
var id = ss.getId();
// The next variable gets a value that counts how many CoachNames there are in the spreadsheet
var coaches = ss.getRangeByName("Coaches");
var numcoaches = coaches.getValue();
//here's the function
for(i=0; i<numcoaches; i++){
var drive=DriveApp.getFileById(id);
var name=CoachObjects[i].toString();
var folder=DriveApp.getFoldersByName(SchoolObjects[i]).next();
var folderid=folder.getId();
var folder2=DriveApp.getFolderById(folderid)
if(folder) {
drive.makeCopy(name, folder2)}
else{
drive.makeCopy(name);
}
return;
}
}
You are on the right track.
I have modified you below, with explanation:
function createcopies2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the range of cells that store necessary data.
var CoachNames = ss.getRangeByName("CoachNames");
//The below statements a 2D dimensional array.
//To access the individual value you will have a statement like this
//CoachObjects[0][0],CoachObject[1][0],[2][0] ..., down the row
var CoachObjects = CoachNames.getValues();
var schoolNames = ss.getRangeByName("SchoolNames");
//The below statements a 2D dimensional array.
var SchoolObjects = schoolNames.getValues();
var id = ss.getId();
// The next variable gets a value that counts how many CoachNames there are in the spreadsheet
var coaches = ss.getRangeByName("Coaches");
var numcoaches = coaches.getValue();
//Moved the below statement out of the loop
// Baceause you are using the same file
var drive=DriveApp.getFileById(id);
//here's the function
for(i=0; i<numcoaches; i++){
var name=CoachObjects[i][0].toString();
var folder=DriveApp.getFoldersByName(SchoolObjects[i][0]).next();
var folderid=folder.getId();
var folder2=DriveApp.getFolderById(folderid)
if(folder) {
drive.makeCopy(name, folder2)}
else{
drive.makeCopy(name);
}
return;
}
}
I modified the code since you get a 2D array from getValues
//The below statements a 2D dimensional array.
var CoachObjects = CoachNames.getValues();
To access the individual value you will use a statement like this
`CoachObjects[0][0]`
CoachObjects[1][0]
....... [2][0] ...
down the row
Also, These are redundant lines of code:
var folder=DriveApp.getFoldersByName(SchoolObjects[i][0]).next();
var folderid=folder.getId();
var folder2=DriveApp.getFolderById(folderid)
you can just replace it with
var folder2=DriveApp.getFoldersByName(SchoolObjects[i][0]).next();
Hello I wrote a small script to copy one template sheet in a spreadsheet, as a new sheet in the same spreadsheet.
I wrote two versions of it, one driven by a menu that asks for the name of the new sheet to be created:
function addonenewSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var temp = ss.getSheetByName('template');
var naame = Browser.inputBox("CustomerID to be created");
try {
ss.setActiveSheet(ss.getSheetByName(naame));
}
catch (e) {
ss.insertSheet(naame, {template:temp});
}
}
This one works as intended, and names the new sheet 234 if I say so in the inputbox.
The second function is very similar, but parses some values and attempts to create many sheets at once:
function addmissingSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var temp = ss.getSheetByName('template');
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 10; i < data.length; i++) {
if(typeof data[i][1] == 'number'){
try {
ss.setActiveSheet(ss.getSheetByName(data[i][1]));
}
catch (e) {
Logger.log('Customer ID: ' + data[i][1]);
var insertpage = data[i][1];
ss.insertSheet(insertpage, {template:temp});
}
}
}
}
As long as Logger.log is concerned, data[i][1] has the right value, but somehow insertSheet creates sheets named "copy of template", "copy of template 2"... Instead of taking the value assigned in data[i][1]
Would anyone know why this behaviour and how I can solve this issue?
your second script does not use correct variable types. The method you are using insert sheet uses types (<string>, {template:<sheet>}). Since your customer ID is a number it does not work. There is a simple fix you can do
Change
var insertpage = data[i][1];
into:
var insertpage = data[i][1].toString();
and you will now be able to use the customer ID (which is a number) to create a sheet name (which is a string)
I have tried and combined a few pieces of script to delete rows, but this does not reset the counter. Help resetting responses would be appreciated.
My copy sheet function, and delete all rows function works, but the counter remains, showing 58 responses.
I use the triggers to set the copy and delete functions to occur daily. (sheet url excluding the "docs.google.com..." 0AvTM4SfinH2NdGp1MHdzWms2QnpUMnFiMHJXd1dlV1E&usp) This is what I have so far:
function CopySheet() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheets()[0];// here I chose to always get the first sheet in the spreadsheet
var inputRange = ss.getRange(1,1,ss.getLastRow(),7);
var data = inputRange.getValues();
var newData = [];
newData.push(['Timestamp','Full Name?','Email?','RAG']);
for(var n=1;n<data.length;++n){ // skip headers by starting at 1
for(var c=0;c<7;c=c+3){
var row = [];
if(c==0){row.push(data[n][0]) ; c++}else{row.push('')};
row.push(data[n][c])
row.push(data[n][c+1]);
row.push(data[n][c+1+1]);//Keep adding a new row and +1 for each extra column
newData.push(row);
}
}
//This next bit creates a copy of the sheet. I would rather a spreadsheet copy but could only get document copy to work
sh.insertSheet().getRange(1,1,newData.length,newData[0].length).setValues(newData);
var doc = DocumentApp.create('Responses document'); // create document
var file = DocsList.getFileById(doc.getId());
file.removeFromFolder(DocsList.getRootFolder());
file.addToFolder(DocsList.getFolder("Folder 1"));
var table = doc.getBody().appendTable(newData); // create table in a separate process so I can set the style below
var style = {};
style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER; // this one has no effect
style[DocumentApp.Attribute.FONT_FAMILY] = DocumentApp.FontFamily.ARIAL;
style[DocumentApp.Attribute.FONT_SIZE] = 10;
style[DocumentApp.Attribute.FOREGROUND_COLOR] = '#0000ff';
style[DocumentApp.Attribute.BORDER_COLOR] = '#dddddd' ;
table.setAttributes(style);
}
//This section deletes the sheet, leaving the headers; "function deleteAllResponses()" at the bottom should reset counter but does not work
function DeleteSheet() {
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
for (i=lastrow;i>=2;i--) {
var tempdate = values[i-1][2]; // arrays are 0 indexed so row1 = values[0] and col3 = [2], If I add more columns I need to up this number
{
sheet.deleteRow(i);
function deleteAllResponses() {}
}
}
}
If you mean the counter responses shown on the form:
One option may be to use deleteAllResponses() (read carefully the documentation) from Class Form.
A minimal implementation can be:
/* CODE FOR DEMONSTRATION PURPOSES */
function deleteAllResponses() {
var form, urlForm = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
if (urlForm) {
form = FormApp.openByUrl(urlForm);
if (form) form.deleteAllResponses();
}
}
#user2847142, #brian-tompsett, #wchiquito
New Google Forms allows you to delete even individual responses from within a Google Form itself without the need of a script.
There is now a simpler method than the answer given by #wchiquito.
--This is now possible on the New Google Forms--
Google announcement on the 10th of February 2016. (googleappsupdates.blogspot.com/2016/02/new-google-forms-now-default-option.html)
How to delete ALL of the responses:
How to delete individual responses:
To delete individual responses you click on the "Responses" tab and choose "Individual". You locate the record you wish to delete and click on the trash can icon to delete that individual response.
This will also reset the counter.
However. The response/s will NOT be deleted from the connected to the form spreadsheet. You will have to manually delete those ones (or using a script).