I wrote my first web app using Google App Maker, which edits a spreadsheet file to create a report customized for the user, and allows the user to download a copy of the report. The program is really simple, it just:
enters the user's user account name into a certain cell of the Sheets document,
flushes the spreadsheet (which dynamically updates it according to the user account name),
creates a PDF of the updated spreadsheet - which is a test score report,
and allows the user to download a pdf of their report.
It all works fine, except when several students run the web app simultaneously. If that happens, some of the students end up with another student's report.
My understanding was that changes made to a Google Sheets document by the server script in my web app would not be saved to the document unless I saved them intentionally. So my hope was that simultaneous users would not have any impact on each other.
I'm really stuck as to how to resolve this issue. Here's my code:
App startup script (runs as soon as the app is loaded):
downloadReport();
Client script
function showDownloadLink(downloadURL){
console.log(downloadURL);
var spinnerWidgets = app.pages.appPage.descendants.progressSpinner;
spinnerWidgets.visible = false;
var linkWidgets = app.pages.appPage.descendants.downloadPDFLink;
linkWidgets.href = downloadURL;
linkWidgets.visible = true;
}
function downloadReport() {
var status = app.pages.appPage.descendants.downloadStatus;
var user = app.user.username;
var downloadLink = google.script.run.withSuccessHandler(showDownloadLink)
.createDownload(user);
}
Server script:
// Spreadsheet file must have a tab called Report
function createDownload(user) {
///*
// SET SPREADSHEET FILE ID
var ssID = 'xxxxxxxxx_SHEETS_FILE_ID_GOES_HERE_xxxxxxxxxx';
var ss = SpreadsheetApp.openById(ssID);
SpreadsheetApp.setActiveSpreadsheet(ss);
var sheet = ss.getSheetByName("Report");
SpreadsheetApp.setActiveSheet(sheet);
// determines timezone
var timeZone = Session.getScriptTimeZone();
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
// gets the destination folder using spreadsheet location (folder)
var ssFolder = DriveApp.getFileById(ssID).getParents();
var folder = ssFolder.next();
var folderId = folder.getId();
var destination = DriveApp.getFolderById(folderId);
// Logger.log('destination name: ' + destination.getName());
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
var formattedDate = Utilities.formatDate(new Date(), timeZone , "yyyy-MM-dd' 'HH:mm:ss");
// gets the name of the original file and appends the ID and the timestamp stored in formattedDate
var PDFfileName = ss.getName()+formattedDate+ "-" + user;
// inputs ID into daughter spreadsheet reference cell to personalize report
ss.getSheetByName("Report").getRange(3, 1).setValue(user); // Puts ID into cell A3
SpreadsheetApp.flush(); // Updates the spreadsheet with changes and refreshes report
// Export URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ssID);
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&top_margin=0.30' // sets top margin of pdf file
+ '&bottom_margin=0.28' // sets bottom margin of pdf file
+ '&left_margin=0.43' // sets left margin of pdf file
+ '&right_margin=0.43' // sets right margin of pdf file
+ '&fitw=true' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
// Take snapshot and save to PDF
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
//convert the response to a blob and store in our array
var blob = response.getBlob().getAs('application/pdf').setName(PDFfileName + '.pdf');
var newPDFFile = folder.createFile(blob);
newPDFFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// Link URL
var downloadURL = "https://drive.google.com/uc?export=download&id="+newPDFFile.getId();
return downloadURL;
}
Related
I have this existing code that I use in google sheet that whenever I run the script, the paychecks would automatically convert to pdf and will have its own folder. Now, I want to add the link of the pdf inside the google sheet but I don't know what code should I add into it. Below is my existing code that I use in my google sheet.
function finishedPayslip(){
makePDF()
mnthly()
clearPayslipFields()
}
function makePDF() {
// Get the currently active spreadsheet URL (link)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var token = ScriptApp.getOAuthToken();
var sheet = ss.getSheetByName("Paycheck");
//Creating an exportable URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
var folderID = "1A691O2hh96wlKWDuZ-P9S9CK_MDTAWcm"; // Folder id to save in a folder.
var folder = DriveApp.getFolderById(folderID);
var employeeName = ss.getRange("'Paycheck'!C4").getValue()
var pdfName = employeeName;
/* Specify PDF export parameters
From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
*/
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
// Convert individual worksheet to PDF
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
var blobs = response.getBlob().setName(pdfName + '.pdf');
var folders = folder.getFoldersByName(pdfName);
folder = folders.hasNext() ? folders.next() : folder.createFolder(pdfName);
var newFile = folder.createFile(blobs);
// Define the scope
Logger.log("Storage Space used: " + DriveApp.getStorageUsed());
}
The output will be like I will add an another column at the end of my sheet named "pdf link" and the links for pdf per employee will appear inside my google sheet. What code should I add?
Additional information: My sheet looks like I have a template generator or paycheck generator inside my sheet, and whenever I run the script, it would automatically go to their designated sheet since it is separated by period (January, February and so on)
Thank you!
I believe your goal is as follows.
You want to create a PDF file and retrieve the URL of the PDF file, and want to put the URL to the column "S".
In this case, the sheet name is retrieved from the cell 'Paycheck'!H3. And, you want to search the name from the column "A" of the sheet and put the URL to the column "S" of the same row.
In this case, how about the following modification?
From:
var newFile = folder.createFile(blobs);
To:
var newFile = folder.createFile(blobs);
var sheetName = ss.getRange("'Paycheck'!H3").getValue();
var range = ss.getSheetByName(sheetName).getRange("A2:A").createTextFinder(employeeName).findNext();
if (range) {
range.offset(0, 18).setValue(newFile.getUrl());
}
In this modified script, the sheet name is retrieved from the cell 'Paycheck'!H3 and the row is searched by TextFinder, and the URL is put to the column "S" of the searched row.
Reference:
Class TextFinder
I have this existing code in google app script that commands my work to convert it to pdf file and will go to its folder.
Example:
I am making invoices, and it would automatically covert to PDF and all PDF files will go to the another folder name "invoices", and all the invoices of the customer will be gather there.
However, I need to separate those pdf files and create folder named on the customers. Meaning, inside the folder invoices, I need to have another folder named accordingly to the name of the customer, and inside the folder of the customer, all of their invoices will gather there.
However, I cannot get the right code for that. What would be the possible code I should add inside my code right now?
function makePDF() {
// Get the currently active spreadsheet URL (link)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var token = ScriptApp.getOAuthToken();
var sheet = ss.getSheetByName("Paycheck");
//Creating an exportable URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
var folderID = "1y2_Rw_4l4SZ1i7SaMJLW3953FI9YRTAs"; // Folder id to save in a folder.
var folder = DriveApp.getFolderById(folderID);
var employeeName = ss.getRange("'Paycheck'!C4").getValue()
var pdfName = employeeName + " - " + Utilities.formatDate(new Date(), "GMT+8", "yyyy-MM-dd");
/* Specify PDF export parameters
From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
*/
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
// Convert individual worksheet to PDF
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
//convert the response to a blob
var blobs = response.getBlob().setName(pdfName + '.pdf');
//saves the file to the specified folder of Google Drive
var newFile = folder.createFile(blobs);
// Define the scope
Logger.log("Storage Space used: " + DriveApp.getStorageUsed());
}
I believe your goal is as follows.
You want to create a new folder with the value of pdfName and put the created PDF file in the folder.
In this case, how about modifying your script as follows?
From:
//convert the response to a blob
var blobs = response.getBlob().setName(pdfName + '.pdf');
//saves the file to the specified folder of Google Drive
var newFile = folder.createFile(blobs);
To:
//convert the response to a blob
var blobs = response.getBlob().setName(pdfName + '.pdf');
// I added below script.
var folders = folder.getFoldersByName(pdfName);
folder = folders.hasNext() ? folders.next() : folder.createFolder(pdfName);
//saves the file to the specified folder of Google Drive
var newFile = folder.createFile(blobs);
In this modification, if the same folder has already been existing in the folder of var folder = DriveApp.getFolderById(folderID);, the file is put to the existing folder. When the same folder has not been existing in the folder, a new folder is created under the folder of var folder = DriveApp.getFolderById(folderID); by pdfName and the file is put to the created folder.
References:
getFoldersByName(name)
createFolder(name)
I already had a look to the community but I'm struggling in building a script should do the following:
select a specific range of cells of a Google Sheet sheet
save the selected area as a PDF to be downloaded on the PC (no need to save to GDrive or to add a specif name to the PDF)
I tried recording a Macro but dosn't work.
Can you give some hints on how to move on?
Thanks.
These reference links may help you to have some hints on how you will achieve your target:
This first link shows you how to download a range of cell as PDF:
Script to download a range of cells in google sheet as PDF to local computer and other automation scripts?
Script that exports a range to PDF without borders in reference to the first link and answered by ZektorH:
function downloadRangeToPdf() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1:E20");
//Create temporary Spreadsheet
var tempSpreadsheet = SpreadsheetApp.create("tempSheetInvoiceExport", range.getValues().length, range.getValues()[0].length);
var tempSheet = tempSpreadsheet.getSheets()[0];
var tempRange = tempSheet.getRange("A1:E20");
tempRange.setValues(range.getDisplayValues());
tempRange.setTextStyles(range.getTextStyles());
tempRange.setBackgrounds(range.getBackgrounds());
tempRange.setFontColors(range.getFontColors());
tempRange.setFontFamilies(range.getFontFamilies());
tempRange.setFontLines(range.getFontLines());
tempRange.setFontStyles(range.getFontStyles());
tempRange.setFontWeights(range.getFontWeights());
tempRange.setHorizontalAlignments(range.getHorizontalAlignments());
tempRange.setNumberFormats(range.getNumberFormats());
tempRange.setTextDirections(range.getTextDirections());
tempRange.setTextRotations(range.getTextRotations());
tempRange.setVerticalAlignments(range.getVerticalAlignments());
tempRange.setWrapStrategies(range.getWrapStrategies());
SpreadsheetApp.flush(); //Force changes to be written before proceeding.
//Generate Download As PDF Link
var url = 'https://docs.google.com/spreadsheets/d/{ID}/export?'.replace('{ID}', tempSpreadsheet.getId());
var exportOptions = 'exportFormat=pdf&format=pdf' + // export as pdf / csv / xls / xlsx
'&size=letter' + // paper size legal / letter / A4
'&portrait=true' + // orientation, false for landscape
'&fitw=true&source=labnol' + // fit to page width, false for actual size
'&sheetnames=false&printtitle=false' + // hide optional headers and footers
'&pagenumbers=false&gridlines=false' + // hide page numbers and gridlines
'&fzr=false' + // do not repeat row headers (frozen rows) on each page
'&top_margin=0.00' + //All four margins must be set!
'&bottom_margin=0.00' + //All four margins must be set!
'&left_margin=0.00' + //All four margins must be set!
'&right_margin=0.00' + //All four margins must be set!
'&gridlines=false' + //true/false
'&gid=' + tempSheet.getSheetId(); // the sheet's Id
var token = ScriptApp.getOAuthToken();
var blob = UrlFetchApp.fetch(url + exportOptions, {
headers: {
Authorization: 'Bearer '+token
}
}).getBlob().setName(tempSpreadsheet.getName()+".pdf");
var pdfFile = DriveApp.createFile(blob);
var downloadLink = HtmlService
.createHtmlOutput('<p>Download your file here.</p>')
.setWidth(200)
.setHeight(100);
SpreadsheetApp.getUi().showModalDialog(downloadLink, "Download PDF");
DriveApp.getFileById(tempSpreadsheet.getId()).setTrashed(true); //Place temporary sheet on trash
}
Then this second link shows you how to download a spreadsheet as PDF to local computer. You can refer best to Tanaike's answer here:
How to download single sheet as PDF (not export to Google Drive)
EDITED (Answer to your question below):
You need to use the sheetID of the second sheet in order to save the PDF for the second sheet.
You should change
FROM:
var sheetId = SpreadsheetApp.getActiveSheet().getSheetId();
TO:
var sheetId = ss.getSheetId();
Because you get the reference of the second spreadsheet by the following logic:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
So, you just need to use the following code to get the sheet id of the second spreadsheet:
var sheetId = ss.getSheetId();
For your original logic, which is var sheetId = SpreadsheetApp.getActiveSheet().getSheetId(); it will only get the sheetId of the first sheet.
thanks a lot for your hints. I basically reached what I need even if I need a small adaptation.
My spreadsheet has 2 sheets, I would like to add a script button to sheet 1 to download as PDF sheet 2, I slightly modified the script as reported below but I'm still getting my sheet 1 as PDF.
function downloadSheetAsPDF() {
var filename = "Filename.pdf"; // Please set the filename here.
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var sheetId = SpreadsheetApp.getActiveSheet().getSheetId();
// Creat PDF file as a temporary file and create URL for downloading.
var url = "https://docs.google.com/a/mydomain.org/spreadsheets/d/" + SpreadsheetApp.getActiveSpreadsheet().getId() + "/export?exportFormat=pdf&gid=" + sheetId + "&access_token=" + ScriptApp.getOAuthToken();
var blob = UrlFetchApp.fetch(url).getBlob().setName(filename);
var file = DriveApp.createFile(blob);
var dlUrl = "https://drive.google.com/uc?export=download&id=" + file.getId();
// Open a dialog and run Javascript for downloading the file.
var str = '<script>window.location.href="' + dlUrl + '"</script>';
var html = HtmlService.createHtmlOutput(str);
SpreadsheetApp.getUi().showModalDialog(html, "Download PDF");
file.setTrashed(true);
// This is used for closing the dialog.
Utilities.sleep(3000);
var closeHtml = HtmlService.createHtmlOutput("<script>google.script.host.close()</script>");
SpreadsheetApp.getUi().showModalDialog(closeHtml, "Download PDF");
}
Any suggestion?
Thanks.
I'm doing an invoice program in google spreadsheets, and in the program I'm using drawings as buttons (create new invoice, increase/decrease invoice number and clear data). Those functions works well, but I also want to add a button that will create a PDF file and open as a tab in the web browser. So far I had to duplicate the Invoice-sheet as a tempSheet and then remove the buttons, and then download pdf of the tempSheet. I've found this code below which I've tried to change so it will fit with my program (the original program is only for the active sheet). I think maybe the problem is when I use getId() and getSheetId().
If I use ss.getId() in the url I only get the active sheet (invoice-sheet) as the pdf.
If I use tempSheet.getId(), I get an error (ERROR TypeError: tempSheet.getId is not a function)
Do you know what I'm doing wrong? If I understand it correctly it is the same Id for all the sheets and then the different sheets also have its own id which I can get from getSheetId().
getId()- the number after docs.google.com/spreadsheets/d/
getSheetId() - #gid=xxxxxxxxx
function createPDF(){
//create a temporary sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tempSpreadsheet = SpreadsheetApp.getActiveSpreadsheet().duplicateActiveSheet();
SpreadsheetApp.getActiveSpreadsheet().renameActiveSheet('temp');
var tempSheet = SpreadsheetApp.getActive().getSheetByName('temp');
var invoiceNbr = tempSheet.getRange('C3').getValue().toString();
var tempId = tempSheet.getId(); //ERROR TypeError: tempSheet.getId is not a function
var tempSheetId = tempSheet.getSheetId();
var drawings = tempSheet.getDrawings();
for (var i = 0; i < drawings.length; i++) {
drawings[i].remove();
}
const url = 'https://docs.google.com/spreadsheets/d/{ID}/export?'.replace('{ID}', tempId);
const exportOptions =
'exportFormat=pdf&format=pdf' + // export as pdf
'&size=letter' + // paper size letter / You can use A4 or legal
'&portrait=true' + // orientation portal, use false for landscape
'&fitw=false' + // fit to page width false, to get the actual size
'&sheetnames=false&printtitle=false' + // hide optional headers and footers
'&pagenumbers=false&gridlines=false' + // hide page numbers and gridlines
'&fzr=false' + // do not repeat row headers (frozen rows) on each page
'&gid=' + tempSheetId; // the sheet's Id. Change it to your sheet ID.
// You can find the sheet ID in the link bar.
// Select the sheet that you want to print and check the link,
// the gid number of the sheet is on the end of your link.
var params = {method:"GET",headers:{"authorization":"Bearer "+ ScriptApp.getOAuthToken()}};
var blob = UrlFetchApp.fetch(url + exportOptions, params).getBlob().setName(invoiceNbr+'.pdf');
var pdfFile = DriveApp.createFile(blob);
var downloadLink = HtmlService
.createHtmlOutput('<p>Download your file here.</p>')
.setWidth(200)
.setHeight(100);
SpreadsheetApp.getUi().showModalDialog(downloadLink, "Download PDF");
ss.deleteSheet(tempSheet);
Add SpreadsheetApp.flush() before getSheetByName('temp'):
SpreadsheetApp.getActiveSpreadsheet().renameActiveSheet('temp');
SpreadsheetApp.flush();
var tempSheet = SpreadsheetApp.getActive().getSheetByName('temp');
Related
Google Script import form data to new spreadsheet, then send as PDF
Why do we use SpreadsheetApp.flush();?
I have a script that makes a PDF of my invoice and sends it to a Google drive folder.Now I want to get the "Shareable link" from the file it just made and paste it into a specific Cell in google Sheets. I don't have a coding background and at most I can understand and modify some code. So I'm using code I found online to create the PDF. I tried making my own code for the shareable link but I'm getting nowhere. Can anyone help me. This is the code I'm using for the PDF. If I can provide any more useful information please let me know. Thank you! :)
// Get the currently active spreadsheet URL (link)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var token = ScriptApp.getOAuthToken();
var sheet = ss.getSheetByName("Invoice");
//Creating an exportable URL
var url = "https://docs.google.com/spreadsheets/d/SS_ID/export?".replace("SS_ID", ss.getId());
var folderID = "#### Folder ID ####"; // Folder id to save in a folder.
var folder = DriveApp.getFolderById(folderID);
var invoiceNumber = ss.getRange("'Invoice'!I16").getValue()
var InvoiceDate = ss.getRange("!I17").getValue()
var pdfName = "Invoice #"+ invoiceNumber + " - " + Utilities.formatDate(new Date(), "GMT-7", "MM-dd-yyyy");
/* Specify PDF export parameters
From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
*/
var url_ext = 'exportFormat=pdf&format=pdf' // export as pdf / csv / xls / xlsx
+ '&size=letter' // paper size legal / letter / A4
+ '&portrait=true' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to page width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
// Convert individual worksheet to PDF
var response = UrlFetchApp.fetch(url + url_ext + sheet.getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
//convert the response to a blob
var blobs = response.getBlob().setName(pdfName + '.pdf');
//saves the file to the specified folder of Google Drive
var newFile = folder.createFile(blobs);
// Define the scope
Logger.log("Storage Space used: " + DriveApp.getStorageUsed());
}```
Intuitively, you can achieve a lot only by yourself, and this is the way to go at the beginning:
You want to retrieve the link of the pdf, so you know for sure that it can happen only when the pdf has been created, which is after this line var newFile = folder.createFile(blobs);
Therefore newFile is the PDF you've created, what's left is just to get the link of this file, you can use either getUrl() or getId():
var newFileLink = newFile.getUrl()
or
var newFileLink = "http://drive.google.com/uc?export=view&id=" + newFile.getId()
Now you have stored the link of the created PDF, and you want to write data into your spreadsheet within a specific cell, maybe you want it in J16, since you're using invoiceNumber = ss.getRange("'Invoice'!I16").getValue() to get a value from I16
Assuming you want to set a value in J16. Intuitively again, since getValue retrieve something, so maybe something link setValue will do the opposite:
var writePDFLink = ss.getRange("'Invoice'!J16").setValue(newFileLink)
Hope this was insightful.