Method template.copy is not working - Google Script - google-apps-script

I am facing a problem that is leaving me without a real solution.
I want that the script create a new Google Doc copy from a template, then transfer data from Sheet toward the new Doc.
The script is running without a bug, but fail to create a copy of the document as it supposed to do.
function transferData() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Tour de ContrĂ´le");
var templateId = "119ORijiWNQEdpG4KqjNCYzilbf_ob7SMdOPXUK3woMQ";
var folder = DriveApp.getFolderById("166g1akWuHR3MtoDzbh9ydMhEpr4euCts");
var data = sheet.getDataRange().getValues();
for (var i = 3; i < data.length; i++) {
if (data[i][12] === true) {
var nom = data[i][3];
var nFonction = data[i][6];
var template = DocumentApp.openById(templateId);
var newDoc = template.copy("Mouvement_" + nFonction + "_" + nom + "_EMBA");
newDoc.getAs(MimeType.GOOGLE_DOCS).getFile().moveTo(folder);
var docBody = newDoc.getBody();
var matricule = data[i][4];
var compta = data[i][16];
var division = data[i][1];
var fonction = data[i][6];
var respDoss = sheet.getRange("GDD!B3").getValue();
var endosGDD = sheet.getRange("GDD!B6").getValue();
var sign = sheet.getRange("GDD!B7").getValue();
docBody.replaceText("{Nom}", nom);
docBody.replaceText("{Matricule}", matricule);
docBody.replaceText("{NFonction}", nFonction);
docBody.replaceText("{Compta}", compta);
docBody.replaceText("{division}", division);
docBody.replaceText("{Fonction}", fonction);
docBody.replaceText("{RespDoss}", respDoss);
docBody.replaceText("{EndosGDD}", endosGDD);
docBody.replaceText("{Sign}", sign);
}
}
}

There's no DocumentApp.openById(templateId).copy() method. To make a copy of a doc you should use Driveapp instead.
You should change these lines of code
var template = DocumentApp.openById(templateId);
var newDoc = template.copy("Mouvement_" + nFonction + "_" + nom + "_EMBA");
newDoc.getAs(MimeType.GOOGLE_DOCS).getFile().moveTo(folder);
For these ones:
var template = DriveApp.openById(templateId);
var newDoc = template.makeCopy("Mouvement_" + nFonction + "_" + nom + "_EMBA", folder);
makeCopy(name, destination)
With SpreadsheetApp you can make a copy of a spreadsheet but you can't choose the folder where it will be created (it's created in the same folder where your spreadsheet "file" is located).
copy(name)

