Sort files Google Apps Script - google-apps-script

I have this code in Apps Script, it gets all files of a folder (expofd) and print the ID's in the Google Sheet. But each time the code get the files in a different order and I need any kind of sort (Alphabetically by name, not by id) or whatever, but the printing in the Sheet have to be always in the same order. Also, It would be nice a code modification to filter the folder files and get only which have certain suffix.
var list = [];
list.push(['ID']);
var files = expofd.getFiles();
while (files.hasNext()){
file = files.next();
var row = []
row.push(file.getId())
list.push(row)
;
}
sh.getRange(2,2,list.length,list[0].length).setValues(list);
Thanks!

Try this
function myFunction() {
var expofd = DriveApp.getFolderById('#############') // your folder id
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var suffix = '.pdf' // adapt as necessary
var list = [];
var files = expofd.getFiles();
while (files.hasNext()) {
file = files.next();
list.push([file.getName(),file.getId()])
}
var result = [['name','id'], ...list.filter(r => r[0].includes(suffix)).sort()]
sh.getRange(1,1, result.length, result[0].length).setValues(result);
}
You will get the list sorted by name and filtered by suffix

Just in case here is the more fancy (and probably more quick) way to search files with Drive API:
function myFunction() {
var mask = '.jpg';
var folder_id = '###'; // put your folder ID instead of ###
var query = `title contains "${mask}" and trashed = false and "${folder_id}" in parents`;
var findings = Drive.Files.list({ q: query }) || [];
var table = [['name', 'id'], ...findings.items.map(f => [f.title, f.id]).sort()];
SpreadsheetApp.getActiveSheet().clear().getRange(1,1,table.length,2).setValues(table);
}
Make sure you add the Drive API in Script Editor:
Reference:
Search for files and folders with Drive.list() method

Related

List Google drive folder contents to google sheets with only new files

