Server error when trying to save range of Google sheet cells to PDF in Google Apps Script - google-apps-script

I have the following code, running in Google Apps Script. The code attempts to take a range of a spreadsheet and save it as a PDF:
var range = sheet.getRange("A586:K" + lastRow);
// Get the currently active spreadsheet URL (link)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var pdfBlob = DriveApp.getFileById(ss.getId()).getAs('application/pdf');
pdfBlob.setName('test.pdf');
// Save the PDF to your Google Drive.
var folder = DriveApp.getFolderById("1EJIzvW-oOnFfFOopiAAsudhbHsF5FGKz");
var file = folder.createFile(pdfBlob);
When I run the code, it runs for a while, then gives me the error:
We're sorry, a server error occurred. Please wait a bit and try again
Any tips on what might be going on?

I believe your goal is as follows.
You want to export the specific range of a sheet as a PDF format using Google Apps Script.
In your script, range is not used. And, the whole sheet is exported. In this case, I think that your goal can be achieved by the query parameter. Ref When this is reflected in your script, how about the following modification?
Modified script:
function myFunction() {
const sheetName = "Sheet1"; // Please set your sheet name.
const folderId = "###"; // Please set your folder ID.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const range = sheet.getRange("A586:K" + sheet.getLastRow());
const startRow = range.getRow() - 1;
const endRow = startRow + range.getNumRows();
const startCol = range.getColumn() - 1;
const endCol = startCol + range.getNumColumns();
const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}&r1=${startRow}&c1=${startCol}&r2=${endRow}&c2=${endCol}`;
const res = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } });
const folder = DriveApp.getFolderById(folderId);
folder.createFile(res.getBlob().setName("sample.pdf"));
}
References:
fetch(url, params)
Related thread.
How to adjust as custom size margins and paper size in script to save google spreadsheet in PDF?

Related

Saving a specific sheet from the workbook as a PDF

I have 3 sheets in my workbook and I want to save a specific sheet as a PDF to a specific folder in my Google Drive. I have this code below. However, it is saving the entire workbook as PDF and not that specific sheet.
function checkSheet() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Tracking");
var fldr = DriveApp.getFolderById("folder id");
if(sh.getRange("C6").getValue() == "Yes") {
var theBlob = ss.getBlob().getAs('application/pdf').setName("mypdf");
fldr.createFile(theBlob);
}
}
Tracking - is the sheet that I want to save as PDF
folder id - is the id taken from the url of the folder where I want to save the PDF file
I tried looking for different syntax for getBlob and I am not able to get that specific sheet saved as a PDF. Here are is a link that I referenced.
Export Single Sheet to PDF in Apps Script
I thought that your referenced thread will be useful. But, from your question, I couldn't understand your tested script. So, in this case, I would like to introduce the modified script using your referenced thread.
Modified script:
function checkSheet() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Tracking");
var fldr = DriveApp.getFolderById("folder id");
if (sh.getRange("C6").getValue() == "Yes") {
const sheetName = "Sheet1"; // Please set the sheet name you want to export as PDF format.
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() !== sheetName) {
sheets[i].hideSheet();
}
}
SpreadsheetApp.flush(); // This might not be required to be used.
var theBlob = ss.getBlob().setName("mypdf");
fldr.createFile(theBlob);
for (var i = 0; i < sheets.length; i++) {
sheets[i].showSheet();
}
}
}
When this script is run, only "Sheet1" is included in the exported PDF file.
By the way, in this case, when the blob is retrieved from Spreadsheet, the Spreadsheet is automatically converted to PDF format. So, in this case, var theBlob = ss.getBlob().getAs('application/pdf').setName("mypdf"); can be replaced with var theBlob = ss.getBlob().setName("mypdf");.
Note:
As another approach, in this case, when the endpoint for exporting the Spreadsheet as PDF format is used, the modified script is as follows.
function checkSheet() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Tracking");
var fldr = DriveApp.getFolderById("folder id");
if (sh.getRange("C6").getValue() == "Yes") {
const sheetName = "Sheet1"; // Please set the sheet name you want to export as PDF format.
const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${ss.getSheetByName(sheetName).getSheetId()}`;
const blob = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob();
fldr.createFile(blob.setName("mypdf"));
}
}

How to copy and paste as values Google Apps Script/Sheets from one spreadsheet to another?

