I made this script to list all subfolders of a given folder, so later I can look up values in another worsheet. But the script is not picking up all the folders. Why's that
function ListaPasta() {
var foldername= 'Main folder name';
var folderlisting = 'folders';
var mainfolder = DriveApp.getFoldersByName(foldername).next();
var folders = mainfolder.getFolders();
var ss = SpreadsheetApp.create(folderlisting);
var sheet = ss.getActiveSheet();
sheet.appendRow( ['Nome'] );
while(folders.hasNext()) {
var folder = folders.next();
var nome = folder.getName();
sheet.appendRow([nome]);
};
}
Edit:
I don't want the subfolders of the folders inside the main folder, so I don't need recursion.
The script is not listing all folders and I'm sure of it, because when I look for some folder in the listing (via LOOK functions and using the filter) I can't find it, but if I search in the drive I find it inside the main folder(not as a subfolder of other folders).
Now since the time I posted this question I noticed that when I opened some folders looking for a file it would show the folder as empty, but if I searched for the file name it would find it. After I opened the file and closed it, and then went to the folder and opened it the file would be there. Could be a similar behaviour causing the script to not find some folders?
Not sure if relevant, but the main folder is part of a shared drive.
I believe your goal as follows.
You want to retrieve the folder names of all subfolders in the specific folder using Google Apps Script.
You want to put the retrieved folder names to Google Spreadsheet.
From your script, you want to put each folder name to each row of the 1st tab of the created new Spreadsheet.
Modification points:
In your script, the folders in the specific folder are retrieved. In this case using getFolders(), only the folders just under the specific folder are retrieved. I think that this is the reason of your issue. When you want to retrieve all subfolders in the specific folder, as one of several methods Ref, how about recursively traverse all subfolders using the recursive function?
In your script, appendRow is used in a loop. In this case, the process cost will be high.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ListaPasta() {
const foldername= 'Main folder name'; // Please set the folder name of the specific folder.
const folderlisting = 'folders'; // Please set the Spreadsheet name.
// 1. Retrieve the folder names including all subfolders.
const getAllSubfolders = (f, folders = [foldername]) => {
const fols = f.getFolders();
const temp = [];
while (fols.hasNext()) {
const fol = fols.next();
temp.push({name: fol.getName(), folder: fol});
}
if (temp.length > 0) {
folders.push(temp.map(({name}) => name));
temp.forEach(({folder}) => getAllSubfolders(folder, folders));
}
return folders.flat().map(a => [a]);
}
const mainfolder = DriveApp.getFoldersByName(foldername);
if (!mainfolder.hasNext()) throw new Error("No folder.");
const folderNames = getAllSubfolders(mainfolder.next());
// 2. Create new Spreadsheet and put the retrieved folder names to the Spreadsheet.
var ss = SpreadsheetApp.create(folderlisting);
var sheet = ss.getSheets()[0];
sheet.getRange(1, 1, folderNames.length, 1).setValues(folderNames);
}
In this modified script, the top row is the folder name of the specific folder. If you don't want to include it, please modify folders = [foldername] to folders = [].
Related
I am a beginner in creating Scripts in Google Sheets, so I would like some help to reference a folder that is inside the spreadsheet folder.
I would like to create a script that checks if there are more than 3 files in a given folder, if so, I would like it to return an error on the screen.
Important point: the files that need to be checked will always be in a folder that is inside the spreadsheet folder, so I would need to reference this, in the CMD it would be something like .\FolderWithFiles.
In this case, I cannot use the ID a of the folder which I want to be checked, because this is a model worksheet that will be duplicated several times.
Any idea how I can do this?
I believe your goal is as follows.
You want to check the number of files in the folder including the active Spreadsheet you are using.
When the number of files is more than 3, you want to show an error.
In this case, how about the following sample script?
Sample script:
function myFunction() {
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.");
}
}
When this script is run, the number of files in the folder including the active Spreadsheet is checked. When the number of files is more than 3, an error like Your expected error. occurs.
If you want to use another Spreadsheet instead of the active Spreadsheet, please modify const ss = SpreadsheetApp.getActiveSpreadsheet(); to const ss = SpreadsheetApp.openById("###spreadsheetId###");.
Reference:
getParents()
I currently have a Google Sheet Tab called Main that allows the person to enter data in a few cells. Then in another tab called IXRE-BOF it creates the config file that the person needs based upon the values entered on the Main tab. I currently have 2 macro buttons on the MAIN tab the person clicks on. The first is button calls the CreateFolder Macro seen below. It looks up the Main Google Drive folder ID stored in K25 on the MAIN tab and searches that drive for a folder name stored in cell B9 of the MAIN tab and if not found creates it. This Macro works fine and creates folder name correctly.
I second button calls the ExportBof macro and copies everything on the IXRE-BOF tab in Column A. This works correctly and exports the file called bof.cfg but stores it in the parent Google Drive folder currently. I have been searching online for the past week and can't find a way to make it create the bof.cfg file in the subfolder created by name from the cell value B9 on the MAIN tab. So currently I just manually move the bof.cfg file into the subfolder but wish to have this done automatically. This script will create multiple files called bof.cfg which I don't want to happen. If it finds a bof.cfg I wish to have it overwrite the existing file with the same name if possible.
Lastly I would love to have all of this in 1 macro versus 2. So ultimately the button would be called Create Configs and when pressed it would look in the parent folder for the subfolder name. If it isn't found it will create a new folder from the value in B9 of the MAIN tab and then create the bof.cfg file in that folder. If the folder is found it will just add the bof.cfg file into that folder.
My code isn't the cleanest, as I've been just creating it based upon various examples that partially match what I'm trying to accomplish. So if there is a cleaner way to write this I greatly appreciate the guidance. I used to have this all in Excel but migrating to Google Sheets.
'''
function CreateFolder() {
var ss = SpreadsheetApp.getActive();
var gdrive = ss.getSheetByName('MAIN').getRange('K25').getValue();
var parent=DriveApp.getFolderById(gdrive);
SpreadsheetApp.getActive().getSheetByName('MAIN').getRange('B9').getValues()
.forEach(function (r) {
if(r[0]) checkIfFolderExistElseCreate(parent, r[0]);
})
}
function checkIfFolderExistElseCreate(parent, folderName) {
var folder;
try {
folder = parent.getFoldersByName(folderName).next();
} catch (e) {
folder = parent.createFolder(folderName);
}
};
function ExportBof() {
var ss = SpreadsheetApp.getActive();
var sheet=ss.getSheetByName('IXRE-BOF');
var firstRow = 1; // Skip first two rows => start at 3rd
var range = sheet.getRange(firstRow, 1, sheet.getLastRow() - firstRow + 1, sheet.getLastColumn());
var rows = range.getValues();
var columns = rows[0].map((_, colIndex) => rows.map(row => row[colIndex]));
var gdrive = ss.getSheetByName('MAIN').getRange('K25').getValue();
var folder=DriveApp.getFolderById(gdrive);
columns.forEach(function(column, index) {
folder.createFile("bof.cfg", column.join("\n")); // New line
});
}
};
'''
You can accomplish both things (merge into one script and create in the correct folder) with the following steps:
Return folder from checkIfFolderExistElseCreate
Integrate the last line of CreateFolder into ExportBof as shown below, using the return value as your target folder. Use getValue instead of getValues since the range B9 is only one cell. No need to iterate.
You no longer need CreateFolder, so just connect the single script ExportBof to your spreadsheet button.
Updated script (** indicates changes):
function checkIfFolderExistElseCreate(parent, folderName) {
var folder;
try {
folder = parent.getFoldersByName(folderName).next();
} catch (e) {
folder = parent.createFolder(folderName);
}
return folder // **Add this
};
function ExportBof() {
var ss = SpreadsheetApp.getActive();
var sheet=ss.getSheetByName('IXRE-BOF');
var firstRow = 1; // Skip first two rows => start at 3rd
var range = sheet.getRange(firstRow, 1, sheet.getLastRow() - firstRow + 1, sheet.getLastColumn());
var rows = range.getValues();
var columns = rows[0].map((_, colIndex) => rows.map(row => row[colIndex]));
var gdrive = ss.getSheetByName('MAIN').getRange('K25').getValue();
var parent=DriveApp.getFolderById(gdrive); // **Change this to parent
var folderName = ss.getSheetByName('MAIN')
.getRange('B9').getValue() // **Add this line
var folder = checkIfFolderExistElseCreate(parent, folderName) // **Add this line
columns.forEach(function(column, index) {
folder.createFile("bof.cfg", column.join("\n")); // New line
});
}
};
I have many spreadsheets inside a folder and today I have developed a new sheet (tab) inside one of these spreadsheets. I have a code to copy and paste one sheet to another spreadsheet, but for this there is another way without code.
My goal is:
a way to copy one sheet (order number 13) to all other spreadsheets at the same folder.
This is my code for copying one-to-one:
function sendspreadsheet(){
var source = SpreadsheetApp.getActiveSpreadsheet();
var aba = source.getSheets()[13];
var destination = SpreadsheetApp.openByUrl('https:sheetID');
aba.copyTo(destination);}
I have a great help from #Tanaike about similar issue at post: "Array for Google Sheet celarcontet" and I have studied this code and Google Class Sheet, but something is not working.
Code I'm trying to copy one-to-multiple spreadsheets at the same folder:
Function sendtomultiple(){
var source = SpreadsheetApp.getActiveSpreadsheet();
var aba = source.getSheets()[13];
var destination = DriveApp.getFolderById('1o6p-53Q1ntJAVIJt9w4k0UK___XXXXXX');
var sheetDestino = destination.getFilesByType(MimeType.GOOGLE_SHEETS);
while (sheetDestino.hasNext()) {
SpreadsheetApp.open(sheetDestino.next()).getSheets().forEach(aba => {
aba.copyTo(sheetDestino);
});
};
}
I guess this line into while instruction is the problem, I have tried other options, but all them return error message, kind this:
Exception: The parameters (DriveApp.FileIterator) don't match the method signature for SpreadsheetApp.Sheet.copyTo.
Hope someone can help to fix this.
Explanation / Issue:
Your goal is to copy a particular sheet (tab) into multiple spreadsheets within the same folder.
The issue has do to with the fact that sheetDestino.next() is a file object but copyTo(spreadsheet) accepts a spreadsheet object.
You can get the id of the file and then use openById(id) to get the spreadsheet object which you can use to copy the sheet to.
Bonus code:
I added an if condition to make sure the code does not try to copy the sheet to the origin spreadsheet file.
I added a code to rename the copied sheet to the destination sheet to the original name. The default would be copy of Sheet14...
Solution:
function sendtomultiple(){
const source = SpreadsheetApp.getActiveSpreadsheet();
const source_id = source.getId();
const aba = source.getSheets()[13];
const destination = DriveApp.getFolderById('1o6p-53Q1ntJAVIJt9w4k0UK___XXXXXX');
const sheetDestino = destination.getFilesByType(MimeType.GOOGLE_SHEETS);
while (sheetDestino.hasNext()) {
let target_file = sheetDestino.next();
let target_id = target_file.getId();
let target_ss = SpreadsheetApp.openById(target_id);
if(target_id!=source_id){
aba.copyTo(target_ss).setName(aba.getName());
}
};
}
Keep in mind that sheet getSheets()[13] is the 14th sheet in the spreadsheet file. If you want to get the 13th sheet you need to use getSheets()[12].
Trying to get a list of folders (not sub folders or files) in a Gsuite shared drive to populate a Google Sheet worksheet, but the script below fails after first time and will not overwrite the list when I re-run.
I have tried quite a few variations, but my problem is:
1. I would love to have a 3rd column produced showing the html
2. Script works once, but when I refresh or place a trigger on the sheet it no longer works or updates.
Here is my current code:
function listFilesInFolder(folderName) {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(["Name", "File-Id"]);
//change the folder ID below to reflect your folder's ID (look in the URL when you're in your folder)
var folder = DriveApp.getFolderById("0ADTcDD3ZSaa5Uk9PVA");
var contents = folder.getFolders()
var cnt = 0;
var file;
while (contents.hasNext()) {
var file = contents.next();
cnt++;
data = [
file.getName(),
file.getId(),
];
sheet.appendRow(data);
};
};
Any suggestions on how to:
1. Get the folder list to update each hour on the Google sheet?
2. Fix error where the folder list is not updating/overwriting?
3. Add the html of each folder into a third column?
4. Have the list produced in alphabetical order?
Many thanks in advance if you have made it this far!
function listFoldersInAFolder() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
sh.clearContents();
sh.appendRow(["Name", "Folder-URL"]);
var fA=[];
var folders=DriveApp.getFolderById("folder id").getFolders();//Enter folder id
while (folders.hasNext()) {
var folder= folders.next();
fA.push([folder.getName(),folder.getUrl()]);
}
sh.getRange(sh.getLastRow()+1,1,fA.length,2).setValues(fA);
sh.getRange(2,1,sh.getLastRow()-1,2).sort({column:1,ascending:true});
}
If you want to get all folders and sub folders you will have to recurse them. Here's an example of that: https://stackoverflow.com/a/55248127/7215091
I am trying to automate the creation of new folders in Google drive from data in a spreadsheet. The spreadsheet gets information from a form. I have figured out how to make the new folders using scripts but have the following questions:
How do I avoid creating duplicate folders each time the script is run? For example if a folder named '1808' has already been created, I don't want the script to create '1808(1)'
I want to copy a spreadsheet from another location and insert in the new folders that I am creating. The new file name is to be created with information from the same spreadsheet.
function makeFolders(){
//Get array of folder names (fixed & variable)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var bottomRow = sheet.getMaxRows()-2;
var folderNames = sheet.getRange(2, 12, bottomRow, 1).getValues();
//If fixed name cell is blank it throws up an alert and stops
if (folderNames === "") {
SpreadsheetApp.getUi().alert('Cell A3 is blank.');
}
//If folder ID cell is blank it throws up an alert and stops
var folderUrl = sheet.getRange(3,14).getValue();
if (folderUrl === "") {
SpreadsheetApp.getUi().alert('Cell C3 is blank.');
}
//If fixed name cell and file URL aren't empty, run the program
if (folderNames !== "" && folderUrl !== "") {
//Get ID from folder URL
var folderId = folderUrl.match(/[-\w]{25,}/);
//Get master folder
var getRootFolderId = DriveApp.getFolderById(folderId);
//Copy master n times and rename each copy with the new name
//stops when it comes to a blank cell
for (n=0;n<bottomRow;n++){
if(folderNames[n] == ""){break;}
var newFolder = getRootFolderId.createFolder(folderNames[n]);
}
ss.toast("Your folders have been made! :)","Finished",3);
}
}**strong text**
How do I avoid creating duplicate folders each time the script is run?
For example if a folder named '1808' has already been created, I don't
want the script to create '1808(1)'
You could get a list of all the folder names and loop through them to check if any match the new folder name! Something like:
function MakeNewFolders(rootFolderID, folderNames)
// Get master folder
var rootFolder = DriveApp get folder (rootFolderID)
var subfolders = rootFolder.getFolders()
var alreadyExists = false;
var newFolder;
// Check if any subfolder already exists
// loop for folders first, to minimize GAS function calls
while (subfolders.hasNext()) {
var subfolderName = subfolders.next().getName();
loop (each folder in folderNames) {
if (subfolderName == newFolderName)
alreadyExists = true;
}
if (!alreadyExists)
newFolder = rootFolder.createFolder(newFolderName)
}
If there are a TON of folders you need to loop through (more than 60-ish), here is a code snippet you might find handy. Basically, the original Google example for getting a list of subfolders runs twice as slow as necessary by checking for folder.hasNext(), when you can just use a try-catch block and ignore the "you've reached the end!" exception it throws. Should cut execution time in half! :)
I want to copy a spreadsheet from another location and insert in the
new folders that I am creating. The new file name is to be created
with information from the same spreadsheet.
Try something like this!
function CopyFile(originalFileID, destinationFolderID) {
var destinationFolder = DriveApp.getFolderById(destinationFolderID) ;
var fileToCopy = DriveApp.getFileById(originalFileID)
var copiedFile = fileToCopy.makeCopy(<new name>, destinationFolder)
fileToCopy.setTrashed(true); // alternatively, you can use
// Drive.Files.remove(originalFileID);
}
Hope this helps! Been a while since I used GAS, hopefully this compiles :)
You could add a timestamp to the name of the new folder created. Each timestamp would be unique to the millesecond:
function uniqueFolderName() {
var folderName = "1808";
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyyMMddHHmmssSSS");
var newFolderName = folderName + formattedDate;
Logger.log(newFolderName);
}