Make Folders from Spreadsheet Data in Google Drive? - google-apps-script

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.

Related

File not found when using Drive.Files.get(path)

I am trying to automate attaching and sending emails using Gscripts. Each recipient has his own unique attachment so I have a Google sheet containing the details:
Column A contains the email addresses.
Column B contains the message.
Column D contains the filename of the attachment along with the file extension.
Here's my code:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowEmails = sheet.getRange(1,1,3);
var rowMessage = sheet.getRange(1,2,3);
var rowAttachments = sheet.getRange(1,4,3);
var vEmails = rowEmails.getValues();
var vMessage = rowMessage.getValues();
var vAttachments = rowAttachments.getValues()
for (i in vEmails) {
var emailAddress = vEmails[1] + '';
var message = vMessage[1];
var subject = "Test"
var path = 'certs\\' + vAttachments[i]
var file = Drive.Files.get(path);
MailApp.sendEmail(emailAddress, subject, vMessage[1], {attachments: files});
}
}
Everything works well but when it comes to var file = Drive.Files.get(path), I get an error saying no file is found. I checked my drive and I'm sure it is there. I've also checked the Drive API. I don't know what is wrong.
Something like this might be a little closer to working:
function sendEmails() {
var sheet=SpreadsheetApp.getActiveSheet();
var Emails=sheet.getRange(1,1,3).getValues();
var Message=sheet.getRange(1,2,3).getValues();
var fileids=sheet.getRange(1,3,3).getValues();//you need to add file ids
Emails.forEach(function(r,i){
MailApp.sendEmail(Emails[i][0],"Test",Message[i][0],{attachments:[DriveApp.getFileById(fileids[i][0])]});
});
}
The code in the question has several flaws:
path : Google Drive doesn't use "path"
for in / vAttachments[i]: a different property name is assigned to the variable on each iteration but vAttachments hasn't named properties, so vAttachments[i] will return null on each iteration.
The files property on the options should be an Array.
How to "fix" the script
The way to directly get files in Google Drive is by using file ids.
If you want to get files by "path", first your script should get the folder, then look for the file inside that folder but bear in mind that getting a folder/file by name returns a folder/file iterator
Instead of for in use for of or use for (most of the scripts I have seen usen use for)

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.

returning document owner for large list of Google Drive doc IDs

I am trying to identify the owner of a long list (almost 1000 items) of documents within Google Drive. The owners may vary between the docs. I have the unique doc ID for each item.
I'm the admin for Google Apps at my domain, and have a utility that lets me punch in a doc ID, and see the ownership of that particular item. However, I have to do this through a web interface, one at a time.
Is there any way to leverage Google's API, say in a spreadsheet, that can return a document's owner when given the doc ID? Or Google scripts perhaps?
It can be done in Apps Script.
Class File
A file in Google Drive. Files can be accessed or created from DriveApp.
Code Sample
var files = DriveApp.getId('DOC_ID');
while (files.hasNext()) {
var file = files.next();
var owner = file.getOwner()
}
getOwner()
Gets the owner of the File.
Return
User — a User object representing the owner
Hope this helps.
Worked with a colleague of mine to generate the following Google Apps script that works great.
Usage: With a list of doc ID's in column D, highlight the rows you want to check (any cells are fine, just have something highlighted in the desired rows) and execute using the custom "Automation" menu.
Result: Owner is then returned in column E.
function getOwnerFromValue(val){
var doc = DriveApp.getFileById(val);
var user = doc.getOwner();
return user;
}
function scanRange() {
var range = SpreadsheetApp.getActiveRange();
var start = range.getRow();
var stop = range.getLastRow();
var range = SpreadsheetApp.getActive().getRange("D"+start+":D"+stop)
var resultRange = SpreadsheetApp.getActive().getRange("E"+start+":E"+stop);
var result = [];
range.getValues().forEach(function(obj){
obj = obj[0];
if(obj.trim() != "")
result.push([getOwnerFromValue(obj).getEmail()]);
});
resultRange.setValues(result);
}
function onOpen(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Get Owners' User",
functionName : "scanRange"
}];
sheet.addMenu("Automation",entries);
}
function onAdd(e){
var editted = e.range;
if(editted.getRow() != 1 && editted.getColumn() == 4) {
Logger.log(getOwnerFromValue(editted.getValue()));
}
}

I need to save a copy of a google spreadsheet to a specific directory using script

