Updated File List - google-apps-script

I am using a script to show me new files in google drive so people can see that a file has been updated. The script worked fine with one or two users using the system but after migrating a few more we started getting errors about exceeding maximum execution time. Currently the script deletes the current list and recreates it each and every hour
function listFilesInFolder() {
//try {
// If you want a tree of any sub folder
//var parent = DriveApp.getFoldersByName("FOLDER_NAME").next();
// If you want to search from the top (root) folder
var files = DriveApp.getFiles();
var ss = SpreadsheetApp.openById("1XbAaoNTiRXKSUAovY-QUvnc8V2q68g7Q3z4NIH_ioTc");
var sheet = ss.getSheetByName("Sheet1");
var file,folder,folders,folderTop,newFiles,lastUpdated,difference;
var j = 0;
var data = []
var parentFolders = []
var newDate = new Date();
while (files.hasNext()) {
file = files.next();
lastUpdated = file.getLastUpdated();
difference = newDate.getTime() - lastUpdated.getTime();
if ((difference/86400000) < 100) {
folders = file.getParents();
while (folders.hasNext()) {
folder = folders.next();
folders = folder.getParents();
parentFolders.push(folder.getName())
}
folderTop = parentFolders[parentFolders.length -2]
data.push (new Array());
data[j].push(file.getName());
data[j].push(file.getLastUpdated());
data[j].push("open_in_new");
data[j].push("https://drive.google.com/file/d/"+file.getId()+"/edit");
data[j].push("folder_open");
if (file.getParents().hasNext())
{
data[j].push("https://drive.google.com/drive/u/0/folders/" + file.getParents().next().getId());
}
else
{
data[j].push("https://drive.google.com/drive/");
}
data[j].push(folderTop+"#xxx.net");
j = j+1;
}
}
sheet.deleteRows(2, sheet.getMaxRows()-1)
sheet.deleteColumns(2, sheet.getMaxColumns()-1)
sheet.clear()
sheet.appendRow(["File Name","Date Updated","View File","File Link", "Open Folder","Folder Link", "Cell" ]);
sheet.appendRow(["","","HyperlinkType(D)-iconType","", "HyperlinkType(F)-iconType","", "Permissions-Hidden"] );
var destinationRange = sheet.getRange(3, 1,j , 7);
destinationRange.setValues(data);
}
// catch (e) {
// Logger.log(e.toString());
// }
// }
function getFilesFromFolder(parent,targetSheet,topLevelName) {
var files = parent.getFiles();
var data
var file
while (files.hasNext()) {
file =files.next()
data = [
file.getName(),
file.getDateCreated(),
file.getSize(),
file.getUrl(),
file.getDescription(),
file.getLastUpdated(),
file.getMimeType(),
topLevelName+"#xxx.net"
];
targetSheet.appendRow(data);
row.push([file.getId(),
file.getName(),
file.getDateCreated(),
file.getSize(),
file.getUrl(),
file.getDescription(),
file.getLastUpdated(),
file.getMimeType()
]);
}
}
function getChildFolders(parent,targetSheet,topLevelName) {
var childFolders = parent.getFolders();
while (childFolders.hasNext()) {
var childFolder = childFolders.next();
getFilesFromFolder(childFolder,targetSheet,topLevelName);
getChildFolders(childFolder,targetSheet,topLevelName);
}
// Recursive call for any sub-folders
}
Thank you.