Finally, I modified const googleDocTemplate = DriveApp.getFolderById by const googleDocTemplate = DriveApp.getFileById
It worked.
Thanks for your help.
function transferData() {
//Here we store the sheet as a variable
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tour de ContrĂ´le");
//This value should be the id of your document template that we created in the last step
const googleDocTemplate = DriveApp.getFileById('119ORijiWNQEdpG4KqjNCYzilbf_ob7SMdOPXUK3woMQ');
//This value should be the id of the folder where you want your completed documents stored
const destinationFolder = DriveApp.getFolderById('166g1akWuHR3MtoDzbh9ydMhEpr4euCts');
const respDoss = sheet.getRange("GDD!B3").getValue();
const endosGDD = sheet.getRange("GDD!B6").getValue();
const sign = sheet.getRange("GDD!B7").getValue();
const rows = sheet.getDataRange().getValues();
//Logger.log(rows)
rows.forEach(function(row, index){
//Here we check if this row is the headers, if so we skip it
if (index === 0) return;
//Here we check if a document has already been generated by looking at 'Generated date', if so we skip it
//if (row[16] === true);
//Here we check if the value in column Q (index 17) is true, if not we skip this row
if (index >= 4) {
if (row[17] !== true){
return;
}
}
Logger.log(rows);
//Using the row data in a template literal, we make a copy of our template document in our destinationFolder
const copy = googleDocTemplate.makeCopy(`Mouvement_${row[6]}, ${row[3]}_EMBA` , destinationFolder);
//Once we have the copy, we then open it using the DocumentApp
const doc = DocumentApp.openById(copy.getId());
//All of the content lives in the body, so we get that for editing
const body = doc.getBody();
...

Related

Is there any way that we can translate a whole Google Docs file using Google (Apps) Script, BUT still keep all the formats, tables, images intact?

I'm trying to imitate the "Translate Document" tool in Google Docs by writing a Google (Apps) Script that can automatically translate a series of documents.
I've tried LanguageApp.Translate() but this syntax only returns an unformatted string and removes all the table borders (purely string).
This is my code:
function TranslateFunction() {
//Get the files in your indicated Folder
var TargetFolderID = '1VUNGtqiNbnHhIFCXmbdSwNZ-vZ5NWVTE'; //Paste the folder ID here to start
var folder = DriveApp.getFolderById(TargetFolderID);
var files = folder.getFiles();
//Get all the files' ID in the folder above
while (files.hasNext()){
var file = files.next();
var fileID = file.getId();
//Convert each file in the folder from Docx (Word) to Docs (Google)
var docx = DriveApp.getFileById(fileID);
var newDoc = Drive.newFile();
var blob = docx.getBlob();
var file=Drive.Files.insert(newDoc,blob,{convert:true});
DocumentApp.openById(file.id).setName(docx.getName().slice(0,-5));
//Activate the file
var doc = DocumentApp.openById(file.id);
//Create a new Docs file to put the translation in + Name
var newDoc = DocumentApp.create("|EN| " + docx.getName().slice(0,-5));
//Get the text in the file
var bodyText = doc.getBody().getText();
//Translate the text and append it into the new Docs
Translatedtext = LanguageApp.translate(bodyText,'vi','en');
newDoc.getBody().appendParagraph(Translatedtext);
}
}
Came across the same challenge and what seems to work for me is first creating a copy of the document I'm trying to translate and then looping through all paragraphs and translating them one by one.
In my use case I wanted to coordinate this from a Google Sheet as I mostly need multiple different translations from the same Doc that then need to be reviewed by colleagues, therefore I'd usually put source Doc URLs into a sheet, run a script that translates it to all needed languages and then feeds back the URLs into the sheet.
For source and target language use ISO-639 codes.
function translateDoc (sourceURL, sourceLang, targetLang) {
// get ID from URL, open file and get ID from the folder it's in
var sourceDocId = sourceURL.match(/[-\w]{25,}/);
var sourceDoc = DriveApp.getFileById(sourceDocId);
var sourceTitle = sourceDoc.getName();
var sourceFolderId = sourceDoc.getParents().next().getId();
// create a copy in the same folder and indicate the language in the title
var targetTitle = "[" + String(targetLang).toUpperCase() + " translation] " + sourceTitle;
var targetDoc = sourceDoc.makeCopy(targetTitle, DriveApp.getFolderById(sourceFolderId));
// open copied Doc and start looping through the content
var doc = DocumentApp.openById(targetDoc.getId());
var body = doc.getBody();
var elements = body.getParagraphs();
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var translatedText = LanguageApp.translate(element.getText(), sourceLang, targetLang);
if(translatedText != "") element.setText(translatedText);
// I had an error with an empty paragraph, so avoiding this by only updating when not empty
}
doc.saveAndClose();
return "https://docs.google.com/document/d/" + targetDoc.getId(); // returns URL of translated doc
}
function translateDocs () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var rows = sheet.getDataRange().getValues();
var languages = ["de", "fr", "es", "it", "nl", "pl", "pt"];
var sourceFileURL, translationDoc;
rows.forEach(function(row, index){
if (index === 0) return; // table headers
if (row[0] == "") return; // no source file
if (row[1] != "") return; // column 2 not empty --> already translated this file
sourceFileURL = row[0];
for (var i = 0; i < languages.length; i++) {
translationDoc = translateDoc(sourceFileURL, "en", languages[i]);
sheet.getDataRange().getCell(parseInt(index) + 1,parseInt(i) + 2).setValue(translationDoc);
}
});
}

Copy Row from one Sheet to another sheet which is linked in the row

I have previously been able to copy rows from one Spreadsheet to another, however what I am trying to do now is copy a row to a specific spreadsheet that is linked on the same row and I can't seem to work it out.
I have around 500 rows of names with data (I cannot share a sheet as my firms Google does not allow for sharing externally but I have a screenshot of dummy data below). I used a formula to find the Unique names and then created a Google Sheet for each person and linked the Sheet back to the master data.
Master Data Sheet (tab is called Sheet1)
I am looking for a script that will work through the sheet, copying the rows to the link that is on the same row but I just can't figure it out - the following script does not work but I am at a loss so any help would be appreciated.
Apologies if I haven't explained myself very well!!
function copyTo(){
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = sSheet.getSheetByName("Sheet1");
var data = srcSheet.getDataRange().getValues();
for(var i = 1; i < data.length; i++) {
var row = data[i];
var link = row[3];
var id = row[4];
var complete = row[5];
var COMPLETE = "Complete"
if(link !== "" && complete !== "Complete"){
var srcRange = srcSheet.getRange("A" + i + ":C" + i);
var linkID = id.toString()
var tarSS = DriveApp.getFileById(linkID);
var tarSheet = tarSS.getSheetbyName("Sheet1");
var tarRow = tarSheet.getLastRow();
//tarSheet.insertRowAfter(tarRow);
var tarRange = tarSheet.getRange("A" + (tarRow+1) + ":C" + (tarRow+1));
srcRange.copyTo(tarRange);
srcSheet.getRange(i+1,6).setValue(COMPLETE)
}
}};
Try this:
function copyTo() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Sheet1");
var vs = sh.getDataRange().getValues();
for (var i = 1; i < vs.length; i++) {
var row = vs[i];
var link = row[3];
var id = row[4];
var complete = row[5];
var COMPLETE = "Complete"
if (link !== "" && complete !== "Complete") {
var rg = sh.getRange("A" + i + ":C" + i);
var linkID = id.toString()
var tarSS = SpreadsheetApp.openById(linkID);
var tarSheet = tarSS.getSheetbyName("Sheet1");
var tarRow = tarSheet.getLastRow();
var tarRange = tarSheet.getRange("A" + (tarRow + 1) + ":C" + (tarRow + 1));
rg.copyTo(tarRange);
sh.getRange(i + 1, 6).setValue(COMPLETE)
}
}
};
As much as I understand you question here is the script which will work for you exactly you want .
This will copy data from sheet you want and then create a new spreadsheet and paste into that sheet then link will show of this in same sheet from data copied
Let me know how this worked .
Code.gs
const sendDataToSheet = () => {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const ws = ss.getSheetByName(''paste here your sheet name'')
const newSheet = SpreadsheetApp.create("give new sheet name here")
ws.copyTo(newSheet).setName("Copied Data")
const sheetURL = newSheet.getUrl()
ws.insertRowBefore(1)
ws.getRange(1,1).setValue(sheetURL)
}

