Get folder hierarchy with Google Drive API [C# / .NET] - c#

I am looking for an elegant way to get the folder hierarchy, beginning with my root folder, using the C# Google Drive API V3.
Currently, you can get the root folder and its parents by
var getRequest = driveService.Files.Get("root");
getRequest.Fields = "parents";
var file = getRequest.Execute();
but I am looking for a way to get the children, not the parents, so I can recursively go down the file structure.
Setting getRequest.Fields = 'children' is not a valid field option.

recursively fetching children is a very time consuming way to fetch the full hierarchy. Much better is to run a query to fetch all folders in a single GET (well it might take more than one if you have more than 1,000 folders) and then traverse their parent properties to build up the hierarchy in memory. Bear in mind that (afaik) there is nothing that prevents a folder hierarchy being cyclic, thus folder1 owns folder2 owns folder3 owns folder1, so whichever strategy you follow, check that you aren't in a loop.
If you're new to GDrive, it's important to realise early on that Folders are simply labels, rather than containers. So cyclic relationships and files with multiple parents is quite normal. They were originally called Collections, but got renamed to Folders to appease those members of the community that couldn't get their head around labels.

I hope this is the answer you were looking for. getHeirarchy Recursively digs Google Drive and stores the file titles into a text file.
public System.IO.StreamWriter w = new System.IO.StreamWriter("Hierarchy.txt", false);
string intend = " ";
private void getHierarchy(Google.Apis.Drive.v2.Data.File Res, DriveService driveService)
{
if (Res.MimeType == "application/vnd.google-apps.folder")
{
w.Write(intend + Res.Title + " :" + Environment.NewLine);
intend += " ";
foreach (var res in ResFromFolder(driveService, Res.Id).ToList())
getHierarchy(res, driveService);
intend = intend.Remove(intend.Length - 5);
}
else
{
w.Write(intend + Res.Title + Environment.NewLine);
}
}
You can call the function something like:
w.Write("My Drive:" + Environment.NewLine);
foreach (var Res in ResFromFolder(driveService, "root").ToList())
getHierarchy(Res, driveService);
w.Close();
Here, root can be replaced with the ID of any Directory to get it's structure. This will generate the entire Drive's structure.
The ResFromFolder method returns a list of Google.Apis.Drive.v2.Data.File metadata contained in a directory.
public List<Google.Apis.Drive.v2.Data.File> ResFromFolder(DriveService service, string folderId)
{
var request = service.Children.List(folderId);
request.MaxResults = 1000;
List<Google.Apis.Drive.v2.Data.File> TList = new List<Google.Apis.Drive.v2.Data.File>();
do
{
var children = request.Execute();
foreach (ChildReference child in children.Items)
{
TList.Add(service.Files.Get(child.Id).Execute());
}
request.PageToken = children.NextPageToken;
} while (!String.IsNullOrEmpty(request.PageToken));
return TList;
}
This code produces output something like
However as pinoyyid mentioned, it does consume a good deal of time if Drive contains a large number of files and folders.

Get folder hierarchy with Google Drive API [C# / .NET]
Google.Apis.Drive.v3.DriveService service = GetService();
List<GoogleDriveFile> folderList = new List<GoogleDriveFile>();
Google.Apis.Drive.v3.FilesResource.ListRequest request = service.Files.List();
//https://developers.google.com/drenter code hereive/api/v3/search-shareddrives
request.Q = string.Format("mimeType='application/vnd.google-apps.folder' and '{0}' in parents", folderId)`enter code here`;
request.Fields = "files(id, name)";
Google.Apis.Drive.v3.Data.FileList result = request.Execute();
foreach (var file in result.Files)
{
GoogleDriveFile googleDriveFile = new GoogleDriveFile
{
Id = file.Id,
Name = file.Name,
Size = file.Size,
Version = file.Version,
CreatedTime = file.CreatedTime,
Parents = file.Parents
};
folderList.Add(googleDriveFile);
}
return folderList;

Related

Unable to view child elements from OneDrive C# SDK

I am coding against the OneDrive C# SDK and I was shared a folder which contains multiple files. When accessing the shared folder from the onedrive.com, I am able to view the files -- however when trying to check the Item the children count is always at zero. I am assuming this may be some mix up on my end or permissions issue -- but I just wanted to run it past for a sanity check.
Code:
private async Task GetItem(string id = null)
{
List<string> idsToSearch = new List<string>();
var expandValue = this.clientType == ClientType.Consumer
? "thumbnails,children(expand=thumbnails)"
: "thumbnails,children";
try
{
Item folder;
if (id == null)
{
folder = await this.oneDriveClient.Drive.Root.Request()
.Expand(expandValue).GetAsync(); //root
}
else
{
folder = await this.oneDriveClient.Drive.Items[id].Request()
.Expand(expandValue).GetAsync(); //children of root
}
WriteToFile(new List<string>(new[] { #"Folder: " + folder.Name }));
if (folder.Children.Count == 0)
{
WriteToFile(new List<string>(new[] { #"NO Children" }));
}
else
{
foreach (var child in folder.Children)
{
WriteToFile(new List<string>(new[] {
#"Children of " + folder.Name + " : " + child.Name }));
}
foreach (var item in folder.Children)
{
GetItem(item.Id);
idsToSearch.Add(item.Id);
}
}
}
catch (Exception exception)
{
PresentServiceException(exception);
}
}
I also included a snapshot of the Item object when it reaches the Shared folder object:
Update
After looking through the folder object some more I found that there is RemoteItem which is returning the correct number of child counts -- however does not have any meta data to fetch the child elements.
From the comments on the question it was determined that this is a RemoteItem scenario. Remote items are different to local items - while there's some local metadata that's useful for rendering, the actual metadata for the item lives in another user's drive. Therefore, when such an item is encountered it may be necessary (e.g. if you need to enumerate children of a remote folder) for a subsequent request needs to be made directly for the item in question (using the driveId from the remoteItem.parentReference and the id from remoteItem.Id).
Have a look at this documentation for some more information.

Deleted tasks are being retrieved using the query .net Rest API

I just deleted a task using the Rally website, but when a search for task using the REST API it doesn't return it. I assumed that it should return with the flag "Recycled".
Can anybody help me?
Regards,
Paulo
This is an inconsistency in the WSAPI. Unfortunately all queries are implicitly scoped (Recycled = false) so nothing that has been deleted will ever be returned from the artifact endpoints. There is also no way to access the contents of the recycle bin through the WSAPI.
I would encourage you to vote for the idea for this functionality at https://ideas.rallydev.com/ideas/D2374.
Although it's not ideal, you can get to the Recycle Bin through this REST endpoint:
https://rally1.rallydev.com/slm/webservice/1.40/recyclebin.js?workspace=/workspace/12345678910&project=/project/12345678911
Where the long integers the Workspace and Project OID's of interest.
Recycle bin entries look like the following:
{
_rallyAPIMajor: "1",
_rallyAPIMinor: "40",
_ref: "https://rally1.rallydev.com/slm/webservice/1.40/recyclebinentry/12345678910.js",
_refObjectName: "Test Case 3: Load in, run Analysis on Integer Grids",
_type: "RecycleBinEntry"
}
Where the Recycle Bin OID is unique and different from the OID of the Artifact that was deleted, so there's not a good way to map the Recycle Bin Entry to the Artifact that was deleted to create it. The Object Name could work, although you run the risk of duplicates. The Recycle Bin Entries also come with the same limitations as does the Recycle Bin in the UI - child objects are not shown/accessible.
If you want to walk the Recycle Bin from .NET, here's a quick example:
namespace RestExample_QueryRecycleBin {
class Program
{
static void Main(string[] args)
{
//Initialize the REST API
RallyRestApi restApi;
String userName = "user#company.com";
String userPassword = "topsecret";
// Set Rally parameters
String rallyURL = "https://rally1.rallydev.com";
String rallyWSAPIVersion = "1.40";
//Initialize the REST API
restApi = new RallyRestApi(userName,
userPassword,
rallyURL,
rallyWSAPIVersion);
// Specify workspace and project
string myWorkspace = "/workspace/12345678910";
string myProject = "/project/12345678911";
//Query for items
Request request = new Request("recyclebinentry");
request.Workspace = myWorkspace;
request.Project = myProject;
QueryResult queryResult = restApi.Query(request);
foreach (var result in queryResult.Results)
{
//Process item
string itemName = result["_refObjectName"];
string itemRef = result["_ref"];
Console.WriteLine(itemRef + ", " + itemName);
}
Console.ReadKey();
}
}
}

Programmatically get pending changes for a specific folder in a specific project?

I have been playing around with TFS for a while because I need to upload/check in specific files and folders into various locations on the TFServer after they have been created and placed locally. I am mapping the workspace and everything and I get a bunch of changes from PendingChanges but not the ones I want and not where I want. The problem is really annoying because for every check in process I only want to work on a specific folder or file in a specific location that is already mapped. I am gonna paste part of the code here for reference.
using (TfsTeamProjectCollection collection = new TfsTeamProjectCollection(serverUri, _cred))
{
VersionControlServer versionControl = (VersionControlServer)collection.GetService(typeof(VersionControlServer));
string machineName = Environment.MachineName;
string currentUserName = Environment.UserName;
Workspace myWorkspace = versionControl.GetWorkspace(machineName, currentUserName);
// tried this from Stack but didn't work.
//PendingChange[] changes = myWorkspace.GetPendingChanges().Where(x => x.LocalOrServerFolder.Contains(localPath)).ToArray();
PendingChange[] changes = myWorkspace.GetPendingChanges();
if (changes.Length != 0)
{
foreach (PendingChange c in changes)
{
rt.Text += " path: " + c.LocalItem + ", change: " + PendingChange.GetLocalizedStringForChangeType(c.ChangeType) + "\n";
}
}
else
{
rt.Text += "This didn't work.";
}
}
Basically I want to throw a folder or file at my TFS class and let it check if the file or folder is up to date or present on the TFS and act accordingly. I hope I have described the problem thoroughly.
I think that my answer will not response fully you question, but if you want to get pending changes for a specific folder, you can use something like this..
PendingChange[] changes = myWorkspace.GetPendingChanges(localPath, RecursionType.Full, false);

How do you Load an MP3 file without actually using the Content class?

I am a beginner at Programming with C Sharp, and I am using the XNA SDK. I am trying to make a simple game to help my fellow classmates with studying for school, and I decided I would like it if there was some way to have them put music they want to listen to while playing the game inside of a file, and have the game automatically Load the music files, and play them in a playlist.
So far, I am able to get the game to detect whether the files are music, by detecting whether the file path name Contains(".mp3") , but I am trying to actually load the file name into a list of type Song, using a for Loop. The code looks like this.
(Declaration)
List<Song> songsToPlay;
string[] fileNames
(Initialize)
fileNames[] = Directory.GetFiles(".\Music")
(LoadContent)
for (int i = 0; i < fileNames.Count(); i++)
{
if (fileNames[i].Contains(".mp3")
{
songsToPlay.Add(fileNames[i]);
}
}
I have been trying to find a way to add a whole folder to the Content Directory, and have it do something more like
for (int i = 0; i < fileNames.Count(); i++)
{
songsToPlay.Add(Content.Load<Song>("fileNames[i]")
}
I have been unable to find some way to do this... Does anyone know how to make this work, or a better way to do this?
If you have your files in your project content, you should use the ContentManager class. It gives you more than just file loading. For example you can use Content.Unload to unload all your data when you're no longer using it.
There is no need to avoid that class. This page has an example showing exactly what you're trying to do:
public static Dictionary<string, T> LoadContent<T>(
this ContentManager contentManager, string contentFolder)
{
var dir = new DirectoryInfo(contentManager.RootDirectory
+ "\\" + contentFolder);
if (!dir.Exists)
throw new DirectoryNotFoundException();
var result = new Dictionary<string, T>();
foreach (FileInfo file in dir.GetFiles("*.mp3"))
{
string key = Path.GetFileNameWithoutExtension(file.Name);
result[key] = contentManager.Load<T>(
contentManager.RootDirectory + "/" + contentFolder + "/" + key);
}
return result;
}
You can use it like this:
var songs = Content.LoadContent<Song>("Songs");
Slight improvement to this code...
Once you get the above code working, I also suggest you make a slight change:
var dir = new DirectoryInfo(
System.IO.Path.Combine(contentManager.RootDirectory, contentFolder));
You shouldn't manually build paths via string concatenation when you can possibly avoid it. I don't know that you can do the same for ContentManager paths tho, so you might have to stick with string concatenation for that case.
Edit: Too many constructs you haven't used in class yet
Since you haven't used extension methods or the static keyword in your class yet, and probably haven't used dictionaries, here's a simpler way to do this:
string contentFolder = "Music";
var dir = new DirectoryInfo(Content.RootDirectory + "\\" + contentFolder);
if (!dir.Exists)
{
// Todo: Display a message to the user instead?
throw new DirectoryNotFoundException();
}
string[] files = dir.GetFiles("*.mp3");
for (int i = 0; i < files.Count(); ++i)
{
FileInfo file = files[i];
string key = System.IO.Path.GetFileNameWithoutExtension(file.Name);
var song = Content.Load<Song>(
Content.RootDirectory + "/" + contentFolder + "/" + key);
songsToPlay.Add(song);
}
Edit2: Some explanation of this second code sample
The DirectoryInfo class lets you load up a directory so you can enumerate all the files in it.
The GetFiles method on DirectoryInfo lets you enumerate files using a wildcard style pattern matching. Wildcard pattern matching for files means that when given these patterns:
*.* - you are looking for files named <anything>.<anything>
*.mp3 - you are looking for <anything>.mp3
throw means throwing an exception. This will deliberately stop executing code and display a good error message ("directory not found") and a line number. There is a lot to learn about exception handling, so I won't try to do it justice with a description here.
GetFileNameWithoutExtension should be obvious because it is well named.
Content.RootDirectory + "/" + contentFolder + "/" + key
That last little bit of code will build up a string containing the content root directory, the sub-directory of your songs, and each file name, using a name it can understand (since it doesn't know about filename extensions).
var means "whatever type I assign to it". It is a short-cut. For example, instead of typing:
List<string> someList = new List<string>();
You type:
var someList = new List<string>();
var has to know what type is on the right-hand-side of the assignment. It is useful because you can avoid repeating yourself.
Using var doesn't bestow any magical abilities to the variable though. You won't be able to assign a variable of a different type once you've declared the variable. It is just a short-cut for the exact same functionality.
Use the Song.FromUri method:
Song.FromUri("audio name", new Uri(#"C:\audio.mp3"));
The filepath can't contain spaces!
See here for a workaround: XNA 4 Song.fromUri containing Spaces

Retrieve Windows Experience Rating

I'm looking to retrieve a machine's windows experience rating in C#. If possible I would also like to retrieve the numbers for each component (Graphics, RAM etc.)
Is this possible?
Every time the user goes through control panel to calculate the Windows Experience Rating, the system creates a new file in %Windows%\Performance\WinSAT\DataStore\
You need to find the most recent file (they are named with the most significant date first, so finding the latest file is trivial).
These files are xml files and are easy to parse with XmlReader or other xml parser.
The section you are interested in is WinSAT\WinSPR and contains all the scores in a single section. E.g.
<WinSAT>
<WinSPR>
<SystemScore>3.7</SystemScore>
<MemoryScore>5.9</MemoryScore>
<CpuScore>5.2</CpuScore>
<CPUSubAggScore>5.1</CPUSubAggScore>
<VideoEncodeScore>5.3</VideoEncodeScore>
<GraphicsScore>3.9</GraphicsScore>
<GamingScore>3.7</GamingScore>
<DiskScore>5.2</DiskScore>
</WinSPR>
...
Same with LINQ:
var dirName = Environment.ExpandEnvironmentVariables(#"%WinDir%\Performance\WinSAT\DataStore\");
var dirInfo = new DirectoryInfo(dirName);
var file = dirInfo.EnumerateFileSystemInfos("*Formal.Assessment*.xml")
.OrderByDescending(fi => fi.LastWriteTime)
.FirstOrDefault();
if (file == null)
throw new FileNotFoundException("WEI assessment xml not found");
var doc = XDocument.Load(file.FullName);
Console.WriteLine("Processor: " + doc.Descendants("CpuScore").First().Value);
Console.WriteLine("Memory (RAM): " + doc.Descendants("MemoryScore").First().Value);
Console.WriteLine("Graphics: " + doc.Descendants("GraphicsScore").First().Value);
Console.WriteLine("Gaming graphics: " + doc.Descendants("GamingScore").First().Value);
Console.WriteLine("Primary hard disk: " + doc.Descendants("DiskScore").First().Value);
Console.WriteLine("Base score: " + doc.Descendants("SystemScore").First().Value);
Here is a snippet for VB.NET. Converted to C# (using this, I haven't actually tested the code yet, though it looks to be fine).
/// <summary>
/// Gets the base score of a computer running Windows Vista or higher.
/// </summary>
/// <returns>The String Representation of Score, or False.</returns>
/// <remarks></remarks>
public string GetBaseScore()
{
// Check if the computer has a \WinSAT dir.
if (System.IO.Directory.Exists("C:\\Windows\\Performance\\WinSAT\\DataStore"))
{
// Our method to get the most recently updated score.
// Because the program makes a new XML file on every update of the score,
// we need to calculate the most recent, just incase the owner has upgraded.
System.IO.DirectoryInfo Dir = new System.IO.DirectoryInfo("C:\\Windows\\Performance\\WinSAT\\DataStore");
System.IO.FileInfo[] fileDir = null;
System.IO.FileInfo fileMostRecent = default(IO.FileInfo);
System.DateTime LastAccessTime = default(System.DateTime);
string LastAccessPath = string.Empty;
fileDir = Dir.GetFiles;
// Loop through the files in the \WinSAT dir to find the newest one.
foreach (var fileMostRecent in fileDir)
{
if (fileMostRecent.LastAccessTime >= LastAccessTime)
{
LastAccessTime = fileMostRecent.LastAccessTime;
LastAccessPath = fileMostRecent.FullName;
}
}
// Create an XmlTextReader instance.
System.Xml.XmlTextReader xmlReadScore = new System.Xml.XmlTextReader(LastAccessPath);
// Disable whitespace handling so we don't read over them
xmlReadScore.WhitespaceHandling = System.Xml.WhitespaceHandling.None;
// We need to get to the 25th tag, "WinSPR".
for (int i = 0; i <= 26; i++)
{
xmlReadScore.Read();
}
// Create a string variable, so we can clean up without any mishaps.
string SystemScore = xmlReadScore.ReadElementString("SystemScore");
// Clean up.
xmlReadScore.Close();
return SystemScore;
}
// Unsuccessful.
return false;
}
I guess it only returns the overall rating, but hopefully it should get you started at least. It may only be a matter of changing a filename/parameter to get the individual ratings.

Categories

Resources