Define multiple get Ranges and Sheet Names with javascript - javascript

I am using below script to pull date from all spreadsheets in a folder. It is strong and working.
now i would like to expand this script to define multiple different sheet names instead of one. that is because we have the same file in 3 different langueses, now when it is running and there is a file from an other langues it cannot find the sheet(name) so the script gives the error that GetRange is null. The data is the same, So Ill go through the files looking for
The sheets: "Gegevenskaart" || "DataCard" || "Datenkarte"
The Same for Range. in this specified Sheet I have 6 different ranges. they are all the same size and all have the same kind of data but there are rows that I dont want to use. (20 rows data, 25 empty rows in between)
The ranges to get data from are : [A15:BC34 ,A63:BC82 ,A11:BC130, A159:BC178 ,A207:BC226 ,A255:BC274, A300:3BC319]
(also these data rows have a string in colomn A while the other rows dont have a string)
function CombineDataToMasterFile() {
var folder = DriveApp.getFolderById("1234567890234567891234567");
var filesIterator = folder.getFiles();
var file;
var fileType;
var ssID;
var combinedData = [];
var data;
while(filesIterator.hasNext()){
file = filesIterator.next();
fileType = file.getMimeType();
if(fileType === "application/vnd.google-apps.spreadsheet"){
ssID = file.getId();
data = getDataFromSpreadsheet(ssID);
data = data.map(function(r){return r.concat([file.getName()]); });
combinedData = combinedData.concat(data);
}
}
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Datatest");
ws.getRange("A2:BC").clearContent();
ws.getRange(2, 1, combinedData.length, combinedData[0].length).setValues(combinedData);
}
function getDataFromSpreadsheet(ssID){
var ss = SpreadsheetApp.openById(ssID);
var ws = ss.getSheetByName("Blad1");
var data = ws.getRange("A15:BC34").getValues();
return data;

Related

Get Latest Updated from a column of folder IDs

Hello I have a column with a list of Google Drive folder links and I would like to get latest updated date of all the files contained in the folders.
Example
By looking around I was able to build the following scripts which works fine by giving me the list of all the files contained in the folder and their latest update.
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'UpdateCheck', functionName: 'files'},
];
spreadsheet.addMenu('Menu', menuItems);
}
function files() {
var ui = SpreadsheetApp.getUi();
var result = ui.prompt("Paste the ID of Google Drive Folder");
var folderId = result.getResponseText();
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFiles();
var output = [];
while (files.hasNext()) {
var file = files.next();
output.push([file.getName(), file.getLastUpdated()]);
}
output.sort(function(a, b) {
return a[0] == b[0] ? 0 : a[0] < b[0] ? -1 : 1;
});
SpreadsheetApp.getActiveSpreadsheet().insertSheet().getRange(2, 1, output.length, output[0].length).setValues(output);
However I have to use the UI to open each link, copy and paste the ID from the URL in the input field. I would like to just read the column range, get the IDs from the strings without duplicates and run my script and have my result in a new tab. Any help would be great.
I believe your current situation and your goal are as follows.
You have a sheet that the folder links are put to the column "D" in your providing image.
You want to retrieve the folder ID from the links and want to retrieve the filename and the value of getLastUpdated() of all files in the folders.
You want to put the retrieved data on a new sheet.
Modified script:
Please set the sheet names of srcSheetName and dstSheetName.
function myFunction() {
var srcSheetName = "Sheet1"; // Please set the source sheet name.
var dstSheetName = "Sheet2"; // Please set the destination sheet name.
// Retrieve folder links from "D2:D" and check the duplication.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(srcSheetName);
var folderIds = [...new Set(sheet.getRange("D2:D" + sheet.getLastRow()).getRichTextValues().flatMap(([d]) => {
var url = d.getLinkUrl();
if (url && /^https:\/\/drive\.google\.com\/drive\/folders\/.+/.test(url)) {
return [url.split("/")[5].trim()];
}
return [];
}))];
// Create an array for putting to sheet.
var res = folderIds.flatMap(id => {
var folder = DriveApp.getFolderById(id);
var files = folder.getFiles();
var temp = [];
while (files.hasNext()) {
var file = files.next();
temp.push([file.getName(), file.getLastUpdated()]);
}
return temp;
});
// Put the array to the destination sheet.
ss.insertSheet(dstSheetName).getRange(1, 1, res.length, 2).setValues(res);
}
About some unclear points, I guessed as follows.
I couldn't understand the format of folder links in the cells. In this modification, I guessed that the folder link is like https://drive.google.com/drive/folders/###.
I couldn't understand get the IDs from the strings without duplicates. In this modification, the duplication of retrieved folder IDs is checked.
References:
getValues()
insertSheet(sheetName)
setValues(values)
Added:
From the following your replying,
Quick question, when I run it I get an Unexpected error while getting the method or property getFolderById on object DriveApp at line var res = folderIds.flatMap(id => { var folder = DriveApp.getFolderById(id); it's solvable or should I open another thread?
the folder link was indeed incorrect instead of https://drive.google.com/drive/folders/### the format was https://drive.google.com/corp/drive/u/0/folders/####?resourcekey=#### so I correct the code with /^https://drive.google.com/corp/drive/u/0/folders/.+/.test(url)) { return [url.split("/")[8].trim()]; but now I get 1CHKEThZJ0P-ISv0g0MoGjCCqdZuuJJyA?resourcekey=0-1gzYTJvJv1XDwF2Hx4HvEQ and I need to find a way to get rid of everything right before the "?"
For this, I modified my proposed script as follows.
Modified script:
function myFunction() {
var srcSheetName = "Sheet1"; // Please set the source sheet name.
var dstSheetName = "Sheet2"; // Please set the destination sheet name.
// Retrieve folder links from "D2:D" and check the duplication.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(srcSheetName);
var folderIds = [...new Set(sheet.getRange("D2:D" + sheet.getLastRow()).getRichTextValues().flatMap(([d]) => {
var url = d.getLinkUrl();
if (url) {
if (/^https:\/\/drive\.google\.com\/drive\/folders\/.+/.test(url)) {
return [url.split("/")[5].trim()];
} else if (/^https:\/\/drive\.google\.com\/.+\/drive\/u\/0\/folders\/.+/.test(url)) {
return [url.split("/")[8].trim().split("?")[0]];
}
}
return [];
}))];
// Create an array for putting to sheet.
var res = folderIds.flatMap(id => {
try {
var folder = DriveApp.getFolderById(id);
var files = folder.getFiles();
var temp = [];
while (files.hasNext()) {
var file = files.next();
temp.push([file.getName(), file.getLastUpdated()]);
}
return temp;
} catch (e) {
console.log({id, msg: e.message});
}
});
// Put the array to the destination sheet.
ss.insertSheet(dstSheetName).getRange(1, 1, res.length, 2).setValues(res);
}
By this modification, 2 kinds of URLs like https://drive.google.com/drive/folders/### and https://drive.google.com/corp/drive/u/0/folders/####?resourcekey=#### can be used, and when an error occurs when the files are retrieved from the folder, you can see the folder ID at the log. And, the script is not stopped.

"The JavaScript runtime exited unexpectedly" error when parsing through Google Sheets

I have a function getFnF() that iterates through a Google Drive folder and all of it's subfolders. When getFnF() encounters a Google Sheets file, I have the script parse through that Google Sheets file and extract out any URL Links it finds using my function getLinksFromSheet(). The functions both work, but after about 10 minutes of iterating through the Drive folder and calling getLinksFromSheet() on the Google Sheets files encountered, I get a The JavaScript runtime exited unexpectedly. error. Does anybody have any ideas what would be causing this error? The Google Drive folder is quite large (~500 files total in the subfolders, ~75 of which are Google Sheets). Code below:
function getFnF(folder) {
var folder= folder || DriveApp.getFolderById("0AFZNRhJpE8LKUk9PVA"); //hard goded DEP-Gotham folder
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var files=folder.getFiles();
while(files.hasNext()) {
var file=files.next();
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('File: %s', file.getName()));
Logger.log(file.getName())
//if (file.getMimeType() == 'application/vnd.google-apps.document') {getAllLinks(file.getId(), false);};
//if (file.getMimeType() == 'application/vnd.google-apps.presentation') {getLinksFromSlides(file.getId());};
if (file.getMimeType() == 'application/vnd.google-apps.spreadsheet') {getLinksFromSheet(file.getId());};
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('Fldr: %s', subfolder.getName()));
level++;
getFnF(subfolder);
}
level--;
}
function getLinksFromSheet(sheetId){
var ss = SpreadsheetApp.openById(sheetId);
var sheets = ss.getSheets();
var parentDocName = ss.getName();
var destSs=SpreadsheetApp.getActive();
var destSh=destSs.getSheetByName('Extracted Links');
sheets.forEach(sheet => {
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(1,1, lastRow, lastColumn);
//var rangeValues = searchRange.getValues();
var rangeValues = searchRange.getRichTextValues();
for (var i = 0; i < lastRow; i++){
for (var j = 0; j < lastColumn; j++){
const runs = rangeValues[i][j].getRuns();
for (const v of runs) {
var nextLink = v.getLinkUrl();
if (nextLink != null) {
var row = destSh.getLastRow() + 1;
var r1=destSh.getRange(row, 1);
r1.setValue(parentDocName);
var r2=destSh.getRange(row, 2);
r2.setValue(nextLink);
};
}
}
}
});
Issue stems most likely from exceeding the limitation of runtime execution.
You can use getFilesByType to further whittle down the list and save time. Also did modify a bit in your code but should be able to do the same thing. They should have comments above them. Kindly check.
Usage:
function getFnF(folder) {
var folder = folder || DriveApp.getFolderById("0AFZNRhJpE8LKUk9PVA"); //hard goded DEP-Gotham folder
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Sheet1');
// limit files to only google sheets
var files = folder.getFilesByType(MimeType.GOOGLE_SHEETS);
// assign getLastRow to lessen method calls
var lastRow = sh.getLastRow();
// initialize level value
var level = 1;
while (files.hasNext()) {
var file = files.next();
// I can use appendRow here, but I did't since column has a variable
// and you might change it. Feel free to update if necessary
var firg = sh.getRange(lastRow + 1, level + 1);
firg.setValue(Utilities.formatString('File: %s', file.getName()));
getLinksFromSheet(file.getId());
// iterate lastRow
lastRow++;
}
var subfolders = folder.getFolders()
while (subfolders.hasNext()) {
var subfolder = subfolders.next();
// I can use appendRow here, but I did't since column has a variable
// and you might change it. Feel free to update if necessary
var forg = sh.getRange(lastRow + 1, level + 1);
forg.setValue(Utilities.formatString('Fldr: %s', subfolder.getName()));
level++;
getFnF(subfolder);
// iterate lastRow
lastRow++;
}
// not sure what this does but you can freely remove this if not being used
level--;
}
function getLinksFromSheet(sheetId) {
var ss = SpreadsheetApp.openById(sheetId);
var sheets = ss.getSheets();
var parentDocName = ss.getName();
var destSs = SpreadsheetApp.getActive();
var destSh = destSs.getSheetByName('Extracted Links');
sheets.forEach(sheet => {
// getDataRange already gets all the data
var rangeData = sheet.getDataRange();
// Flatten 2d array
var rangeValues = rangeData.getRichTextValues().flat();
rangeValues.forEach(v => {
var link = v.getLinkUrl();
if(link)
// Use appendRow instead. Adjust array if needed to be in a different column.
destSh.appendRow([parentDocName, link]);
});
});
}
Run time difference:
The 6-second runs is the optimized one above while the 7-second runs is your code.
Test Conditions:
Two sheet files in parent folder, one sheet file in 1 sub-folder:
Each spreadsheet has 2 sheets, each sheet has 1 link.
Parent folder has one non-sheet file.
Note:
Given the significant runtime difference on a lesser number of files, certainly this will have greater impact on a huge number of files.
If you want to include other type of files, then you need to create a separate loop to process each filetype if you have different type of process for each.
You can also join 2 getFilesByType outputs into 1 array but getting links from different file types might vary so separate loop will be much safer.
To get around the time problems you are having, I'd suggest one script to write all the spreadsheet ids and then another script to go down and process them (run getLinksFromSheet()) a line at a time and then mark each line as done so you can rerun it until you can finish.
Getrichtextvalue > getlinkURL is just slowwwwwwwww and you can't get around that.
I think this could also be a memory size, or too much data in a single variable type of issue. I'm getting the same error after seconds of starting a debugging session, but no indication of what is causing it.

Get a List of Drive Files Into a Google Sheet

var folder = DriveApp.getFolderById('1_gA4D7dfybJ60IdfgsnqdfdsgVoo9D76fgsdgf9cqmAnJI7g7');
var contents = folder.getFiles();
var file;
var name;
var sheet = SpreadsheetApp.getActiveSheet();
var date;
var size;
sheet.clear();
sheet.appendRow(["Nome", "Data", "Dimensione"]);
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
date = file.getDateCreated();
size = file.getSize();
id = file.getUrl();
data = [name, date, size,id]
sheet.appendRow(data);
}
};
Every time all the data is cleared, after that the data is added from the starting.
sheet.clear();sheet.appendRow(["Nome", "Data", "Dimensione"]);
Is there any way to check the data?
if the same data present Already then the function does not work on the row.
If a new record is added, it should also be added, but the function does not start again from one.
If I have 20 PDFs in my folder, and add one more PDF in folder, then add the function 21 pdf and do not run the function from the beginning.
Explanation:
The logic behind the following script is the following:
We get all the URLs of the files that are currently in column D of the sheet. These are the URLs of the files that have been recorder so far. We can safely assume that the URLs are always unique:
const aURLs = sheet.getRange('D2:D'+sheet.getLastRow()).getValues().flat();
The second step is to iterate through the files as the original script would do and check if the URL of a file is in aURLs. If the file URL is not in aURLs or in other words in column D, then add it to the newFiles array:
if(!uNames.includes(name)){
newFiles.push(data);
}
After we checked all the files, we can add, after the last row of sheet, only the new files:
sheet.getRange(sheet.getLastRow()+1,1,newFiles.length,newFiles[0].length).setValues(newFiles);
Solution:
Manually add the headers in the file for the first time only:
and then execute the script every next time:
function myFunction() {
const folder = DriveApp.getFolderById('1_gA4D7dfybJ60IdfgsnqdfdsgVoo9D76fgsdgf9cqmAnJI7g7');
const contents = folder.getFiles();
const sheet = SpreadsheetApp.getActiveSheet();
const aURLs = sheet.getRange('D2:D'+sheet.getLastRow()).getValues().flat();
const newFiles = [];
while(contents.hasNext()) {
let file = contents.next();
let name = file.getName();
let date = file.getDateCreated();
let size = file.getSize();
let id = file.getUrl();
let data = [name, date, size,id]
if(!aURLs.includes(id)){
newFiles.push(data);
}
}
sheet.getRange(sheet.getLastRow()+1,1,newFiles.length,newFiles[0].length).setValues(newFiles);
}

