Create new Folder in Drive if it doesn't already exist - google-apps-script

CONTEXT
In my Drive there's the following structure:
Ops folder || Sales folder
Ops contains 3 folders: Process1 / Process2 / Process3
Sales contains 3 folders: Customer1 / Customer2 / Customer3
TARGET
I'm asking for the names of the folders in Drive > SALES
In a Sheet I have a list of Names of some Prospects. I'm checking one by one if the name of the prospect matches with any folder in my Drive.
In case there's a folder that matches the prospect name then do nothing.
In case there's no folder for that Prospect I want to create a new folder in Sales with the prospect name
function prepararPropuesta() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var ssAS = ss.getActiveSheet()
var ssProspects = ss.getSheetByName('Prospects')
var lr = ssProspects.getLastRow()
var lc = ssProspects.getLastColumn()
// I'm looking for the column 'Prospect' in the Sheet
for(i=2; i<lc; i++){
var columnas = ssProspects.getRange(2, i).getValue()
Logger.log(columnas)
if(columnas == 'Nombre Prospect'){
var colProspect = ssProspects.getRange(2, i).getColumn()
Logger.log(colProspect)
}
}
/*
Now I'm looking for the names of the folders in my Drive
This is looking in all my Drive. It would be nice to filter the
search and look just in SubFolders(Sales)
*/
var folders = DriveApp.searchFolders('"me" in owners');
while (folders.hasNext()) {
var folder = folders.next();
Logger.log(folder.getName());
/* The first Prospect Name is in row 2 and I'm creating a loop to
get each Name
If the name matches an existing Folder then ends the function
If the Prospect Name is not found in the Folders list then creates a new Folder under the name prospectName
*/
for(i=2; i<lr; i++){
var prospectName = ssProspects.getRange(i, colCliente).getValue()
var folderName = folder.getName()
if(folderName = prospectName){
return
}
}
DriveApp.createFolder(prospectName)
}
}
In between the lines in the code you will find a description step by step
(1) I'm finding the column in which the prospect names are -> This works fine
(2) I'm using WHILE to get all the name folders in my Drive
This log
Logger.log(folder.getName())
Returns
[18-04-21 06:33:35:985 PDT] FolderIterator
Is it getting the names correctly?
(3) I created the variables folderName and prospectName and looped them to check if there's a match -> This part doesn't work correctly. I guess that my I'm not using the right methods although I researched in developers.google. How could it be done?
Thnx!

It's not a good idea to use .getValue() inside a loop because in that case you make one call to the spreadsheet for every row. It's considered to be good practice to get all values in one call and then process the returning array in the script.
I'm not sure why it is necessary to loop through the columns to find the column with the name? Assuming you know what that column is, you can just get the values (names) of that column directly.
Here's an example that assumes the names in column B (change range to suit) of sheet 'Prospects'.
For every name found the function checkIfFolderExistElseCreate() is called.
That function checks if a folder with the name (second paramater) exists. If it does, nothing happens else a folder with that name is created.
The first paramater of the function is the 'parent' folder in which the new folders will be created.
function prepararPropuesta() {
var parent = DriveApp.getFolderById("id_of_parentFolder")
SpreadsheetApp.getActive().getSheetByName('Prospects').getRange('B2:B').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);
}
}
I hope this helps?

Related

Google Script Question: Is there a way to create copies of files into multiple folders at once?