Put a = before text jump

Hello I made this script to export the data from a column to a .txt on my google drive
function maquinasBonusHunt() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var range = sheet.getRange('G3:G' + sheet.getLastRow());
var rows = range.getValues().filter(([g]) => g.toString() != "");
var fileName="maquinas.txt";
var folderName="Videos";
var data = rows.splice(0);
var str = data.map(function(e) {return e.join()}).join("\n");
var separador = [" - ", " = "];
var content = str;
// get list of folders with matching name
var folderList = DriveApp.getFoldersByName(folderName);
if (folderList.hasNext()) {
// found matching folder
var folder = folderList.next();
// search for files with matching name
var fileList = folder.getFilesByName(fileName);
if (fileList.hasNext()) {
// found matching file - append text
var file = fileList.next();
var combinedContent = content;
file.setContent(combinedContent);
}
else {
// file not found - create new
folder.createFile(fileName, content);
}
}
}
Everything is fine but, I need to put a "=" before they jump to other text like y show you on the screenshot.
https://i.stack.imgur.com/xvXDA.png
I believe your goal as follows.
You want to add = to the last character of each row.
In order to achieve your goal, I would like to propose the following modification.
Pattern 1:
In this pattern, = is added when join is run.
From:
var str = data.map(function(e) {return e.join()}).join("\n");
To:
var str = data.map(function(e) {return e.join()}).join("=\n") + "=";
Pattern 2:
In this pattern, = is added after filter was used.
From:
var rows = range.getValues().filter(([g]) => g.toString() != "");
To:
var rows = range.getValues().filter(([g]) => g.toString() != "").map(([v]) => [v + "="]);
References:
join()
filter()
map()