So I have a huge spreadsheet with 9k rows and more than 30 columns. I want to copy this spreadsheet to another spreadsheet (Values only).
Previously I was using this code successfully, but due to the increase in data, the script now times out (1800s +). Is there a way to optimize this script or maybe an alternative option altogether?
function temp() {
var sss = SpreadsheetApp.openById('XYZ'); // sss = source spreadsheet
//var ss = sss.getSheets()[4]; // ss = source sheet
var ss = sss.getSheets(); // ss = source sheet
var id=4; //default number
for(var i in ss)
{
var sheet = ss[i];
if(sheet.getName()== "ABC")
{ id=i;
break;
}
}
console.log(id);
ss=sss.getSheets()[id];
//Get full range of data
var SRange = ss.getDataRange();
//get A1 notation identifying the range
var A1Range = SRange.getA1Notation();
//get the data values in range
var SData = SRange.getValues();
SpreadsheetApp.flush();
var tss = SpreadsheetApp.getActiveSpreadsheet(); // tss = target spreadsheet
var ts = tss.getSheetByName('ABC'); // ts = target sheet
//set the target range to the values of the source data
ts.getRange(A1Range).setValues(SData);
}
I believe your goal is as follows.
You want to reduce the process cost of the script.
In this case, I would like to propose to use Sheets API. This has already been mentioned in the Yuri Khristich's comment. Also, when the benchmark is measured between Spreadsheet service (SpreadsheetApp) and Sheets API, when Sheets API is used for reading and writing the values for Spreadsheet, it was confirmed that the process cost could be reduced. Ref
When Sheets API is used for your script, it becomes as follows.
Modified script:
Before you use this script, please enable Sheets API at Advanced Google services. And please set the source Spreadsheet ID and the sheet names.
function temp() {
var sourceSpreadsheetId = "XYZ"; // Please set the source Spreadsheet ID.
var destinationSpreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var sourceValues = Sheets.Spreadsheets.Values.get(sourceSpreadsheetId, "ABC").values;
Sheets.Spreadsheets.Values.update({values: sourceValues}, destinationSpreadsheetId, "ABC", {valueInputOption: "USER_ENTERED"});
}
References:
Benchmark: Reading and Writing Spreadsheet using Google Apps Script
Method: spreadsheets.values.get
Method: spreadsheets.values.update
Copy from one spreadsheet to another
function copyfromonetoanother() {
const sss = SpreadsheetApp.getActive();
const dss = SpreadsheetApp.openById("dssid");
const ssh = sss.getSheetByName('Sheet1');
const vs = ssh.getDataRange().getValues();
const dsh = dss.getSheetByName('Sheet1');
dsh.getRange(dsh.getLastRow() + 1,1,vs.length,vs[0].length).setValues(vs);
}
If you wish to select the source range:
function copyfromonetoanother() {
const sss = SpreadsheetApp.getActive();
const dss = SpreadsheetApp.openById("dssid");
const ssh = sss.getSheetByName('Sheet1');
const vs = ssh.activeRange().getValues();
const dsh = dss.getSheetByName('Sheet1');
dsh.getRange(dsh.getLastRow() + 1,1,vs.length,vs[0].length).setValues(vs);
}
This function appends to the bottom of the destination sheet
function appenddatatobottomofdestination() {
const sssId = "source spreadsheet id";
const sss = SpreadsheetApp.openById(sssId);
const dss = SpreadsheetApp.getActive();
const dssId = dss.getId();
const ssh = sss.getSheetByName('Sheet1');//Source sheet
const srg = ssh.getRange(1,1,ssh.getLastRow(),ssh.getLastColumn());
const dsh = dss.getSheetByName("Sheet1");//Destination sheet
var vs = Sheets.Spreadsheets.Values.get(sssId, `${ssh.getName()}!${srg.getA1Notation()}`).values;
const drg = dsh.getRange(dsh.getLastRow() + 1, 1, vs.length,vs[0].length);//appends to bottom of spreadsheet
Sheets.Spreadsheets.Values.update({values: vs}, dssId, `${dsh.getName()}!${drg.getA1Notation()}`, {valueInputOption: "USER_ENTERED"});
}

Script To Write Auto-Gen'd Google Doc URL To Spreadsheet