I have a spreadsheet from which I've written code to populate with the names of folders (column 1) and their IDs (column 2).
I would like to populate each of the folders listed in that spreadsheet with a copy of each of the documents contained a separate folder (a Shared Drive folder, if that matters). When I execute the code below, a copy of each document is created in the source folder (the Shared Drive folder) instead of in the destination folder (aka the folders whose IDs are captured in the spreadsheet). If it matters, each copy is labelled with a folderID from the spreadsheet. Can someone please tell me how I can get this code to create the copies inside the appropriate destination folders instead of in the source folder?
function CopiestoFolder() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange() //Get all non-blank cells
.getValues() //Get array of values
.splice(1); //Remove header line
//Define column numbers for data. Array starts at 0.
var NAME = 0;
var FOLDERID = 1;
//For each folder ID listed in spreadsheet, create a copy of
//each item in the Resume Resources folder.
for (var i = 0; i < data.length; i++) {
var name = data[i][NAME];
var folderId = data[i][FOLDERID];
var srcFolder = DriveApp.getFolderById("folder ID");
var dstFolder = folderId;
var files = srcFolder.getFiles();
while (files.hasNext()) {
var file = files.next();
var f = file.makeCopy(dstFolder);
if (file.getMimeType() == MimeType.GOOGLE_APPS_SCRIPT) {
dstFolder.addFile(file);
f.getParents().next().removeFile(file);
}
}
}
}
I had this problem with Script Files. Here's how I fixed. Or I should say here's how Tanaike fixed it. You will need to enable Drive API.
var res=file.makeCopy(copyName,subFldr);
if (file.getMimeType() == MimeType.GOOGLE_APPS_SCRIPT) {
Drive.Files.update({"parents": [{"id": subFldr.getId()}]}, res.getId(), null, {"supportsTeamDrives":true}); // Added
}
The file.makeCopy() method has 3 overloads:
//Accepts no parameters
file.makeCopy();
//Accepts a name parameter as a string
file.makeCopy(name);
//Accepts a destination parameter as an instance of a Folder class
file.makeCopy(destination);
//Accepts 2 parameters, the first of which is name (String) and the second one is destination (Folder).
file.makeCopy(name, destination);
You are trying to pass folder id instead of the actual folder, which gets interpreted as a folder name (not destination). Also, the following code means that your 'dstFolder' parameter is a string but you try to call the 'addFile()' method on it:
var folderId = data[i][FOLDERID];
var dstFolder = folderId;
dstFolder.addFile(file);
If you want to copy a file to another folder via makeCopy, you should pass a Folder as a parameter, not a folder id (a string). If you provide a string (your id) as a parameter, the script will interpret this id as the name you want the copied file to have. So you should first get the Folder out of its id, via getFolderById(id). So you should change this line:
var dstFolder = folderId;
To this one:
var dstFolder = DriveApp.getFolderById(folderId);
You are making a copy of the file before checking if the file is a Google Apps Script project. I assume you just want GAS projects to be copied, so this should be included inside the if block. Also, inside the if block you are using addFile which can also be used to copy a file to another folder (it adds the file to the desired folder). So you don't need to use both functions, they are doing basically the same (copying your file to the desired folder).
You are using removeFile, which is not necessary if you want to keep the original files in the original folders.
So the while block could be something like:
while (files.hasNext()) {
var file = files.next();
if (file.getMimeType() == MimeType.GOOGLE_APPS_SCRIPT) {
dstFolder.addFile(file);
}
}
Finally, your full code could be like:
function CopiestoFolder() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange() //Get all non-blank cells
.getValues() //Get array of values
.splice(1); //Remove header line
//Define column numbers for data. Array starts at 0.
var NAME = 0;
var FOLDERID = 1;
//For each folder ID listed in spreadsheet, create a copy of
//each item in the Resume Resources folder.
for (var i=0; i<data.length; i++) {
var name = data[i][NAME];
var folderId = data[i][FOLDERID];
var srcFolder = DriveApp.getFolderById("folder ID");
var dstFolder = DriveApp.getFolderById(folderId);
var files = srcFolder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (file.getMimeType() == MimeType.GOOGLE_APPS_SCRIPT) {
dstFolder.addFile(file);
}
}
}
}
I hope this is of any help.

Move Folder Using App Script