Google Spread Sheet Export CSV in the same folder

I am using the script below, working very well (thanks to Ted Bell).
But I need to adapt it because I need the CSV file saved in the same folder as the spreadsheet.
Could you please help me with this matter?
The code below creates a new folder each time on My Drive.
The CSV is ok regarding its name and its format: with semicolon delimiter.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [{
name: "export as csv file",
functionName: "saveAsCSV"
}];
ss.addMenu("CSV Export", csvMenuEntries);
var a1 = ss.getRange("A1").getValue();
var name = "MyCompanyName_"+a1;
ss.rename(name);
};
function saveAsCSV() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssname = ss.getName();
var sheet = ss.getActiveSheet();
var sheetname = sheet.getSheetName();
//Logger.log("DEBUG: the name of the spreadsheet is "+ssname);//DEBUG
//Logger.log("DEBUG: the sheet name is "+sheetname);// DEBUG
//// create a folder from the name of the spreadsheet
var folder = DriveApp.createFolder(ssname.toLowerCase() + '_' +
sheetname.toLowerCase().replace(/ /g, '_') + '_csv_' + new Date().getTime());
//Logger.log("DEBUG: the folder name is "+folder);//DEBUG
// append ".csv" extension to the sheet name
var fileName = ssname + '_' + sheetname + ".csv";
// convert all available sheet data to csv format
var csvFile = so_4225484202(fileName);
// create a file in the Docs List with the given name and the csv data
folder.createFile(fileName, csvFile);
Browser.msgBox('Files are waiting in a folder named ' + folder.getName());
}
function isValidDate(date) {
return date && Object.prototype.toString.call(date) === "[object Date]" && !isNaN(date);
}
function so_4225484202(filename) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var paramsheet = ss.getSheetByName("Parameters");
var linearray = [];
var rowdata = [];
var csv = "";
var fieldvalue = "";
var param = paramsheet.getRange(2, 2, 2);
var paramValues = param.getValues();
//Logger.log("DEBUG: parameters = "+param.getA1Notation());//DEBUG
var fieldDelimiter = paramValues[0][0];
var textDelimiter = paramValues[1][0];
//Logger.log("DEBUG: field delimiter: "+fieldDelimiter+", text delim:
"+textDelimiter);//DEBUG
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
//Logger.log("DEBUG: lastColumn: "+lastColumn+", lastRow: "+lastRow);//DEBUG
// Get array of values in the Data Range
var rangeValues = rangeData.getValues();
// Loop through array and build values for csv
for (i = 0; i < lastRow; i++) {
for (j = 0; j < lastColumn; j++) {
var value = rangeValues[i][j];
var theType = typeof value;
if (theType === "object") {
var testdate = isValidDate(value);
//Logger.log("if typeof is object: testdate: "+testdate);//DEBUG
var testtype = typeof testdate;
if (testtype === "boolean") {
// variable is a boolean
//Logger.log("Its a date");//DEBUG
theType = "date";
} else {
//Logger.log("Its not a date");//DEBUG
}
}
if (theType === "string") {
value = textDelimiter + value + textDelimiter;
}
rowdata.push([value]);
};
//Logger.log("DEBUG: rowdata: "+rowdata);//DEBUG
csv += rowdata.join(fieldDelimiter) + "\n";
var rowdata = [];
};
//Logger.log("DEBUG: csv: "+csv);//DEBUG
return csv;
}
You want to create the CSV file in the same folder of the active Spreadsheet.
You want to achieve this by modifying your script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification point:
In order to retrieve the folder of the active Spreadsheet, DriveApp.getFileById(ss.getId()).getParents().next() is used for retrieving the folder.
Modified script:
Please modify the function of saveAsCSV() in your script as follows.
From:
var folder = DriveApp.createFolder(ssname.toLowerCase() + '_' + sheetname.toLowerCase().replace(/ /g, '_') + '_csv_' + new Date().getTime());
To:
var folder = DriveApp.getFileById(ss.getId()).getParents().next();
References:
getId()
getFileById()
getParents()
If I misunderstood your question and this was not the direction you want, I apologize.

"TypeError: Cannot read property 'getBlob' of undefined" when trying to get two different images and insert them into a document

