How to open google spreadsheet using script and partial filename - google-apps-script

My goal is to get a partial filename from a cell(B2) using a script and find the complete URL, put the resulting URL in cell B3 so I can run a series of queries in the data. I've spent more hours searching google than I care to admit on this. I'm sure the answer is there but my in-experience with google scripting is preventing me from seeing.
Thanks for any help.

Files of a Feather
The feather is the partial string which must be provided. You can use a prompt if you want it to be interactive with a user.
function getFilesOfAFeather(feather) {
var files=DriveApp.getFiles();
var fA=[];
while(files.hasNext()){
var file=files.next();
if(file.getName().indexOf(feather)>-1) {
fA.push({name: file.getName(), id: file.getId(), url: file.getUrl()});
}
}
return fA;
}
If fA.length=0 then no files were found.
If fA.length=1 then var url=fA[0].url;
If fA.length>1 then you have to pick which one you want
function findUrl() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var feather=sh.getRange('B2').getValue();
var f=getFilesOfAFeather(feather);
if(f.length==1) {
return f[0].url;
}else{
SpreadsheetApp.getUi().alert(Utilities.formatString('Number of files found: ',f.length));
}
}

Related

Clear, copy and paste from one file to another using google script

I have this script running from one source file to several other files where I use the data. The script has always tended to work fine but recently started getting the error
Exception: Service Spreadsheets timed out while accessing document with id.
I only get the error when the script is running for a particular destination file, as opposed to others. It also happens that this destination file is much larger than the others (maybe the problem). Anyway, the script is below and wondering if anyone has any alternative suggestions or feedback.
function Function_name() {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var sourcesheet=ss.getSheetByName("RAWFINEX")
var data=sourcesheet.getDataRange().getValues()
var destinationfile=SpreadsheetApp.openById("Sheet ID")
var destinationsheet=destinationfile.getSheetByName("RAWFINEX")
var destinationlr=lastUsableRow(destinationsheet)
var destinationlc=destinationsheet.getLastColumn
var existingdata=destinationsheet.getRange(1,1,destinationlr,5).clearContent
var destinationrange=destinationsheet.getRange(1,1,data.length,data[0].length)
var pastedata=destinationrange.setValues(data)
}
Copy Data And Log
function CopyDataAndLog() {
var log = DriveApp.getFileById("logid");
var ss=SpreadsheetApp.getActive();
var ssh=ss.getSheetByName("RAWFINEX");
var svs=ssh.getDataRange().getValues();
var ss=SpreadsheetApp.openById("Sheet ID")
var fi=DriveApp.getFileById(ss.getId())
log.setContent(log.getBlob().getDataAsString() + `${fi.getSize()},${file.getName()},${file.getId()},${file.getType()}${'\r\n'}}`;
var tsh=ss.getSheetByName("RAWFINEX")
var tlr=lastUsableRow(tsh)
tsh.getRange(1,1,tlr,5).clearContent();
tsh.getRange(1,1,svs.length,svs[0].length).trg.setValues(svs);
}

I need to get the date a file was created from a google apps script but im being told getDateCreated and getlastUpdated arent functions?

As the title suggest I have an Apps script where I need the Date created of the google doc but when i use the following code
''' var file = DriveApp.getFilesByName("filename")
var final = file.getDateCreated()'''
I get told that getDateCreated isnt a function and the same issue happens with getlastUpdated can anyone point out what im missing. Many thanks.
DriveApp.getFilesByName("filename") returns a FileIterator not a File. You need to iterate through the files, even if there is only one file with that name.
function test() {
try {
var files = DriveApp.getFilesByName("filename");
while( files.hasNext() ) {
var file = files.next();
console.log(file.getDateCreated());
}
}
catch(err) {
console.log(err);
}
}

Google Apps Script error - multiple rows from Google Sheets but only one file in Google Drive - "iterator has reached the end"

I have a script that pulls fields from a Google Sheet and inserts them into an email template and sends the emails off. That works fine.
I recently wanted to include a PDF as an attachment to the emails. It would be the same PDF for every email. I uploaded the PDF into Google Drive. When I run the script, the first email is sent off fine with the attachment but the following emails are not sent because I encounter this error: "Cannot retrieve the next object: iterator has reached the end"
Pretty sure it has to deal with the attachment/file and me not handling the iteration correctly. Can someone help? Below is the code:
function send2Email()
{
var filename= 'even_overview2020.pdf';
var file = DriveApp.getFilesByName(filename);
var spread =SpreadsheetApp.getActiveSpreadsheet();
var contactSheet =spread.getSheetByName(contactSheetName);
var bodySheet =spread.getSheetByName(templateSheetName);
var contactData =contactSheet.getDataRange().getValues();
var bodyData =bodySheet.getDataRange().getValues();
var fname,company,sign,template,email,subject,body,sender,file;
for (var i =1;i<contactData.length;i++)
{
contactData[i][statusCol-1]="";
}
contactSheet.getDataRange().setValues(contactData);
for (var i =1;i<contactData.length;i++)
{
fname=trim_(contactData[i][fnameCol-1]);
company=trim_(contactData[i][companyCol-1]);
sign=trim_(contactData[i][signCol-1]);
template=trim_(contactData[i][templateCol-1]);
email=trim_(contactData[i][emailCol-1]);
sender=trim_(contactData[i][senderCol-1]);
Logger.log(email);
for(var j=1;j<bodyData.length;j++)
{
if(trim_(bodyData[j][tempRefCol-1]).toUpperCase()==String(template).toUpperCase())
{
body=bodyData[j][bodyCol-1];
subject=bodyData[j][subjectCol-1];
}
}
Logger.log(j+","+email+','+body+','+subject);
body=body.replace(/\n/g,"<br>");
body=body.replace("(w)",sign).replace("(x)",fname).replace("(y)",company).replace("(s)",sender.split(" ")[0]);
Logger.log(email+','+body+','+subject);
MailApp.sendEmail({to:email,subject:subject,name:sender,htmlBody:body,attachments: [file.next().getAs(MimeType.PDF)]});
contactSheet.getRange(i+1, statusCol).setValue('Y');
}
}
How about this modification?
Modification point:
In your script, attachments: [file.next().getAs(MimeType.PDF)]} is used in the loop. By this, the 1st loop works by file.next(). But after 2nd loop, an error occurs at file.next() because the file of filename is one file in Google Drive. I think that this is the reason of your issue.
In order to avoid this issue, how about the following modification?
Modified script:
From:
var file = DriveApp.getFilesByName(filename);
To:
var files = DriveApp.getFilesByName(filename);
var file;
if (files.hasNext()) {
file = files.next().getAs(MimeType.PDF);
} else {
throw new Error("No file");
}
And also, please modify as follows.
From:
MailApp.sendEmail({to:email,subject:subject,name:sender,htmlBody:body,attachments: [file.next().getAs(MimeType.PDF)]});
To:
MailApp.sendEmail({to:email,subject:subject,name:sender,htmlBody:body,attachments: [file]});
Reference:
Class FileIterator

