I have a created a slide template in which multiple teams will use to use to create an information sheet about their respective Process. This content is intended to be produced in a standardised way.
To make things easier I have created a sheet in which key information can be input into cells to populate elements of the template.
The issue I'm having is I want each process to have a different colour scheme. Is there a way I can set a cell to the desired font or background colour in sheets and then get the template to create in that colour? or even a colour scheme?
How do I single out certain text or text boxes of the slide to set the background colour?
Desired Solution
Any help is much appreciated
// Add a custom menu to the active spreadsheet, including a separator and a sub-menu.
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Templates')
.addItem('Cheat Sheet', 'createNewCase')
.addToUi();
}
function createNewCase(input) {
var category = input
var ui = SpreadsheetApp.getUi();
// get active spreadsheet
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = activeSpreadsheet.getActiveSheet();
// Check to ensure script being run from 'Option 1' sheet
if (activeSheet.getName() != "Sheet1") {
ui.alert("Active sheet must be 'Sheet1', terminating script");
return;
}
//Get active cell
var sourceRow = activeSheet.getActiveCell().getRow();
// Read case variables
var sourceProcess = activeSheet.getRange('A' + sourceRow).getValue();
var sourceName1 = activeSheet.getRange('B' + sourceRow).getValue();
var sourceName2 = activeSheet.getRange('C' + sourceRow).getValue();
var sourceTeam = activeSheet.getRange('D' + sourceRow).getValue();
var sourcePurpose = activeSheet.getRange('E' + sourceRow).getValue();
var sourceDef = activeSheet.getRange('F' + sourceRow).getValue();
var sourceColor = activeSheet.getRange('G' + sourceRow).getFontColor();
// Message prompt
var userChoice = ui.alert("You are about to generate a Cheat Sheet template for " + sourceProcess +"\n\nDo you wish to continue?\n\n",ui.ButtonSet.YES_NO);
if (userChoice == ui.Button.NO) {
ui.alert("Terminating script");
return;
}
//Set case document filename
var newFilename = sourceProcess + "Process Cheat Sheet"
//Get template folder
var templateFolder = DriveApp.getFolderById("1MCQn9_oiaaUt4_dNzr2EoDWkUGsYOH-O");
// Get case Template document
var docTemplate = DriveApp.getFileById("1cmm1ifvfRv7omGVmRhD0zunCngBcIY_qo2jpCA_sHV4");
var caseDocument = docTemplate.makeCopy(newFilename);
var caseDocumentID = caseDocument.getId();
var caseSlide = SlidesApp.openById(caseDocumentID);
var caseName1 = caseSlide.replaceAllText("%NAME1%", sourceName1);
var caseName2 = caseSlide.replaceAllText("%NAME2%", sourceName2);
var caseProcess = caseSlide.replaceAllText("%PROCESS%", sourceProcess);
var caseTeam = caseSlide.replaceAllText("%TEAM%", sourceTeam);
var casePurpose = caseSlide.replaceAllText("%PURPOSE%", sourcePurpose);
var caseDef = caseSlide.replaceAllText("%DEFINITION%", sourceDef);
// Record Case Document link
activeSheet.getRange('H' + (sourceRow)).setFormula('=HYPERLINK("' + caseDocument.getUrl() + '", "Cheat Sheet Template")');
}
Related
I started with this project hoping to be able to have the Google Places API automate the look up of specific business and place information. While I started with individual functions, I see that this created an inordinate amount of requests that blew through my free monthly Google Cloud credit. This was because I wrote functions which were being recalled every time the Sheet was opened in any instance.
Instead, I want to only have the functions run when I use the custom UI button to call it. But how can I specify that it should only run when places do not have their information already populated in the Sheet?
I would have entered the name of a place in Column A, and in Columns B - F, I will have the function find the requested information. My script looks at the name of the place in Column A and finds the Google Place ID. From there, it formats a URL for that Google Places entry and pulls in the requested information from the concatenated URL.
Here is my current code:
// This location basis is used to narrow the search -- e.g. if you were
// building a sheet of bars in NYC, you would want to set it to coordinates
// in NYC.
// You can get this from the url of a Google Maps search.
const LOC_BASIS_LAT_LON = "37.7644856,-122.4472203"; // e.g. "37.7644856,-122.4472203"
function COMBINED2(text) {
var API_KEY = 'AxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxQ';
var baseUrl = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json';
var queryUrl = baseUrl + '?input=' + text + '&inputtype=textquery&key=' + API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var placeId = JSON.parse(json);
var ID = placeId.candidates[0].place_id;
var fields = 'name,formatted_address,formatted_phone_number,website,url,types,opening_hours';
var baseUrl2 = 'https://maps.googleapis.com/maps/api/place/details/json?placeid=';
var queryUrl2 = baseUrl2 + ID + '&fields=' + fields + '&key='+ API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;
if (ID == '') {
return 'Give me a Google Places URL...';
}
var response2 = UrlFetchApp.fetch(queryUrl2);
var json2 = response2.getContentText();
var place = JSON.parse(json2).result;
var placeAddress = place.formatted_address;
var placePhoneNumber = place.formatted_phone_number;
var placeWebsite = place.website;
var placeURL = place.url;
var weekdays = '';
place.opening_hours.weekday_text.forEach((weekdayText) => {
weekdays += ( weekdayText + '\r\n' );
} );
var data = [ [
place.formatted_address,
place.formatted_phone_number,
place.website,
place.url,
weekdays.trim()
] ];
return data;
}
// add menu
// onOpen is a special function
// runs when your Sheet opens
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Custom Menu")
.addItem("Get place info","COMBINED2")
.addToUi();
}
I received help on a separate post which advised that I use another function to call COMBINED2 but I am not sure whether that still applies with my change of plans.
// this function calls COMBINED2()
function call_COMBINED2() {
var ss = SpreadsheetApp.getActiveSheet();
var text = ss.getRange("A2").getValue();
var data = COMBINED2(text);
var dest = ss.getRange("B2:F2");
dest.setValues(data);
}
Should it make a difference, my plan for down the road will be to have two buttons in the custom UI. One will work to do the initial lookup of place data. The second will do a refresh. If a change is detected and a cell is changed/updated, then it will highlight in some fashion so that I can make note of this.
The project is part of how I travel. I will often make running Google Sheet lists of recommended and vetted places of interest, bars, and restaurants so that I can import the Sheet into a Google MyMap for reference when we're actually visiting. Over time, these Sheets/MyMaps tend to become obsolete with changes (especially with COVID). I hope this serves to future-proof them and make updating them easier.
The onOpen trigger in your script is only for adding the Custom Menu in your Sheet. The function will only get executed when the user selected an Item in the menu that is associated with the function. In your example, clicking Get place info will execute the COMBINED2 function.
Also, executing the script only when the place information is not present in the sheet is not possible, you have to run the script to get the identifier of the place and compare it to the data in the Sheet. In your example, place.url can be used as identifier. The only thing you can do is to prevent the script from populating the Sheet.
Here I updated your script by changing the function associated to the Get place info to writeToSheet(). writeToSheet() will call COMBINED2(text) to get the place information and use TextFinder to check if the place url exists in the Sheet. If the result of TextFinder is 0, it will populate the Sheet.
// const LOC_BASIS_LAT_LON = "40.74516247433546, -73.98621366765811"; // e.g. "37.7644856,-122.4472203"
const LOC_BASIS_LAT_LON = "37.7644856,-122.4472203";
function COMBINED2(text) {
var API_KEY = 'enter api key here';
var baseUrl = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json';
var queryUrl = baseUrl + '?input=' + text + '&inputtype=textquery&key=' + API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var placeId = JSON.parse(json);
var ID = placeId.candidates[0].place_id;
var fields = 'name,formatted_address,formatted_phone_number,website,url,types,opening_hours';
var baseUrl2 = 'https://maps.googleapis.com/maps/api/place/details/json?placeid=';
var queryUrl2 = baseUrl2 + ID + '&fields=' + fields + '&key='+ API_KEY + "&locationbias=point:" + LOC_BASIS_LAT_LON;
if (ID == '') {
return 'Give me a Google Places URL...';
}
var response2 = UrlFetchApp.fetch(queryUrl2);
var json2 = response2.getContentText();
var place = JSON.parse(json2).result;
var weekdays = '';
place.opening_hours.weekday_text.forEach((weekdayText) => {
weekdays += ( weekdayText + '\r\n' );
} );
var data = [
place.name,
place.formatted_address,
place.formatted_phone_number,
place.website,
place.url,
weekdays.trim()
];
return data;
}
function getColumnLastRow(range){
var ss = SpreadsheetApp.getActiveSheet();
var inputs = ss.getRange(range).getValues();
return inputs.filter(String).length;
}
function writeToSheet(){
var ss = SpreadsheetApp.getActiveSheet();
var lastRow = getColumnLastRow("A1:A");
var text = ss.getRange("A"+lastRow).getValue();
var data = COMBINED2(text);
var placeCid = data[4];
var findText = ss.createTextFinder(placeCid).findAll();
if(findText.length == 0){
ss.getRange(lastRow,2,1, data.length).setValues([data])
}
}
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Custom Menu")
.addItem("Get place info","writeToSheet")
.addToUi();
}
Output:
Example 1:
After clicking custom menu:
Example 2:
After clicking custom menu:
Note: Since we are using lastRow, new entry must be inserted below the last row of column A. Otherwise it will overwrite the last entry.
Reference:
Custom Menu
Place Details
I've been working on the process of auto populating a form based on a Google form submission. All of my progress to date has been what I learned on this site. Thanks for all the help!
I got everything to work exactly how it needed to run, assuming that the form is filled out in its entirety. The technician fills out the google form and submits. After he/she submits, the script automatically generates a custom document with all the data.
After some runtime, the technicians asked that we make the Google Form editable after submission. I did some research and found that you can make a form editable and wrote some script to auto generate a custom URL for each form.
Now I'm running into an issue with the auto Doc when partial forms are submitted. If the technician fills out the 1'st half of the form and submits, a Doc is auto generated with only the 1st half data, which I expected. Now if they go back and fill out the 2nd half of the form, only the 2nd portion of the Doc is filled out. I thought it would pull the entire row of data from the Sheet, but this isn't the case.
After some thought, I think I need to rewrite the code to not auto generate the Doc when a form is submitted, because more and more forms will be filled out in pieces, and completed at a later date.
Here's is what I would like to do. Any insight or helpful links would be greatly appreciated! Thanks so much!
All of the form data currently goes to the Sheet, Tab A. I would have a second Tab B on the sheet named "Data to Doc". I would manually comb through Tab A and copy and past the data to Tab B that I would like to create Doc's for. On Sheet B, There would be a button that I can click that would run the script to convert the sheet data to a Doc file.
I found a video of what I think I want to do, but I cant seem to make my code work the way they did.
https://www.youtube.com/watch?v=r9uU_KwGgzQ
Below is my current code.
var docTemplate = "fdgfdg";
var docName = "Technician Report";
var folder = DriveApp.getFolderById('hjhjghhgjgh');
function onFormSubmit(e) {
var replaceTextToImage = function(body, searchText, fileId) {
var width = 300; // Please set this.
var blob = DriveApp.getFileById(fileId).getBlob();
var r = body.findText(searchText).getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, blob);
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
//Get information from form and set as variables
var Technician = e.values[1];
var Customer_Name = e.values[2];
var Date = e.values[3];
var Facility_Location = e.values[4];
var WO_Project_No = e.values[5];
var PO_No = e.values[6];
var Tag_No = e.values[7];
var Site_Contact = e.values[8];
var Repair_Scope = e.values[9];
var Valve_Serial_No = e.values[10];
var Valve_Model = e.values[11];
var Valve_Condition = e.values[12];
var Valve_Action = e.values[13];
var Act_Serial_No = e.values[14];
var Act_Model = e.values[15];
var Act_Condition = e.values[16];
var Act_Action = e.values[17];
var Cont_Serial_No = e.values[18];
var Cont_Model = e.values[19];
var Cont_Condition = e.values[20];
var Cont_Action = e.values[21];
var Recommended_Actions = e.values[30];
var Call_Notes = e.values[32];
var Picture1_Notes = e.values[23];
var Picture2_Notes = e.values[25];
var Picture3_Notes = e.values[27];
var Picture1_Image = e.values[22].split("=")[1];
var Picture2_Image = e.values[24].split("=")[1];
var Picture3_Image = e.values[26].split("=")[1];
// Get document template, copy it as a new temp doc, and save the Doc’s id
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName+' for '+Customer_Name+' '+Tag_No, folder)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getBody();
copyBody.replaceText('A1', Technician);
copyBody.replaceText('A2', Customer_Name);
copyBody.replaceText('A3', Date);
copyBody.replaceText('A4', Facility_Location);
copyBody.replaceText('A5', WO_Project_No);
copyBody.replaceText('A6', PO_No);
copyBody.replaceText('A7', Tag_No);
copyBody.replaceText('A8', Site_Contact);
copyBody.replaceText('A9', Repair_Scope);
copyBody.replaceText('B1', Valve_Serial_No);
copyBody.replaceText('B2', Valve_Model);
copyBody.replaceText('B3', Valve_Condition);
copyBody.replaceText('B4', Valve_Action);
copyBody.replaceText('B5', Act_Serial_No);
copyBody.replaceText('B6', Act_Model);
copyBody.replaceText('B7', Act_Condition);
copyBody.replaceText('B8', Act_Action);
copyBody.replaceText('B9', Cont_Serial_No);
copyBody.replaceText('C1', Cont_Model);
copyBody.replaceText('C2', Cont_Condition);
copyBody.replaceText('C3', Cont_Action);
copyBody.replaceText('C4', Recommended_Actions);
copyBody.replaceText('C5', Call_Notes);
copyBody.replaceText('C8', Picture1_Notes);
copyBody.replaceText('D1', Picture2_Notes);
copyBody.replaceText('D3', Picture3_Notes);
replaceTextToImage(copyBody, 'C7', Picture1_Image);
replaceTextToImage(copyBody, 'C9', Picture2_Image);
replaceTextToImage(copyBody, 'D2', Picture3_Image);
copyDoc.saveAndClose();
Assuming you have a script bound to your Form with an onSubmit trigger, you can retrieve the length of the latest response and comprare it to the total amount of questions:
function myFunction(e) {
var length = FormApp.getActiveForm().getItems().length;
if(e.response.getItemResponses().length == length){
//proceed to generate custom document
}else{
Logger.log("Form is not complete");
}
}
UPDATE: Alternative solution create doc if checkbox has been checked
In this case, you do not want to run the function when the form is submitted, but when an edit of the checkbox has been made.
So, replace the onSubmit trigger through an onEdit trigger.
This will alow you to access the event objects e.range, e.oldValue and e.value.
You can implement an if statement that runs the code only if an edit in the checkbox column has been made and if the old value was false (unchecked) and the new one istrue` (checked).
Unfortunately you cannot retrieve your form response onEdit with e.values, instead, you can verify in which row the checkbox has been checked and retrieve the values of this row with sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues();
To implement the change, create a column with checkboxes (lets say in column 29, since the columns before are ocupied with responses) and mofify your code as following:
function onEdit(e){
if(e.range.getColumn() == 29 || e.oldValue == false && e.value == true){
var row = e.range.getRow();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var values = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues()[0];
var replaceTextToImage = function(body, searchText, fileId) {
var width = 300; // Please set this.
var blob = DriveApp.getFileById(fileId).getBlob();
var r = body.findText(searchText).getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, blob);
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
//Get information from form and set as variables
var Technician = values[1];
var Customer_Name = values[2];
...
...
copyDoc.saveAndClose();
}
}
I'm building a search tool by which one can convert a google range into an image using google script. I tried to paste that data range to google slides and from there I get thumbnails and their url. I'm searching any other tools that directly give me the url of image of the selected range of a google sheet.
Thanks
This is a very interesting question.
I am unsure of the reasoning behind doing this, but nonetheless, here is an answer:
This creates a custom menu on the top of your sheet that says:
Custom Functions => Export Range to Image Files.
When you click that it:
Turns whatever you have selected into a table in sheets
Saves it
Generates an image from that
Saves image to drive
Show a pop-up with the links of the saved images.
The code is ready to handle multiple ranges being exported, but right now it exports only the selected range.
function onOpen(e) {
//Create custom menu to export range to Slides.
SpreadsheetApp.getUi()
.createMenu('Custom Functions')
.addItem('Export Range to Image Files', 'SelectedRangeToImage')
.addToUi();
}
function SelectedRangeToImage() {
var slide = RangeToSlides();
var slideId = slide.getId();
var images = [];
for (var x=0; x<slide.getSlides().length;x++) {
var image = SlidesToImage(slide.getName()+x, slideId, slide.getSlides()[x].getObjectId());
images.push(image);
}
//Show interface with links to all images
var ui = SpreadsheetApp.getUi();
var html = HtmlService.createHtmlOutput();
html.append("<p>Your images:</p>");
html.append("<ul>");
for (var i=0; i<images.length; i++) {
html.append("<li><a href='"+images[i].getUrl()+"'>"+images[i].getName()+"</a></li>");
}
html.append("</ul>");
html.append("<input type='button' value='Close' onclick='google.script.host.close()' />");
ui.showModalDialog(html, "Exporting results:");
}
function RangeToSlides() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getActiveRange();
var rangeValues = range.getDisplayValues();
var rangeHorizontalAlignments = range.getHorizontalAlignments()
var rangeBackgrounds = range.getBackgrounds();
var rangeFontWeights = range.getFontWeights();
var sl = SlidesApp.create("ExportToImage"+new Date());
var slide = sl.getSlides()[0];
//Create table with size of the range
var table = slide.insertTable(rangeValues.length, rangeValues[0].length);
for (var x=0; x<rangeValues.length; x++) {
for (var y=0; y<rangeValues[x].length; y++) {
var cell = table.getCell(x,y);
cell.getText().setText(rangeValues[x][y]); //Set text
cell.getFill().setSolidFill(rangeBackgrounds[x][y]); //Set background
cell.getText().getTextStyle().setBold(rangeFontWeights[x][y]=="bold"?true:false); //Set text formatting
var alignment;
switch(rangeHorizontalAlignments[x][y]) {
case "general-left":
alignment = SlidesApp.ParagraphAlignment.START;
break;
case "general-right":
alignment = SlidesApp.ParagraphAlignment.END;
break;
case "center":
alignment = SlidesApp.ParagraphAlignment.CENTER;
break;
}
cell.getText().getParagraphStyle().setParagraphAlignment(alignment); //Set text alignment
}
}
sl.saveAndClose();
return sl;
}
function SlidesToImage(name, presentationId, slideId) {
var url = "https://slides.googleapis.com/v1/presentations/"+presentationId+"/pages/"+slideId+"/thumbnail";
var options = {
headers: {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
}
};
var response = UrlFetchApp.fetch(url, options);
var responseJson = JSON.parse(response.getContentText());
var imageurl = responseJson.contentUrl;
var imageResponse = UrlFetchApp.fetch(imageurl, options);
var blob = imageResponse.getBlob();
blob.setName(name);
var resultingFile = DriveApp.createFile(blob);
return resultingFile;
}
Hope this helps!
References:
https://stackoverflow.com/a/51391196/11869748
How to download Google Slides as images?
https://developers.google.com/slides/reference/rest/v1/presentations.pages/getThumbnail
I created some simple code that pulls a Google Doc URL from a spreadsheet in cell B2. The code works for some documents, but not others.
The program correctly creates a duplicate of the doc, which preserves all formatting. From there, I aim to replace the English text with Spanish.
I run into errors with Http:// links and some documents fully reformat, others do not.
Suggestions?
//This function works to create a nearly perfect copy of the document from English to Spanish. Problems with URLs and some long docs.
function spanishConverter() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //bound to Google Sheet
var listDoc = ss.getSheetByName('Doc List'); //SpreadSheet Page
var docEng = listDoc.getRange(2,2).getValue(); //Cell pulling URL from
var currentDoc = DocumentApp.openByUrl(docEng); //Opens the English version from URL on SpreadSheet
var docName = currentDoc.getName();
var docId = currentDoc.getId();
var newDoc = DriveApp.getFileById(docId).makeCopy('Spanish ' + docName).getUrl(); //Opens and makes a copy and captures URL
var newSp = DocumentApp.openByUrl(newDoc);
var body = newSp.getBody();
var paragraphs = body.getParagraphs();
//This script loops throught the paragraphs
for(var i=0; i<paragraphs.length; i++)
{
var text = paragraphs[i].getText(); //pulls text per paragraph
var transText = LanguageApp.translate(text, 'en', 'es');
Logger.log([text,transText]);
if (text !== "") //skips blanks
{
body.replaceText(text, transText);
}
}
}
This is the full solution. HTML links are skipped unless they are interpreted as a text element.
// This function works to create a nearly perfect copy of the document
// from English to Spanish.
function spanishConverter() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //bound to Google Sheet
var listDoc = ss.getSheetByName('Doc List'); //SpreadSheet Page
var docEng = listDoc.getRange(2,2).getValue(); //Cell pulling URL from
var currentDoc = DocumentApp.openByUrl(docEng); //Opens the English version from URL on SpreadSheet
var docName = currentDoc.getName();
var docId = currentDoc.getId();
var newDoc = DriveApp.getFileById(docId).makeCopy('Spanish - ' + docName).getUrl(); //Opens and makes a copy and captures URL
var newSp = DocumentApp.openByUrl(newDoc);
var body = newSp.getBody();
var paragraphs = body.getParagraphs();
//This script loops throught the paragraphs
for(var i=0; i<paragraphs.length; i++)
{
var para = paragraphs[i];
var text = para.getText(); //pulls text per paragraph
var atts = para.getAttributes(); //pulls formatting attributes to set when appending text
var transText = LanguageApp.translate(text, 'en', 'es');
Logger.log([atts]);
if (text !== "") //skips blanks
{
para.clear();
para.appendText('{sp}').setAttributes(atts);
para.replaceText('{sp}', transText);
}
}
}
I am attempting to create a PDF based on the entries in a spreadsheet with the basic flow of the following.
Fill in spreadsheet
Fire off the submit from a menu button (pop up for asking for Job Number)
Templated Doc gets a copy made and cell contents appended to a table in the copy doc.
Copy doc gets saved as a PDF in a specific folder.
copy of doc gets deleted.
I am just trying to get the process of getting the PDF created in the specified folder. It was working prior to my creating a copy of the Doc and just using the template so I am not sure why it will not work now. Any input would be appreciated as well as any info on how to append the cell data from the sheet into the copy of the doc. Newby to be sure so any help would be appreciated. Code attached
function createDoc () {
var job = Browser.inputBox('Enter Job Number', 'Job Number', Browser.Buttons.OK);
var dtStr = Utilities.formatDate(new Date(), "GMT", "MMddyy")
// create temp file before edited with spreadsheet data
var tmpName = "tmpname"
var folder = DriveApp.getFolderById('1C_k3MvoT33WhSXVNMmFQNFhqaW8')
var tmpl = DriveApp.getFileById('225xZAECq0rkdJnsr4k9VjL91B7vgJh8Y- t9YrsbCEgc').makeCopy(tmpName).getID();
// get document and make PDF in folder
var doc = DriveApp.getFileByID(tmpl).getAs("application/pdf");
var pdf = doc.setName(job +"-"+dtStr+".pdf");
folder.createFile(pdf)
}
I only see some typos in some functions names, remember Google Apps Script is a scripting language based on JavaScript, so:
JavaScript is a case-sensitive language. This means that language
keywords, variables, function names, and any other identifiers must
always be typed with a consistent capitalization of letters.
On this line:
var tmpl = DriveApp.getFileById('').makeCopy(tmpName).getID();
.getID() the D must be lowercase, just change it to .getId()
On this line:
doc = DriveApp.getFileByID(tmpl).getAs("application/pdf");
.getFileByID() the D must also be lowercase, just change it to .getFileById()
To delete the temporary document, you can use the removeFile() but first you need to get the file, not just the id, so I recommend before getting the id of the copy, you get the file and then get the file's id, like this:
var blob = DriveApp.getFileById('yourId').makeCopy(tmpName)
var tmpl = blob.getId();
Then after the creation of the pdf, you can delete with this:
folder.removeFile(blob);
To create Custom Menus, the official documentation has some good examples.
EDIT:
This is an example to append a table to a Google Doc that can get you started, the cell variable you can change it to the range of data of your Spreadsheet:
function appendTable(){
var document = DocumentApp.openById('docId');
var body = document.getBody();
var cells = [
['Row 1, Cell 1', 'Row 1, Cell 2'],
['Row 2, Cell 1', 'Row 2, Cell 2']
];
body.appendTable(cells);
document.saveAndClose();
}
I have included the simple of the code that I use for this same purpose. This includes a checker that denotes where the merge code is in the process. This is helpful when processing many rows at a time. It also provides the url of the created file. The way that I use this is with Google Forms where the data is sent to "Form Responses" and is then sent to "Merge Data" for continued functions using =QUERY().
This code leaves you with both the Google Doc and the final .pdf file. If you would like to have only the .pdf, simply repeat the .setTrashed() method on the Google Doc variable.
I do realize there are some redundencies in the code as the more complicated version contains vastly more if/else statements, condition checkers, data processing, etc. You can pare down the code as you see fit.
In my Google Sheet, the first two columns were the timestamp of submission and the name of the person submitting. These were used in the naming of the file.
function mergeApplication() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Merge Data");
var range = sheet.getActiveRange();
var formSheet = ss.getSheetByName("Form Responses");
var lastRow = formSheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
function checkAndComplete() {
var urlColumn = lastColumn;
var checkColumn = (urlColumn - 1);
var checkRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
for (var i = 0; i < check.length; i++) {
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, 1, (lastColumn - 2));
function mergeTasks() {
function docCreator() {
var docTemplate = DriveApp.getFileById("docid");
var docToUse = docTemplate;
var folderDestination = DriveApp.getFolderById("folderid");
var name = sheet.getRange((i+2), 2, 1, 1).getValue();
var rawSubmitDate = sheet.getRange((i+2), 1, 1, 1).getValue();
var submitDate = Utilities.formatDate(rawSubmitDate, "PST", "MM/dd/yy");
var docName = "File Name - " + name + " - " + submitDate;
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = DriveApp.getFileById(docId).getUrl();
var docToSend = DriveApp.getFileById(docId);
var docInUse = DocumentApp.openById(docId);
var docBody = docInUse.getBody();
var docText = docBody.getText();
function tagReplace() {
var taggedArray = docText.match(/\<{2}[\w\d\S]+\>{2}/g);
var headerArray = sheet.getRange(1, 1, 1, (lastColumn - 2)).getValues();
var dataArray = dataRow.getValues();
var strippedArray = [];
function tagStrip() {
for (var t = 0; t < taggedArray.length; t++) {
strippedArray.push(taggedArray[t].toString().slice(2, -2));
}
}
function dataMatch() {
for (var s = 0; s < strippedArray.length; s++) {
for (var h = 0; h < headerArray[0].length; h++) {
if (strippedArray[s] == headerArray[0][h]) {
docBody.replaceText(taggedArray[s], dataArray[0][h]);
}
}
}
docInUse.saveAndClose();
}
tagStrip();
dataMatch();
}
tagReplace();
statusCell.setBackground(yellow);
var pdfDocBlob = docToSend.getAs(MimeType.PDF);
var pdfDocInitial = DriveApp.createFile(pdfDocBlob).setName(docName);
var pdfDoc = pdfDocInitial.makeCopy(folderDestination);
pdfDocInitial.setTrashed(true);
urlCell.setValue(docURL);
}
statusCell.setBackground(red);
docCreator();
statusCell.setBackground(green);
}
mergeTasks();
}
}
}
checkAndComplete();
}
This process will run through systematically, takes about 5 seconds per row, and will be creating each file at the root of your Drive but quickly deletes it from the root. There may be a more simple way to perform this that saves space in your trash but I did not research more efficient methods.