Listing the content of a Drive ( without exceeding quotas) - google-apps-script

I have an folder in a Google Drive which contains Archives of the last 40 years. I want to get the name of each of the file in this folder with their ids in a Google Sheet.
I wrote a Google Script to read all the content of the folder and to append the informations of each file on a specific Google Sheet. The Google Sheet is totally cleare and reconstructed each time I run the script.
while(contenuDossierRapports.hasNext()) {
rapport = contenuDossierRapports.next();
nom_rapport = rapport.getName().split(".")[0];
id = rapport.getId();
telechargement = "https://drive.google.com/uc?export=download&id=" + id;
if (nom_rapport != "Rapports") {
feuille.appendRow( [ nom_rapport, telechargement, id] );
}
}
A Google Script can only run for a total of 6 minutes top and since I have too many files, the script only has time to write the informations of around 2000 files. Do you have an idea on how to achieve what I want without having to upgrade my account?

You might be able to get just the file id's and then do them in batches of 1000.
function getFileNames() {
var fA=[];
var folder=DriveApp.getFolderById('FolderId')
var files=folder.getFiles();
while(files.hasNext()) {
fA.push(files.next().getId());
}
Logger.log(fA.join('~~~'));
}
However you do it. It boils down to figuring out how to do it in smaller batches.

Related

Automate moving of files with certain names to specific folders on Google Drive

