Please see my spreadsheet.
https://docs.google.com/a/zigmens.com/spreadsheet/ccc?key=0AtXr7M_f8JAadG1qclprdXRFb3VuNXV0OXg5bDNpNkE&usp=drive_web#gid=0
I have a menu item, Create/Job Scope that pulls data from the spreadsheet and creates a new document in "my drive" location of google drive. Problem I have, is that it's not including the stye and format that I have in the spreadsheet. I have tried many ways of setting the variable in the script to "bold", for example, but can't seem to get it to work. I am hoping there is an object that I can add to the variable that will set it's style.
For example, in my script, I have a variable called "desc"
var descr = values[n][3] ;
I would like to just create a new variable such as
var descr = values[n][3] ;
var desc_style = descr.setStyle("bold") ;
Here is the script from the spreadsheet but I think it's best if you just update the script in my spreadsheet.
function jobScope() {
var ss = SpreadsheetApp.getActiveSheet();
var values = ss.getDataRange().getValues();
var docTemplate = "19ANrZluvbavWU4Ttgh1z9_DVJgEQ1hrGohd4lQAg7vI";
var job_name = ss.getRange("D4").getValue();
var docName = job_name+' Job Scope ';
var x = 1 ;
while(values[x][0] ^= "") {
++x ;
}
var textToDoc = "" ;
for(n=1;n<x;++n){
var cell = values[n][4] ;
if (cell ^ "0") {
var line_item = values[n][1];
var descr = values[n][3] ;
textToDoc = textToDoc + line_item + " " + descr + "\n\n" ;
}
}
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(docName)
.getId();
var copyDoc = DocumentApp.openById(copyId);
var copyBody = copyDoc.getActiveSection();
copyBody.replaceText('keyScope', textToDoc);
copyBody.replaceText('keyJobName', job_name);
copyDoc.saveAndClose();
}
Have you tried setting the formatting of the keys to your preferred style? If you format the key (ie keyScope) in the document template, then the variable that replaces the key will retain they key's formatting.
//...trimmed previous code
// Get document template, copy it as a new temp doc, and save the docs id
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(docName)
.getId();
//Open the temporary document and set it to a variable
var copyDoc = DocumentApp.openById(copyId);
//Get the documents body section
var copyBody = copyDoc.getActiveSection();
//Replace place holder keys, in our google doc template
copyBody.replaceText('keyline_item', line_item);
copyBody.replaceText('keyDescr', descr);
copyBody.replaceText('keyJobName', job_name);
copyDoc.saveAndClose();
}
Although this is the only answer I know of that answers the original question, what the author is looking for is a way to preserve formatting (bold) in a variable that joins two values from different cells (with different formatting). To the best of my knowledge, it is not possible to carry formatting from spreadsheets to documents, regardless of the number of variables because the keyReplace object is only composed of the raw text (or formula) that occupies the targeted cell or range where .getValues() is used.
If I'm wrong about this, I would appreciate someone correcting me, but since the author combines two adjacent cells into a single variable, and that is the answer he is seeking, I would think that it still wouldn't be possible to do as a single keyObject replacement in a template because two cells with different formatting are being merged into a single object in the script. I know that you can get background and foreground colors, but you have to get those by R1C1 notation. Are there other formatting options that can be captured from spreadsheets?
Either way, I'm going to leave this to the pros, because the only way I could see, from his source files, was to completely rewrite his document template and script, and that is not what the author of the question is looking for. I will leave my script above for people who may want to do something similar in the future.
Related
I'm building a script that will ultimately take data from a csv file, populate a spreadsheet, use that spreadsheet to autofill a number of documents, and then automatically e-mail those documents to customers. It's also moving the documents from each time the script runs to a new folder with just that day's reports. I'm pretty new to Google Scripts, and this has been my learning project.
The steps I've got to work so far is the creation of documents for the spreadsheet with dummy data.
I ended up creating a second script to send e-mails which sends e-mails, but it is set up to look for all documents in the folder that are google docs, so it send a copy of every document to each customer. I thought that if I pointed the e-mail back to the original spreadsheet to grab just the correct document IDs (instead of the type) , I could only send customers the reports that belonged to them (all in PDF form). The step of creating a new folder and moving the documents into it afterwards works fine.
What I'm having an issue with is inputting data into my document ID column in the original spreadsheet. I have been able to watch it put the document ID of the first document into every row that has info to iterate over in the column, and then replace every row again with the second document's ID, etc.
I looked for ways to add data to a spreadsheet. Every method I've found so far involves creating a new column or row with new information from data within the spreadsheet. I need to put in data that I'm just now creating outside of the spreadsheet and then put it in the right place so I can point to it later.
I've gone over the methods within the documentation. It looks like .getCell.setvalue(variable) should work...if I could find a way to get the cell from the range (Which keeps showing me out of range).
function createDocument() {
var headers = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A1:P1');
var tactics = Sheets.Spreadsheets.Values.get('17jXy9IlLt8C41tWEG5iQR31GjzOftlJs73y2L_0ZWNM', 'A2:P');
var templateId = '1DU13OJHWyYnO5mLFovm97pWwXuU7ZTTDVJb2Mpdeebk';
for(var i = 0; i < tactics.values.length; i++){
var customer = tactics.values[i][0];
var pcname = tactics.values[i][1];
var date = tactics.values[i][2];
var virusvalue = tactics.values[i][3];
var cpuuse = tactics.values[i][4];
var ramuse = tactics.values[i][5];
var harddrive = tactics.values[i][6];
var netuse = tactics.values[i][7];
var downtime = tactics.values[i][8];
var cpuperform = tactics.values[i][9];
var ramperform = tactics.values[i][10];
var harddiskperform = tactics.values[i][11];
var reccomend = tactics.values[i][13];
var custID = tactics.values[i][14];
var newdoc = tactics.values[i][15];
//Make a copy of the template file
var documentID = DriveApp.getFileById(templateId).makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
//rename the copied file
DriveApp.getFileById(documentID).setName('MCHA ' + customer + ' - ' + pcname);
Logger.log('value1 ' + documentID);
//THIS IS THE AREA I'M TRYING TO FIX
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
var cell = ss.getRange('P2:P').getCell([i], [15]);
cell.setValue(documentID);
SpreadsheetApp.flush();
Logger.log('value2 ' + documentID);
//This area has code to replace the tags in the document with values from the spreadsheet. I cut it for not being relevant.
}
}
Obviously defining the range is just going to fill and autofill each cell. That code worked when I tried it
I originally tried using the variable from my earlier list for the autofill, but I've since realized that shouldn't work at all.
I tried to set the range of cells and then set the current cell by the same notation ([i][15] etc.) which throws an 'Range not found' error. I feel like I must be missing some syntax.
A link to the folder with all the documents is below, if that helps.
https://drive.google.com/drive/folders/1sRhti3R8R-Cym0hr2S4XkAVn3wyBbSRu?usp=sharing
I may not have entirely understood the problem you are facing. But I can see the cause of the 'Range not found' error.
Your loop starts with a value of 0 for i. This causes the script to look for a range called P0 in the first iteration.
One way to avoid that is to try :
var cell = ss.getRange("P"+(i+1));
For code efficiency, I'd also suggest moving some of the code outside the loop. For example, the following line runs each time in the loop. But it could be defined once outside the loop and then the variable ss could be reused inside the loop.
var ss = SpreadsheetApp.openById('113aqWVAjjUYCmI2oFc_BTbXkWMPFPjk_SschsKEx6qU');
Similarly, you could define the template file outside the loop and then sue it inside to make copies:
var templateFile = DriveApp.getFileById(templateId); // Outside the loop
And then inside the loop:
var documentID = templateFile.makeCopy(DriveApp.getFolderById('1zV-WpzUKoRurE9FnBcfjBygBA5rCO67I')).getId();
Google apps script best practices suggests minimising calls to the spreadsheet, i.e. get the data in one call, process it, and then post the data to the sheet in one call. More here.
Hope this helps.
Some of you may be familiar with content grouping in Google Analytics, which basically lets you group any number of URL's in user-specified groups (this is useful for analyzing pages that belong together all at the same time). I'm working on a script to take that to the next level and use it in Google Sheets as well.
Goal: have a working script that rewrites URL's and gives them another name, regardless of whether it uses upper or lower cases in the URL.
So far I have this:
function onOpen() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("a1:a10000");
var to_replace = /.*example.*/;
var replace_with = "TEST";
var to_replace2 = /.*another-example.*/;
var replace_with2 = "TEST-Nr2";
replaceInSheet(sheet,range, to_replace, replace_with);
replaceInSheet(sheet,range, to_replace2, replace_with2);
}
This script works in the sense that it rewrites URL's with 'Example' in it to 'Test' and it rewrites 'Another-example' into TEST-Nr2.
However, the final script will probably have thousands of URL's that will need to be rewritten. Furthermore, some URL's have uppercases in them, which I want to ignore and just rewrite.
All of the above leads me to two questions:
How can I write the script in such a way (with regular expressions for example?) that I won't have a Googleplex number of To_replace's and replace_with's?
How can I make my to_replace variables case-incensitive?
If any more information is needed on this matter I will gladly provide so.
Kind regards,
JNeu
Somehow you know the patterns and the replacement values, yes? You need to impart that knowledge to your script.
The simplest way is to read it from a spreadsheet, e.g. on some sheet in some workbook, you have 1 column with the pattern, and another column with the replacement. Then you just read that data in (Range#getValues()), and then iterate that array to process your data range. Note that the pattern you store in the sheet should not include the literal constructor slashes, i.e. you'd want \d{1,3} and not /\d{1,3}/ in the cell.
Example:
function processAll() {
const source = SpreadsheetApp.openById("id of the spreadsheet with pattern - replacement data"),
info = source.getSheetByName("some sheet name")
.getDataRange().getValues();
const databook = SpreadsheetApp.getActive(),
sheet = databook.getSheetByName("name of the sheet with data to process");
if (!sheet) return; // sheet with that name doesn't exist.
const range = sheet.getRange(1, 1, sheet.getLastRow(), 1);
info.forEach(function (row) {
// Create case-insensitive pattern from the string in Column A, e.g. \d{1,3} and NOT /\d{1,3}/
var pattern = new RegExp(row[0], "i");
var repl = row[1]; // replacement text from Column B
replaceInSheet(sheet, range, pattern, repl);
});
}
Additional Reading:
RegExp
Array#forEach
Fellow Stackers. I'm using a Google Apps Script to (a) capture all "Comments" in a Google Document...
...and (b) list them in a column of a Google Sheet...
However, I'm wondering if it's possible to...
(1) Array "Comments" into individual cells down a column in my Sheet rather than into a single column, as I have now. This is the bit of GAS I'm currently using to grab comment contents:
var comments = JSON.parse(Drive.Comments.list(id));
var items=comments.items;
var string = "";
for(var i in items){
string+='\n';
string+=items[i].content;
}
(2) Order the "Comments" by anchor position in my Google Document—i.e. the comment anchored highest in the doc would appear in the first cell of the Sheet's column.
(3) Also include "Suggested Edits" from my Google Document alongside the comments. Can those be accessed via API yet?
Thanks in advance to anyone who may be able to help!
Google Document: https://docs.google.com/document/d/1O7zAdkCmxhYihtfJhZ3OGkWfO8UUJ_deoHEYr7rQHW4/edit?usp=sharing
Google Apps Script: https://script.google.com/macros/d/1MgTtU0cKSS_XghRjAMtjZFQAdsbU9SkD_2zx03KVKb1Vy4iBBp3MI2QW/edit?uiv=2&mid=ACjPJvHY-vp53Ek1wBR4-W3Q1Ur8dSdyN0g6ZI7n3I48-e7EWyq6v9gY82OAeVNlnpQBbY3ICOzi4PCRtp-pjuqAbH3oePLelcIp-YUPs2FNbB7Cl7CC-AvgnoJPcXCnrO8CrIJEI2v8ns8&splash=yes
Google Sheet: https://docs.google.com/spreadsheets/d/1uAtmAO0we7h3HUAFlDBLXlShSLvHVM_W6OSXPwpX_t8/edit?usp=sharing
(1) Array "Comments" into individual cells down a column in my Sheet rather than into a single column, as I have now.
This bit of code is taking an array of comments, and concatenating them into a single string:
var string = "";
for(var i in items){
string+='\n';
string+=items[i].content;
}
To be able to put each comment into a separate cell in a column, you need to change that array into a 2-dimensional array, with each of the original elements in its own "row". Something like:
var data = []; // start with an empty array
for (var i=0; i<items.length; i++) {
var item = items[i]; // current comment
// A row is an array of cells
var row = [item.htmlContent,item.author.displayName,item.createdDate];
data.push(row); // Add this row to the data array
}
This line writes the content of a single cell, albeit using setValues() which can fill a rectangular range:
var targetRange = sheet.getRange(lastRow+1,1,1,1).setValues([[string]]);
With the 2-D array created earlier, you can append to the sheet like so:
var targetRange = sheet.getRange(lastRow+1,1,data.length,data[0].length);
targetRange.setValues(data);
Result:
function driveApiComment(id){
var comments = JSON.parse(Drive.Comments.list(id));
var items=comments.items;
var data = []; // start with an empty array
for (var i=0; i<items.length; i++) {
var item = items[i]; // current comment
// A row is an array of cells
var row = [item.htmlContent,item.author.displayName,item.createdDate];
data.push(row); // Add this row to the data array
}
var sheet = SpreadsheetApp.openById(submissionSSKey).getSheets()[0];
var lastRow = sheet.getLastRow();
var targetRange = sheet.getRange(lastRow+1,1,data.length,data[0].length);
targetRange.setValues(data);
}
(2) Order the "Comments" by anchor position in my Google Document—i.e. the comment anchored highest in the doc would appear in the first cell of the Sheet's column.
You're out of luck (for now, at least). See:
How to match comments on an image using kix anchor (or not) in Google Docs
Anchor documentation does not exist?
Creating anchored comments programmatically in Google Docs
Summary: Google's anchors are not decipherable. (Likely they are a key to a hidden database that includes the actual line & char refs to your document, along with your social security number and mother's maiden name.) You could retrieve them & sort them alphabetically... but that would have no relation to where the comments appear in a document.
(3) Also include "Suggested Edits" from my Google Document alongside the comments. Can those be accessed via API yet?
Nope.
I am working in goggle sheets and think I need to use a google apps script to do what I want, but I am a psychologist at a non-profit University hospital trying to do some good and not a programmer (which probably shows) and I am desperately in need of help. I am trying to set up a series of spreadsheets to track participation in workshops for our treatment method.
1) I have a sheet “Participant_Registration” where basic information is entered
2) I want to transfer information from only the first four columns (A:D) of “Participant_Registration” to a second sheet “Learning_Sessions_Attendance”
3) I am also transferring the same information to a third sheet 'Consultation1_Attendance' – but I need to first filter and select only those people assigned to that group.
Here is a link to a copy of my spreadsheet.
https://docs.google.com/spreadsheets/d/17d0bT4LZOx5cyjSUHPRFgEZTz4y1yEL_tO3gtSJ4UJ8/edit?usp=sharing
More generically this is what I am trying to do. Is this possible in google app scripts? It seems it should be.
1) I have original data in sheet1
2) I want the first four columns (A:D) to transfer to sheet2 (it is fine if I need a trigger variable)
3) I want them to transfer in such a way that if you sort either sheet, the data are still fine (still linked to the right line).
4) Ideally if there is a change to the data in the source sheet (Sheet1) the same change will be made in Sheet2.
5) Ideally this would all happen automatically without human intervention through a script.
Any ideas?? I so need your help. I have been all over the forum, git hub, and done a ton of searches and tried following a lot of examples I saw but nothing works. I really need help.
Here are my sample scripts each with a problem:
//The following code copies a range from sheet1 to sheet2 as I wanted. A problem occurs if after if we copy the data from sheet1 we add data to other columns on sheet2. Later if we sort on some variable (which people are bound to do) if the function is deployed again it will overwrite data meaning the data from sheet1 are not connected to the right individual on sheet2
function CopyRange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Participant_Registration");
var range = sheet.getRange("A14:D");
var values = range.getValues();
var target = ss.getSheetByName("Learning_Sessions_Attendance");
var target_range = target.getRange("A10:D");
range.copyTo(target_range);
}
So I tried again. This time I tried to just copy the last edited row from sheet1 to sheet2. This function does not appear to work for me.
function CopyRow2() {
// Get Spreadsheets
var source = SpreadsheetApp.openById("1egn6pnRd6mKMGuQxX_jtgwYDtkuMUv2QJItLdh7aIEs");
var target = SpreadsheetApp.openById("1egn6pnRd6mKMGuQxX_jtgwYDtkuMUv2QJItLdh7aIEs");
// Set Sheets
var source_sheet = source.getSheetByName("Participant_Registration");
var target_sheet = target.getSheetByName("Learning_Sessions_Attendance");
var rowIdx = source_sheet.getActiveRange().getRowIndex();
var rowValues = source_sheet.getRange(rowIdx,1,1,source_sheet.getLastRow()).getValues();
Logger.log(rowValues);
var destValues = [];
destValues.push(rowValues[0][0]);// copy data from col A to col A
destValues.push(rowValues[0][1]);//copy data from col B to col B
destValues.push(rowValues[0][2]);//copy data from col C to col C
destValues.push(rowValues[0][3]);//copy data from col D to col D
var dest=source.getSheets()[4];
dest.getRange(dest.getLastRow()+1,1,1,destValues.length).setValues([destValues]);//update destination sheet with selected values in the right order, the brackets are there to build the 2D array needed to write to a range
}
So I tried again and again. I have lots of examples but none seem to work.
Thanks so much.
Chandra
For that to happen automatically (one sheet's change updating another sheet), you will surely need an "event/trigger" to run a script whenever you change a cell. (that is the "onEdit()" function).
But since scripts are likely to fail sometimes (even when they are perfect, that's because of some Google issues), it's not guaranteed that the sheets will always contain the same data.
But, if I could suggest another way, do not let ID be optional. If that is a real ID (like the person ID card number), create another ID exclusively for working with the sheet.
I have edited your second sheet showing a suggestion of how to do it without using scripts. The only things you must be aware of are:
Do not create two people with the same ID.
You have to insert (only) the ID manually in the second sheet.
The VLOOKUP forumla will search for that ID in the first sheet and return the data in the same line. You can sort any sheet in whatever way you like. As long as you don't change people's IDs.
So, in sheet 2, use this in the First Name, Last Name and Email address:
=vlookup(A10,Participant_Registration!$A:$D,2,false)
=vlookup(A10,Participant_Registration!$A:$D,3,false)
=vlookup(A10,Participant_Registration!$A:$D,4,false)
Just extend this formula downwards
I hope this helps. I would avoid scripting for that at any cost. It would be my last resort. (Scripts also need to be changed if you want to rearrange your sheet, and if not, they might cause trouble, write over existing data...)
I also added a button (insert - drawing) and put a script in it (right button, click down arrow, "transfer? script" -- translated from Portuguese).
If you lock all four columns in sheet2 and lock the ID column in sheet 1, people will not be able to chang IDs and cause mess. They can edit people in sheet 1 and not change the formula in sheet2. Script is not affected by sorting or empty spaces (it adds the person in the first empty row it finds).
I added "named ranges" for the four column headers. (With named ranges, the script can refer to names instead of coordinates, which enables you to rearrange the sheet inserting and deleting columns, or moving them with CUT and paste - but the VLOOKUP formula will need manual update if you rearrange columns).
Here is the code: (it could get better if you manage to create dialog boxes and ask for the person's data inside that dialog, then you could lock everything - and you would need an edit button besides the add).
function AddPerson()
{
var S1Name = "Participant_Registration";
var S2Name = "Learning_Sessions_Attendance";
var ID1Name = "regID";
var ID2Name = "learnID";
//these vars are not used in this script
var FN1Name = "regFirstName";
var FN2Name = "learnFirstName";
var LN1Name = "regLastName";
var LN2Name = "learnLastName";
var Email1Name = "regEmail";
var Email2Name = "learnEmail";
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var Sheet1 = sSheet.getSheetByName(S1Name);
var Sheet2 = sSheet.getSheetByName(S2Name);
var ID1 = getRangeByName(sSheet, Sheet1.getName(), ID1Name);
var ID2 = getRangeByName(sSheet, Sheet2.getName(), ID2Name); Logger.log("ID2: " + ID2.getValue());
var Empty1 = getFirstEmpty(ID1);
var Empty2 = getFirstEmpty(ID2);
var Biggest1 = getBiggestID(ID1); Logger.log("Biggest 1: " + Biggest1);
var Biggest2 = getBiggestID(ID2); Logger.log("Biggest 2: " + Biggest2);
if (Biggest1 !== Biggest2)
Browser.msgBox("Warning: there are IDs in one sheet that are not in the other sheet");
var Biggest;
if (Biggest1 > Biggest2) Biggest = Biggest1;
else Biggest = Biggest2;
Biggest++;
Empty1.setValue(Biggest);
Empty2.setValue(Biggest);
}
function getFirstEmpty(Header)
{
while (Header.getValue() !== "")
{
Header = Header.offset(1,0);
}
return Header;
}
function getBiggestID(Header)
{
var Sheet = Header.getSheet();
var LastRow = Sheet.getLastRow();
var Values = Sheet.getRange(Header.getRow(), Header.getColumn(), LastRow - Header.getRow() + 1).getValues();
var len = Values.length;
var MaxID = 1;
for (var i = 0; i < len; i++)
{
var val = Number(Values[i]);
if (!isNaN(val) && val > MaxID)
MaxID = val;
}
return MaxID;
}
function getRangeByName(spreadSheet, sheetName, rangeName)
{
Logger.log("Trying range: " + "'" + sheetName + "'!" + rangeName);
return spreadSheet.getRangeByName("'" + sheetName + "'!" + rangeName);
}
I have sucessfully written a small script, which creates a serial letter (physical letter to several recipients) based on data in a Google spreadsheet creating a new document for each letter/addresse.
It works, but for large mailings this approach is a bit cumbersome as a large amount of documents are created and need to be printed individually.
Now i would like to do the same but as a result having all of the letters in one single Google document.
Is there any way to copy the content of an existing document and inserting it a number of times into the same or any other documents (i.e. copy/paste via apps script)?
Following your comment, here is the full code I use to merge an undetermined number of docs in a new one.
All document IDs are in an array of IDs as argument for the main function, the results is a new doc with "multi-page" appended to the name. If you need more explanation than provided by the in code comments just let me know... (note that it will work only for documents containing text and tables, if you have images ot other data type you'll have to handle that case in the main loop where we check the ElementType following the same logic)
EDIT : first code removed, following your update I tried this approach assuming you have only paragraphs in your master doc... give it a try and I guess you could start from there to developp your project.
function Serialletter_Singledocument() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Datenbank");
var LastColumn = sheet.getLastColumn();
//here you need to get document id from url (Example, 1oWyVMa-8fzQ4leCrn2kIk70GT5O9pqsXsT88ZjYE_z8)
var FileTemplateFileId = "1Wrf2qvUTyc5tMmJIly40Z4U4sJb4-QhT5z-UfJmtQ-M" //Browser.inputBox("ID der Serienbriefvorlage (aus Dokumentenlink kopieren):");
var doc = DocumentApp.openById(FileTemplateFileId);
var DocName = doc.getName();
var headpara=' ***** ';
// Fetch entire table containing data
var data = sheet.getDataRange().getValues();
//Create copy of the template document and open it
var SerialLetterID = DocsList.getFileById(FileTemplateFileId).makeCopy(DocName +" Serienbrief").getId();
var docCopy = DocumentApp.openById(SerialLetterID);
var totalParagraphs = docCopy.getBody().getParagraphs() ;// get the total number of paragraphs elements
Logger.log(totalParagraphs);
var elements = [];
for ( var i = 1; i < data.length; i++) { //do for every record in the spreadsheet (containing the content to replace the variables in the letter)
for (var e=0;e<totalParagraphs.length;e++){
var element = totalParagraphs[e].copy();
// Logger.log(element.editAsText().getText())
for(var c=0;c<data[0].length;c++){
element.replaceText("<" +data[0][c] +">", data[i][c]); //replace variable (from column title) with actual value
}
elements.push(element);// store paragraphs in an array
}
for(var el=0;el<elements.length;el++){
var paragraph = elements[el].copy();
docCopy.getBody().appendParagraph(paragraph);
}
docCopy.getBody().appendPageBreak()
}
docCopy.saveAndClose();
Browser.msgBox("Serienbrief ist erstellt. Sie finden die erstellten Dokumente in Google Drive unter Meine Ablage");
}