Looking to learn how to improve my use of loops. Currently I need to list the names and URLS from a google drive Folder to a sheet and this is the code that I have:
Existing Code
function wthFolderContents() {
var folder_id = 'myFolderID';
var folders = DriveApp.getFolderById(folder_id)
var contents = folders.getFiles();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("SheetName");
sheet.clearContents()
sheet.appendRow( ['name', 'link'] );
var file;
var name;
var link;
var row;
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
link = file.getUrl();
sheet.appendRow ( [name, link] );
with this code everytime the script is run the contents are cleared and then relisted. I am looking at a way of doing this dynamically / only update the new files so the script runs more effeciently.
Ive tried the following
New Code
function wthFolderContents2() {
var folder_id = '1vBzucZsb0SMOoHSWGtkUF-5QLQr5Fh1C';
var folders = DriveApp.getFolderById(folder_id)
var contents = folders.getFiles();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("WHTCert");
var lastRow = sheet.getLastRow()
var existing = sheet.getRange(1,1,lastRow,1).getValues()
for(i=1;i<lastRow;i++) {
var existingFilename = existing [i][0]
Logger.log(existingFilename)
while(contents.hasNext()) {
var file;
var name;
var link;
file = contents.next();
name = file.getName();
link = file.getUrl();
if (!name == existingFilename) {
sheet.appendRow ( [name, link] );
}
}
}
I cant get this to work, not sure what exactly where I have gone wrong. Hope someone can point me int he right direction!
Cheers
I believe your goal is as follows.
You want to reduce the process cost of your script.
Modification points:
In your script, appendRow is used. In this case, the process cost will become high. Ref
The search for files is run in a loop. In this case, the process cost will become high.
In your situation, it seems that you want to retrieve the file list just under the specific folder. In this case, I thought that when Drive API is used, the process cost can be reduced. In this answer, I would like to propose using Drive API in your script. When this is reflected in your script, it becomes as follows.
When Drive API is used, all values can be retrieved. So, I thought that your 1st process might be able to be used.
Modified script:
Before you use this script, please enable Drive API at Advanced Google services.
function wthFolderContents2() {
var folder_id = '1vBzucZsb0SMOoHSWGtkUF-5QLQr5Fh1C';
// Retrieve file list.
var q = `'${folder_id}' in parents and trashed = false and mimeType != '${MimeType.FOLDER}'`;
var fileList = [['name', 'link']];
var pageToken = "";
do {
var obj = Drive.Files.list({ q, maxResults: 1000, pageToken, fields: "nextPageToken,items(id,title)", corpora: "allDrives", supportsAllDrives: true, includeItemsFromAllDrives: true });
if (obj.items.length > 0) {
fileList = [...fileList, ...obj.items.map(({ id, title }) => [title, `https://docs.google.com/presentation/d/${id}/edit?usp=drivesdk`])];
}
pageToken = obj.nextPageToken;
} while (pageToken);
// Put the values to Spreadsheet.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("WHTCert");
sheet.clearContents();
sheet.getRange(1, 1, fileList.length, fileList[0].length).setValues(fileList);
}
When this script is run, the file list (filename and URL) is retrieved from the specific folder. And, the retrieved values to the "WHTCert" sheet.
Reference:
Files: list
Please convert this 2 script for scan folder only one subfolder and file, show folder name and link.
function wthFolderContents()
function wthFolderContents2()

Google Apps Script Filter Output Before Moving

Good Morning All,
I should filter the output by the name (must contain a specific word) of the file before it is moved... How can I do this? With this script it moves all the files it finds indiscriminately, but that is not what I want
function moveFiles(sourceFileId, targetFolderId) {
var file = DriveApp.getFileById(sourceFileId);
var folder = DriveApp.getFolderById(targetFolderId);
file.moveTo(folder);
}
function searchDrive() {
var inputMe = 'MYDRIVEID';
var inputFolder = DriveApp.getFolderById(inputMe);
var files = inputFolder.getFiles();
while (files.hasNext()) {
var destinationFolderId = "MYDRIVEID";
var file = files.next();
var moveMe = file.getId();
var moveMeName = file.getName();
Logger.log(moveMeName);
moveFiles(moveMe,destinationFolderId);
}
}
As a quick fix you can just replace:
moveFiles(moveMe,destinationFolderId);
with:
if (moveMeName.contains('specific word')) moveFiles(moveMe,destinationFolderId);
or (for case insensitive mode):
if (moveMeName.toLowerCase().contains('specific word')) moveFiles(moveMe,destinationFolderId);
In your situation, how about the following modification?
Modified script:
In this modification, your searchDrive is modified. Please set the filter texts to filterTexts.
function searchDrive() {
var filterTexts = ["sample1", "sample2",,,]; // Please set the filter text you want to use.
var inputMe = 'MYDRIVEID';
var inputFolder = DriveApp.getFolderById(inputMe);
var files = inputFolder.getFiles();
while (files.hasNext()) {
var destinationFolderId = "MYDRIVEID";
var file = files.next();
var moveMe = file.getId();
var moveMeName = file.getName();
if (!filterTexts.some(e => moveMeName.toUpperCase().includes(e.toUpperCase()))) {
moveFiles(moveMe, destinationFolderId);
}
}
}
When this script is run, the files with the filename which doesn't include the texts of filterTexts are moved.
If you want to distinguish the uppercase and lowercase of the filename, please remove toUpperCase().
Reference:
some()
Added:
From your following reply,
I only want to move files that contain a certain name within the filename. I suppose I have to remove ! in front of filterTexts.some to do this?
In this case, when you use the above script, please set the value you search like var filterTexts = ["sample1"];. From your question, I couldn't understand whether you wanted to search for only one value. So, in my answer, I proposed the script for searching multiple values by considering the scalability of the function.
If you want to search for only one value, you can also the following script.
But I get an error on the previous part of the script, on the line var file = DriveApp.getFileById(sourceFileId); with error Exception: Invalid argument: id what can it be?
I think that this is not related to my proposed script. From your situation, I'm worried that the files you cannot move might be included. In that case, how about skipping such files as following modification?
Modified script:
function searchDrive() {
var filterTexts = "sample1"; // Please set the filter text you want to use.
var inputMe = 'MYDRIVEID';
var inputFolder = DriveApp.getFolderById(inputMe);
var files = inputFolder.getFiles();
while (files.hasNext()) {
var destinationFolderId = "MYDRIVEID";
var file = files.next();
var moveMe = file.getId();
var moveMeName = file.getName();
if (!moveMeName.toUpperCase().includes(filterTexts.toUpperCase())) {
try {
moveFiles(moveMe, destinationFolderId);
} catch (e) {
console.log({ errorMessage: e.message, filename: moveMeName, fileId: moveMe });
}
}
}
}
In this sample script, the files with the filename which includes the value of filterTexts are moved using your function of moveFiles. But, if an error occurs at moveFiles, the file is skipped. At that time, the filename and file ID are shown in the log.

Using Logger.Log to log different value

I was wondering: is it even possible to use Logger.Log in Google Apps Script to log different string to be posted to a spreadsheet?
I have the following code:
var ss = SpreadsheetApp.openByUrl("spreadsheet url");
var sheet = ss.getSheetByName("spreadsheet sheet");
var DocNumber = e.parameter.DocNumber;
var folderId = "Folder ID 1";
var lastFileUrl = getLatestFile(folderId); // just a function that retrieves url of latest file in the folder
Logger.log(lastFileUrl);
var addUrl = sheet.getRange(1,2,sheet.getLastRow(),1);
var fileURL = "https://drive.google.com/uc?export=view&id="+lastFileUrl;
var folderId2 = "Folder ID 2";
var lastFileUrl2 = getLatestFile(folderId2); // same as above
Logger.log(lastFileUrl2);
var addUrl2 = sheet.getRange(1,3,sheet.getLastRow(),1);
var fileURL2 = "https://drive.google.com/uc?export=view&id="+lastFileUrl2;
sheet.appendRow([DocNumber,fileURL,fileURL2]);
}
When this get posted to the spreadsheet, it only posts the second url (fileURL2) - I assume because the last value in the log is this. But I was hoping to post both URL into the spreadsheet.
I tried setting it as a var first as well:
var URL2 = Logger.log(lastFileURL2);
but then the posted value will be https://drive.google.com/uc?export=view&id=Logger
I also tried using appendRow before the second URL logging but it still only takes the second url and disregard the first url.
Therefore, I was curios whether this is even possible at all?
And if not, what's the best way to achieve this without using Logger.log?
Spreadsheet output:
URL1 and URL2 is the URL from Google Drive folder.
Also, forgot to mention, I'm using the script as a Web App, used by an android app. Posting files into the Drive folder is okay, the only problem is fetching the links of the files in different folders.
These are the codes I used to get the latest file url from my folders:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById("Folder_1_ID").getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
function getLatestFile(folderId2) {
var files2 = DriveApp.getFolderById("Folder_2_ID").getFiles();
var fileObj2 = [];
while (files2.hasNext()) {
var file2 = files2.next();
fileObj2.push({id: file2.getId(), date: file2.getDateCreated()});
}
fileObj2.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj2[0].id;
}
Problem
Having two functions declared under the same name
Solution
Step by step:
Remove one of the functions (they are identical in terms in usage)
Make the remaining one use the parameter passed in it:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById(folderId).getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
Change Logger to console - as of recently, all logs are sent to Stackdriver service, and thus there is no benefit in using Logger (besides by using console you make script more portable).
Commentary
What happens when you declare two or more functions under same name? Normally, the last one declared gets executed (basically, second declaration overwrites the first):
function clone(original) {
return `I am the clone of ${original}`;
}
function clone(cloned) {
return `I am a clone of ${cloned}'s clone`;
}
const elem = document.querySelector("#cloned");
elem.textContent = clone("Gary");
<h2 id="cloned"></h2>

Trying to create tree-view of google drive folders

Many thanks for the comments and response. That code was a little too advanced for me and I ended up finding a very inelegant solution that I used because I ran out of time. I was able to get the code to list Folder and first level of subFolders with links, but I have not yet been able to get it to iterate through all levels of folders, mostly because I just need to back up and learn a lot of the basics. I was also able to get all folders to list using some code I found to create a tree, but I couldn't get it to format in a way that you could actually see the structure, or add links. I'm going to continue to try, and will post if I sort it out. Here is what I used, which was fine for our purposes because our shared drive is fairly limited.
For reference, this was the code I used to start with:
https://superuser.com/questions/1095578/list-of-subfolder-names-and-file-links-in-google-sheets-script
function listFolders(foldername) {
var ss = SpreadsheetApp.openById(ID);
var sheet = ss.getSheetByName("sheet name");
sheet.appendRow("Parent Folder", "Name", "Link" ]);
//change the folder ID below to reflect your folder's ID (look in the
URL when you're in your folder)
var folders = DriveApp.getFolderById(ID);
var contents = folders.getFolders();
var cnt = 0;
var folderD;
while (contents.hasNext()) {
var folderD = contents.next();
cnt++;
data = [
folders.getName(),
folderD.getName(),
folderD.getUrl(),
];
sheet.appendRow(data);
};
};
Original Post:
I am a beginner using script in google sheets and I am trying to create a list of folders in a google drive with many subfolders. Ideally it would be a tree form but I'd settle for a list at this point. I don't need to list all the files, just the folders. I have been trying to get the code below to work but it keeps hanging up at calling up the spreadsheet. Can anyone help?
I have tried calling up both the folders and the spreadsheet by both name and ID but it always tells me it can't execute the getactivespreadsheet command. I have also tried to modify the code referred to in another another question but I can't get that to work either: https://ctrlq.org/code/19923-google-drive-files-list
function generateFolderIndex(myfoldername) {
var folder = DriveApp.getFolderById('0B8vOJQUb-IIVTHdudlZSVkdtdE0');
var subFolders = folder.getFolders();
var childFolders = subFolders
var ss = SpreadsheetApp.getActiveSpreadsheet('1Trv9OtJFnD4AdSHrZKFfsSu6JMV9f78H6wwZNhF2_M4');
var sheet = ss.getSheetByName('Directory');
sheet.clear(directory);
sheet.appendRow([name, link]);
while (subFolders.hasNext())
{
var childFolder = childFolders.next();
var foldername = childFolder.getname();
var name = childFolder.getName()
var link = childFolder.getUrl()
var date = childFolder.getDateCreated()
data = [name, link]
sheet.appendRow(data);
}
};
I am trying to get a sheet that lists folders and subfolders with URL links. I am currently receiving the following error message:
[19-05-31 15:32:20:911 EDT] Execution failed: Cannot find method getActiveSpreadsheet(string). (line 5, file "Code") [0.432 seconds total runtime]
Or.. the easy way...
Use DRIVE or FS DRIVE APP for desktop in PC. Usea A CMD (windows)... AND THE FUNCTION
TREE >a.txt
The generated file a.txt will display all the tree.
IT SAVES HOURS OF RESEARCH.
SpreadsheetApp.getActiveSpreadsheet() doesn't have any parameters.
However
SpreadsheetApp.openById('ssid') does require and id. I think perhaps you meant to be using openById();
openById
getActiveSpreadsheet
This is a script that I'm currently working on but it generates a list of Spreadsheets and you can exclude folders by id and files by id.
function getAllSpreadsheets() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
if(sh.getLastRow()>0) {
sh.getRange(1,1,sh.getLastRow(),2).clear().clearDataValidations();
}
getFnF();
SpreadsheetApp.getUi().alert('Process Complete')
}
var level=0;
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
//var folder=DriveApp.getRootFolder();
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
var files=folder.getFilesByType(MimeType.GOOGLE_SHEETS)
while(files.hasNext()) {
var file=files.next();
if(isExcluded(file.getId(),'file')){continue;}
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',file.getUrl(),'FILE: ' + file.getName()));
firg.offset(0,1).insertCheckboxes('Exclude','Include');
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
if(isExcluded(subfolder.getId(),'folder')){continue;}
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',subfolder.getUrl(),'FOLDER: ' + subfolder.getName()));
//forg.offset(0,1).insertCheckboxes('Exclude','Include');
//level++;
getFnF(subfolder);
}
//level--;
}
function isExcluded(id,type) {//type: file or folder
var type=type||'Missing Input';
var xFldrIdA=['Excluded folder ids'];
var xFileIdA=['Excluded file ids'];
var type=type.toLowerCase();
switch(type) {
case 'file':
return xFileIdA.indexOf(id)>-1;
break;
case 'folder':
return xFldrIdA.indexOf(id)>-1;
break;
default:
throw(Utilities.formatString('Error: Invalid Type: %s in isExcluded.',type));
return true;//assume excluded
break;
}
}
Your welcome to use it, perhaps it will help.

