I'm having trouble creating a script in Google Apps Script that checks the number of files in a subfolder of the spreadsheet folder.
A friend here on the forum helped me to create a script that checks the spreadsheet folder, but now I need to modify it to check the subfolder of the spreadsheet folder.
The script I have at the moment is this one:
/** Written by Tanaike */
function CheckForFiles() {
const ss = SpreadsheetApp.getActiveSpreadsheet(); // This is your active Spreadsheet.
const parentFolder = DriveApp.getFileById(ss.getId()).getParents();
if (parentFolder.hasNext()) {
const files = parentFolder.next().getFiles();
let count = 0;
while (files.hasNext()) {
const file = files.next();
// console.log(file.getName()); // When you use this line, you can see the filename of the files.
count++;
}
if (count >= 3) {
throw new Error("Your expected error.");
}
} else {
throw new Error("Spreadsheet has no parent folder.");
}
}
Important point: note that the spreadsheet folder is a template folder, that is, it will be duplicated and its name will be changed several times, for this reason I need the script to be something like "Check how many files there are inside the subfolder FolderToBeCheck which is inside the folder of this worksheet, if the number of files is more than 3, return an error"
How can I achieve this?
Test Folder
In your situation, how about the following modification?
Modified script:
In this modification, from your showing image, it supposes that the folder name of the subfolder is FolderToBeCheck. In your question, it seems that the folder name is FolderToBeChecked. Please be careful about this difference.
function CheckForFiles() {
const subFolderName = "FolderToBeCheck"; // Please set your subfolder name.
const ss = SpreadsheetApp.getActiveSpreadsheet(); // This is your active Spreadsheet.
const parentFolder = DriveApp.getFileById(ss.getId()).getParents();
if (parentFolder.hasNext()) {
const innerFolder = parentFolder.next().getFoldersByName(subFolderName); // <--- Modified
if (innerFolder.hasNext()) {
const files = innerFolder.next().getFiles();
let count = 0;
while (files.hasNext()) {
const file = files.next();
// console.log(file.getName()); // When you use this line, you can see the filename of the files.
count++;
}
if (count >= 3) {
throw new Error("Your expected error.");
}
} else {
throw new Error("Folder of 'FolderToBeCheck' was not found.");
}
} else {
throw new Error("Spreadsheet has no parent folder.");
}
}
When this script is run, the subfolder of FolderToBeCheck is checked. And, the number of files in the folder is counted.
Related
I have two parent folders that I want to check if Folder(folderName) exists in. folderName is defined by the range S2:2 in my spreadsheet
If Folder(folderName) does not exist in EITHER parent folder, then I want that folder to be created in Parent Folder 1.
So far my script is managing to check Parent Folder 1 and create in Parent Folder 1, but is not checking Parent Folder 2.
This is the script I have so far:
function myfunction() {
var parent = DriveApp.getFolderById("parent Folder 1")
SpreadsheetApp.getActive().getSheetByName('Letter History').getRange('S2:S').getValues()
.forEach(function (r) {
if(r[0]) checkIfFolderExistElsesCreate(parent, r[0]);
})
}
function checkIfFolderExistsElseCreate(parent, folderName) {
var folder;
try {
folder = parent.getFoldersByName(folderName).next();
} catch (e) {
folder = parent.createFolder(folderName);
}
function myFunction() {
var parentFolder1 = DriveApp.getFolderById('<ParentFolder1 ID>').getFolders(); //Get ID for Folder 1
var parentFolder2 = DriveApp.getFolderById('<ParentFolder2 ID>').getFolders(); //Get ID for Folder 2
var sheet = SpreadsheetApp.getActive().getSheetByName('Letter History');
//Iterating to read all the subfolders in ParentFolder 1 and 2
var childNames = [];
while (parentFolder1.hasNext()) {
var child = parentFolder1.next();
childNames.push([child.getName()]);
}
while (parentFolder2.hasNext()) {
var child = parentFolder2.next();
childNames.push([child.getName()]);
}
//Checking if the subfolder/s is existing or not, then creating in folder 1 if not yet existing
var data = sheet.getRange(2, 19, sheet.getLastRow() - 1, 1).getValues(); //S = 19, Change 19 according to column.
for (var i = 0; i < data.length; i++) {
if (childNames.flat().includes(data[i][0])) {
console.log(data[i][0] + "- already exist")
} else {
DriveApp.getFolderById('<ParentFolder1 ID>').createFolder(data[i][0])
console.log(data[i][0] + "- has been created")
}
};
}
The above code will create subfolder/s in Folder 1 based on range S2:S column in spreadsheet, if it is checked that the subfolder name does not yet exist either in Parent Folder 1 or 2.
Please take note to change the following:
ParentFolder 1 ID
ParentFolder 2 ID
Reference: How to create a folder if it doesn't exist?
I believe your goal is as follows.
You want to expand your showing script for using 2 parent folders.
In this case, how about the following sample script?
Sample script:
Please set your parent folder IDs to parentFolderIDs and save the script.
function myfunction() {
// Please set your parent folder IDs you want to use.
const parentFolderIDs = ["###parent folder ID1###", "###parent folder ID2###"];
// 1. Retrieve subfolders from folders of parentFolderIDs.
const parentFolders = parentFolderIDs.map(id => DriveApp.getFolderById(id));
const subFolders = parentFolders.map(folder => {
const temp = [];
const folders = folder.getFolders();
while (folders.hasNext()) {
temp.push(folders.next().getName());
}
return { parent: folder, subFolderNames: temp };
});
// 2. Create new folders when the folder names are not existing.
const sheet = SpreadsheetApp.getActive().getSheetByName('Letter History');
const values = sheet.getRange('S2:S' + sheet.getLastRow()).getDisplayValues();
values.forEach(([s]) => {
if (s) {
subFolders.forEach(({ parent, subFolderNames }) => {
if (!subFolderNames.includes(s)) {
parent.createFolder(s);
}
});
}
});
}
When this script is run, the subfolders in parentFolderIDs are retrieved. And, using the retrieved subfolders, the new subfolders are created in the parent folders of parentFolderIDs.
References:
map()
forEach()
I have a problem with my script, I would like it to search all folders and subfolders for files with this word in them.
But it only stops at the first folder.
Thank you for your help
function doGet(){
return HtmlService.createHtmlOutputFromFile('search').setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function idevlupSearch(sparams) {
if (sparams.length==0){
return `<em style='color:blue'> Champs de saisie vide`;
};
//Folder start
var folderName = "0-CentreDeRessources" ;
var listSearch = [];
var folder = DriveApp.getFoldersByName(folderName);
while (folder.hasNext())
{
Logger.log(folder);
var files = folder.next().searchFiles('title contains "'+sparams+'" or fullText contains "'+sparams+'"');
while (files.hasNext())
{
var file = files.next();
listSearch.push("<li> <a href='"+file.getUrl()+"'>"+file.getName()+"</a>"+file+"</li>");
}
}
Logger.log(listSearch);
if (listSearch.length > 0) {
listSearch.unshift("<ul>");
listSearch.shift("</ul>");
return listSearch;
}else{
return `<em style='color:red'> Pas de document trouvé !`;
}
}
I believe your goal is as follows.
From the discussions in the comment, you don't want to create the path including all subfolder names. You just want to search the files under the subfolders and want to retrieve the searched filenames.
In this case, how about the following modification?
Modified script:
function idevlupSearch(sparams) {
if (sparams.length == 0) {
return `<em style='color:blue'> Champs de saisie vide`;
};
var folderName = "0-CentreDeRessources";
var folder = DriveApp.getFoldersByName(folderName).next();
var searchFlies = (folder, res = []) => {
var files = folder.searchFiles(`(title contains '${sparams}' or fullText contains '${sparams}') and trashed=false`);
while (files.hasNext()) {
var file = files.next();
res.push({name: file.getName(), url: file.getUrl()});
}
var folders = folder.getFolders();
while (folders.hasNext()) searchFlies(folders.next(), res);
return res;
}
var listSearch = searchFlies(folder);
return listSearch.length > 0 ? listSearch.reduce((s, {name, url}) => s += `<li><a href='${url}'>${name}</a></li>`
, "<ul>") + "</ul>" : `<em style='color:red'> Pas de document trouvé !`;
}
Note:
I cannot understand that how the function idevlupSearch is run. But, in your script, Web Apps is used. In this case, when you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
About some unclear points, I guessed as follows.
I couldn't understand file of </a>"+file+"</li> in your script. Because in your script, file is an object of Class File. So in this modification, I modified to <li><a href='${url}'>${name}</a></li>.
I couldn't understand whether there are several same folder names of "0-CentreDeRessources". So in this modification, I guessed that the folder name of "0-CentreDeRessources" is only one folder in your Google Drive.
I couldn't understand the output from the function idevlupSearch. In your script, a text or an array is returned from the function idevlupSearch. But I cannot know about the function which calls idevlupSearch. So in this modification, a text is returned.
If my modification was not in the direction you expect, please modify the above script for your actual situation.
I'm super bad in apps script in Google drive ... I'm upset.
I need your help.
I have folders like this in my root drive :
students
__kev
___math
___english
__donald
___math
___english
__tony
___math
___english
transfer
__math
I want to automate an operation: copy or move all the contents of the "transfer" folder to each student folder.
I can do this task easily on PowerShell or bash with a sync client but I need it directly on drive now.
Any idea ? I want to add a menu on a file with the tool script.
The algorithm is quite simply, but there is one trick. You need to decide what should the script do in case a destination folder already has a file with the same name. By default Google Drive allows to have many files with the same name within a folder, which can be confusing.
My script checks a target folder and removes a file with the same name (it's supposed that there is one file) before copying.
function main() {
const root_folder_ID = "###"; // here is ID of your folder
const root_folder = DriveApp.getFolderById(root_folder_ID);
const transfer_folder = root_folder.getFoldersByName("transfer").next(); // transfer folder
// get array of subjects from transfer folder
const subjects_generator = transfer_folder.getFolders();
var subjects = [];
while (subjects_generator.hasNext()) {
var subj = subjects_generator.next();
subjects.push(subj);
}
// get students folders (a generator)
const students_folder = root_folder.getFoldersByName("students").next();
const students = students_folder.getFolders();
// copy all subject folders (with content) into students folders
while(students.hasNext()) {
var student = students.next();
for (var s in subjects) {
Logger.log("folder '" + subjects[s] + "' started to copy to '" + student + "'");
copy_folder(subjects[s], student);
Logger.log("---");
}
}
}
function copy_folder(source_folder, target_folder) {
// pick or create subject subfolder within target folder
try {
var subfolder = target_folder.getFoldersByName(source_folder.getName()).next();
} catch(e) {
var subfolder = target_folder.createFolder(source_folder.getName());
}
// get all files from source folder
var files = source_folder.getFiles();
// copy the files into the subfolder
while(files.hasNext()) {
var file = files.next();
var file_name = file.getName();
// try to remove old file with the same name in the target folder
try {
var old_file = subfolder.getFilesByName(file_name).next();
old_file.setTrashed(true);
Logger.log("old file '" + old_file + "' was removed")
} catch(e) {}
file.makeCopy(file.getName(), subfolder);
Logger.log("file '" + file + "' is copied")
}
}
To run this script you have to create a new document somewhere on Google Drive, go the menu 'Tools' and click on 'Script Editor'.
Then you need to paste the text of the script into the editor and click the 'Run' icon:
But first you need to paste your folder ID into the second line of the script of course.
Alternatively you can make a custom menu in your document and run the script from there, without Script editor.
To make a custom menu just add this function at the end (or start) of the script in Script editor:
// custom menu
function onOpen() {
DocumentApp.getUi().createMenu('Scripts')
.addItem('📁 Copy files', 'main')
.addToUi();
}
And you'll get the custom menu 'Script' after reload your document:
This should fit your needs. Adapted from: this site. Change the id's of the two folders.
function start() {
var transfertFolder = DriveApp.getFolderById("xxxxxxx-YhSCZXh1PDeqpE8mXjHHJJF");
var studentFolder = DriveApp.getFolderById("xxxxxxx_cQgd3DZV9Ukt4yI_-jTS_86Z").getFolders();
while (studentFolder.hasNext()){
const student = studentFolder.next()
copyFolder(transfertFolder,student)
}
}
function copyFolder(source, target) {
var folders = source.getFolders();
var files = source.getFiles();
while (files.hasNext()) {
var file = files.next();
file.makeCopy(file.getName(), target);
}
while (folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var targetFolder = target.createFolder(folderName);
copyFolder(subFolder, targetFolder);
}
}
How I can get all files name from current directory?
I have a code
function showAllFollderFronRoot() {
// get all files from the ROOT folder
var files = parentFolder;
while (files.hasNext()) {
var file = files.next();
// Logger.log(file.getName());
DocumentApp.getUi().alert(file.getName());
}
}
But it work only with ROOT dir.
How I can get all file names in array in current dir?
UPDATE:
I have a file structure: MT/MT.100-107/MT.100-1007.1001.doc
I need make code, if somebody open New Template from Docs - script need automatically safe this file with true structure - with next filename + 1 (example MT.100-1007.1002.doc, next new file from Template - MT.100-1007.1003.doc ...)
Script need to find all filenames => show last bigger count (1002.doc) => count + 1 => save this file with new filename MT.100-1007.1003.doc
My code work, but it make tmp file in Root dir & not work perfect, because it not calculate last bigger count in current dir and if I delete file, example MT.100-1007.1003.doc in dir MT, and make new file in dir UA - count be MT.100-1007.1004.doc no matter what the names of the files are in the folder UA.
These script with mistakes, how I can fix it?
/**
* #OnlyCurrentDoc
*/
function saveFilename() {
// Get current file name
const ui = DocumentApp.getUi(),
doc = DocumentApp.getActiveDocument(), //Added
thisFileId = doc.getId(),
thisFileName = doc.getName();
const thisFile = DriveApp.getFileById(thisFileId);//Modified from getFolderById
const parentFolder = thisFile.getParents();
const currentFolder = parentFolder.next();//Modified from currentFolderName
const currentFolderName = currentFolder.getName();//Added
//ui.alert(currentFolderName);
/*Store a init file in root to getLatestFileNumber*/
var initIter = DriveApp.getFilesByName(currentFolderName + 'init00'),
initBool = initIter.hasNext(),
init;
if (!initBool) {
init = DriveApp.createFile(currentFolderName + 'init000', '0');
} else {
init = initIter.next();
}
/*Get current Number and format it to 4 digits*/
var currentNum = init.getBlob().getDataAsString() * 1 + 1,
formatNum = ('0000' + currentNum).substr(-3);
/*If filename already contains folderName, do nothing*/
if (!(thisFileName.search(currentFolderName) + 1)) {
doc.setName(currentFolderName +'.' + formatNum).saveAndClose();
init.setContent(currentNum);
}
// delete TMP file from ROOT dir
DriveApp.getFileById(init.getId()).setTrashed(true)
}
You want to retrieve filenames of all files in the parent folder of the active document.
If my understanding is correct, how about this answer?
Flow:
The flow of this script is as follows.
Retrieve file ID of the active document.
Retrieve parent folder ID of the active document.
Retrieve files in the parent folder ID.
Retrieve filenames of files.
Modified script:
function showAllFollderFronRoot() {
var fileId = DocumentApp.getActiveDocument().getId();
var parentFolderId = DriveApp.getFileById(fileId).getParents().next().getId();
var files = DriveApp.getFolderById(parentFolderId).getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName())
}
}
Note:
This sample script supposes as follows.
The parent of the active document is only one.
All files in the parent folder of the active document are retrieved. But the folders are not retrieved.
References:
getId()
getParents()
getFolderById(id)
getFiles()
If I misunderstand your question, please tell me. I would like to modify it.
I fix this problem, but idk how to parse last 4 digits in filename and find MAX from it. Do you have any idea? method slice(4) not working in apps script :(
function currFiles() {
const ui = DocumentApp.getUi(),
doc = DocumentApp.getActiveDocument(),
thisFileId = doc.getId(),
thisFileName = doc.getName();
const thisFile = DriveApp.getFileById(thisFileId);
const parentFolder = thisFile.getParents();
const currentFolder = parentFolder.next();
const currentFolderName = currentFolder.getName();
const currentFolderId = currentFolder.getId();
// get all files in currentFolder
var folderId = currentFolderId;
// Log the name of every file in the folder.
var files = DriveApp.getFolderById(folderId).getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName());
}
}
I have a Google spreadsheet file with almost 2000 rows of URL such as this one:
https://docs.google.com/uc?id=0B4ptELk-D3USblk1aWd1OWowRWs
Each URL contains an image, stored in Google Drive. My question is: How can I transform this link ( https://docs.google.com/uc?id=0B4ptELk-D3USblk1aWd1OWowRWs) to a file name? For instance, I would like to know if it could be a /something/folder/Photofile1.jpg.
Is there anyway of doing this?
Try script:
function TESTgetFileName() {
Logger.log(getFileName('0B4ptELk-D3USVi1NZ3ZGbkhqYXc'));
// ^^^^^^^^^^ file ID ^^^^^^^^^
}
function getFileName(id) {
var file = DriveApp.getFileById(id);
var fileName = file.getName();
var strFolders = getFolders(file);
return strFolders + '/' + fileName;
}
function getFolders(object) {
var folders = object.getParents();
if (!folders.hasNext()) { return 'My Drive'; }
var folder = folders.next();
var folderNames = [];
while (folder.getParents().hasNext()) {
var folderName = folder.getName();
folderNames.unshift(folderName);
folder = folder.getParents().next();
}
return folderNames.join('/');
}
Script function will return the result:
0B4ptELk-D3USVi1NZ3ZGbkhqYXc → My Drive/something/folder/Photofile1.jpg
Tests
I opened script editor, selected function TESTgetFileName and get the result
My Drive/Detalhes_Farmacia.Fotografia_Fachada_1.JPEG.
This function can be run from script, not directly from spreadsheets. When you try using it as custom formula from sheet, it throws error:
You do not have permission to call getFileById (line 8).
So better use it from script to get data and then write the result to the sheet.