Here's a script that uses DriveActivity API to monitor changes to Google Drive. The Qwikstart is here and I took that script and modified it.
I hardwired the timezone into the query filter so you'll need to address that and there's also the variable n and nl that I used to limit the number of calls so that I would hit quota limits during development.
It generates the html for the results and displays everything in a dialog.
Code.gs:
/**
* Lists activity for a Drive user.
*/
function listDriveActivity() {
const dt=new Date();
const dt0=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1);
const dt1=Utilities.formatDate(dt0, Session.getScriptTimeZone(),"yyyy-MM-dd");
const dt2=Utilities.formatDate(dt0, Session.getScriptTimeZone(), "HH:mm:ss")
const dt3=dt1+"T"+dt2+"-07:00";
const dt4=Utilities.formatDate(new Date(dt3), Session.getScriptTimeZone(), "E MM/dd/yyyy HH:mm:ss")
var html='Recent Activities ' + dt4 + ' ' + Session.getScriptTimeZone();
html+='<style>td,th{padding:2px;border:1px solid black;overflow-wrap: break-word;\} table{table-layout:fixed;width:100%;}</style>';
html+='<table>';
html+='<tbody>';
html+='<tr><th>Time</th><th>Actors</th><th>Actions</th><th>Targets</th></tr>';
var token="";
var n=0;
var nl=10;//I needed to reduce the number of calls to not exceed quota during development
do{
var request = {pageSize: 10,filter:Utilities.formatString('time>=\"%s\"',dt3)};
var response = DriveActivity.Activity.query(request);
var activities = response.activities;
if (activities && activities.length > 0) {
for (var i = 0; i < activities.length; i++) {
var activity = activities[i];
var time = getTimeInfo(activity);
var actions = getActionInfo(activity.actions);
var actors = activity.actors.map(getActorInfo);
var targets = getTargetInfo(activity.targets);
//html+=Utilities.formatString('<tr><td style="width:15%;">%s</td><td style="width:15%;">%s</td><td style="width:25%;">%s</td><td style="width:35%;">%s</td></tr>',time,truncated(actors),actions,targets);
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',time,actors,actions,targets);
Logger.log(JSON.stringify(activities[i]));
if(++n > nl)break;;
}
} else {
html+='<br />No Further Activity';
}
token=response.nextPageToken;
}while(token!='' && n<=nl);
html+='</tbody></table><br />n=' + n;
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html).setWidth(1200), "Drive Activity")
}
/** Returns a string representation of the first elements in a list. */
function truncated(array, opt_limit) {
var limit = opt_limit || 2;
var contents = array.slice(0, limit).join(', ');
var more = array.length > limit ? ', ...' : '';
return '[' + contents + more + ']';
}
/** Returns the name of a set property in an object, or else "unknown". */
function getOneOf(object) {
for (var key in object) {
return key;
}
return 'unknown';
}
function getTimeInfo(activity) {
if ('timestamp' in activity) {
const dt=new Date(activity.timestamp);
return Utilities.formatDate(dt, Session.getScriptTimeZone(), "E MM/dd/yyyy HH:mm:ss") + '<br />' + activity.timestamp;
}
return 'unknown';
}
function getActionInfo(acts) {
let html='';
acts.forEach(function(d,i){
if(i>0)html+='<br /><br />';
html+=JSON.stringify(d)
});
return html;
}
/** Returns user information, or the type of user if not a known user. */
function getUserInfo(user) {
if ('knownUser' in user) {
var knownUser = user.knownUser;
var isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
/** Returns actor information, or the type of actor if not a user. */
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user)
}
return getOneOf(actor);
}
/** Returns the type of a target and an associated title. */
function getTargetInfo(targets) {
let html='';
targets.forEach(function(t,i){
if(i>0)html+='<br /><br />';
//html+=JSON.stringify(t);
html+='target: ' + i;
html+=Utilities.formatString('<br />title: %s, type: %s, personName: %s, domainName: %s, id: %s',t.driveItem.title,t.driveItem.mimeType,getUserInfo(t.driveItem.owner.user),t.driveItem.owner.domain.name,t.driveItem.name.slice(6));
});
return html;
}

Related

Extracting permissions on a Shared Drive