Google script trigger to run only on Monday/Wednesday/Friday

I'm running a SendEmail script with a 3 triggers to be sent out on Mondays, Wednesdays and Fridays.
I have 10 sheets on the spreadsheet (each one contains an SentEmail script and each needs to be sent out on those days but I have only 20 trigger limitation)
This is the code:
function sendEmail() {
var s = SpreadsheetApp.getActive().getSheetByName('BCX');
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getActiveSheet().getDataRange();
var range = s.getRange('B5:Q20');
var row = ss.getSheetByName('BCX').getRange("J1").getValue();
var to = "info#google.com";
var body = '';
var htmlTable = SheetConverter2.convertRange2html(range);
var body = "Hi Team!"
+ htmlTable
+ "<br/><br/><b><i>**This is an automated email**</i></b><br/><br/>Any question please let me know.<br/><br/>Regards,<br/><br/>";
var subject = "Google | Report " + row;
MailApp.sendEmail(to, subject, body, {htmlBody: body});
};
But if I use something like the following script it will create 3 triggers each week until it reaches 20 triggers (trigger limit).
function createTriggers() {
var days = [ScriptApp.WeekDay.MONDAY,
ScriptApp.WeekDay.WEDNESDAY,
ScriptApp.WeekDay.FRIDAY];
for (var i=0; i<days.length; i++) {
ScriptApp.newTrigger("sendEmail")
.timeBased().onWeekDay(days[i])
.atHour(7).create();
}
};
One solution to this question would be to combine the various scripts into a single script that can be triggered to run on Monday, Wednesday and Friday.
Within the script, the sequence of processing would be:
1) loop through the spreadsheets in a given folder/sub-folders of Google Drive. - this provides the unique spreadsheet ID.
2) for each spreadsheet, get the ID and use the openById(ID) to open the spreadsheet.
3) get the sheets for the spreadsheet
4) for each sheet, use the original code to build and send an email.
5) rinse and repeat for the next sheet, and next spreadsheet.
The following untested code combines the search for every spreadsheet within a specific folder and sub-folders, opening the spreadsheet and getting the sheets, and then looping through each sheet. The questioner need only add the name of the Google Drive Folder to initiated the search, and put their own code in the two places indicated.
function 53383834() {
/* Adapted from Code written by #hubgit https://gist.github.com/hubgit/3755293
Updated since DocsList is deprecated https://ctrlq.org/code/19854-list-files-in-google-drive-folder
*/
// List all files and sub-folders in a single folder on Google Drive
// declare the folder name
var foldername = 'XXXXXXXXXXXXXXXXXX'; // enter the folder name
// declare this sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActivesheet();
// getFoldersByName = Gets a collection of all folders in the user's Drive that have the given name.
// folders is a "Folder Iterator" but there is only one unique folder name called, so it has only one value (next)
var folders = DriveApp.getFoldersByName(foldername);
var foldersnext = folders.next();
// list files in this folder
var myfiles = foldersnext.getFiles();
// spreadsheets have a unique MIME-Type = application/vnd.google-apps.spreadsheet
var searchTerm = 'spreadsheet';
// loop through files in this folder
while (myfiles.hasNext()) {
var myfile = myfiles.next();
var fname = myfile.getName();
var fid = myfile.getId();
// get the MIME-Type and test whether the file is a spreadsheet
var ftype = myfile.getMimeType();
var indexOfFirst = ftype.indexOf(searchTerm);
if (indexOfFirst != -1) {
var ssid = fid;
// open the spreadsheet
var sso = SpreadsheetApp.openById(ssid);
// get the sheets
var sheets = sso.getSheets();
var sheetlen = sheets.length;
for (var i = 0; i < sheetlen; i++) {
// get the sheets, one by one
var thissheet = sso.getSheets()[i];
<<
insert questioners code here >>
}
}
}
// Now get the subfolder
// subfolders is a Folder Iterator
var subfolders = foldersnext.getFolders();
// now start a loop on the SubFolder list
while (subfolders.hasNext()) {
var subfolderdata = [];
var mysubfolders = subfolders.next();
var mysubfolder = mysubfolders.getName();
// Get the files
var mysubfiles = mysubfolders.getFiles();
// now start a loop on the files in the subfolder
while (mysubfiles.hasNext()) {
var smyfile = mysubfiles.next();
var sfname = smyfile.getName();
var sfid = smyfile.getId();
var sftype = smyfile.getMimeType();
var indexOffolder = sftype.indexOf(searchTerm);
if (indexOffolder != -1) {
var ssid = sfid;
// open the spreadsheet
var sso = SpreadsheetApp.openById(ssid);
// get the sheets
var sheets = sso.getSheets();
var sheetlen = sheets.length;
for (var i = 0; i < sheetlen; i++) {
// get the sheets, one by one
var thissheet = sso.getSheets()[i];
<<
insert questioners code here >>
}
}
}
}
}