I have a Google Apps Script that automatically extracts data from a Google Sheet and inserts it into a pre specified template. The data is found using unique tagNumbers/identifiers.
The data being extracted includes 3 signatures. I can only extract one of these signatures before I encounter the aforementioned error: TypeError: Cannot read property 'getBlob' of undefined.
This code is being used in two different functions, using all the same variables and names. I have tried changing variable names but this has resulted in the same TypeError.
The spreadsheet can be found here.
The script is here.
And the document template being filled is here.
Here is the code.
function electInstallSignature(row, body){
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
var sigElectInstaller = "%SIGNELECTINSTALL%";
var targetRange = body.findText(sigElectInstaller); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigElectInstaller, ""); // Remove the placeholder
}
function commEngineerSignature(row, body){
var signature = row[35];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
var sigCommEngineer = "%SFCE%";
var targetRange = body.findText(sigCommEngineer); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigCommEngineer, ""); // Remove the placeholder
}
As you can see, the code is the exact same in both functions, but only works in the electInstallSignature(row, body) function.
Below you can find where the row and body parameters are declared.
function chooseRowMethodI(templateId, rowNumber){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var data = sheet.getRange(2, 2, 10, 41).getValues();//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var docTitle = sheet.getRange(2, 2, 10, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 5, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
var row = data[rowNumber];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
//************************** All Instruments data in here**********************
instrumentDetails(body, row);
electInstallSignature(row, body);
commEngineerSignature(row, body);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("1Jylk3uO_WU0ClLQdm9y-mwRfHxlh2Ovn");
newFolder.addFile(file);
var newDocTitle = docTitle[i - 1][0];
var newDocTagNumber = docTitleTagNumber[i - 1][0];
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
}
}
Should it be required, I have included the function where everything is run from (note that any ui and user input code is tabbed out to avoid having to navigate back to the spreadsheet every time the code is run).
var response = "FT101";
function chooseRow(){
// var ui = SpreadsheetApp.getUi(); // Same variations.
// var result = ui.prompt('Please enter the Tag number of the row you wish to print.', ui.ButtonSet.OK_CANCEL);
//
// // Process the user's response.
// var button = result.getSelectedButton();
// response = result.getResponseText();
// if (button == ui.Button.OK) {
// // User clicked "OK".
// ui.alert('Your tag number is' + response + '.');
// } else if (button == ui.Button.CANCEL) {
// // User clicked X in the title bar.
// ui.alert('You closed the dialog.');
// return 'the end';
// }
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var category = sheet.getRange(2, 3, 11, 1).getValues();//Needs to be verified to ensure correct cell is chosen by script
var tags = sheet.getRange(2, 5, 11, 1).getValues();//Needs to be verified to ensure correct cell is chosen by script
for(var i = 0; i < tags.length; i++){
if(tags[i][0] == response && category[i][0] == "Instrument"){
var templateId = "1N3o951ECS5CAVGE6UgqBiCPC7H7LiJbL7Cd59G1xTnA";
chooseRowMethodI(templateId, i);
return "";
} else if(tags[i][0] == response && category[i][0] == "Motor" || tags[i][0] == response && category[i][0] == "Valve"){
var templateId = "1cSPD23qFd-34-IIr5eJ5a5OgHp9YR6xav9T28Y4Msec";
chooseRowMethodMV(templateId, i);
return "";
}
}
}
This errors TypeError: Cannot read property 'getBlob' of undefined means that the object you are trying to getBlob from it does not have any blob data.
The only difference with the frist function and the second one is the first line: row[17] instead of row[35] this means the following:
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
So, you are probably never accessing to the while loop:
while(files.hasNext())
because var files = sigFolder.getFilesByName(sign); never had a next, and thus, as file is not initialized, it is undefined.
In summary, the error you are getting is:
file is undefined
This means that you never assigned anything on this variable, which only happens if you never accessed the while, which only happens if files never had a next.
Which happens because there aren't any files at all there, this means that there isn't any file with the name sign on the sigFolder. Or that the row[17] does not contain any substantial information about the filename you want to access.
So, check this.
Also, take into account the following documentation about iterators like the one you are handling on files:
When you do files.next() you are accesing the first element of the iterator:
File iterator
General documentation on JavaScript iterators:
Iterators and generators on Javascript