I'm curious if there is a way to extract all permissions within a Google Shared Drive. The current script below can do this except that it can only display one permission for each drive at a time. For example, if a shared drive was named X and had 3 users in the drive with the same permission (or different permissions), it will only display 1 out of the 3 users in Drive X. Any help will be greatly appreciated.
var GOOGLE_SHEET_URL = "https://docs.google.com/spreadsheets/";
var GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES = "Drive";
var GOOGLE_SHEET_RESULTS_TAB_NAME_PERMISSIONS = "Permissions";
const GOOGLEAPI_MAX_RESULTS_PER_PAGE = 5;
const NUMBER_OF_ROWS_TO_LOOKUP_PERMISSIONS_PER_LOOP = 4;
const USE_DOMAIN_ADMIN_ACCESS = true;
//const EMAIL_RECIPIENT_ADDRESS = 'test#gmail.com';
const EMAIL_SUBJECT_LINE = 'Appscript Google Shared Drive Audit Complete';
/*
* This is for the headers for Sheets
*/
function job_set_sheet_headers() {
var drives_sheet = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES);
drives_sheet.appendRow(["AUDIT_DATE", "ID", "NAME"]);
var permissions_sheet = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_PERMISSIONS);
permissions_sheet.appendRow(["AUDIT_DATE", "ID", "NAME", "EMAIL_ADDRESS", "TYPE", "ROLE"]);
}
/*
* This is the job that will get the full list of shared drives and put the results into the first tab in your target sheet
*/
function job_get_shared_drives_list() {
var startTime = new Date().getTime();
var audit_timestamp = Utilities.formatDate(new Date(), "UTC", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var newRow = [];
var rowsToWrite = [];
let sharedDrives = Drive.Drives.list(
{
maxResults: GOOGLEAPI_MAX_RESULTS_PER_PAGE,
useDomainAdminAccess: USE_DOMAIN_ADMIN_ACCESS,
hidden: false,
}
);
let sharedDrivesItems = sharedDrives.items;
while(sharedDrives.nextPageToken){
sharedDrives = Drive.Drives.list(
{
pageToken:sharedDrives.nextPageToken,
maxResults: GOOGLEAPI_MAX_RESULTS_PER_PAGE,
useDomainAdminAccess: USE_DOMAIN_ADMIN_ACCESS,
hidden: false,
}
);
sharedDrivesItems = sharedDrivesItems.concat(sharedDrives.items)
//console.log(sharedDrives.items);
//console.log(sharedDrivesItems.length);
}
//console.log(sharedDrivesItems);
sharedDrivesItems.forEach(function(value) {
var newRow = [audit_timestamp, value.id, value.name];
rowsToWrite.push(newRow);
//console.log(newRow);
});
var ss = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES);
ss.getRange(ss.getLastRow() + 1, 1, rowsToWrite.length, rowsToWrite[0].length).setValues(rowsToWrite);
var endTime = new Date().getTime();
var elapsed = (endTime-startTime)/1000;
console.log('Elapsed Seconds: ' + elapsed);
}
/*
* This is the job that will loop through the first tab and look up the permissions for each shared drive.
*/
function job_get_permissions_for_drives() {
var startTime = new Date().getTime();
var drives_sheet = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_DRIVES);
var rangeData = drives_sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = drives_sheet.getRange(2,1, NUMBER_OF_ROWS_TO_LOOKUP_PERMISSIONS_PER_LOOP, lastColumn);
// Get array of values in the search Range
var values = searchRange.getValues();
// console.log("values");
// console.log(values);
// Loop through array and if condition met, do relevant permissions lookup
for ( i = 0; i < NUMBER_OF_ROWS_TO_LOOKUP_PERMISSIONS_PER_LOOP; i++) {
if(values[0][0] == '') {
console.log('Source row empty. Script probably complete now.');
//send_email_simple_(EMAIL_RECIPIENT_ADDRESS,EMAIL_SUBJECT_LINE,'Source row empty for Shared Drive Audit process. The script probably complete now.');
return;
}
var newRow = [];
var rowsToWrite = [];
this_audit_date = values[i][0];
this_drive_id = values[i][1];
this_drive_name = values[i][2];
console.log('GETTING DRIVE NAME: ' + this_drive_name);
var thisDrivePermissions = Drive.Permissions.list(
this_drive_id,
{
useDomainAdminAccess: USE_DOMAIN_ADMIN_ACCESS,
supportsAllDrives: true
}
);
//loop through each permission item
var newRow = [];
if(thisDrivePermissions.items.length >= 1) {
//console.log('permissions were not empty');
thisDrivePermissions.items.forEach(function(value) {
//console.log(value);
newRow = [this_audit_date, this_drive_id, this_drive_name, value.emailAddress, value.type, value.role];
// add to row array instead of append because appending each one at a time is SLOOOOOWWWWW
});
} else {
console.log('permissions were empty');
newRow = [this_audit_date, this_drive_id, this_drive_name, 'ORPHAN', 'ORPHAN', 'ORPHAN'];
}
rowsToWrite.push(newRow);
//console.log(newRow);
// write the permissions items to the permissions tab
var ss = SpreadsheetApp.openByUrl(GOOGLE_SHEET_URL).getSheetByName(GOOGLE_SHEET_RESULTS_TAB_NAME_PERMISSIONS);
ss.getRange(ss.getLastRow() + 1, 1, rowsToWrite.length, rowsToWrite[0].length).setValues(rowsToWrite);
// now delete the row from the drives sheet so we dont have to process it again on the next loop
// this should always be row #2, because we've deleted the previous ROW 2 during the last loop iteration
var rowToDelete = 2;
drives_sheet.deleteRow(rowToDelete);
//console.log('DELETING DRIVE NAME: ' + this_drive_name);
//console.log('DELETING ROW: ' + rowToDelete);
};
var endTime = new Date().getTime();
var elapsed = (endTime-startTime)/1000;
console.log('Elapsed Seconds: ' + elapsed);
};
/*function send_email_simple_(recipient,subject,plain_text_body) {
MailApp.sendEmail(
recipient,
subject,
plain_text_body
);
}
*/
Apparently the problem is caused by this statement:
thisDrivePermissions.items.forEach(function(value) {
//console.log(value);
newRow = [this_audit_date, this_drive_id, this_drive_name, value.emailAddress, value.type, value.role];
// add to row array instead of append because appending each one at a time is SLOOOOOWWWWW
});
It's overwriting the newRow variable.
To avoid this, instead of doing assigning an Array, push it:
thisDrivePermissions.items.forEach(function(value) {
//console.log(value);
newRow.push([this_audit_date, this_drive_id, this_drive_name, value.emailAddress, value.type, value.role]);
// add to row array instead of append because appending each one at a time is SLOOOOOWWWWW
});
Also modify
} else {
console.log('permissions were empty');
newRow = [this_audit_date, this_drive_id, this_drive_name, 'ORPHAN', 'ORPHAN', 'ORPHAN'];
}
as
} else {
console.log('permissions were empty');
newRow = [[this_audit_date, this_drive_id, this_drive_name, 'ORPHAN', 'ORPHAN', 'ORPHAN']];
}
and
rowsToWrite.push(newRow)
as
rowsToWrite.push(...newRow)

