Finding a path to a File given it's ID - google-drive-api

I get a FileList with:
String q = "title contains '"+query+"' and trashed = false";
FileList list = drive.files().list().setQ(q).execute();
I want to find a path to each matching file in the FileList.
I understand that the same File can appear in multiple Folders.
From what I've found, the only way to create a path for a File is to repeatedly call:
drive.files().get(id).execute()
and then choose an ID from that File's parents list, walking up the tree until the root is reached.
While navigating the tree, I could just choose the first parent, or do a "BFS" until I find the root.
Is this really the only way to find a path to a File, or have I missed some part of the API?
If this is the only way, can Folders have multiple parents, too, making cycles possible?
(/a/b/c => /a/b/c/b/c/b/c... if b has parents a & c)?

Since folders (which are simply special Drive File with a specific MIME type) can have multiple parents, I would recommend the BFS approach and making sure you handle loops.
For the stop condition, you can save the root folder's ID that you can retrieve from the about.rootFolderId attribute.

Related

GoogleDrive API - how to get folder hierarchy from file in python

I using the following code to get all the files in my drive
files_list = service_v3.files().list(corpora='drive',
supportsAllDrives=True,
includeItemsFromAllDrives=True,
driveId=drive['id'],
spaces='drive',
fields='*').execute()
Now in each file I have a parent:
files_list[0]['parents']
How can I get data about this parent?
I've tried to use
service_v2.parents().list(fileId='xxx')
but it does not return much data.
You need to implement three steps:
List all files of interest - as you are already doing with files_list = service_v3.files().list
Loop through the list results or pick a certain file (as you are doing with files_list[0]) and retrieve its parents (as you are doing), e.g. parent = files_list[0].get('parents')
Use the method service_v3.files().get(fileId=parents[0], fields="*").execute() to retrieve the information about the parent by its Id
I recommend you to use the Try It API of the list and get methods in order to get a better understanding of the results those methods give you.
Also, please note that service_v2.parents().list(fileId='xxx') is using the old version of the Drive API (v2), and in any case - if you want to retrieve a file with a specified ID, the correct method to do it is getopposed to list.

Google Drive Windows App to/from fileId - items with same names, and multiple parents

