Need help, spreadsheet formula not refreshed in exported PDF - google-apps-script

I have a Google sheet that is exported as PDF and emailed using App Script but there are a few cases where the PDF shows #NAME? in cells where I used a custom function. But in most cases it is fully displayed.
Is there a way around this? It looks like before the sheet has fully refreshed, it is already exported into PDF.
If I manually open the spreadsheet, it shows the figures but sometimes it refreshes and takes about 2 to 3 seconds to redisplay the values.
Here is how it looks like:
My code to email as PDF is here:
//Now, get the file and email as PDF attachment
var file = DriveApp.getFileById(GsheetFileID);
GmailApp.sendEmail(approverEmail, emailIdentifier + " OT Claim for your approval", "", {
cc: ccOthers,
htmlBody: html4Approver,
//attachments : [blob]
attachments : [file.getAs(MimeType.PDF)]
});
I have also tried exporting it this way but the result is still the same.
var url = "https://www.googleapis.com/drive/v3/files/" + fileID + "/export?mimeType=application/pdf";
var options = {
method: "GET",
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
var pdfFile = UrlFetchApp.fetch(url, options).getBlob();
Thanks in advance for any ideas.

This is a common issue that arise then there's a lot of data and latency problems arise. You can workaround it using getDisplayValues() to create another sheet which will contain pure data.
This is sample code, which will load data correctly but w/o formatting
function copyTab(sheet) {
var data = SpreadsheetApp.getActive().getSheetByName(sheet).getDataRange().getDisplayValues();
if(data.length > 0) {
var bufferSheet = SpreadsheetApp.getActive().getSheetByName('Buffer');
if(!bufferSheet)
bufferSheet = SpreadsheetApp.getActive().insertSheet('Buffer');
else
bufferSheet.clear({contentsOnly: true});
bufferSheet.getRange(1,1,data.length,data[0].length).setValues(data);
/* Here you can add your code to create PDF from Buffer sheet */
}
}

Related

How do I download a Sheets file then send it to a server in .XLSX format?

I have a Drive with my files and would like to send them to my server for backing up. I would like to convert all Sheets to Excel format (.xlsx). So, I select my file and then click upload button and it goes to myfunction(). In myfunction, I download the .xlsx file and then send the text to my server but then I try to open it with Open Office and I get an error saying the file is corrupted. (I print off the url in the console and when I open that url in the browser it works correctly, though). I download the file in Scripts so that I can make sure it opens, as doing so offline on my server may not open it since it doesn't have proper permissions.
function myfunction(file, ext){
var url = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + file.getId() + "&exportFormat=xlsx";
var params = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
console.log("url: "+url);
var txt = UrlFetchApp.fetch(url, params).getContentText();
var options = {
'method': 'POST',
'payload': {
file: file.getName() + '.xlsx',
file_body: txt,
},
};
var response = UrlFetchApp.fetch('https://example.com', options).getContentText();
console.log("upload response: "+response);
}
What am I doing wrong? Also, I am worried about Sheets that are on a team drive or are shared and want to make sure I can download those also.
EDIT:
I've also tried the getAs(contentType) but, unfortunately, it tells me that I can only download it as a PDF.

Can I copy a script from a Form to the linked spreadsheet?

I have a form template which I will be duplicating as needed, modifying, and sending out for responses. I have written a script for the linked responses spreadsheet which rearranges the response data in a specific way. The script itself works fine, and it only needs to run on my account.
The problem is that the script is tied to the spreadsheet, not the form; but it’s the form and not the spreadsheet that gets duplicated. I tried linking my script to the form template and using the code below to create a linked spreadsheet and copy the script over. I set up a trigger to run this function on form submit, but the trigger disappears when the form template is duplicated. This code is largely copied from this answer: https://stackoverflow.com/a/48353155/12131953 with a few lines added at the beginning to create the linked spreadsheet.
function copyScript() {
//create destination spreadsheet
var form = FormApp.getActiveForm();
var ss = SpreadsheetApp.create(form.getTitle() + " (Responses)");
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
var srcProjectId = "###my project id###"; // Source project ID
var dstGoogleDocsId = ss.getId(); // Destination spreadsheet ID
var baseUrl = "https://script.googleapis.com/v1/projects";
var accessToken = ScriptApp.getOAuthToken();
// Retrieve filename of bound-script project.
var srcName = JSON.parse(UrlFetchApp.fetch(baseUrl + "/" + srcProjectId, {
method: "get",
headers: {"Authorization": "Bearer " + accessToken}
}).getContentText()).title;
// Retrieve bound-script project.
var obj = UrlFetchApp.fetch(baseUrl + "/" + srcProjectId + "/content", {
method: "get",
headers: {"Authorization": "Bearer " + accessToken}
}).getContentText();
// Create new bound script and retrieve project ID.
var dstId = JSON.parse(UrlFetchApp.fetch(baseUrl, {
method: "post",
contentType: 'application/json',
headers: {"Authorization": "Bearer " + accessToken},
payload: JSON.stringify({"title": srcName, "parentId": dstGoogleDocsId})
}).getContentText()).scriptId;
// Upload a project to bound-script project.
var res = JSON.parse(UrlFetchApp.fetch(baseUrl + "/" + dstId + "/content", {
method: "put",
contentType: 'application/json',
headers: {"Authorization": "Bearer " + accessToken},
payload: obj
}).getContentText());
}
Then I tried to create the trigger programmatically when the duplicated form is opened, but as far as I can tell creating installable triggers is outside the authorization of the simple trigger onOpen.
function onOpen(); {
ScriptApp.newTrigger("copyScript").forForm(FormApp.getActiveForm()).onFormSubmit().create();
.
.
.
}
I am not a developer and am self-taught on this stuff; I’m pretty comfortable with the scripting aspect but have no familiarity at all with APIs and web deployments.
So my question is: Is there a way to copy a script to a new, form-linked spreadsheet from the form? I’m also fine with a solution that somehow applies my script (maybe as a standalone) to all new spreadsheets, because the code creates a menu option; so making the script an add-on may be the answer here, but that seems like overkill for a script that only one person is ever going to run.
Instead of rebounding your old script from the spreadsheet to the form, leave as it is, but add the following script to your Form:
function onOpen() {
FormApp.getActiveForm().setDestination(FormApp.DestinationType.SPREADSHEET, 'ID of your destination spreadsheet');
}
Run this script once manually in order to give necessary permissions to the script
Copy your form as much as you like - each copy will contain a copy of the script
The script will fire onFormOpen and set the destination of the respective form copy to the same spreadsheet - the one containing your rearranging script
Form responses from different forms will be automatically inserted in different sheets of the spreadsheet

Google Script UrlFetchApp.fetch returns a 404 eventhough the page exist

I am sending automated report with Google spreadsheet and Google script.
So far it was working perfectly. But somehow when I try to create a new report to be emailed, the function "UrlFetchApp.fetch" return a 404. The same situation happened when I tried copying the old report.
The line with "UrlFetchApp.fetch" give me this error:
Request failed for https://docs.google.com/spreadsheets/d/1qm_bCKn4MbLKy7AIuIeu7bTZcTk8ObYBln0GAxwfsX8/pub?gid=195635557&single=true&output=pdf returned code 404. Truncated server response
It seems that I am not the only one having the issue but I cannot find any solution to it.
Here is the code:
function emailSpreadsheetAsCSV() {
var ss = SpreadsheetApp.openById("1qm_bCKn4MbLKy7AIuIeu7bTZcTk8ObYBln0GAxwfsX8");
var url = ss.getUrl();
url = url.replace(/edit$/,'');
var token = ScriptApp.getOAuthToken();
var sheets = ss.getSheets();
//make an empty array to hold your fetched blobs
var blobs = [];
for (var i=0; i<sheets.length; i++) {
var response = UrlFetchApp.fetch("https://docs.google.com/spreadsheets/d/1qm_bCKn4MbLKy7AIuIeu7bTZcTk8ObYBln0GAxwfsX8/pub?gid=195635557&single=true&output=pdf", {
headers: {
'Authorization': 'Bearer ' + token
},
'muteHttpExceptions': false
});
//convert the response to a blob and store in our array
blobs[i] = response.getBlob().setName(sheets[i].getName() + '.csv');
}
//create new blob that is a zip file containing our blob array
var zipBlob = Utilities.zip(blobs).setName(ss.getName() + '.zip');
return blobs[0];
}
Thanks a lot for your help.
Aymeric.
I've gotten this problem too and after some research, Spence Easton (question 34573295) solved my issue. I simply added this bogus call to drive so access is granted, and so the script can now get to your file.
Add this near the top before trying to grab the url:
var bogus = DriveApp.getRootFolder();
Now it runs fine with no 404 errors.
Remove the headers from the request:
var response = UrlFetchApp.fetch("https://docs.google.com/spreadsheets/d/1qm_bCKn4MbLKy7AIuIeu7bTZcTk8ObYBln0GAxwfsX8/pub?gid=195635557&single=true&output=pdf", {
'muteHttpExceptions': false
});
Check if it works for you.
You use the "pub" url so are you sure the spreadsheet is published on the web ? If you get a 404 it means the page is not published.
If file is published the bearer is not necessary as it is public now.
After I don't really understand your code because you iterate all the sheets of your file
for (var i=0; i<sheets.length; i++)
but the param "gid" (pub?gid=195635557&single=true&output=pdf) for the url in your urlfetch is fixed ?
Second point you get the file as pdf and after you create a csv ?
Why not get the file as csv directly : https://docs.google.com/spreadsheets/export?id=TheIdOfTheFile&exportFormat=csv
By adapting this code you can get the csv directly, see : https://stackoverflow.com/a/28503601/3556215
Take care you will get the first page and not others.
Stéphane
Try with:
var response = UrlFetchApp.fetch(url, options);
var result = JSON.parse(res.getContentText());

UrlFetch from Google Sheet exportLink['application/pdf'] not returning PDF data

I create and send a periodic email as an update from a Google Sheet. For various client reasons this is done 3 ways, as a link to the Sheet, and as attachments (PDF and XLSX).
This was working 'til recently. The XSLX attachment still works, but the PDF is no longer sent as a response to a UrlFetch to the file.exportLinks('application/pdf') url. No matter what the request headers it always returns as Content-Type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
Did something else undocumented change that I am missing here?
function exportAsPDF(spreadsheetId) {
spreadsheetId = spreadsheetId || 'SECRET_ID';
var file = Drive.Files.get(spreadsheetId),
url = file.exportLinks['application/pdf'];
url += '&format=pdf&size=7&fzr=true&portrait=true&fitw=true&gid=0&gridlines=false&printtitle=false&sheetnames=false&pagenum=UNDEFINED&attachment=true'
var token = ScriptApp.getOAuthToken(),
response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var headers = response.getAllHeaders(); // revealing content-type returned isn't pdf
var pdfBlob = response.getBlob().getAs('application/pdf');
var pdfString = pdfBlob.getDataAsString(); // this naturally throws an error
return response.getBlob(); // this returns to the send mail script
}
I'm able to get PDFs using the utility from Convert all sheets to PDF with Google Apps Script.
That working script modifies the spreadsheet's edit URL into an export URL, which looks like:
https://docs.google.com/spreadsheets/d/<%SS-ID%>/export?exportFormat=pdf...
The advanced Drive service gives an export URL formatted like:
https://docs.google.com/spreadsheets/export?id=<%SS-ID%>&exportFormat=pdf...
I'd expect the URL provided by exportLinks to be more reliable than the hack in the working script. Apparently, it's not.
This has been raised as Issue 5114. Star it to receive updates.

google script doc to pdf resizes rows

I have a document that i use as a template, url: https://docs.google.com/document/d/1Ng00Sw0V-3_htLF2-SyPj895g_V9bi1mKQc1LOaHPNY/edit?usp=sharing
I'm using a script when a form is submited and export it to pdf
function testExport() {
var pdf = DocumentApp.openById(docTemplate).getAs("application/pdf");
DriveApp.createFile(pdf);
};
But the exported link looks like https://drive.google.com/file/d/0B_YrT5Ue-LAvUllyQ0ZpbkRodVU/view?usp=sharing
Size of rows between the table seems to increased and the overall quality looks bad, is there a way to fix it? When I download the doc file as pdf the then it looks really good.
Document.getAs() uses a slightly different converter then the Google Docs UI does. You can get closer by using the conversion functionality built into the Drive API, exposed in File.exportLinks. The sample below uses the Drive Advanced Service to do the conversion and save the result.
function exportAsPdf(documentId) {
var file = Drive.Files.get(documentId);
var url = file.exportLinks['application/pdf'];
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var contents = response.getBlob();
contents.setName(file.title + '.pdf');
DriveApp.createFile(contents);
}