I want to search and list google spreadsheet that start with "A3_" in my google drive & log file id, url and owner name into another google sheet

I have this code, but this gives an error of cannot read property 'getName' of null
how do I fix this. There are about 2000 files on my drive. There are files shared by other users as well.
function getMyFilesFromDrive() {
var myFiles = DriveApp.searchFiles('title contains "A3_"');
var sheet = SpreadsheetApp.getActive().getSheetByName("Files");
sheet.clear();
var rows = [];
rows.push(["ID", "Name", "Url", "user"]);
while(myFiles.hasNext()) {
var file = myFiles.next();
if(file != null) {
rows.push([file.getId(), file.getName(), file.getUrl(), file.getOwner().getName()]);
}
}
sheet.getRange(1,1,rows.length,4).setValues(rows);
}
Find Spreadsheets
This is what I use:
function getSpreadsheets() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Files');
if (sh.getLastRow() > 1) {
const rg = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn());
rg.clearContent();
SpreadsheetApp.flush();
}
var token = '';
var vA = [["Id", "MimeType", "Kind", "Title", "Path", "OwnerEmails"]];
do {
let resp = Drive.Files.list({ pageToken: token, q: "mimeType='application/vnd.google-apps.spreadsheet'" });
token = resp.nextPageToken;
for (let i = 0; i < resp.items.length; i++) {
let item = resp.items[i];
if (item.title.match(/^A3_.*$/)) {//with names that begin with A3_
vA.push([item.id, item.mimeType, item.kind, item.title, getPathFromId(item.id), item.owners.map(function (o) { return o.emailAddress }).join(',')]);
}
}
if (token == '') {
ss.toast('That was the last ' + resp.items.length + '. Your Done!');
} else {
ss.toast('Another ' + resp.items.length + '. Keep Going!');
}
} while (token)
sh.getRange(2, 1, vA.length, vA[0].length).setValues(vA);
SpreadsheetApp.flush();
}
function getPathFromId(id) {
try {
var file = DriveApp.getFileById(id)
var pA = [];
pA.push(file.getName());
var folder = file.getParents();
while (folder.hasNext()) {
var f = folder.next();
pA.push(f.getName());
folder = f.getParents()
}
var r = pA.reverse().join(' / ');
}
catch (e) {
return e;
}
return r;
}
You will need to enable drive API.
You can add the downloadUrl to this
vA.push([item.id, item.mimeType, item.kind, item.title, getPathFromId(item.id), item.owners.map(function (o) { return o.emailAddress }).join(','),item.downloadUrl]);
and to the header here:
var vA = [["Id", "MimeType", "Kind", "Title", "Path", "OwnerEmails","Url"]];

