I am trying to create export to PDF to my drive. This script currently does it. However I only want a specific range of cells to export out and I would like the name of the saving file to pull the information from a Cell in the sheet. Please help! I have the following script:
function getpdf2(){
SpreadsheetApp.flush();
var theurl = 'https://docs.google.com/spreadsheets/d/'
+ '1C-ONDP4pZeUrlMYekvg0i8oNceASL1s4_o8oBpQuzHk'
+ '/export?exportFormat=pdf&format=pdf'
+ '&size=LETTER'
+ '&portrait=false'
+ '&fitw=false'
+ '&top_margin=0.50'
+ '&bottom_margin=0.50'
+ '&left_margin=0.50'
+ '&right_margin=0.50'
+ '&sheetnames=false&printtitle=false'
+ '&pagenum=false'
+ '&gridlines=false'
+ '&fzr=FALSE'
+ '&gid='
+ '1101884643';
var token = ScriptApp.getOAuthToken();
var docurl = UrlFetchApp.fetch(theurl, { headers: { 'Authorization': 'Bearer ' + token } });
var fileid = DriveApp.createFile(docurl.getBlob()).setName('CAITLYN!B7.pdf').getId();
var pdf = docurl.getBlob().setName('CAITLYN!B7.pdf');
var pdf = docurl.getBlob().getAs('application/pdf').setName('testss.pdf');
var filetodel = DriveApp.getFileById(fileid);
if (DriveApp.getFoldersByName("PDF Quotes").hasNext()){
var folder = DriveApp.getFoldersByName("PDF Quotes").next();
filetodel.makeCopy(folder);
}
DriveApp.removeFile(filetodel);
}
Since there is no native way to export specific range, we need to access the data inside the spreadsheet.
What we need to do to simplify the exporting process is we copy the data range into a temporary sheet, and then paste it to the first set of cells.
After that, we need to hide the cells outside the copied cells.
Lastly, we export that temporary sheet into a pdf in our drive.
Here is the whole working code.
function exportRangeToPdf() {
// Get the sheet id, sheet name, range of cells you want to export
// and the folder ID in your drive where you want to store the pdf
var sheetId = '1ZzkBAR1EgismFCd-hxOUyC_uZKYucu7Av-lDHhrEMx4';
var sheetName = 'Sheet1';
var sheetSrcRange = 'B5:C6';
var driveId = '0B55C21aJsSBlfk9FTjRqOG8tb3hjR1N4MTU1YjVPNU4weGVhSldfU3F4OXladVVNMF9Ccms';
var sheet = SpreadsheetApp.openById(sheetId).getSheetByName(sheetName);
var range = sheet.getRange(sheetSrcRange);
var sheetDstRange = 'A1:' + sheet.getRange(range.getHeight(), range.getWidth()).getA1Notation();
var rangeValues = range.getValues();
var folder = DriveApp.getFolderById(driveId);
// Feel free to modify if needed
// From sheet name (e.g. Sheet1.pdf)
var pdfName = sheetName + '.pdf';
// From specific cell, can be inside or outside your selected range to export (e.g. exportedPDF.pdf)
// var pdfName = sheet.getRange('C7').getValue() + '.pdf';
// From the combination of sheet name and range you chose (e.g. Sheet1_B5:C6.pdf)
// var pdfName = sheetName + '_' + sheetSrcRange + '.pdf';
// Create a blank spreadsheet to be able to export just the range
var destSpreadsheet = SpreadsheetApp.create('PDF');
// If our range is 2 rows and 2 columns we want it to be copied from A1 to B2
// Then we can hide the rest of columns and rows and export
var sheet2 = destSpreadsheet.getSheetByName('temp');
if(!sheet2){
destSpreadsheet.insertSheet('temp').getRange(sheetDstRange).setValues(rangeValues);
var sheet2 = destSpreadsheet.getSheetByName('temp');
}
// Hide all the rows and columns that do not have content
sheet2.hideRows(sheet2.getLastRow() + 1, sheet2.getMaxRows() - sheet2.getLastRow());
sheet2.hideColumns(sheet2.getLastColumn() + 1, sheet2.getMaxColumns() - sheet2.getLastColumn());
// Delete the first sheet that is automatically created when you create a new spreadsheet
destSpreadsheet.deleteSheet(destSpreadsheet.getSheetByName(sheetName));
// Export our new spreadsheet to PDF
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
folder.createFile(theBlob);
// Delete the spreadsheet we created to export this range.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Sample Data:
Output:
I have solved the same challenge in a different way that eliminates code and works effectively for me. Instead of writing a script to create a tab, copy data, delete what I don't want, etc. I just create an "Export" tab and use query to pull in the data I want to export on a regular basis.
In my case, I want to export columns in a different order, so I use arrayformula to pull in individual columns from the Primary Data Tab into an array and then run a query against it. Notice I also filter data ("Col7<>'Completed') in the query to pull in only the rows I want. Finally, you'll see that the filter references Col7 and not a column letter. When you use arrayformula, you must refer to the columns by their position in the array (Col1, Col2, etc.)
=query(ArrayFormula({'Primary Data Tab'!AJ:AJ,{'Primary Data Tab'!G:G}&" "&{'Primary Data Tab'!I:I},'Primary Data Tab'!M:M,'Primary Data Tab'!E:E,'Primary Data Tab'!F:F,'Primary Data Tab'!AK:AK,'Primary Data Tab'!AI:AI,'Primary Data Tab'!W:W,'Primary Data Tab'!K:K,'Primary Data Tab'!B:B,'Primary Data Tab'!C:C,'Primary Data Tab'!Y:Y}),"select * where Col7<>'Completed' and Col7<>'Cancelled'",1)
You can simplify this by just bringing in the entire sheet and using a query to select columns and filter it:
=query('Primary Data Tab'!A:AJ,"select A,B,D,G,T,AD where G<>'Completed' and G<>'Cancelled'",1)
Once you've done this, the data in the tab automatically reflects the Primary Data Tab and you can just export it at any time.
Related
I have a Google Form that saves data into a Google Sheet. The form is completed by students in an online class.
Additional data is periodically added to each row of the Google Sheets by their teacher to track their attendance. I'd like to be able to share a read-only mirror of the data with each student such that they can only see their own data. (All data from a given student is in a single row of the worksheet).
I know that could do this manually by creating a new sheet for each student, using =IMPORTRANGE() to mirror their row of data into the sheet, and share a link to the sheet with the student. However, this isn't feasible given the number of sheets that would need to be manually created.
Is there a way to automate sharing of a limited section of the data with people?
This script will create a spreadsheet per row and will have its first row equate to the first row of the original spreadsheet (header) and use IMPORTRANGE on its 2nd row. It will then add the email in that row as viewer to the newly created spreadsheet.
Script:
function exportData() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var header = values.shift();
var ssId = spreadsheet.getId();
values.forEach((row, index) => {
var newName = `Data: ${row[2]}, ${row[1]}`;
var files = DriveApp.getFilesByName(newName);
// file is already existing
if (files.hasNext())
return;
var newSpreadsheet = SpreadsheetApp.create(newName);
var newSheet = newSpreadsheet.getActiveSheet();
var rowNum = index + 2;
var newSsId = newSpreadsheet.getId();
// append header to new file's first row
newSheet.appendRow(header)
// use importrange on A2
newSheet.getRange(2, 1).setFormula(`=IMPORTRANGE("${spreadsheet.getUrl()}", "${sheet.getSheetName()}!A${rowNum}:${rowNum}")`);
// add permission to automatically allow access to importrange
addImportrangePermission(ssId, newSpreadsheet.getId());
// add the email in the third column as the viewer
DriveApp.getFileById(newSsId).addViewer(row[5]);
});
}
function addImportrangePermission(donorId, ssId) {
// adding permission by fetching this url
const url = `https://docs.google.com/spreadsheets/d/${ssId}/externaldata/addimportrangepermissions?donorDocId=${donorId}`;
const token = ScriptApp.getOAuthToken();
const params = {
method: 'post',
headers: {
Authorization: 'Bearer ' + token,
},
muteHttpExceptions: true
};
UrlFetchApp.fetch(url, params);
}
Original spreadsheet:
Sample Output:
Sample new spreadsheet content:
Note:
Script is updated based on your sample data
addViewer will fail if email is invalid so be sure to use one. Be sure to adjust the column passed to row to avoid failing.
I have 6 columns A-F.. I want to be able to copy them from one sheet to another automatically - That I am able to do. I am using .getLastRow() on the range, but in column D I have x references that are being seen by .getLastRow() - so it is copying lots of blank cells with references on column D. What I would like to do is modify my code so it will copy my range referencing column A (e.g. Column A,B,C,E&F have 5 rows, D still has x values, but only copy the data subject to .getLastRow on column A only).
I've spent hours working on it but I'm not having a lot of luck...
/** Transfer from one sheet to another **/
function transferData() {
// references
var logSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Log');
var appendSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Archive');
// Create range strings for the rows in Log and Archive sheets
var logSheetRange = "Log!9:" + logSheet.getLastRow();
var archiveLastRow = logSheet.getLastRow() + appendSheet.getLastRow();
var archiveAppendRange = "Archive!" + (appendSheet.getLastRow() + 1) + ":" + archiveLastRow;
// Get range of data to copy
var destRange = logSheet.getRange(archiveAppendRange);
// Copy to archive
var sourceDataValues = logSheet.getRange(logSheetRange).copyTo(destRange);
// Clear range arrays after transfer
logSheet.getRangeList(['A9:A59', 'C9:H59', 'K9:L59']).activate()
.clear({contentsOnly: true, skipFilteredRows: true});
// Return to first empty cell
logSheet.getRange('A9').activate();
};
Every week I have a few different Google Sheets that I have to download as excel, delete hidden rows, fix data and then email out.
This can be very time consuming, so I would like to make/deploy a script that automates this process.
I did a ton of searching to try to get this to work, however I am running into a few issues.
First, the data in the sheet is pulled via a query/importrange formula, so once downloaded to excel all data looks like the following:
=IFERROR(__xludf.DUMMYFUNCTION("""COMPUTED_VALUE"""),"No")
This prevents being able to filter any of the data. Currently I'm fixing this by copying all data in excel and pasting values only. I'd like the excel file to be corrected so that I don't have to do that.
Also, I would like to be able to email the converted Excel file.
I found the following code that works for this:
function getGoogleSpreadsheetAsExcel(){
try {
var ss = SpreadsheetApp.getActive();
var url = "https://docs.google.com/feeds/download/spreadsheets/Export?key=" + ss.getId() + "&exportFormat=xlsx";
var params = {
method : "get",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true
};
var blob = UrlFetchApp.fetch(url, params).getBlob();
blob.setName(ss.getName() + ".xlsx");
MailApp.sendEmail("amit#labnol.org", "Google Sheet to Excel", "The XLSX file is attached", {attachments: [blob]});
} catch (f) {
Logger.log(f.toString());
}
}
This mostly works, but again runs into the issue above of the importrange data being incorrect and showing as the =IFERROR(__xludf.DUMMYFUNCTION.
There are also two columns (A and J) that are hidden and need to be deleted.
Additionally, I would love to be able to change the email address, subject and message via an GUI. But this is much less important and not needed.
I know that this is a big ask, but any help would be amazing!
This script addresses part of your question.
Create a copy of a sheet, and in the process convert formulas to values
Delete columns
Note: There are several options for converting the formula to values.
So far as changing the email address, subject and message via a GUI, I suggest using a custom sidebar.
function so5665379502(){
/*
// The purpose of this script is to copy the content of one sheet to another;
// converting formulas to values
// and deleting columns
*/
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "ejbsheet01";
var sourcesheet = ss.getSheetByName(sheetname);
var destname = "ejbsheet02";
var destinationSheet = ss.getSheetByName(destname);
sourcesheet.activate();
var range = sourcesheet.getDataRange();
var rFC = range.getColumn();
var rLC = range.getLastColumn();
var rFR = range.getRow();
var rLR = range.getLastRow();
//Logger.log("DEBUG: the data range = "+range.getA1Notation()+", column 1 = "+rFC+", the last column = "+rLC+", first row = "+rFR+", last row = "+rLR);
// syntax copyValuesToRange(sheet, column, columnEnd, row, rowEnd)
range.copyValuesToRange(destinationSheet, rFC, rLC, rFR, rLR);
// delete column J first so that other column numbers don't chnage.
// column J = 10
destinationSheet.deleteColumn(10);
// Delete Column A
// column A = 1
destinationSheet.deleteColumn(1);
}
BEFORE
Note: Columns A and J are hidden
AFTER
Note: The hidden Columns are gone and the data ais values, not formula.
I’ve been trying to find a solution for a while but now it’s time to ask for aid :)
Goal: Add the hyperlinks to specific PDF documents stored in G Drive to all the individual cells in a column in google sheet depending on the document's name.
I explain myself better. Here you have the screenshot:
Sheet Layout
I’m trying to have the content of the cells in column A replaced by a hyperlink (maintaining the link label as it is). The hyperlink should point to the document in google drive where the filename contains the name shown in the relative cell in this spreadsheet.
The function should look (for each value of column A) for the file in the G Drive folder (where all the documents are) with the name containing the value of the cell (the cell has just the invoice number, like the one in the screenshot, while the file also has “Invoice-“ before the number and ends with “.pdf”). If there’s a match each cell value should be replaced with a hyperlink pointing to the relative document while maintaining the cell value as link label.
Sorry for the long description.
I was able to create a function that does exactly what I need for one row. The problem for me is to understand how to do it for each row and how to do the “search and match” with the relative document in google drive.
function invoiceLinking() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// set active sheet to the second tab of the spreadsheet
var activeSheet = ss.getSheets()[1];
var data = activeSheet.getDataRange().getValues();
var invoiceNumber = activeSheet.getRange(2,1).getValue();
// get the drive folder containing the invoices by ID
var folder = DriveApp.getFolderById("----Folder ID ----");
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
var fullname = file.getName();
var url = file.getUrl();
}
// check if the number of the invoice in the cell is contained in the name of the PDF file in the folder
// applies an hyperlink to the realitve PDF to the cell with invoice number
if (fullname.indexOf(invoiceNumber) !== -1 ) {
activeSheet.getRange(2,1).setValue('=HYPERLINK("'+url+'","'+invoiceNumber+'")');
}
}
Goal:
Link sheet cells invoice names to Drive PDFs
Flow:
Use a temporary object to store Drive PDF names and urls to lookup all cell data later
Code Snippets:
var range = activeSheet.getRange(2, 1, activeSheet.getLastRow() - 1, 1); //A2:A8
var invoiceNumbers = range.getValues();
var nameUrl = {}; //Object to hold name and Url
/*Fill the nameUrl*/
while (files.hasNext()) {
var file = files.next();
var fullname = file.getName();
var url = file.getUrl();
nameUrl[fullname] = url;
}
var links = invoiceNumbers.map(function(e) {
return [
'=HYPERLINK("' + nameUrl['Invoice-' + e[0] + '.pdf'] + '","' + e[0] + '")'
];
});
range.setValues(links);
References:
JS Objects
Array.map
I'm trying to write some code using App Scripts that will (via a daily trigger), copy/paste data from the cells F13:G13 to the first empty cell in column I. Here is my code:
function TrackCurrentValues()
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheets()[0];
var lastRow = getLastRowInColumn(sheet, 'I:I');
// Logger.log('I' + parseInt(lastRow + 1));
var pasteRange = sheet.getRange('I' + parseInt(lastRow + 1) );
pasteRange.activate();
// now that we have the first empty cell in column I, paste the values we found from cells F13:G13
spreadsheet.getRange('F13:G13').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
function getLastRowInColumn(sheetObj, range) {
return sheetObj.getRange(range)
.getValues().filter (String).length + 1
}
When it runs, what happens is that the data is copied from the proper location, but it's always pasted to cell A1. Moreover, the number pasted appears is prefixed with the British pound sterling character (£).
What could be wrong? It (usually) works if I run it manually. The main thing is that it doesn't find the empty cell in I:I.
This code should do what you're looking for:
function TrackCurrentValues(){
var sheet = SpreadsheetApp.getActive().getSheets()[0];
var lastRow = getLastRowInColumn(sheet, 'I:I');
var pasteRange = sheet.getRange(lastRow + 1,9,1,2);
var copyRange = sheet.getRange(13,6,1,2)
pasteRange.setValues(copyRange.getValues())
};
function getLastRowInColumn(sheetObj, range) {
return sheetObj.getRange(range).getValues().filter(String).length
}
On your £ chracter question, that range is likely formatted to display that currency symbol. To update this, select the range, go to the toolbar and select Format > Number > Specify the format you would like
Additional Thoughts:
i) You are adding one to lastRow variable twice (once in getLastRowInCOlumn function and again in pasteRange definition)
ii) I would reocmmend not using "active ranges" to store a location, instead store that range in a variable
iii) It seems your copy range was 2 columns wide but your pasteRange was only 1 column wide