I have a Google Form that does two things upon hitting the Submit button. First, it dumps that data into a Spreadsheet, then it autofills a Google Doc Template with the info from the Form.
In my script to autofill the Google Doc, I've grabbed the URL for the Google Doc. But I need to write this URL into the last row of Column J in my Google Sheet. Correction: using the getActiveSheet functions are fine, I forgot this script is running from the (Active) Google Sheet (apologies!).
Can anyone assist with this? Here's a snippet of the script to get the URL:
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var TimeStamp = e.values[0];
var Technician = e.values[1];
var Vendor = e.values[2];
var xxx = e.values[3];
var yyy = e.values[4];
var SerialNumber = e.values[5];
var AssetTag = e.values[6];
var TicketNumber = e.values[7];
var HostName = e.values[8];
var DocumentLink = e.values[9];
var Return = e.values[10];
var Platform = e.values[11];
var Summary = e.values[12];
var URL = "";
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('aaa');
//Put auto filled Google Doc into the appropriate Vendor Folder
//file.makeCopy will return a Google Drive file object
if (Vendor == "111") {
var folder = DriveApp.getFolderById('bbb')
var copy = file.makeCopy(TicketNumber + ' - ' + SerialNumber, folder);
}
if (Vendor == "222") {
var folder = DriveApp.getFolderById('ccc')
var copy = file.makeCopy(TicketNumber + ' - ' + SerialNumber, folder);
}
if (Vendor == "333") {
var folder = DriveApp.getFolderById('ddd')
var copy = file.makeCopy(TicketNumber + ' - ' + SerialNumber, folder);
}
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Get the url of the newly created Google Doc
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('{{EmailAddress}}', Technician);
body.replaceText('{{TicketNumber}}', TicketNumber);
body.replaceText('{{HostName}}', HostName);
body.replaceText('{{SerialNumber}}', SerialNumber);
body.replaceText('{{AssetTag}}', AssetTag);
body.replaceText('{{Summary}}', Summary);
body.replaceText('{{Vendor}}', Vendor);
body.replaceText('{{URL}}', url);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
Thanks in advance!
From your following situation in your question,
Also this is a shared Google Sheet, so I can't use the getActiveSheet function, I'd need to reference the Google Sheet URL, I believe.
If you have the permission to write the values to the shared Google Spreadsheet, how about the following modification?
From:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
To:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
sheet.appendRow([url]);
In this modification, please set the Spredsheet ID and sheet name. By this, the value of url is appended to the next row of the last row of the sheet in the Google Spreadsheet.
If you want to use the Spreadsheet URL instead of Spreadsheet ID, please modify openById("### Spreadsheet ID ###") to openByUrl("### Spreadsheet URL ###").
References:
openById(id)
openByUrl(url)
appendRow(rowContents)
Edit:
From the following replying,
That doesn't seem to work to insert the URL into the last row of column J unfortunately. It looks like this script can use the Active Sheet afterall, I'll edit the OP.
In this case, how about the following modification?
From:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
To:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
// var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
var sheet = SpreadsheetApp.getActiveSheet(); // or SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname")
sheet.getRange("J" + (sheet.getLastRow() + 1)).setValue(url);
Or
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
// This is from https://stackoverflow.com/a/44563639/7108653
Object.prototype.get1stEmptyRowFromTop = function (columnNumber, offsetRow = 1) {
const range = this.getRange(offsetRow, columnNumber, 2);
const values = range.getDisplayValues();
if (values[0][0] && values[1][0]) {
return range.getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
} else if (values[0][0] && !values[1][0]) {
return offsetRow + 1;
}
return offsetRow;
};
// var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
var sheet = SpreadsheetApp.getActiveSheet(); // or SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname")
sheet.getRange("J" + sheet.get1stEmptyRowFromTop(10)).setValue(url);
Suggestion:
If you need to write the URL value into the last row of Column J in a shared Google Spreadsheet file, you can try this sample below:
function sample() {
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/SHEET_ID_IS_HERE/edit#gid=0');
var sheet = ss.getSheets()[0]; // access first sheet
var activeSheet = ss.setActiveSheet(sheet);
//Sample setValue() action to the shared sheet file
activeSheet.getRange("A1").setValue("URL");
}
NOTE: You would need to have an editor permission on the Shared Google Sheet file that you're accessing
References:
openByUrl(url)
getSheets()
setActiveSheet(sheet)
Here's the code that ended up working for me.
//Get the url of the newly created Google Doc
var url = doc.getUrl();
// var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
var sheet = SpreadsheetApp.getActiveSheet(); // or SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname")
sheet.getRange("J" + (sheet.getLastRow())).setValue(url);

Google sheet create pdf from another sheet ID