So i'm brand new to JS and trying to build a program for internal operation at the company I work for. Below is the first bit of my code. I have a created a google form, and set a trigger so that when the submit button is clicked the below code will start. The trigger works and the first two functions work, but I cannot seem to get the Karrie folder to move from the root to the sub folder id (I do have the correct ID but omitted it from this post). Maybe I'm doing this all backwards so any help would be appreciated.
// Create Karrie Folder In Root
function start() {
var sourceFolder = "Property Template";
var targetFolder = "Karrie";
var source = DriveApp.getFoldersByName(sourceFolder);
var target = DriveApp.createFolder(targetFolder);
if (source.hasNext()) {
copyFolder(source.next(), target);}}
// Adds Property data to Karrie Folder
function copyFolder(source, target) {
var folders = source.getFolders();
var files = source.getFiles();
while(files.hasNext()) {
var file = files.next();
file.makeCopy(file.getName(), target);}
// Adds Photo Folders to Karrie Folder
while(folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var targetFolder = target.createFolder(folderName);
copyFolder(subFolder, targetFolder);}}
// Moves Karrie folder to propertie folder
function MoveFiles(){
var files = DriveApp.getFolderByName("Karrie");
var destination = DriveApp.getFolderById("ID NA");
destination.addFolder(files);}
Debugger
I understood that MoveFiles() in your script doesn't work. If my understanding is correct, how about this modification?
Modification points :
In order to move a folder, it required to move the parent of the folder.
But when addFolder() is used to the folder, the folder has 2 parents.
So original parent has to be removed.
There is no getFolderByName(). Please use getFoldersByName().
Modified script :
function MoveFiles(){
var folder = DriveApp.getFoldersByName("Karrie").next();
var destination = DriveApp.getFolderById("ID NA");
destination.addFolder(folder);
folder.getParents().next().removeFolder(folder);
}
Note :
In this script, The folder and parent of "Karrie" supposes only one in the Google Drive, respectively.
References :
getFoldersByName()
addFolder()
removeFolder()
If I misunderstand your question, please tell me. I would like to modify it.
Edit 1 :
When you run this function, does the ID of Logger.log(r) show the folderId of "Karrie"?
function sample() {
var r = DriveApp.getFoldersByName("Karrie").next().getId();
Logger.log(r)
}
Edit 2 :
Please confirm and consider the following points.
Confirm the folder name and the existence of the folder again.
Run sample() and confirm folder ID of "Karrie".
Confirm whether there are no functions with the same name.
Can I ask you the folder ID of "Karrie"? If you cannot retrieve the folder ID using DriveApp.getFoldersByName("Karrie").next().getId(), I propose to use directly the folder ID of "Karrie".
Updated on March 30, 2022:
In the current stage, the files and folders can be moved using moveTo. Ref This method has added on July 27, 2020. When moveTo is used, the above script can be modified as follows.
function MoveFiles(){
var folder = DriveApp.getFoldersByName("Karrie").next();
var destination = DriveApp.getFolderById("ID NA");
folder.moveTo(destination);
}
Reference:
moveTo(destination) of Class Folder
As of 2020, the easier way I found to move a file is to use this function:
function moveToFolder(file, sourceFolder, destFolder) {
var file = DriveApp.getFileById(file.getId());
destFolder.addFile(file);
sourceFolder.removeFile(file);
}
Because of the first line, you can convert your Spreadsheet, Google Form, Document or others Google objects into a File.
Then we just use methods of the Folder class : https://developers.google.com/apps-script/reference/drive/folder
Notes:
If you created your file by using .create() your sourceFolder will be DriveApp.getRootFolder().
If you have multiple files to move, just iterate over them.

Google Scripts - Move a pacific file to a folder when Cell contains certain text

Okay so the plan is to, When a certain cell contains a certain text, it will automaticlly move the file to a pacific folder. Here is what i have, also i am very new to this coding language so im learning at the same time!
Here what i got so far, bearing in mind this does work but ill explain more below:
function filemove(root_folder, dest_folderinactive, dest_folderactive) {
var root_folder = DriveApp.getFolderById('0B_Nz0cop3s-ITTlYM21SNGIwNDg');
var dest_folderinactive = DriveApp.getFolderById('0B_Nz0cop3s-IQVZEWUNiQ1d2TzQ');
var dest_folderactive = DriveApp.getFolderById('0B_Nz0cop3s-IalFITGhzN3lTTzg');
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var file = DriveApp.getFileById(spreadsheet.getId());
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "StatsData" ) {
var range = s.getRange("E16")
var values = range.getValues();
for(var i in values){
if(values[i][0].match("Not Police")!=null){
dest_folderinactive.addFile(file);
root_folder.removeFile(file);
}
if(values[i][0].match("Police")!=null){
dest_folderactive.addFile(file);
root_folder.removeFile(file);
}
}
}
};
Okay so what this does is if cell "E16" contains "Not Police" it will move the file to a folder called "Inactive" (dest_folderinactive) and if the cell contains "Police" it will move it to (dest_folderactive) again these both come from the so called "root" folder - i know root means the very fist page of google drive but in this case the base folder which contains these files is named the root. This code works (Sometimes) sometimes you have to run it multiple time for it to actully work and its not very effichant.
My problem also comes when i would like it to do this, When a file is in the In-Active folder and the cell changes to "Police" i would like it to move from In-Active to Active and same for Active saying "non police" to go to inactive but i just can't seem to get this working. It would be greatly helpful if someone could help out, also knowing this code is kinda sheet as it doesn't also do it first time.
Try this code. It will move the file to the required folder depending upon the value of your target cell. It will also return the file to the nominated "root" folder if there is junk data in the target cell. You need to set the specific folder IDs at the top of the code: if you need to pass them in, then you'll need to add them as parameters after e. The parameter e is required to consume the onEdit trigger event data (which this function doesn't use, but you would need to account for if you are passing in the folder IDs).
function filemove(e){
// folder IDs
var root_folder = 'root_folder_id_string';
var dest_folderinactive = 'inactive_files_folder_id_string';
var dest_folderactive = 'active_files_folder_id_string';
// the specific destination folder on this run
var dest_folder;
// assume the file will move
var move_file = true;
// the spreadsheet & its parent folders
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var file = DriveApp.getFileById(spreadsheet.getId());
var parents = file.getParents();
var sheet = spreadsheet.setActiveSheet(spreadsheet.getSheetByName("StatsData"));
// now grab the value of E16 & look for the string "police"
// string.search(regEx) returns the position of the first match, otherwise -1
var values = sheet.getRange("E16").getValues();
switch(values[0][0].search(/police/i)){
case -1:
dest_folder = root_folder;
break;
case 0:
dest_folder = dest_folderactive;
break;
default:
dest_folder = dest_folderinactive;
}
// is the dest_folder in the parents array?
while(parents.hasNext()){
var fldr = parents.next().getId();
if(fldr == dest_folder){
// YES -- don't move the file
move_file = false;
} else{
// NO -- are the other folders in the parents array?
if(fldr == root_folder || fldr == dest_folderinactive || fldr == dest_folderactive){
// YES -- remove the file from that folder
DriveApp.getFolderById(fldr).removeFile(file);
}
}
}
// do we have to move the file?
if(move_file){
DriveApp.getFolderById(dest_folder).addFile(file);
}
}
I'm stepping over the parents array & comparing them to the destination folders so that you don't remove the file from another folder by mistake.
If the target cell has more free-form data & you are looking for the string "police" or "not police", then the switch() will not work. In this case you will need to use if(...){...} else if(...){...} else{...} to test.