I'm trying to translate from a Google Drive link on the web (well, the fileId anyway) to the Windows Google Drive app's path on the hard disk, and back again.
It would be helpful if there was something in the API for this (eg produce a path excluding the C:\Users\[User]\Google Drive\ from a file/folder ID, and vice versa), but there isn't.
So far I do:
Windows Path to ID: get the first folder of the path and (starting from the root) look for a matching folder, then repeat until finished (possibly with a file name). PROBLEM: Items can be called the same thing, whether files or folders or combinations of both, which is tricky in Windows. The app adds a number ' (1)' and so on, which I have to catch, but how can I know which item ID is the correct one? I believe that numbering is based on date but I'm not sure. So I can potentially end up with multiple results and no way to tell which is which.
ID to Windows Path: take the name of the file/folder from the ID, then keep adding the parent folder(s) until I build up a path. PROBLEM: same as 1 above, if there are multiple matching items then I can't tell which I should use when translating to Windows. PROBLEM: Apparently items in Google Drive can have more than one parent. Not sure how that works in the Windows app.
Can anyone help me fine tune how I do this, or tell me the exact details of how the Google Drive app does it? Code is welcome but not required, and I in turn can provide the code I use if needed.
I'm not sure if I fully understand the question, but I try to smack an answer anyway:
1/ assuming you have a Windows path,
C:\Users\User\Google Drive\myfile.ext
you create a file with a similar path on GooDrive iterating your path's tokens
recursively creating a tree structure on GooDrive. If the tree nodes (folders/files) exist, return ID's, otherwise create the objects. The main difference in GooDrive is that title query may return multiple objects (list of folders/files). Bad luck, you either use the first one or quit with an error.
global path = "C:\Users\User\Google Drive\myfile.ext"
createTree(String path) {
rootFolderId = create your root or use GooDrive root
fileId = iterate (firstToken(path, "\"), rootFolderId);
}
iterate(title, parentFolderId) {
ID (or multiple IDs) = search for title in parentFolderId
if (multiple IDs exist)
BOOM - report error and quit or use the first one
if (token not last) {
if (single ID for title exists) {
folderId = found ID
} else {
folderId = createFolder with title and parentFolderId metadata
}
iterate(nextToken(path, "\"), folderId)
} else { (last token represent file)
if (single ID for title exists) {
fileId = found ID
} else {
fileId = createFile with title and parentFolderId metadata
}
return fileId
}
}
You did not specify the language, but in case it is Java, you can see similar procedure here in the createTree() method (it is Android code, so there is a lot of Android specific goo there, sorry)
2/ assuming you have a Google Drive fileId, you construct the Windows path with this pseudocode (going from bottom up to the root). Again, you may have multiple parents you have to deal with (error or multiple paths with links to a single object)
String path = fileId's title
while () {
parentID = get fileId's parent
if (multiple parentIDs exist)
BOOM - report error and quit or construct multiple paths
(multiple paths would represent file/folder links)
if (parentID not valid or parentId's title not valid)
break
path = parentID's title + "\" + path
if (parentID's title is your root)
break
}
One more thing: You say "Folders and files can be called the same thing..."
In GooDrive, look at the MIME type, there is a specific MIME type "application/vnd.google-apps.folder" that tells you it is a folder. Also, any parentId metadata represents folder, since files can't be parents.
Good Luck

Using the Android Api, how to retrieve entire file path?

Given a com.box.androidlib.Utils.BoxUtils.BoxFolder object, I would like to recurse the object’s parent folders to retrieve the path from the root.
I would hope to do this with something like the below code, where currentBoxFolder is retrieved using Box.getAccountTree(…) as done in the Browse class of the included sample code. However, the getParentFolder returns null (for non-root folders for which I expect it to be non-null).
I figure that it might be possible to populate the parent variable by modfiying the source to fetch additional attributes, but I was able to. Any suggestions?
List<BoxFolder> parentDirs = new ArrayList<BoxFolder>();
parentDirs.add(new BoxFolderEntry(currentBoxFolder));
BoxFolder parent = currentBoxFolder.getParentFolder();
while(parent != null)
{
parentDirs.add(0, parent);
parent = parent.getParentFolder();
}
If the end goal is for you to know the path from the root to a folder, there are a couple ways to solve this:
OPTION 1:
Maintain your own map of folder_ids and folder_names as your application fetches them. Presumably, in order to get the id of currentBoxFolder, you would have had to do getAccountTree() calls on all its parents before-hand. So if that's the case, you could maintain 2 maps:
Folder ID => Parent Folder ID
Folder ID => Folder Name
From those two maps, you should always be able to get the path from the root.
OPTION 2:
There are 2 params that can be added to the Box.getAccountTree() method that will allow you to know the path:
"show_path_ids"
"show_path_names"
These params haven't been documented yet (we'll do that), but they will cause BoxFolder.getFolderPathIds() and BoxFolder.getFolderPath() to return values such as:
"/5435/4363"
"/blue folder/green folder"

How can I access the information associated to an object from a Mercurial plugin?

I am trying to write a small Mercurial extension, which, given the path to an object stored within the repository, it will tell you the revision it's at. So far, I'm working on the code from the WritingExtensions article, and I have something like this:
cmdtable = {
# cmd name function call
"whichrev": (whichrev,[],"hg whichrev FILE")
}
and the whichrev function has almost no code:
def whichrev(ui, repo, node, **opts):
# node will be the file chosen at the command line
pass
So , for example:
hg whichrev text_file.txt
Will call the whichrev function with node being set to text_file.txt. With the use of the debugger, I found that I can access a filelog object, by using this:
repo.file("text_file.txt")
But I don't know what I should access in order to get to the sha1 of the file.I have a feeling I may not be working with the right function.
Given a path to a tracked file ( the file may or may not appear as modified under hg status ), how can I get it's sha1 from my extension?
A filelog object is pretty low level, you probably want a filectx:
A filecontext object makes access to data related to a particular filerevision convenient.
You can get one through a changectx:
ctx = repo['.']
fooctx = ctx['foo']
print fooctx.filenode()
Or directly through the repo:
fooctx = repo.filectx('foo', '.')
Pass None instead of . to get the working copy ones.

Construct an Iterator

Let's say you want to construct an Iterator that spits out File objects. What type of data do you usually provide to the constructor of such an Iterator?
an array of pre-constructed File objects, or
simply raw data (multidimensional array for instance), and let the Iterator create File objects on the fly when Iterated through?
Edit:
Although my question was actually ment to be as general a possible, it seems my example is a bit to broad to tackle general, so I'll elaborate a bit more. The File objects I'm talking about are actually file references from a database. See these two tables:
folder
| id | folderId | name |
------------------------------------
| 1 | null | downloads |
file
| id | folderId | name |
------------------------------------
| 1 | 1 | instructions.pdf |
They reference actual folders and files on a filesystem.
Now, I created a FileManager object. This will be able to return a listing of folders and files. For instance:
FileManager::listFiles( Folder $folder );
... would return an Iterator of File objects (or, come to think of it, rather FileReference objects) from the database.
So what my question boils down to is:
If the FileManager object constructs the Iterator in listFiles() would you do something like this (pseudo code):
listFiles( Folder $folder )
{
// let's assume the following returns an multidimensional array of rows
$filesData = $db->fetch( $sqlForFetchingFilesFromFolder );
// let the Iterator take care of constructing the FileReference objects with each iteration
return FileIterator( $filesData );
}
or (pseudo code):
listFiles( Folder $folder )
{
// let's assume the following returns an multidimensional array of rows
$filesData = $db->fetch( $sqlForFetchingFilesFromFolder );
$files = array();
for each( $filesData as $fileData )
{
$files.push ( new FileReference( $fileData ) );
}
// provide the Iterator with precomposed FileReference objects
return FileIterator( $files );
}
Hope this clarifies things a bit.
What is your "File" object meant to be? An open handle to a file, or a representation of a file system path which can be opened in turn?
It would generally be a bad idea to open all the files at once - after all, part of the point of using an iterator is that you only access one object at a time. Your iterator could yield one open file at a time, and let the caller take responsibility for closing it, although again that might be slightly odd to use.
Your requirements aren't clear, to be honest - in my experience, most iterators which yield a series of files use something like Directory.GetFiles(pattern) - you don't pass them the raw data at all, you pass them something which they can use to find the data for you.
It's not obvious what you're trying to get at - it feels like you're trying to ask a general question, but you haven't provided enough information to let us advise you. It's like asking, "Do I want to use a string or an integer?" without giving any context.
EDIT: I would probably push all of that logic into FileIterator, personally. Otherwise it's hard to see what value it's really providing. In a language like C# or Python you wouldn't need a separate class in the first place - you'd just use a generator of some description. In that sense this question isn't language agnostic :(
What exactly is your iterator supposed to do? Write data to files? Create them?
An iterator is a pattern for iterating through data, which means providing sequential data in a uniformous way, not mutating them.
I find the question to be unclear.
Are we talking Iterator or Factory?
To me an Iterator is operating on a pre-existing collection of things and allows the caller to work on each thing in turn.
When you say "Spits Out" do you mean allows the client to work with one file from a pre-existing set of files or do you mean that you are iterating some data and intend to store that data in files you are generting. If we are geneating, then we've got a File factory.
My guess is that you are intending to process some files in a file sytstem. I think that your Iterator is akin to a Directory, it can give you the next file it knows about. So I construct the "Driectory" by passing enough data to allow it to know which files you mean (could be just an OS path, could be some kind of "find" enxpression, a list of ftp-like references, etc.) and expect it to give me the next File as I iterate.
----updated following question clarification
I think that the key question here is when the individual files should be opened. The Iterator itself will reasonably return a File object corresponding to an open file handle, the caller can then just work with the file. But iternally should the iterator be working against a list of pre-opened files or a list of file references, the files being opened as the iterator next() is used.
I think we should do the latter, because there is overhead in having an open file, hence we should open the files only when we need them.
That leads to one other point: who closes the file? We can't afford to keep them all open. Perhaps the iterator should close each file as next() is called. This implies that that the iterator itself needs a close() method to allow tidy up of the currently open file. Alterntaivelywe need to explictily document that closing is the client's responsibility.