With this script I could export a spreadsheet to a excel file keeping formulas and a specific range that I need.
But I need also to keep the style of the spreadsheet like colors, row height, text style...
What to add then to the code below?
function downloadXLS_GUI() {
var sh = SpreadsheetApp.getActiveSheet();
var nSheet = SpreadsheetApp.create(sh.getName()+": copy");
var numCols = 10;
var d = sh.getRange(1,1,sh.getLastRow(),numCols);
nSheet.getSheets()[0].getRange(1,1,sh.getLastRow(),numCols).setValues(d.getValues());
["A3", "C3", "F3", "G3", "H3", "F5", "G5", "H5"].forEach(a1Notation => {
var sourceFormulas = sh.getRange(a1Notation).getFormulas();
nSheet.getRange(a1Notation).setFormulas(sourceFormulas);
});
var URL = 'https://docs.google.com/spreadsheets/d/'+nSheet.getId()+'/export?format=xlsx';
var htmlOutput = HtmlService
.createHtmlOutput('Clicca qui per scaricare')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(380)
.setHeight(160);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Scarica Allegato 2 in Excel');
}
Update 2
For copying styles between different sheets you can use this method.
function downloadXLS_GUI() {
const numCols = 10
const sh = SpreadsheetApp.getActiveSheet();
const nSheet = SpreadsheetApp.create(sh.getName() + ": copy");
const o_range = sh.getRange(1, 1, sh.getLastRow(), numCols)
const c_range = nSheet.getSheets()[0].getRange(1, 1, sh.getLastRow(), numCols)
// Get the values and styles from the original
const v = o_range.getValues()
const b = o_range.getBackgrounds()
const c = o_range.getFontColors()
const f = o_range.getFontSizes()
// Set the values and styles to the copy
c_range.setValues(v)
c_range.setBackgrounds(b)
c_range.setFontColors(c)
c_range.setFontSizes(f)
const formulaA1Notation = ["A3", "C3", "F3", "G3", "H3", "F5", "G5", "H5"]
formulaA1Notation.forEach(a1Notation => {
var sourceFormulas = sh.getRange(a1Notation).getFormulas();
nSheet.getRange(a1Notation).setFormulas(sourceFormulas);
});
var URL = 'https://docs.google.com/spreadsheets/d/' + nSheet.getId() + '/export?format=xlsx';
SpreadsheetApp.getUi()
.showDialog(
HtmlService
.createHtmlOutput(`Download`)
)
}
Why not use the copy method?
Copies the spreadsheet and returns the new one.
Instead of copying a certain range, you copy it all and get rid of what you don't need.
For example:
Code.gs
function downloadXLS_GUI() {
const ss = SpreadsheetApp
.getActiveSpreadsheet()
.copy("new_copy")
const sheet = ss.getSheets()[0]
const numRows = 10;
sheet
.getRange(1, numRows + 1, sheet.getLastRow(), sheet.getLastColumn())
.clear()
const URL = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=xlsx`;
SpreadsheetApp.getUi()
.showDialog(
HtmlService
.createHtmlOutput(`Download`)
)
}
Documentation
clear()
Related
I am a newbie with coding and am trying to get the minimum value of a column (of a temporary sheet) and then copy that value and some data from the corresponding row to another sheet. For some reason it seems to work only once during the loop, is it because I'm trying to define a range based on an object?
function create_filter(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = ss.getSheetByName("APperCountry");
const sheet2 =ss.getSheetByName("Tender");
var lanelist = sheet2.getRange(2, 1, sheet2.getLastRow(), 8).getValues();
var country = ('B:B');
var origin = ('E:E');
for(var i=2; i<=lanelist.length;i++){
Logger.log("Filter has been added.");
country = ('B'+i);
Logger.log(country);
var origin = ('E'+i)
var calc = ('M'+i);
var apname = ('N'+i);
const ori = sheet2.getRange(origin);
if(ori.isBlank()){
continue;
}
const range = sheet1.getRange("A:D");
const filter = range.createFilter();
var Ctry = sheet2.getRange(country).getValue();
const Filter_Criteria1 = SpreadsheetApp.newFilterCriteria().whenTextContains(Ctry);
const coll1 = 1;
const add_filter1 = filter.setColumnFilterCriteria(coll1,Filter_Criteria1);
const new_sheet = ss.insertSheet();
new_sheet.setName("AirportDistanceCalc");
var tempsheet = ss.getSheetByName('AirportDistanceCalc');
range.copyTo(new_sheet.getRange(1,1));
var aplist = new_sheet.getRange(2, 1, new_sheet.getLastRow()-1, 8).getValues();
new_sheet.getRange(1,5,1,1).setValue("Origin")
new_sheet.getRange(1,6,1,1).setValue("DistanceKM")
for(var j=0; j<aplist.length;j++){
Logger.log(origin);
ori.copyTo(new_sheet.getRange(j+2,5));
mainFun()
}
let comparisonRange = tempsheet.getRange("F2:F");
let comparisonRangeValues = comparisonRange.getValues().filter(String);
let minimum = comparisonRangeValues[0][0];
comparisonRangeValues.forEach((rowItem, rowIndex) => {
comparisonRangeValues[rowIndex].forEach((columnItem) => {
minimum = Math.min(minimum, columnItem);
});
});
console.log(minimum);
sheet2.getRange(calc).setValue(minimum);
tempsheet.getRange(minimum, 2).copyTo(sheet2.getRange(apname));
if (tempsheet) {
ss.deleteSheet(tempsheet);
}
filter.remove();
}
}
The minimum of a column
function mincolumn(col=1) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet1");
const min = sh.getRange(1,col,sh.getLastRow()).getValues().flat().filter(e => !isNaN(e)).sort((a,b) => a - b)[0];
Logger.log(min);
}
In here I am trying to make a checking where if the "STATUS" column is 'NEW FILE', then i would like to perform a file conversation from excel to spreadsheet.
For the "STATUS" column i created an IF-ELSE statement,
if (row[0] === "last week file") {
newValues.push(['OLD FILE'])
}
else{
newValues.push(['NEW FILE'])
ConvertFiles()
return
}
Therefore, I am making a check through the "STATUS", if the status column is empty it will be written as 'NEW FILE', and then it will perform an file conversion from excel to spreadsheet since i already called the method inside it.
Here is the EDITED version code of the file conversion:
function ConvertFiles() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var range = sheet.getRange(2, 1, sheet.getLastRow()-1, 5); // get A2:E6 range
var data = range.getValues(); // get A2:E6 data
for(var i = 0; i < data.length; i++){
if(data[i][2] == " "){
for( var r= 2;r < sheet.getLastRow()+1; r++){
var fileId = sheet.getRange(r,1).getValue();
var folderID = sheet.getRange(r,2).getValue(); //for destination folder
var files = DriveApp.getFileById(fileId);
var name = files.getName().split('.')[0];
var blob = files.getBlob();
var newFile = {
title: name.replace('_converted','') + '_converted',
parents: [{id: folderID}] };
var destinationFolderId = DriveApp.getFolderById(folderID);
var existingFiles = destinationFolderId.getFilesByName(newFile.title);
while(existingFiles.hasNext()) {
var oldConvertedFileWithSameNameID = existingFiles.next().getId();
Drive.Files.remove(oldConvertedFileWithSameNameID,{supportsAllDrives: true});
}
var newFileID = Drive.Files.insert(newFile, blob, { convert: true,supportsAllDrives: true }).id;
Logger.log(newFileID);
var Url = "https://drive.google.com/open?id=" + newFileID;
//sheet.getRange(r,4).setValue(newFileID);
//sheet.getRange(r,5).setValue(Url);
}
sheet.getRange(i+2,4).setValue(newFileID); //set value in column D
sheet.getRange(i+2,5).setValue(Url); //set value in column E
}
}
}
The error that i am facing is, when i call the method ConvertFiles() inside the if statement, the conversion happens from row 2 until 6 CONTINOUSLY without stopping as shown in sample in red circle.
I only wanted to make conversion on the "NEW FILES" only which will be on row 5 and 6.
How can i make a conversion on the selected/specified row?
It would be more efficient if you obtain all the values in your Sheet, loop the 2D array the getValues() method will return and add an if statement that will only process new files.
Example:
Here in my example below I created a script that will only process rows that have a blank value for the status column.
Code:
function ConvertFiles() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var range = sheet.getRange(2, 1, sheet.getLastRow()-1, 5); // get A2:E6 range
var data = range.getValues(); // get A2:E6 data
/*the content of data is 2D array,
each sub array represent rows in your table*/
for(var i = 0; i < data.length; i++){
if(data[i][2] == ""){ //the 2 in [i][2] represent the value of C column in sheet
//Add your file conversion code here
sheet.getRange(i+2,4).setValue("Test only"); //set value in column D
sheet.getRange(i+2,5).setValue("Test only"); //set value in column E
}
}
}
Data:
Output:
References:
Sheet.getRange(row, column, numRows, numColumns)
Range.getValues()
Range.setValue(value)
EDIT:
The value of data variable in the code below is a 2D array containing all the data in the range provided. In your example, it is the data of A2:E6.
Example output:
[
[fileId1,folderId1,Status1,,],
[fileId2,folderId2,Status2,,],
[fileId3,folderId3,Status3,,],
[fileId4,folderId4,,,],
[fileId5,folderId5,,,],
]
The for loop will access each sub array per iteration and since we already knew the position of our target data (fileID and folderID) we don't need to create another for loop to access it, instead we just specify the index on which the data is located. data[i][0] for file id and data[i][1] for folder id. The if(data[i][2] == "") is added to check if the column C of each row is empty and ignore the one with data.
Code:
function ConvertFiles() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var range = sheet.getRange(2, 1, sheet.getLastRow()-1, 5);
var data = range.getValues();
for(var i = 0; i < data.length; i++){
if(data[i][2] == ""){
var fileId = data[i][0];
var folderID = data[i][1];
var files = DriveApp.getFileById(fileId);
var name = files.getName().split('.')[0];
var blob = files.getBlob();
var newFile = {
title: name.replace('_converted','') + '_converted',
parents: [{id: folderID}] };
var destinationFolderId = DriveApp.getFolderById(folderID);
var existingFiles = destinationFolderId.getFilesByName(newFile.title);
while(existingFiles.hasNext()) {
var oldConvertedFileWithSameNameID = existingFiles.next().getId();
Drive.Files.remove(oldConvertedFileWithSameNameID,{supportsAllDrives: true});
}
var newFileID = Drive.Files.insert(newFile, blob, { convert: true,supportsAllDrives: true }).id;
var Url = "https://drive.google.com/open?id=" + newFileID;
sheet.getRange(i+2,4).setValue(newFileID);
sheet.getRange(i+2,5).setValue(Url);
}
}
}
Here is the GS code which will fetch the email id and and subject and the table.
var EMAIL_DRAFTED = "EMAIL DRAFTED";
function draftMyEmails() {
var sheet = SpreadsheetApp.getActiveSheet(); // Use data from the active sheet
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow() - 1; // Number of rows to process
var lastColumn = sheet.getLastColumn(); // Last column
var dataRange = sheet.getRange(startRow, 1, numRows, lastColumn) // Fetch the data range of the active sheet
var data = dataRange.getValues(); // Fetch values for each row in the range
// Work through each row in the spreadsheet
for (var i = 0; i < data.length; ++i) {
var row = data[i];
// Assign each row a variable
var clientName = row[0]; // Col A: Client name
var clientEmail = row[1]; // Col B: Client email
var sub = row[2]; // Col C: subject
var body = row[3]; // Col D: emailbody
var emailStatus = row[lastColumn - 1]; // Col E: Email Status
var range = sheet.getRange(2, 6, 1, 5).getDataRegion(SpreadsheetApp.Dimension.ROWS);
var values = range.getValues();
// Prevent from drafing duplicates and from drafting emails without a recipient
if (emailStatus !== EMAIL_DRAFTED && clientEmail) {
// Build the email message
var emailBody = '<p>Hi ' + clientName + ',<p>';
//emailBody += '<p>We are pleased to match you with your vegetable: <strong>' + veg + '</strong><p>';
//emailBody += '<h2>About ' + veg + '</h2>';
emailBody += '<p>' + values + '</p>';
// emailBody += '<p>' + clientName + ', we hope that you and ' + veg + ' have a wonderful relationship.<p>';
// Create the email draft
GmailApp.createDraft(
clientEmail, // Recipient
sub, // Subject
'', // Body (plain text)
{
htmlBody: emailBody // Options: Body (HTML)
}
);
sheet.getRange(startRow + i, lastColumn).setValue(EMAIL_DRAFTED); // Update the last column with "EMAIL_DRAFTED"
SpreadsheetApp.flush(); // Make sure the last cell is updated right away
}
}
}
What i am getting is by using this code:
Body
7 & 8,9 & 10,IOS,5 & 6,,a,b,c,d,,1,2,3,4,,1,2,3,4,,1,2,3,4,,1,2,3,4,,1,2,3,4,,1,2,3,4,,1,2,3,4,,1,2,3,4,
expected result should be like this.
This how the google sheet looks like.
Try this:
function draftMyEmails() {
var sh=SpreadsheetApp.getActiveSheet();
var startRow=2;
var lastCol=sh.getLastColumn();
var dataRange=sh.getRange(startRow,1,sh.getLastRow()-startRow+1,lastCol);
var data=dataRange.getValues();
for (var i=0;i<data.length;i++) {
var row=data[i];
var clientName=row[0];
var clientEmail=row[1];
var sub=row[2];
var body=row[3];
var emailStatus=row[lastCol-1];
var range=sh.getRange(2, 6, 1, 5).getDataRegion(SpreadsheetApp.Dimension.ROWS);
var values=range.getValues();
if (emailStatus!="EMAIL DRAFTED" && clientEmail) {
var emailBody= Utilities.formatString('<p>Hi %s </p>,<br />',clientName);
emailBody+='<style>td{border:1px solid black;)</style><table>';
values.forEach(function(r,j){
emailbody+='<tr>
r.foreach(function(c,k){
emailBody+=Utilities.formatString('<td>%s</td>',c);
});
emailBody+='</tr>';
});
emailBody+='</table>';
GmailApp.createDraft(clientEmail,sub,'',{htmlBody: emailBody});
sh.getRange(startRow + i, lastColumn).setValue("EMAIL_DRAFTED");
}
}
}
Please provide me with an image of your spreadsheet so I can see what your table looks like and I'll debug the table area for you or you can do it yourself.
I have a problem.
How to get the filename from the url?
enter image description here
I wouldn't normally do this since you haven't shown us what you've tried, but I'm feeling generous.
This function should work for you. (Note that you'll need to grant permissions for it to run.)
function getFileNames() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Get_File_Name");
var links = sheet.getRange("A2:A").getValues();
var filenames = [];
for (var i = 0; i < links.length; i++) {
var url = links[i][0];
if (url != "") {
var filename = SpreadsheetApp.openByUrl(links[i][0]).getName();
filenames.push([filename]);
}
}
var startRow = 2; // print in row 2 since row 1 is the header row
var fileNameColumn = 2; // Column B = column 2
var destination = sheet.getRange(startRow, fileNameColumn, filenames.length, filenames[0].length);
destination.setValues(filenames);
}
Another way
function getFileNames() {
var driveApp = DriveApp;
// SET THE SHEET HERE
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
//SET THE URL LINK COLUMN HERE : From row 2 since row 1 is the header row till last row
var links = sheet.getRange("P2:P").getValues();
var filenames = [];
for (var i = 0; i < links.length; i++) {
var fileId = getIdFromUrl(links[i][0]);
if (fileId != "" && fileId != null) {
var getfile = DriveApp.getFileById(fileId);
var filename = getfile.getName();
Logger.log(filename);
filenames.push([filename]);
} else {
filenames.push([""]);
}
}
// SET STARTING ROW TO PRINT: From row 2 since row 1 is the header row
var startRow = 2;
// SET WHICH COLUMN TO PRINT : Column A = column 1 / Column B = column 2
// MAKE SURE THE SHEET LAST COLUMN HEADER IS FILLED + 1 (next column)
var fileNameColumn = sheet.getLastColumn() + 1;
var destination = sheet.getRange(startRow, fileNameColumn, filenames.length, filenames[0].length);
destination.setValues(filenames);
}
function getIdFromUrl(url) { return url.match(/[-\w]{25,}/); }
You can create a custom function in spreadsheets like this.
function getSSName(name) {
var ss = SpreadsheetApp.openByUrl(url);
return ss.getName();
}
I am trying to set a formula in Google Spreadsheet F2:F if E2:E has a date. If E2:E is blank, then no formula is set to F2:F.
My Code so far. Any Help or suggestions would be much appreciated.
function formula(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var date = Utilities.formatDate(new Date(), "America/New_York", "MMMM dd, yyyy");
var colE = sheet.getRange("E2:E");
var colF = sheet.getRange("F2:F");
if (colE != date){
colF.setFormula("=round(abs(E2:E-NOW()),-1)");
} else {
colF.setValue("");
}
}
Here is how to do it...
function formula(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var colE = sheet.getRange("E2:E").getValues();
var colF = sheet.getRange("F2:F").getFormulas();
for(var n in colE){
if (typeof(colE[n][0])=='object'){
colF[n][0] = "=round(abs(E"+n+":E-NOW()),-1)";
} else {
colF[n][0] = "";
}
}
sheet.getRange('F2:F').setFormulas(colF);
}