Iterate Through Folders/Subfolders/Files in Google Drive

I have a Google Drive structure setup like this:
Client A
--Project 1
----Drafts
----Presentations
----Minutes
--Project 2
----Drafts
----Presentations
----Minutes
Client B
<and so on>
I want to write a script where I can pass in the ID of Project 1, and it will loop through each of the three subfolders and count the total number of files (ultimately I want to count the number of files updated over a date range, but baby steps!).
I've tried using the Class FolderIterator code, but that wants you to assign a group of folders to a variable "folders" (it showed var folders = DriveApp.getFolders(); and I tried var folders = DriveApp.getFolderById("<id of Project 1 here>"); but in my version that's just one folder, and it really needs the collection of folders). Is there a way to assign those three subfolders to that folders variable if I have the ID of the parent folder? Or is there a better way to loop through the subfolders?
As a complement to Bryan's answer (thx, I upvoted) I wrote a test function to see results in the logger and to run the function with appropriate parameters to get the count you wanted.
(This answer should not be selected as the right answer since the main part is from Bryan P)
here is how it goes :
function testTraverse(){
var originFolder = DriveApp.getFoldersByName('Client A').next(); // use the folder name you want here
var totalCount = traverseFolder(originFolder,0);
Logger.log('total files = '+totalCount);// note that if some files are
// in more than one subfolder the count will count every occurrence of
//this file so the total might be > to the sum of intermediate values
}
function traverseFolder(folder,total) {
var name = folder.getName();
var count = 0;
var files = folder.getFiles();
while (files.hasNext()) {
count++;
Logger.log('fileName ('+count+') in '+name+' is : '+files.next().getName());
}
Logger.log('\n'+name+' has ' + count+' files\n----------------------\n' );
var subs = folder.getFolders();
while (subs.hasNext()) {
total+=traverseFolder(subs.next(),count);
}
return total;
}
Note that this code will work for a "reasonable" number of files, if you have many files and folders it might exceed the 5 minutes execution time limit. In this case you'll need to modify it in order to proceed by smaller bunches of files using the token provided by the method.
It may look some thing like this with DriveApp...
function traverseFolder(folder) {
var name = folder.getName();
if ( /^(Drafts|Presentations|Minutes)$/.test(name) ) {
var count = 0;
var files = folder.getFiles();
while ( files.hasNext() ) {
count++;
}
Logger.log(name + ' : ' + count);
}
var subs = folder.getFolders();
while (subs.hasNext()) {
traverseFolder(subs.next());
}
}
We now have access to the Advanced Drive Service too. Maybe I'll look at an example with that if some one doesn't post one soon.

Make Folders from Spreadsheet Data in Google Drive?