Get .xlsx attachment from Gmail and update rows in already existing Google sheet

I am new to Google AppScripts, and really struggling with achieving my end goal. The following code get's me very close, but I am wondering if anyone can please point me in a direction that enables me to do the following:
Extract .xlsx file from gmail that is received daily
Transfer the contents of the .xlsx file to an already existing Google sheet
Any advice would be hugely appreciated.
James
// GLOBALS
//Array of file extension which you would like to extract to Drive
var fileTypesToExtract = ['jpg', 'tif', 'png', 'gif', 'bmp', 'svg'];
//Name of the folder in google drive i which files will be put
var folderName = 'GmailToDrive';
//Name of the label which will be applied after processing the mail message
var labelName = 'GmailToDrive';
function GmailToDrive(){
//build query to search emails
var query = '';
//filename:jpg OR filename:tif OR filename:gif OR fileName:png OR filename:bmp OR filename:svg'; //'after:'+formattedDate+
for(var i in fileTypesToExtract){
query += (query === '' ?('filename:'+fileTypesToExtract[i]) : (' OR filename:'+fileTypesToExtract[i]));
}
query = 'in:inbox has:nouserlabels ' + query;
var threads = GmailApp.search(query);
var label = getGmailLabel_(labelName);
var parentFolder;
if(threads.length > 0){
parentFolder = getFolder_(folderName);
}
var root = DriveApp.getRootFolder();
for(var i in threads){
var mesgs = threads[i].getMessages();
for(var j in mesgs){
//get attachments
var attachments = mesgs[j].getAttachments();
for(var k in attachments){
var attachment = attachments[k];
var isDefinedType = checkIfDefinedType_(attachment);
if(!isDefinedType) continue;
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
parentFolder.addFile(file);
root.removeFile(file);
}
}
threads[i].addLabel(label);
}
}
//This function will get the parent folder in Google drive
function getFolder_(folderName){
var folder;
var fi = DriveApp.getFoldersByName(folderName);
if(fi.hasNext()){
folder = fi.next();
}
else{
folder = DriveApp.createFolder(folderName);
}
return folder;
}
//getDate n days back
// n must be integer
function getDateNDaysBack_(n){
n = parseInt(n);
var date = new Date();
date.setDate(date.getDate() - n);
return Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy/MM/dd');
}
function getGmailLabel_(name){
var label = GmailApp.getUserLabelByName(name);
if(!label){
label = GmailApp.createLabel(name);
}
return label;
}
//this function will check for filextension type.
// and return boolean
function checkIfDefinedType_(attachment){
var fileName = attachment.getName();
var temp = fileName.split('.');
var fileExtension = temp[temp.length-1].toLowerCase();
if(fileTypesToExtract.indexOf(fileExtension) !== -1) return true;
else return false;
}
That's the codes I'm using for getting attachments from gmail to existing spreadsheet.
By setting up time driven trigger.
In order to make this function run faster. set the label name and the thread limites
function attachfromGmail(attachmentname, destsheet_id, destsheetname, label_name, all_or_part) {
const folder = DriveApp.getFolderById("xasdawfes2342ke");
const labels = GmailApp.getUserLabelByName(label_name).getThreads(0, 10);
const threads = GmailApp.getMessagesForThreads(labels);
const today_date = Utilities.formatDate(new Date(), "GMT-6", "MM/dd/yyyy");
var fileid_FromGmail = "";
threads.forEach(threads => {
threads.filter(messge => {
if (Utilities.formatDate(messge.getDate(), "GMT-6", "MM/dd/yyyy") == today_date) {
messge.getAttachments().filter(attachments => {
if (attachments.getName() == attachmentname && messge.isUnread() === true) {
messge.markRead();
const blob = attachments.copyBlob();
var file_FromGmail = Drive.Files.insert(
{ title: attachmentname, parents: [{ "id": folder.getId() }] },
blob,
{ convert: true }
);
fileid_FromGmail = file_FromGmail.id;
}
})
}
})
})
const created_file = SpreadsheetApp.openById(fileid_FromGmail);
const values = created_file.getDataRange().getDisplayValues();
const destfiles_sheet = SpreadsheetApp.openById(destsheet_id).getSheetByName(destsheetname);
if (all_or_part === 'all' && values != undefined) {
destfiles_sheet.clear();
destfiles_sheet.getRange(1, 1, created_file.getLastRow(), created_file.getLastColumn()).setValues(values);
} else if (values.length === 1 && values.toString() != 'No Data Available' || values.length > 1) {
values.shift()
destfiles_sheet.getRange(destfiles_sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
//delete created file
DriveApp.getFileById(fileid_FromGmail).setTrashed(true);
}
It works well, but I'm trying to get a better way to do the job. Instead of insert a new file and delete. I know we can use csv parse function to get the data. but I can not fix the leading 0 issue in the csv file.
Then I'm trying to use XML file to transfer data, still working on it.
I will update this post if I get a better solution.

Stuck at G Suite Developer Quickstart for "check student attendance in Google Meet courses"

I would be very appreciative of any input. Thank you;
I'm using the G Suite Developer Quickstart for a Meet attendance script
https://developers.google.com/gsuite/add-ons/editors/sheets/quickstart/attendance
Unfortunately, upon using the provided code, and having made adjustments, I either always get "Absent" or "Present" for all students (regardless of whether they were present or not) depending on if I use "null" of "false" in an if statement for activities for applicationName 'meet'.
Here is the code I'm using as a superuser:
...
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Opciones SG')
.addItem("Importar Curso", 'importCourses')
.addItem('Verificar Asistencia', 'checkAll')
.addToUi();
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list();
var courses = response.courses;
for (var i = 0; i < 1; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
// No Meet code given
if (meetCode == null) {
break;
}
else {
// check whether each student was present in Meet
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
// universal settings - static
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName, optionalArgs);
var activities = response.items;
if (activities == false) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace("/-/g","")
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("Absent");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("Present");
}
...
For the correct functionality of the code, check for if (activities == null) or if (activities == undefined) - NOT if (activities == false)
If there are no response items (that is no items have been found for a given user and meeting code) - activities will be undefined, but not false.
if (activities == false) will never be fulfilled and if you use it - all participants will be marked as present
On the other hand, if activities == undefined for other reasons - for example because you introduced the wrong meet code (don't forget to replace 'Asistencia' through a valid meet code), all participants will be marked as absent
Note that the Reports API has some delay, so do not expect to retrieve participation data in live time.
If all participants are marked as absent - probably the data did not propagate yet, wait some time and try again.
Make sure you pass the correct meet code to the script. Your meet code should look something like xxx-xxxx-xxx.
The script needs to remove the - for correct functionality. For this please change in function getCleanCode(meetCode) the line return meetCode.replace("/-/g","") to return meetCode.replace(/-/g, "");
As for the question in your comment: here you can see all available Hangouts Meet Audit Activity Events that you can use for filtering instead of the meeting_code. Unfortunately the timestamp is not one of possible query parameters.
Here is the code we have been using for a few months now, no glitches:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Asistencia')
.addItem('Asistencia', 'checkAll')
.addItem('*Crear*', 'createNewSheets')
.addToUi();
}
function createNewSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the range of cells that store employee data.
var employeeDataRange = ss.getRangeByName("A:A");
var employeeObjects = employeeDataRange.getValues();
var template = ss.getSheetByName('Template');
for (var i=0; i < employeeObjects.length; i++) {
// Put the sheet you want to create in a variable
var sheet = ss.getSheetByName(employeeObjects[i]);
// Check if the sheet you want to create already exists. If so,
// log this and loop back. If not, create the new sheet.
if (sheet) {
Logger.log("Sheet " + employeeObjects[i] + "already exists");
} else {
template.copyTo(ss).setName(employeeObjects[i]);
}
}
return;
}
function importCourses() {
var optionalArgs = {
teacherId: 'me',
pageSize: 5
};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
for (var i = 0; i < 2; i++) {
var courseName = courses[i].name;
var courseId = courses[i].id;
insertCourse(courseName, courseId)
}
}
function insertCourse(courseName, courseId) {
var spreadsheetName = courseName + "(" + courseId + ")"
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var yourNewSheet = activeSpreadsheet.getSheetByName(spreadsheetName);
if (yourNewSheet != null) {
return
}
yourNewSheet = activeSpreadsheet.insertSheet();
yourNewSheet.setName(spreadsheetName);
yourNewSheet.appendRow(['Nombre', 'Email', 'Asistencia'])
yourNewSheet.setFrozenRows(1)
var studentNames = getRoster(courseId)["studentNames"]
var studentEmails = getRoster(courseId)["studentEmails"]
for (var i = 0; i < studentNames.length; i++) {
yourNewSheet.appendRow([studentNames[i],studentEmails[i]])
}
yourNewSheet.autoResizeColumns(1, 2)
yourNewSheet.setFrozenColumns(2)
}
function getRoster(courseId) {
var studentNames = []
var studentEmails = []
var optionalArgs = {
pageSize: 100
};
var response = Classroom.Courses.Students.list(courseId, optionalArgs)
var students = response.students
for (var i = 0; i < 100; i++) {
try {
studentNames.push(students[i].profile.name.fullName)
studentEmails.push(students[i].profile.emailAddress)
} catch (err) {
return { "studentNames":studentNames, "studentEmails":studentEmails }
}
}
}
function checkAll() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
for (var i = 2; i < sheet.length * 100; i++){
var meetCode = getCleanCode(sheet[0][i])
// No Meet code given
if (meetCode == null) {
break;
}
else {
// check whether each student was present in Meet
checkMeet(meetCode, i+1);
}
}
}
function checkMeet(meetCode, index) {
// universal settings - static
var userKey = 'all';
var applicationName = 'meet';
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getDataRange().getValues();
//let today = new Date()
//var staringTime = new Date().toISOString();
// var DateValue = ss.getRange(1, 1).getValues();
for (var i = 0; i < sheet.length-1; i++) {
var emailAddress = sheet[i+1][1]
var optionalArgs = {
startTime: formatStartDate(),
event_name: "call_ended",
filters: "identifier==" + emailAddress + ",meeting_code==" + meetCode
};
try {
var response = AdminReports.Activities.list(userKey, applicationName, optionalArgs);
var activities = response.items;
if (activities == undefined) {
markAbsent(ss,i+2,index)
}
else {
markPresent(ss,i+2,index)
}
} catch (err) {
continue
}
}
}
function getCleanCode(meetCode) {
try{
return meetCode.replace(/-/g, "");
} catch (err) { return meetCode; }
}
function markAbsent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("A");
}
function markPresent(sheet, i, j) {
var cell = sheet.getRange(i, j);
cell.setValue("");
}
function formatStartDate(){
let date = new Date();
const offset = date.getTimezoneOffset();
let offsetDate = new Date(date.getTime() - (offset*145*1000));
return offsetDate.toISOString();
}