Reading multiple spreadsheets in google app script

I'm trying to get all text from a specific cell of all spreadsheets in a folder. My current issue is I can only read them in as a file type which doesn't allow me to access the getRange() function.
Here's my code so far.
function createLog() {
var folder = DriveApp.getFolderById("id#");//not placing actual id for privacy
var contents = folder.getFiles();
var data; //array for relevant text
var file;
var d = new Date();
var log = SpreadsheetApp.create("Events log "+d.getMonth()+"/"+d.getDay());
while(contents.hasNext()) {
file = contents.next();
file.getRange("A6");//error is here because it is a file type not a spreadsheet
}
for(var i =0; i<contents.length;i++){
log.getRange(0,i).setValue(data[i]);
}
}
Once you have the list of files you need to open them with SpreadsheetApp. Then you can work on Spreadsheet using the Sheet and Range functions.
var spreadsheet = SpreadsheetApp.openById(file.getId());
var sheet = spreadsheet.getSheetByName('your sheet name');
var value = sheet.getRange("A6");
See:
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
https://developers.google.com/apps-script/reference/drive/file#getId()
Cameron's answer is correct but I suggest to open spreadsheets by ID instead of names because in Google Drive many files can have the same name...
Below is a simplified demo code to test the idea and a few comments to highlight what needs to be adjusted
function createLog() {
var folder = DriveApp.getFolderById("0B3###############ZMDQ");//not placing actual id for privacy
var contents = folder.getFilesByType(MimeType.GOOGLE_SHEETS);
var data; //array for relevant text
var fileID,file,sheet;
var data = [];
var d = new Date();
var log = SpreadsheetApp.create("Events log "+d.getMonth()+"/"+d.getDay());
while(contents.hasNext()) {
file = contents.next();
Logger.log('Sheet Name = '+file.getName());
fileID = file.getId();
sheet = SpreadsheetApp.openById(fileID).getSheets()[0];// this will get the first sheet in the spreadsheet, adapt to your needs
data.push([sheet.getRange("A6").getValue()]);// add a condition before this line to get only the data you want
}
log.getActiveSheet().getRange(1,1,data.length, data[0].length).setValues(data);
}

Categories