I am just beginning to learn Javascript and Google Apps Script. I have looked at and played with a few scripts, and so am attempting to create my own. What I want to do is take a list of students (Name and Email) and run a script to create "dropboxes", or folders that uses their name and is shared to their email address. This way, they can easily submit work to me in an organized manner. I have written this rough script, which I know will not work.
I was wondering if anyone can give me some tips?
function createDropbox () {
// Get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = sh1.getDataRange().getValues();
// For each email address (row in a spreadsheet), create a folder, name it with the data from the Class and Name column, then share it to the email
for(n=1;n<data.length;++n){
var class = sheet.getSheetValues(n, 1, 1, 1);
var name = sheet.getSheetValues(n, 2, 1, 1);
var email = sheet.getSheetValues(n, 3, 1, 1);
var folder = DocsList.createFolder('Dropbox');
folder.createFolder(class . name);
var share = folder.addEditor(email);
}
}
You've got the basic structure of your script right, it's only the semantics and some error handling that need to be worked out.
You need to determine how you want to access the contents of your spreadsheet, and be consistent with that choice.
In your question, you are first getting all the contents of the spreadsheet into a two-dimensional array by using Range.getValues(), and then later trying to get values directly from the sheet multiple additional times using .getSheetValues().
Since your algorithm is based on working through values in a range, use of an array is going to be your most effective approach. To reference the content of the data array, you just need to use [row][column] indexes.
You should think ahead a bit. What will happen in the future if you need to add more student dropboxes? As your initial algorithm is written, new folders are created blindly. Since Google Drive allows multiple folders with the same name, a second run of the script would duplicate all the existing folders. So, before creating anything, you will want to check if the thing already exists, and handle it appropriately.
General advice: Write apps script code in the editor, and take advantage of auto-completion & code coloring. That will help avoid mistakes like variable name mismatches (ss vs sh1).
If you are going to complete the exercise yourself, stop reading here!
Script
This script has an onOpen() function to create a menu that you can use within the spreadsheet, in addition to your createDropbox() function.
The createDropbox() function will create a top level "Dropbox" folder, if one does not already exist. It will then do the same for any students in the spreadsheet, creating and sharing sub-folders if they don't already exist. If you add more students to the spreadsheet, run the script again to get additional folders.
I've included comments to explain some of the tricky bits, as a free educational service!
/**
* Create menu item for Dropbox
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Create / Update Dropbox",
functionName : "createDropbox"
}];
sheet.addMenu("Dropbox", entries);
};
function createDropbox () {
// Get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange() // Get all non-blank cells
.getValues() // Get array of values
.splice(1); // Remove header line
// Define column numbers for data. Array starts at 0.
var CLASS = 0;
var NAME = 1;
var EMAIL = 2;
// Create Dropbox folder if needed
var DROPBOX = "Dropbox"; // Top level dropbox folder
try {
// getFolder throws an exception if folder not found.
var dropbox = DocsList.getFolder(DROPBOX);
}
catch (e) {
// Did not find folder, so create it
dropbox = DocsList.createFolder(DROPBOX);
}
// For each email address (row in a spreadsheet), create a folder,
// name it with the data from the Class and Name column,
// then share it to the email
for (var i=0; i<data.length; i++){
var class = data[i][CLASS];
var name = data[i][NAME];
var email = data[i][EMAIL];
var folderName = class + ' ' + name;
try {
var folder = DocsList.getFolder(DROPBOX + '/' + folderName);
}
catch (e) {
folder = dropbox.createFolder(folderName)
.addEditor(email);
}
}
}
Even though the question is ~6 years old it popped up when I searched for "create folders from sheets data" .
So , taking the answer as inspiration, I had to update the code to allow for changes in the Google API, i.e. no more DocsList.
So here it is.
function createMembersFolders () {
// Get current spreadsheet
var MembersFolder = DriveApp.getFolderById("1Q2Y7_3dPRFsblj_W6cmeUhhPXw2xhNTQ"); // This is the folder "ADI Members"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange().getValues() // Get array of values
// Define column numbers for data. Array starts at 0.
var NAME = 1;
// For each name, create a folder,
for (var i=1; i<data.length; i++){ // Skip header row
var name = data[i][NAME];
Logger.log(name);
if(MembersFolder.getFoldersByName(name).hasNext()){
Logger.log("Found the folder, no need to create it");
Logger.log(Object.keys(MembersFolder.getFoldersByName(name)).length);
} else {
Logger.log("Didn't find the folder so I'll create it");
var newFolder = MembersFolder.createFolder(name);
}
}
}
Note that I'm taking the parent folder directly using it's ID (which you get from the URL when viewing the folder).
You could also do this by name using the DriveApp.getFoldersByName("Folder Name").hasNext() condition which checks if the folder exist. If it does exist then you can access that folder you have found via DriveApp.getFoldersByName("Folder Name").next()
I didn't find that use of hasNext and next very intuitive but there it is.