How to optimize fileiterator (to avoid timeout) in Google Scripts

I am hitting the Google Scripts 6-minute timeout. The script is running through quite a few folders and subfolders. I am hoping that there is either (or both):
A way to optimize the script to more efficiently/intelligently run through the dataset/iterators?
A way to use a getContinuationToken()
Any help would be much appreciated!
I have tried to limit the number of folders/subfolders the script runs through. Even on a 'minimal' run, it times out. And FYI, my goal is to:
make a copy of files that are NOT owned by 'me'...
change the name of the original file (that was not owned by me) to be 'delete'
Here is the code:
FOLDERS = ["0B4FiuEqe8ftGb2lSbjJzakJDNWs"]; /*, "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "1D_1u_KwcLOsBuKyQ7NCsKDd8DB5JwOn6"];*/
function copyNonOwnedFiles() {
var arr = FOLDERS
for (var j = 0; j < arr.length; j++) {
var folderTOP = DriveApp.getFolderById(arr[j]);
var folders = folderTOP.getFolders();
var me = Session.getActiveUser().getEmail();
//RUN #1 = this runs through all of the subfolders
//iterator 1
while (folders.hasNext()) {
var folder = folders.next();
//get all files within these folders
var files = folder.getFiles();
//iterator 2
while (files.hasNext()) {
var file = files.next();
var Owner = file.getOwner().getEmail();
if( Owner !== me ){
var name = file.getName();
file.makeCopy(name);
file.setName('delete');
}
}
}
//RUN #2 = this runs through the parent folder
var files2 = folderTOP.getFiles();
//iterator 2
while (files2.hasNext()) {
var file2 = files2.next();
var Owner = file2.getOwner().getEmail();
if( Owner !== me ){
var name2 = file2.getName();
file2.makeCopy(name2);
file2.setName('delete');
}
}
}
}
Try this:
This script will find the files owned by the active user, in the folders with the listed ids and display them on a modeless dialog. This script will probably need to utilize lock service for multiple simultaneous users.
var myFiles=[];
var ids=["0B4FiuEqe8ftGb2lSbjJzakJDNWs", "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "0B4FiuEqe8ftGN2YxZFU5RlhMSDQ", "1D_1u_KwcLOsBuKyQ7NCsKDd8DB5JwOn6"];
var me='';
function getMyFiles() {
myFiles=[];
me=Session.getActiveUser().getEmail();
for(var i=0;i<ids.length;i++) {
getFnF(DriveApp.getFolderById(ids[i]));
}
var html="<style>th,td{border:1px solid black;}</style><table><tr><th>Item</th><th>Name</th><th>Url</th></tr>";
for(var i=0;i<myFiles.length;i++) {
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>Link</td></tr>',i+1,myFiles[i].name,myFiles[i].url);
}
html+='</table><input type="button" value="Close" onClick="google.script.host.close();" />';
var userInterface=HtmlService.createHtmlOutput(html);
var title=Utilities.formatString('Files Owned by: %s',me);
SpreadsheetApp.getUi().showModelessDialog(userInterface, title)
}
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
var files=folder.getFiles();
while(files.hasNext()) {
var file=files.next();
if(file.getOwner().getEmail()==me) {
myFiles.push({name:file.getName(),url:file.getUrl()});
}
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
getFnF(subfolder);
}
}
for anyone who has this same question or a similar one... I found that it was much faster/easier/less resource intensive to use a search than a file iterator
dataSheetName = "List";
function onOpen(){
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('List Files', 'ListFiles')
.addSeparator()
.addToUi();
}
function ListFiles() {
var sheet = SpreadsheetApp.getActive();
var dataSheet = sheet.getSheetByName(dataSheetName);
// Clear Sheet
var lastRow = dataSheet.getLastRow();
if (lastRow > 0) {
dataSheet.getRange(2, 1, lastRow, dataSheet.getLastColumn()).clearContent();
}
dataSheet.getRange("E2").setValue("Process Started at: " + new Date());
Logger.log("Start: " + new Date());
// var row = ['File Name', 'File Id'];
var row = ['File Name', 'File Id'];
var data = [];
data.push(row);
// Find files modified in the last 24 hours
var totalDays = 30;
var today = new Date();
var oneDayAgo = new Date(today.getTime() - totalDays * 24 * 60 * 60 * 1000);
var startTime = oneDayAgo.toISOString();
// The magic search expression
var search = '(trashed = false) and (modifiedDate > "' + startTime + '") and not (title contains \'DELETE\') and not (\'me\' in owners) ';
// var search = "(mimeType='image/jpeg') and not (title contains 'DELETE') ";
// var search = " not ('me' in owners) and not (title contains 'DELETE') ";
// var search = " not ('me' in owners) ";
//var search = 'sharedWithMe';
// 'me' in owners
var i = 1;
var files = DriveApp.searchFiles(search);
Logger.log("2: " + new Date());
dataSheet.getRange("E3").setValue("Search Completed at: " + new Date());
// while (files.hasNext() && (i < 10000) {
while (files.hasNext() ) {
var file = files.next();
try {
// data.push([file.getName(),file.getId()]);
data.push([file.getName(), file.getId()]);
}
catch (e) {
Logger.log(file.getName());
}
// i += i;
}
// Write data
var dataRange = dataSheet.getRange(1, 1, data.length, row.length);
dataRange.setValues(data);
Logger.log("3: " + new Date());
dataSheet.getRange("E4").setValue("List Completed at: " + new Date());
}