Copy the body of all documents found in a specific folder into a Google Spreadsheet

I am trying to create a script that obtains all (Google Doc) document IDs within a Google Drive folder, grabs the body from each of those documents, and returns this into rows in this spreadsheet.
The following syntax gets me the file IDs and Names, but doesn't grab the body:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi().createAddonMenu();
ui.addItem('List all body components of files inside a folder without including subfolders', 'list_all_body_components_of_files_inside_one_folder_without_subfolders')
.addToUi();
}
function list_all_body_components_of_files_inside_one_folder_without_subfolders() {
var sh = SpreadsheetApp.getActiveSheet();
// Prompt the user for a google drive folder ID
var folderID = Browser.inputBox("Enter the folder ID to search in - It's the bit in the URL after /folders/:");
// return the folder identified by that ID number for further manipulation
var folder = DriveApp.getFolderById(folderID);
// print the list to columns A through C
var list = [];
list.push(['Body','Name','ID']);
var files = folder.getFiles();
while (files.hasNext()){
file = files.next();
var row = []
row.push(file.getBlob(),file.getName(),file.getId())
list.push(row);
}
sh.getRange(1,1,list.length,list[0].length).setValues(list);
}
I think that the problem is with file.getBlob I've tried
file.getBlob.getDataAsString(), but that returns:
TypeError: Cannot find function getDataAsString in object function getBlob() {/* */}.
How do I retrieve the text of each document body?
Since it's Google Docs that we're interested in, focus the file scan on those:
var files = folder.getFilesByType(MimeType.GOOGLE_DOCS);
Then when working through the file iterator, use the file ID with DocumentApp to open the file as a Google Document - that way, the body will be readily accessible.
Updated code:
function list_all_body_components_of_files_inside_one_folder_without_subfolders() {
var sh = SpreadsheetApp.getActiveSheet();
// Prompt the user for a google drive folder ID
var folderID = Browser.inputBox("Enter the folder ID to search in - It's the bit in the URL after /folders/:");
// return the folder identified by that ID number for further manipulation
var folder = DriveApp.getFolderById(folderID);
// print the list to columns A through C
var list = [];
list.push(['Body','Name','ID']);
var files = folder.getFilesByType(MimeType.GOOGLE_DOCS);
while (files.hasNext()){
var fileId = files.next().getId();
var doc = DocumentApp.openById(fileId);
var row = []
row.push(doc.getBody().editAsText().getText(),
doc.getName(),
fileId)
list.push(row);
}
sh.getRange(1,1,list.length,list[0].length).setValues(list);
}