Hi how could this code be modified to create a PDF file from another worksheet instead of the active open worksheet.
Here's part of the code I'm using and it works but I would like to create a PDF from another worksheet ID
Thanks
var source = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheet = spreadsheetId ? SpreadsheetApp.openById(spreadsheetId) : SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var ticketnum = source.getSheetByName("Print Page").getRange('X1').getValue();
var body = 'Attached is your copy'
var Client = source.getSheetByName("Print Page").getRange('O5').getValue();
var Cons = source.getSheetByName("Print Page").getRange('T4').getValue() ;
var jobN = source.getSheetByName("Ticket PDF").getRange('G9').getValue() ;
var mailTo = source.getSheetByName("Print Page").getRange('Z1').getValue() ;
var folder = DriveApp.getFoldersByName('Ticket').next();
var token = ScriptApp.getOAuthToken();
var ssID = spreadsheetId;
var url = "https://docs.google.com/spreadsheets/d/"+ssID+"/export"+
"?format=pdf&"+
'&portrait=true' +
'&fzr=true' +
'&fitw=True' +
//'&scale=4' +
'&top_margin=0.25' +
'&bottom_margin=0.25' +
'&left_margin=0.25' +
'&right_margin=0.25' +
'&horizontal_alignment=CENTER' +
'&gridlines=false'+
'&sheetnames=False';
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
var pdf = response.getBlob().setName( Client + " " + ticketnum + " # " + jobN + " " + '.PDF');
folder.createFile(pdf)
How about this modification?
In this modification, it supposes that another worksheet ID in your question is the other sheet in the active Spreadsheet.
Modified script:
In order to export the specific sheet in the Spreadsheet as PDF file, you can use the query parameter of gid as follows.
From:
"?format=pdf&"+
To:
"?format=pdf&gid=" + spreadsheet.getSheetByName("###").getSheetId() + "&" +
In this case, please set the sheet name you want to export as a PDF to ###.
Note:
If you want to export several sheets in the Spreadsheet as a PDF file, this thread might be useful.
If I misunderstood your question and this was not the direction you want, I apologize.
Added
You want to export the specific Spreadsheet, which is not the active Spreadsheet, as a PDF file.
In your script, please give spreadsheetId. When spreadsheetId is not given the active Spreadsheet by var spreadsheet = spreadsheetId ? SpreadsheetApp.openById(spreadsheetId) : SpreadsheetApp.getActiveSpreadsheet();. And spreadsheetId is retrieved from the active Spreadsheet. So please modify as follows.
From:
var source = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheet = spreadsheetId ? SpreadsheetApp.openById(spreadsheetId) : SpreadsheetApp.getActiveSpreadsheet();
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
To:
var spreadsheetId = "###"; // Please set the Spreadsheet ID.
var source = SpreadsheetApp.getActiveSpreadsheet();
var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
var spreadsheetId = spreadsheet.getId();
If you want to export the specific sheet in the Spreadsheet, please use the query parameter gid. And if you want to export several sheets in the Spreadsheet, this thread might be useful.

Google script for google sheets - Range copy paste special 'add'

i'm really newbie at scripting, i used to do my work with microsoft office that have a special paste "add" feature and now for google sheet i can't really find it.
I will have a source range of C2:C102 and destination at same sheet D2:D102 i want the script (that i can run manually weekly) to copy all the range from source and sum it with the already existing data at D2:D102 (only values).
Here is a small example - Before after
I tried to use this code but ofc it just replaces the values.
function copyCells(){
var thisSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var SourceSheet = thisSpreadsheet.getSheetByName("test");
var SourceRange = thisSpreadsheet.getRange("C2:C102");
var destinationSheet = thisSpreadsheet.getSheetByName("test");
var destinationRange = destinationSheet.getRange("D2:D102");
SourceRange.copyTo(destinationRange, {contentsOnly: true});
}
Any help will be really appreciated :)
Haven't tested the code but try this.
grab values with getValues()
sum the values
copy back values with setValues()
function copyCells(){
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const SourceSheet = spreadsheet.getSheetByName("test");
const SourceRange = spreadsheet.getRange("C2:C102");
const SourceValues = SourceRange.getValues();
const destinationSheet = spreadsheet.getSheetByName("test");
const destinationRange = destinationSheet.getRange("D2:D102");
const destinationValues = destinationRange.getValues();
for (let i = 0; i < SourceValues.length; i++)
destinationValues[i][0] = parseFloat(destinationValues[i][0]) + parseFloat(SourceValues[i][0])
destinationRange.setValues(destinationValues);
}
REFERENCES
range.getValues()
range.setValues()