Before I go down the rabbit whole. Can I automate the moving of specific files to specific folders on google drive using Google Scripts?
This is the setup. I have class meets session named as "CAD 142...", "CAD 242..." and "CAD 203..." so the recorded sessions are named as such. The session also are incrementally named as "CAD 142 #01", "CAD 142 #02".... Every couple hours I would like to run an Automated google aps script to move "CAD 142..." recordings to a folder "Classes\CAD 142\Fall 2021\Recordings" and "CAD 242..." recordings to "Classes\CAD 242\Fall 2021\Recordings"...
Just asking if this is possible with google app scripts. This is something I know I could write up with a visual basic script in windows but need to be able to have this done on my google files without my computer being on all the time.
Is this already out there? This would be such a time saver and error reducer and allow me to work on things like teaching and not waiting for file to process or forgetting about them while waiting.
I am still very new to google apps script.
Yes. This can be done. You can set procedures to be run on Google App Scripts to be executed on a trigger as frequently as every five minutes. These will run irrespective of whether you are logged in or not.
You would need to setup a procedure that is authorized to view/modify your google drive. You can see all methods/objects in Google App Scripts well documented library here.
Here's a procedure I first started with that captures a lot of Google Drive Files' properties and dumps them into a Spreadsheet.
function grabFiles() {
var someFiles = DriveApp.searchFiles('modifiedDate > "2020-05-28"');
// var someFiles = DriveApp.searchFiles(
// 'before:2020-01-01 after:2019-01-01 type:pdf');)
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var existingValues = ss.getRange(1, 1, ss.getLastRow(),1).getValues();
existingValues.forEach(function(rows){
rows.forEach(function(aCell){
currentList.push(aCell)
})
})
while(someFiles.hasNext()){
var aFile = someFiles.next();
if(!currentList.includes(aFile.getName())){
aRow = [aFile.getId(),
'=HYPERLINK("' + aFile.getUrl() + '","' + aFile.getName() + '")',
//aFile.getUrl(),
aFile.getDateCreated(),
aFile.getSize()/1000,
aFile.getLastUpdated(),
aFile.getDescription(),
'=HYPERLINK("' + getFolderURL(aFile.getParents()) + '","' + getFolderNames(aFile.getParents()) + '")',
aFile.getOwner().getEmail(),
listEmails(aFile.getEditors()),
listEmails(aFile.getViewers()),
aFile.getMimeType().replace('application/',''),
aFile.getSharingAccess(),
aFile.getSharingPermission()
];
ss.appendRow(aRow)
}
}
}
Yes it's possible.
For this specific case I'm doing it with the following assumptions:
CAD XXX is the class/group
The path of the target folder is is Classes/<class name>/<quarter of the year> <year>/Recordings
The seasons are just names for the quarters of the year
const SEASONS = [
'Winter',
'Spring',
'Summer',
'Autumn',
]
function moveRecordings() {
// Get the recordings folder
const recFolder = DriveApp.getRootFolder().getFoldersByName('Meet Recordings').next()
// Get the files to move
const files = recFolder.getFiles()
// Alternatively you can add further requirements by using `searchFiles` instead:
// const files = recFolder.searchFiles(`createdDate > '2021-07-01' and name constains 'CAD'`)
while (files.hasNext()) {
const file = files.next()
const name = file.getName()
const created = file.getDateCreated()
// Extract the class name from the recording name using a regular expression
const re = /^CAD \d{3}/.exec(name)
if (!re) continue
const className = re[0]
// Get the season from the creation date
const season = SEASONS[Math.floor(created.getMonth() / 3)]
// Move the file by using a helper function
const destination = folderFromPath(`Classes/${className}/${season} ${created.getFullYear()}/Recordings`)
file.moveTo(destination)
}
}
/**
* Gets a folder by the given path. If it doesn't exist, it creates the necessary folders.
*
* It doesn't support relative paths.
*
* #param {String} path Path representing where the folder is.
* #param {DriveApp.Folder} parent Root folder to look from.
* #returns {DriveApp.Folder}
*/
function folderFromPath(path, parent = null) {
let folder = parent || DriveApp.getRootFolder()
for (let part of path.split('/')) {
folder = getOrCreateFolder(part, folder)
}
return folder
}
/**
* Gets a folder by name or creates one if it doesn't exist.
*
* If multiple exist, the first one given by Apps Scripts will be returned.
*
* #param {String} name Name of the folder.
* #param {DriveApp.Folder} parent Parent folder to get the folder from.
* #returns {DriveApp.Folder}
*/
function getOrCreateFolder(name, parent = null) {
if (!parent)
parent = DriveApp.getRootFolder()
const it = parent.getFoldersByName(name)
return it.hasNext() ? it.next() : parent.createFolder(name)
}
This code simply iterates all the files and move them into the proper folder. You only need to extract some information and then move them into the correct folder. The 2 utility functions are quite self explanatory and they are slight modifications of code I already had.
There is a part with an alternative code. It can be used to avoid moving old recordings, or recordings that are not from a class (doesn't start with CAD). Feel free to modify as you see fit.
Even if there is some assumption that's not correct, you should be able to modify this code to make it your own.
The only other thing you need to do is to make a trigger to call the function moveRecordings every few hours.
References
Record a video meeting (Google Meet Help)
class DriveApp (Apps Script reference)
class Folder (Apps Script reference)
class File (Apps Script reference)

Google Forms Rename "File upload" File to "Question - Submitter"

I am using Google Forms to collect images from team members and I want to ensure that each file that is uploaded to Google Forms and saved in Google Drive has the same naming convention.
There are five File upload questions that team members are asked to upload their images to. The files are placed into Google Drive folders with random file names followed by - firstName lastName. On an onFormSubmit trigger I would like to change the names of the user-provided files to fileUploadQuestionName - firstName lastName.
I am pretty new to Google Apps Script and I have no idea how to go about doing this. Any help would be greatly appreciated!
You can change the name of the uploaded file on each form submit by the following process
retrieve the last form response onFormSubmit with form.getResponses()[LAST FORM SUBMISSION]
retrieve the ID of the uploaded file with getItemResponses()[QUESTION NUMBER].getResponse()
open the file ID with DriveApp and change its name as desired
function myFunction() {
var form=FormApp.getActiveForm();
// returns the total number of form submissions
var length=form.getResponses().length;
//replace QUESTION NUMBER through the index of the question prompting the file upload - keep in mind that the first question has the index 0
var id=form.getResponses()[length-1].getItemResponses()[QUESTION NUMBER].getResponse();
//getResponses()[length-1] retrieves the last form response, accounting for the fact that the first index is zero and hte last length-1
//gets the name of the question
var fileUploadQuestionName=form.getResponses()[length-1].getItemResponses()[QUESTION NUMBER].getItem().getTitle();
//accesses the uploaded file
var file=DriveApp.getFileById(id);
name = file.getName();
//changes the file name
var name = fileUploadQuestionName+' - '+name.split(' - ')[1]
file.setName(name);
}
PS: If you want to change a posteriori the names of all the files submitted and not just the last files - you need to loop through all form responses:
for(var i=0;i<length;i++){
var id=form.getResponses()[i].getItemResponses()[QUESTION NUMBER].getResponse();
...
...
}
The easiest way to do this would likely be by either iterating through the specified folder in google drive on a time-based trigger and either checking if they meet your specified condition or moving them to a different folder after they're renamed.
function checkFile()
{
var files = DriveApp.getFolderById('ID of Folder').getFiles();
// you can find this by going to form responses and clicking the link to an attached file and copying the id from the URL
// https://drive.google.com/drive/folders/xxxxxxxxxxxxxxxxxxxxxxxxxxx
var fileUploadQuestionName = 'Test';
while(files.hasNext())
{
var file = files.next();
var name = file.getName();
if(name.indexOf(fileUploadQuestionName) == -1)
{
name = fileUploadQuestionName+' - '+name.split('-')[1]
file.setName(name);
}
}
}
From there you'll need to add a time-based trigger to run every hour or day or minute depending on how critical it is to always find files of the correct name.
I can't find any documentation on accessing the file from within the response item on google's formApp documentation.

Sorting Google Docs into Google Drive folders based upon a common string in file and folder names

I have hundreds of Google Docs, and lots of Google Folders. They share a common string in the filename. The Docs need to go into the corresponding Folder, and I'd like to automate this with some Script.
What I want to do is as follows:
I have hundreds of Google Docs which follow a similar pattern in the filename:
SURNAME, Forename_userID_fileName
So for instance,
DOE, Jane_jd5678_Document3
PUBLIC, Tom_tp9123_reportTPS
SMITH, John_js1234_finaldocument
and so on and so forth.
I also have corresponding Folders in a Google Team Drive, set up as follows:
SURNAME, Forename (userCode) (userID) - userType
So for instance,
DOE, Jane (145374578) (jd5678) - AET Full Time
PUBLIC, Tom (673468714) (tp9123) - NR Full Time
SMITH, John (874512456) (js1234) - AET Part Time
The common string between the files and the folder names is the userID string.
Currently I download all the Docs, and then upload drag-and-drop into each corresponding folder. Takes ages! I was hoping there is a bit of Apps Script that I can do to automate this but I really don't know where to start at all. Anyone have any pointers?
This StackOverflow question seems to want to do what I want to do, but as I'm such a beginner with Apps Script I can't really decipher the solution.
This is a basic example to point you in the right direction using the basic Google Apps Script DriveApp (as opposed to the advanced Script Services version or other Google Drive API methods). The main concern that comes to mind is the script timing out if this example takes too long to complete. If that happens, you'll need a more complex solution. You could also chunk it up by having the idRegex only work on, e.g., surnames A-D then E-G, etc. The example regular expression could match random files you have with underscores; you may want to find a more complicated regular expression if that is a concern.
function organizeFilesById() {
var idRegex = /_[A-z]+[0-9]+_/;
var files = DriveApp.getFiles();
while (files.hasNext()) {
var file = files.next();
var filename = file.getName();
var id = idRegex.exec(filename);
if (! id) {
continue;
} else {
id = id[0];
}
var query = "title contains '" + id + "'";
var folders = DriveApp.searchFolders(query);
if (folders.hasNext()) {
var folder = folders.next();
Logger.log("Will add %s to %s", filename, folder.getName());
//folder.addFile(file); // uncomment line to actually do it
} else {
Logger.log("No folder found for %s", filename);
// could create folder or just report "folder not found"
}
}
}

Exporting data from Cloud SQL to excel using apps script

Is there a way to export cloud SQL data to excel sheet without copying it to Google Spreadsheet using Google Apps Script.
Since there is limitation of Google Spreadsheet of 4,00,000 cells, I am looking to export data directly to Excel sheet rather than copying it to Spreadsheet.
I specifically want to implement it using Google Apps Script.
Yes, you can serve a CSV file, which you can set up to be downloaded. I attached a sample script to show you how it might work.
Warning: Because this is a Comma Separated Values file, you must ensure that your data does not contain commas.
function doGet(e) {
if (parseInt(e.parameter.download) == 1) {
var someData = [{name:"Jerry",
age:27,
married:true},
{name:"Harry",
age:16,
married:false},
{name:"Gary",
age:65,
married:true},
{name:"Larry",
age:41,
married:false}];
var output = "Name,Age,Married?\n";
for (var i in someData) {
output += someData[i].name + ","+someData[i].age + ","+someData[i].married + "\n";
}
return ContentService.createTextOutput(output).downloadAsFile("data.csv");
} else {
var app = UiApp.createApplication();
var anchor = app.createAnchor("Download the CSV File",ScriptApp.getService().getUrl()+"?download=1");
app.add(anchor);
return app;
}
}
Note: Because you are looking for such a large file, this actually might not work. Your script will probably time out. Suppose each entry that you have is 100 bytes, then you will have a 400 MB excel file? That is just large in general.
Could avoid limits of g apps and spreadsheets by using odbc/jdbc directly from excel https://developers.google.com/cloud-sql/docs/external

Google apps script to delete old files permanently and keep last 100 files

I created a folder in my root google Drive that contains video files (.avi). I need to write a google apps script to delete the old video files permanently when the total numbers of the files are more than 100 files? i.e deleting all video files except last (newer) 100 files.
The name for each file is related to the time that this file were created example: 2013-02-25__20-29-45-01.avi
2013-02-25__20-24-49-09.avi
2013-02-25__18-26-24-08.avi
......
So I think the script should first list these files alphabetical starting with the newer and ended with the old one, then keep first 100 files and delete all others permanently.
I know how to do that in bash script, but not in google drive which I think they use javascript (.gs).
As I said in the comments, the script you referred to was not very far from what you want... but I admit your situation is a bit more complex so let's say this will be another exception to sto politics ;-)
That said, I didn't test this code thoroughly so it will probably need some tuning. I left a couple of commented logs throughout the script to test intermediate results, don't hesitate to use them. Also, think about updating the mail adress and don't forget that setTrashed can be manually reversed ;-) (better so when trying new code)
EDIT : I took some time this morning to test the script, it had a couple of "approximations";-)
here is a "clean" version that works nicely
function DeleteMyOldAvi() {
var pageSize = 200;
var files = null;
var token = null;
var i = null;
var totalFiles = []
var toDelete = []
Logger.clear()
do {
var result = DocsList.getAllFilesForPaging(pageSize, token);
var files = result.getFiles()
var token = result.getToken();
for(n=0;n<files.length;++n){
if(files[n].getName().toLowerCase().match('.avi')=='.avi'){
totalFiles.push([files[n].getName(),files[n].getDateCreated().getTime(),files[n].getId()]);// store name, Date created in mSec, ID in a subarray
// Logger.log(files[n].getName()+' created on '+Utilities.formatDate(files[n].getDateCreated(), 'GMT','MMM-dd-yyyy'))
}
}
} while (files.length == pageSize);// continue until job is done
totalFiles.sort(function(x,y){ // sort array on milliseconds date created (numeric/descending)
var xp = x[1];
var yp = y[1];
return yp-xp ;
});
// Logger.log(totalFiles.length)
if(totalFiles.length>100){
for(nn=totalFiles.length-1;nn>=100;nn--){
toDelete.push(totalFiles[nn]) ;// store the files to delete
}
// Logger.log(toDelete)
for(n=0;n<toDelete.length;++n){
var file = toDelete[n]
DocsList.getFileById(file[2]).setTrashed(true);// move to trash each file that is in the toDelete array
Logger.log(file[0]+' was deleted');// log the file name to create mail message
}
MailApp.sendEmail('myMail#gmail.com', 'Script AUTODELETE report', Logger.getLog());// send yourself a mail
}else{
MailApp.sendEmail('myMail#gmail.com', 'Script AUTODELETE report', 'No file deleted');// send yourself a mail
}
}