Trying to create tree-view of google drive folders

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.

List Google app script projects

I'm trying to list all my google script files using the Google Drive File API as documented here. However I always get back an empty [] list. I think my token and my scopes are fine as I'm getting back this from Google OAuth2:
{ "access_token": "xxxxx", "expires_in": 3600, "refresh_token": "yyyyy",
"scope": "https://www.googleapis.com/auth/drive.scripts https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive",
"token_type": "Bearer"
}
But then when I issue the query using mimeType filtering (I just want to get google app scripts list):
mimeType = 'application/vnd.google-apps.script'
I'm only getting back an empty items list ([]), even if I've just created a google script inside a Google Sheet:
{ "kind": "drive#fileList", "etag": "....", "selfLink": "https://www.googleapis.com/drive/v2/files?q=mimeType+%3D+'application/vnd.google-apps.script'", "incompleteSearch": false, "items": []}
I'm guessing that app-script means something else than google script code which is part of a Google Sheet... Any help appreciated :)
About retrieving the file list of projects of the container-bound script type and exporting each script from the project, please check the following answer.
Retrieve file list of projects of container-bound script type
Unfortunately, the file list of projects of the container-bound script type cannot be retrieved yet, while the file list of projects of the standalone type can be retrieved using drive.files.list of Drive API.
This has been reported as a future request. https://issuetracker.google.com/issues/111149037
Export scripts in a project
When you want to export each script from a project of the containter-bound script type and the standalone type, you can do it using Google Apps Script API.
This might become a GAS sample script for your situation. here.
Also you can do it using a GAS library like this.
A Patchwork Quilt of a Work Around to get a list of Standalone and Bound Project Ids
I posted this here because I ran across this page several times during the last couple of days and in case there is someone else who would really like to get a complete list of their projects and doesn't mind having to jump through a few hoops to get it then this might be useful to them.
I found a way to get all of the my Apps Script Project File Ids. It's not pretty but but I got all 1393 of them bound and standalone. I went into Google Developer Hub and noticed that my project id's could be found in an attribute name data-script-id="project id" and by continuing to page down to the bottom of the list I was able to get the page to keep seeking more pages until there were none left. Then I went into Chrome developers tools and I found the div that contained all the divs with the above attribute and for me this one was named <div class="uBvHbd" and I copied the entire div and tried pasting it into an ascii file on my Google Account but I found that to be a problem so I opened up my copy of UltraEdit and pasted it in there. I played around with the regex for a while and spent the day retraining myself on the UltraEdit Document Model and their version of the Javascript Engine and developed the following routines which allowed me to build a complete list of projectids.
UltraEdits Scripting Language:
function getDocumentPaths() {
UltraEdit.outputWindow.write('File Paths:');
for(var i=0;i<UltraEdit.document.length;i++) {
UltraEdit.outputWindow.write('[' + i + ']: ' + UltraEdit.document[i].path);
}
}
function getFileNames() {
var nA=[];
var fnToIdx={fnA:[]};
for(var i=0;i<UltraEdit.document.length;i++) {
var p=UltraEdit.document[i].path;
var pA=p.split(/\\/);
var fn=pA[pA.length-1].split('.')[0];
fnToIdx.fnA.push(fn);
fnToIdx[fn]=i;
}
UltraEdit.outputWindow.write('FileNames: \r\n' + fnToIdx.fnA.join('\r\n'));
return fnToIdx;
}
function getScriptIdsIndex() {
for(var i=0;i<UltraEdit.document.length;i++) {
if(UltraEdit.document[i].isName('ScriptIds')) {
return i;
}
}
}
function getFileIndexByName(name) {
var name=name||'ScriptIds';
if(name) {
for(var i=0;i<UltraEdit.document.length;i++) {
if(UltraEdit.document[i].isName(name)) {
return i;
}
}
}else{
UltraEdit.messageBox("Invalid or Missing Inputs at getFileIndexByName().","Alert")
}
}
function getAllFileIndices(obj) {
UltraEdit.outputWindow.write('File Indices:');
var fnIndicesA=[]
for(var j=0;j<obj.fnA.length;j++) {
fnIndicesA.push({name:obj.fnA[j] , index:obj[obj.fnA[j]]})
}
var_dump(fnIndicesA);
}
function updateWorkingTabs(index) {
UltraEdit.outputWindow.write('Index: ' + index);
UltraEdit.document[index].selectAll();
UltraEdit.document[index].copy();
var workingTabsA=[];
for(var i=0;i<UltraEdit.document.length;i++) {
if(UltraEdit.document[i].path.slice(0,-1)=='Edit') {
UltraEdit.document[i].paste();
workingTabsA.push(i);
}
}
return workingTabsA;
}
function findAllIds() {
var fnToIndex=getFileNames();
UltraEdit.document[fnToIndex['ScriptIds']].selectAll();
UltraEdit.document[fnToIndex['ScriptIds']].copy();
var s=UltraEdit.clipboardContent;
var re=/data-script-id="[^"]+"/g;
var matchA=s.match(re);
UltraEdit.document[fnToIndex['FileIds']].selectAll();
UltraEdit.document[fnToIndex['FileIds']].cut();
for(var i=0;i<matchA.length;i++) {
UltraEdit.document[fnToIndex['FileIds']].write(matchA[i].slice(16,-1) + '\r\n');
}
}
function removeDuplicates() {
var fnToIndex=getFileNames();
UltraEdit.document[fnToIndex['FileIds']].selectAll();
UltraEdit.document[fnToIndex['FileIds']].copy();
var fnA=UltraEdit.clipboardContent.split('\r\n');
if(!fnA[fnA.length-1]) {
fnA.pop();
}
var uA=[];
for(var i=0;i<fnA.length;i++) {
if(uA.indexOf(fnA[i])==-1) {
uA.push(fnA[i]);
}
}
var s='';
for(var i=0;i<uA.length;i++) {
if(i>0){
s+='\r\n';
}
s+=uA[i];
}
UltraEdit.document[fnToIndex['FileIds']].selectAll();
UltraEdit.document[fnToIndex['FileIds']].cut();
UltraEdit.document[fnToIndex['FileIds']].write(s);
}
UltraEdit.outputWindow.clear();
//UltraEdit.open('E:\\Projects\\ScriptIds\\ScriptIds.txt');
//var ScriptIds_idx=getScriptIdsIndex();
//var wtA=updateWorkingTabs(ScriptIds_idx);
//findAllIds()
removeDuplicates();
The input file was 24 MB and the output list was 81KB and it took the script running on my laptop less then 5 seconds to strip out the id's and remove duplicates (didn't find any either).
I ran these two routines in Google Apps Script to get script info from Google Apps Script API so that I could figure out which were Standalone and which were container bound. And for the container bound I was able to get the Container Filename and id.
function getInfo(projectId) {
var projectId=projectId||pid;
var rObj={'ProjectId':projectId};
var params = {muteHttpExceptions:true,headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()}};
try {
var url=Utilities.formatString('https://script.googleapis.com/v1/projects/%s',projectId);
}
catch(e) {
return rObj['Error']=e;
}
var resp=UrlFetchApp.fetch(url,params);
var data=JSON.parse(resp.getContentText());
if(data.hasOwnProperty('parentId')) {
var pFile=DriveApp.getFileById(data.parentId)
var parentName=pFile.getName();
var pfldrA=[];
var pFolders=pFile.getParents();
while(pFolders.hasNext()) {
var folder=pFolders.next();
pfldrA.push({id:folder.getId(),name:folder.getName()});
}
rObj['ProjectName']=data.title;
rObj['ParentName']=parentName;
rObj['ParentId']=data.parentId;
rObj['ParentFolders']='';
rObj['ParentFolderIds']='';
//var html=Utilities.formatString('<br /><b>Project Name:</b>%s<br /><b>ParentName:</b> %s<br /><b>ParentId:</b> %s',data.title,parentName,data.parentId);
//html+=Utilities.formatString('<br /><b>Parent Folders:</b>');
for(var i=0;i<pfldrA.length;i++) {
if(i>0) {
rObj.ParentFolders+=', ';
rObj.ParentFolderIds+=', ';
}
//html+=Utilities.formatString('<br /><b>Name:</b> %s <b>Id:</b> %s',pfldrA[i].name,pfldrA[i].id);
rObj.ParentFolders+=pfldrA[i].name;
rObj.ParentFolderIds+=pfldrA[i].id;
}
}else{
rObj['ProjectName']=data.title;
//var html=Utilities.formatString('<br /><b>StandAlone Project</b><br /><b>Project Name:</b>%s<br /><b>ProjectId:</b>%s',data.title,projectId);
}
//Logger.log(data);
//var userInterface=HtmlService.createHtmlOutput(html);
//SpreadsheetApp.getUi().showModelessDialog(userInterface, "Project Info");
return rObj;
}
function updateProjects() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Projects');
var idx={hA:[],hr:1}
idx.hA=sh.getRange(1,1,1,sh.getLastColumn()).getValues()[0];
idx.hA.forEach(function(e,i){idx[e]=i;});
var lr=sh.getLastRow();
var sr=getColumnHeight(1,sh,ss) + 1;
var cnt=25;
setGlobal('count',cnt);
if(lr-sr+1<cnt) {
cnt=lr-sr+1;
}
if(sr>=lr) {
setGlobal('count',0);
return;
}
//Item ProjectId ProjectName ParentId ParentName ParentFolders ParentFolderIds
var rg=sh.getRange(sr,1,cnt,sh.getLastColumn());
var vA=rg.getValues();
for(var i=0;i<vA.length;i++) {
if(!vA[i][idx.Item] && vA[i][idx.ProjectId]) {
var pObj=getInfo(vA[i][idx.ProjectId]);
if(!pObj.hasOwnProperty('Error') && pObj.hasOwnProperty('ParentId')) {
vA[i][idx.Item]=i+sr;
vA[i][idx.ProjectName]=pObj.ProjectName;
vA[i][idx.ParentId]=pObj.ParentId;
vA[i][idx.ParentName]=pObj.ParentName;
vA[i][idx.ParentFolders]=pObj.ParentFolders;
vA[i][idx.ParentFolderIds]=pObj.ParentFolderIds;
}else if(!pObj.hasOwnProperty('Error') && !pObj.hasOwnProperty('ParentId')){
vA[i][idx.Item]=i+sr;
vA[i][idx.ProjectName]=pObj.ProjectName;
vA[i][idx.ParentName]='Standalone Project';
}else{
vA[i][idxItem]=i+sr;
vA[i][idx.ProjectName]=(pObj.hasOwnProperty('Error'))?pObj.Error:"Unknown Problem";
}
Utilities.sleep(1000);
}
}
rg.setValues(vA);
}
I'll come back later and put some comments in the code. It's not pretty but I now have a list of all my projects and where to find them and the names of their containers if they have one. I have to thank #Tanaike for the Apps Script library. It really helped me to figure out how access the Apps Script API which turned out to be a lot less difficult than I thought it would be.
A Glimpse at a portion of the final list: