I was wondering: is it even possible to use Logger.Log in Google Apps Script to log different string to be posted to a spreadsheet?
I have the following code:
var ss = SpreadsheetApp.openByUrl("spreadsheet url");
var sheet = ss.getSheetByName("spreadsheet sheet");
var DocNumber = e.parameter.DocNumber;
var folderId = "Folder ID 1";
var lastFileUrl = getLatestFile(folderId); // just a function that retrieves url of latest file in the folder
Logger.log(lastFileUrl);
var addUrl = sheet.getRange(1,2,sheet.getLastRow(),1);
var fileURL = "https://drive.google.com/uc?export=view&id="+lastFileUrl;
var folderId2 = "Folder ID 2";
var lastFileUrl2 = getLatestFile(folderId2); // same as above
Logger.log(lastFileUrl2);
var addUrl2 = sheet.getRange(1,3,sheet.getLastRow(),1);
var fileURL2 = "https://drive.google.com/uc?export=view&id="+lastFileUrl2;
sheet.appendRow([DocNumber,fileURL,fileURL2]);
}
When this get posted to the spreadsheet, it only posts the second url (fileURL2) - I assume because the last value in the log is this. But I was hoping to post both URL into the spreadsheet.
I tried setting it as a var first as well:
var URL2 = Logger.log(lastFileURL2);
but then the posted value will be https://drive.google.com/uc?export=view&id=Logger
I also tried using appendRow before the second URL logging but it still only takes the second url and disregard the first url.
Therefore, I was curios whether this is even possible at all?
And if not, what's the best way to achieve this without using Logger.log?
Spreadsheet output:
URL1 and URL2 is the URL from Google Drive folder.
Also, forgot to mention, I'm using the script as a Web App, used by an android app. Posting files into the Drive folder is okay, the only problem is fetching the links of the files in different folders.
These are the codes I used to get the latest file url from my folders:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById("Folder_1_ID").getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
function getLatestFile(folderId2) {
var files2 = DriveApp.getFolderById("Folder_2_ID").getFiles();
var fileObj2 = [];
while (files2.hasNext()) {
var file2 = files2.next();
fileObj2.push({id: file2.getId(), date: file2.getDateCreated()});
}
fileObj2.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj2[0].id;
}
Problem
Having two functions declared under the same name
Solution
Step by step:
Remove one of the functions (they are identical in terms in usage)
Make the remaining one use the parameter passed in it:
function getLatestFile(folderId) {
var files = DriveApp.getFolderById(folderId).getFiles();
var fileObj = [];
while (files.hasNext()) {
var file = files.next();
fileObj.push({id: file.getId(), date: file.getDateCreated()});
}
fileObj.sort(function(a, b) {return new Date(b.date) - new Date(a.date)});
return fileObj[0].id;
}
Change Logger to console - as of recently, all logs are sent to Stackdriver service, and thus there is no benefit in using Logger (besides by using console you make script more portable).
Commentary
What happens when you declare two or more functions under same name? Normally, the last one declared gets executed (basically, second declaration overwrites the first):
function clone(original) {
return `I am the clone of ${original}`;
}
function clone(cloned) {
return `I am a clone of ${cloned}'s clone`;
}
const elem = document.querySelector("#cloned");
elem.textContent = clone("Gary");
<h2 id="cloned"></h2>
Related
I am trying to create a Google Sheet where all instances of Sheets with the same string prefix in the title are gathered, in particular their file IDs. Out of 110 files with a perfect match, the output is only 24 files, always the same files. I am testing this to not actually alert clients in a different outputtest sheet, for which I have written the code underneath:
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('testMenu')
.addItem('mainFunction', 'main')
.addToUi();
};
function main() {
var sheetName = 'Contacts';
var q = '(title contains "2022 Client Data ")'; //all titles start with this string
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName(sheetName);
var data = sheet.getDataRange().getValues();
//loadSettings(spreadsheet, data);
var clientNames = getClientNames(data);
var clientFiles = getClientFiles(q);
//var eDict = matchClientWithFile(clientNames, clientFiles);
outputTest(spreadsheet, clientFiles);
};
function getClientFiles(q) {
var files = DriveApp.searchFiles(q);
var clientFiles = {};
while (files.hasNext()) {
var clientFile = files.next();
clientFiles[clientFile.getName()] = clientFile.getId();
};
return clientFiles;
};
function outputTest(spreadsheet, dict) {
var sheet = spreadsheet.getSheetByName('outputtest');
if (!sheet) {
var sheet = spreadsheet.insertSheet('outputtest');
}
for (const [key, value] of Object.entries(dict)) {
sheet.appendRow([key, value]);
};
};
The text in the q-string is the only requirement for a file match, as all file titles start with "2022 Client Data" which is really the only identifier. All files are stored in a shared directory.
I have looked up previous StackOverflow questions as well as documentation to see whether everything was in place, as well as making the q-string less specific and cleaning up the code, with the same result.
Drive searchbar with the type:spreadsheet title:"2022 client data" shows the amount of matches I should be getting in the outputtest sheet. I am hoping to put all 110 currently existing files together.
EDIT: when accessing it from a different google account in the shared Drive, the amount of files changes. It is highly likely this has to do with the DriveApp.searchFiles function only accessing the files that have been created by the account running the script. Is there a way to change this?
I have this code in Apps Script, it gets all files of a folder (expofd) and print the ID's in the Google Sheet. But each time the code get the files in a different order and I need any kind of sort (Alphabetically by name, not by id) or whatever, but the printing in the Sheet have to be always in the same order. Also, It would be nice a code modification to filter the folder files and get only which have certain suffix.
var list = [];
list.push(['ID']);
var files = expofd.getFiles();
while (files.hasNext()){
file = files.next();
var row = []
row.push(file.getId())
list.push(row)
;
}
sh.getRange(2,2,list.length,list[0].length).setValues(list);
Thanks!
Try this
function myFunction() {
var expofd = DriveApp.getFolderById('#############') // your folder id
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var suffix = '.pdf' // adapt as necessary
var list = [];
var files = expofd.getFiles();
while (files.hasNext()) {
file = files.next();
list.push([file.getName(),file.getId()])
}
var result = [['name','id'], ...list.filter(r => r[0].includes(suffix)).sort()]
sh.getRange(1,1, result.length, result[0].length).setValues(result);
}
You will get the list sorted by name and filtered by suffix
Just in case here is the more fancy (and probably more quick) way to search files with Drive API:
function myFunction() {
var mask = '.jpg';
var folder_id = '###'; // put your folder ID instead of ###
var query = `title contains "${mask}" and trashed = false and "${folder_id}" in parents`;
var findings = Drive.Files.list({ q: query }) || [];
var table = [['name', 'id'], ...findings.items.map(f => [f.title, f.id]).sort()];
SpreadsheetApp.getActiveSheet().clear().getRange(1,1,table.length,2).setValues(table);
}
Make sure you add the Drive API in Script Editor:
Reference:
Search for files and folders with Drive.list() method
Team members upload content (regardless of file type) into a folder on Drive. I need to copy this content into another folder automatically with a trigger, and be able to move it around from there.
I cannot use a "MoveFile" function as I am not the owner of the original content.
I have already tried to copy files automatically into the destination folder, and this works, using the code below:
function CopyFiles() {
var srcFldr = DriveApp.getFolderById("***ID***");
var srcFiles = srcFldr.getFiles();
var desFldr = DriveApp.getFolderById("***ID***");
var desFiles = desFldr.getFiles();
var dfnA = [];
while (desFiles.hasNext()) {
var df = desFiles.next();
dfnA.push(df.getName());
}
while (srcFiles.hasNext()) {
var sf = srcFiles.next();
if (dfnA.indexOf(sf.getName()) == -1) {
sf.makeCopy(sf.getName(), desFldr);
}
}
}
However, I need to move this copied content into other files throughout the day, yet every time I do, the same file gets copied back into the destination folder above with the new trigger, creating a permanent loop.
Is there a way of either:
moving the files from the original source folder despite not being the owner of those files?
copying contents only once, upon upload or modification?
Or 3) another, better, smarter way of doing this?
Thanks for your help!
I'd suggest the following workflow:
For every file that is copied to the destination folder, store the fileId. You could use Properties Service for this.
When copying files from one folder to the other, check the fileId has not been stored before.
Code snippet:
function CopyFiles() {
var srcFldr = DriveApp.getFolderById("***ID***");
var srcFiles = srcFldr.getFiles();
var desFldr = DriveApp.getFolderById("***ID***");
var desFiles = desFldr.getFiles();
var dfnA = [];
var key = "fileIDs";
var scriptProperties = PropertiesService.getScriptProperties();
var property = scriptProperties.getProperty(key); // Retrieve fileIDs property
// Get array of fileId, or empty array if no file has been copied before:
var arrayIDs = property ? JSON.parse(property) : [];
while (desFiles.hasNext()) {
var df = desFiles.next();
dfnA.push(df.getName());
}
while (srcFiles.hasNext()) {
var sf = srcFiles.next();
// Check not only file name, but also whether fileId has been stored before:
if (dfnA.indexOf(sf.getName()) == -1 && arrayIDs.indexOf(sf.getId()) == - 1) {
sf.makeCopy(sf.getName(), desFldr);
arrayIDs.push(sf.getId()); // Add fileId to array of IDs
}
}
scriptProperties.setProperty(key, JSON.stringify(arrayIDs)); // Store updated array
}
Reference:
Properties.getProperty(key)
Properties.setProperty(key, value)
Many thanks for the comments and response. That code was a little too advanced for me and I ended up finding a very inelegant solution that I used because I ran out of time. I was able to get the code to list Folder and first level of subFolders with links, but I have not yet been able to get it to iterate through all levels of folders, mostly because I just need to back up and learn a lot of the basics. I was also able to get all folders to list using some code I found to create a tree, but I couldn't get it to format in a way that you could actually see the structure, or add links. I'm going to continue to try, and will post if I sort it out. Here is what I used, which was fine for our purposes because our shared drive is fairly limited.
For reference, this was the code I used to start with:
https://superuser.com/questions/1095578/list-of-subfolder-names-and-file-links-in-google-sheets-script
function listFolders(foldername) {
var ss = SpreadsheetApp.openById(ID);
var sheet = ss.getSheetByName("sheet name");
sheet.appendRow("Parent Folder", "Name", "Link" ]);
//change the folder ID below to reflect your folder's ID (look in the
URL when you're in your folder)
var folders = DriveApp.getFolderById(ID);
var contents = folders.getFolders();
var cnt = 0;
var folderD;
while (contents.hasNext()) {
var folderD = contents.next();
cnt++;
data = [
folders.getName(),
folderD.getName(),
folderD.getUrl(),
];
sheet.appendRow(data);
};
};
Original Post:
I am a beginner using script in google sheets and I am trying to create a list of folders in a google drive with many subfolders. Ideally it would be a tree form but I'd settle for a list at this point. I don't need to list all the files, just the folders. I have been trying to get the code below to work but it keeps hanging up at calling up the spreadsheet. Can anyone help?
I have tried calling up both the folders and the spreadsheet by both name and ID but it always tells me it can't execute the getactivespreadsheet command. I have also tried to modify the code referred to in another another question but I can't get that to work either: https://ctrlq.org/code/19923-google-drive-files-list
function generateFolderIndex(myfoldername) {
var folder = DriveApp.getFolderById('0B8vOJQUb-IIVTHdudlZSVkdtdE0');
var subFolders = folder.getFolders();
var childFolders = subFolders
var ss = SpreadsheetApp.getActiveSpreadsheet('1Trv9OtJFnD4AdSHrZKFfsSu6JMV9f78H6wwZNhF2_M4');
var sheet = ss.getSheetByName('Directory');
sheet.clear(directory);
sheet.appendRow([name, link]);
while (subFolders.hasNext())
{
var childFolder = childFolders.next();
var foldername = childFolder.getname();
var name = childFolder.getName()
var link = childFolder.getUrl()
var date = childFolder.getDateCreated()
data = [name, link]
sheet.appendRow(data);
}
};
I am trying to get a sheet that lists folders and subfolders with URL links. I am currently receiving the following error message:
[19-05-31 15:32:20:911 EDT] Execution failed: Cannot find method getActiveSpreadsheet(string). (line 5, file "Code") [0.432 seconds total runtime]
Or.. the easy way...
Use DRIVE or FS DRIVE APP for desktop in PC. Usea A CMD (windows)... AND THE FUNCTION
TREE >a.txt
The generated file a.txt will display all the tree.
IT SAVES HOURS OF RESEARCH.
SpreadsheetApp.getActiveSpreadsheet() doesn't have any parameters.
However
SpreadsheetApp.openById('ssid') does require and id. I think perhaps you meant to be using openById();
openById
getActiveSpreadsheet
This is a script that I'm currently working on but it generates a list of Spreadsheets and you can exclude folders by id and files by id.
function getAllSpreadsheets() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
if(sh.getLastRow()>0) {
sh.getRange(1,1,sh.getLastRow(),2).clear().clearDataValidations();
}
getFnF();
SpreadsheetApp.getUi().alert('Process Complete')
}
var level=0;
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
//var folder=DriveApp.getRootFolder();
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
var files=folder.getFilesByType(MimeType.GOOGLE_SHEETS)
while(files.hasNext()) {
var file=files.next();
if(isExcluded(file.getId(),'file')){continue;}
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',file.getUrl(),'FILE: ' + file.getName()));
firg.offset(0,1).insertCheckboxes('Exclude','Include');
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
if(isExcluded(subfolder.getId(),'folder')){continue;}
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',subfolder.getUrl(),'FOLDER: ' + subfolder.getName()));
//forg.offset(0,1).insertCheckboxes('Exclude','Include');
//level++;
getFnF(subfolder);
}
//level--;
}
function isExcluded(id,type) {//type: file or folder
var type=type||'Missing Input';
var xFldrIdA=['Excluded folder ids'];
var xFileIdA=['Excluded file ids'];
var type=type.toLowerCase();
switch(type) {
case 'file':
return xFileIdA.indexOf(id)>-1;
break;
case 'folder':
return xFldrIdA.indexOf(id)>-1;
break;
default:
throw(Utilities.formatString('Error: Invalid Type: %s in isExcluded.',type));
return true;//assume excluded
break;
}
}
Your welcome to use it, perhaps it will help.
I have just updated my script to use the new advanced google services. In this case i have inserted a file in a folder, and now i need to format the file: insert sheets, change colors, etc.
I am having problems for working the file because after using this service I cannot get the file to work with it, i get this error:
"Can't find the insertSheet function in the object FileIterator".
If you can head me to a place where I can find a solution, it would be very helpful.
This is the code i am using:
function HAR() {
//This is the new part of the code...
var name = "Template";
var folder = DriveApp.createFolder("HAR");
var folder_id = folder.getId();
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folder_id }]
}
var fileJson = Drive.Files.insert(resource);
var File_id = fileJson.id;
var Template = DriveApp.getFilesByName("Template");// I guess this is not working
//This is the old part of the code...
const ssNames = ["California","Arizona","Florida","Los Angeles"];
const states = ssNames.length;
var x;
for (x = 0; x < sNames.length; x++){
Template.insertSheet(sNames[x]); //I can't manage to insert the sheets
}
Template.getSheetByName("Sheet 1").activate();
Template.deleteActiveSheet();
}
Template is a FileIterator. So it's been assigned a value, but your code doesn't show Template as being assigned a value, so you must have other code that you haven't shown.
FileIterator only has 3 methods available to it:
getContinuationToken()
hasNext()
next()
You probably want to insert a sheet into the newly created spreadsheet file.
var File_id = fileJson.getId();
var newSS_file = SpreadsheetApp.openById(File_id);