We currently have a nightly balancing process wherein we open an Excel template spreadsheet, enter the correct processing date, and then click a button, causing a VB script to fire off and create a new copy of the spreadsheet with the appropriate name (eg, "BAL 080114") in the appropriate folder and opens it, at which point the operator then closes the template, and continues work in the new copy.
The folder structure is:
Drive
--->Ops Room
------->Procedural Sheets
----------->Night Shift
--------------->Balancing
------------------->2014
----------------------->01-2014
...
----------------------->12-2014
We are trying to transition this to Google docs spreadsheets, and mostly it's working. But I cannot find a method to allow someone to open the template (stored in Balancing), run a "Start New Day" script, and have the script create the file in the proper sub-sub-folder. To wit, for 08/01/2014, the file should be stored in Balancing/2014/08-2014 as Bal 080114.
This is what I have thus far:
function startNewDay() {
// This code makes a copy of the current spreadsheet and names it appropriately
var ss = SpreadsheetApp.getActiveSpreadsheet();
// The file name is created and stored on sheet "Set Date" in cell B5
var fname = ss.getSheetByName("Set Date").getRange("B5").getValue();
var folderYear = Utilities.formatDate(new Date(), "GMT-6", "yyyy"); // top-level folder is by year in yyyy format
var folderMonth = Utilities.formatDate(new Date(), "GMT-6", "MM-yyyy"); // folder name is in mm-yyyy format
//the above is probably overkill, but I'll work on efficiency once I get it working at all :
//Everything works up to this point...
//This is where I start running into problems...
//The Master Copy SSID is <redacted>
var SSID = '<redacted>'
var folder = DocsList.getFolder(folderYear + "/" + folderMonth);
var backup = DocsList.getFileById(SSID).makeCopy(fname);
backup.addToFolder(folder); //This line will move the file to the appropriate folder
backup.removeFromFolder(DocsList.getRootFolder()); //This line is needed to remove the File from the Root
}
I borrowed the backup.* stuff from another StackOverFlow answer that had similar properties, but my version doesn't create the file.
Is what I'm trying to do even possible in Drive? Or will I just need to have the operators create a copy and then move it manually?
I apologize for any scripting ignorance - I've just started learning Google script this week, and I'm having trouble finding a common ground with my previous VB experience.
Thanks in advance for any help.
James
I am not able to comment so I cannot add on to what is already there.
Are the folders that are "manually" being created being created with the correct permissions by the correct person? Shared Folders can get tricky on Google Drive.
I may even suggest something like this and have the script just create the folders for you:
/*********************************************************
* Function to determine the destination folder based on
* provided criteria.
*
* #param {Folder} root The root folder.
* #param {String} folderYear The year of the report.
* #param {String} folderMonth The month of the report.
* #return {Folder} Destination Folder.
*********************************************************/
function returnFolder(root, folderYear, folderMonth) {
var dir = DocsList.getFolderById(root);
var folders = DocsList.getFolderById(root).getFolders();
var found = false;
var toReturn = "";
for (var i = 0; i < folders.length; i++) {
if (folders[i].getName() == folderYear) {
dir = folders[i];
found = true;
break;
}
}
if (!found) {
dir = dir.createFolder(folderYear);
folders = dir.getFolders();
}
else found = false;
for (var i = 0; i < folders.length; i++)
if (folders[i].getName() == folderMonth) {
toReturn = folders[i].getId();
found = true;
break;
}
if (!found) toReturn = dir.createFolder(folderMonth).getId();
return DocsList.getFolderById(toReturn);
}
Then your code would be as follows:
var SSID = '<redacted>'
var folder = returnFolder(OBJECT_PARENT_FOLDER, folderYear, folderMonth);
var backup = DocsList.getFileById(SSID).makeCopy(fname);
backup.addToFolder(folder); //This line will move the file to the appropriate folder
backup.removeFromFolder(DocsList.getRootFolder()); //This line is needed to remove the File from the Root
You will need to match the code to your needs, but I hope this helps.

Google Apps Script : Making an A Function Execute on Open

I found a script that will move my spreadsheet to a folder. How can I make it so that the functions runs when the spreadsheet is open. I will be applying this to a template so that everytime the template is opened, the "Copy of " spreadsheet file will automatically be moved to my Packing Lists folder.
Here's the code I have....
function MoveFileTo()
{
var docs=DocsList.find('Save Test');
var doc=docs[0]; /*Since we assume there is only one file with the name "Hello World", we take the index 0 */
var folders = doc.getParents();
var newFolder=DocsList.getFolder('Packing Lists');
doc.addToFolder(newFolder); // This will add the document to its new location.
var docParentFolder=folders[0];
doc.removeFromFolder(docParentFolder); // This will remove the document from its original location.
}
EDIT: I tried changing function MoveFileTo to onOpen() and onEdit() but it doesn't seem to work properly. Is there something I am missing?
Try use driveapp instead of doclist (it's deprecated). the impinball comment is really usefull, you should have a look at it.
Anyway here a working code:
function myFunction() {
var destFolder = DriveApp.getFolderById("FOLDER_ID_WHERE8YOU_WANT_TO_MOVE_YOUR_FILES");
var fileList = DriveApp.searchFiles("mimeType = 'application/vnd.google-apps.spreadsheet' and title contains 'FILE_TITLE'"); // change this search if necessary
var originalFileId="THE_ID_OF_THE_ACTUAL_SPREADSHEET_TO_BE_EXCLUDED_FROM_THE_SCRIPT_TREATMENT";
while(fileList.hasNext()){
var fileToMove = fileList.next();
if(fileToMove.getId()==originalFileId){
continue; // don't do anything if it's the original file
}
var papas = fileToMove.getParents();
destFolder.addFile(fileToMove); // do the job
while(papas.hasNext()){
papas.next().removeFile(fileToMove); // remove the actuals parents (can be more than one)
}
}
}