GetNetworkAsync ( String userNameToAnalyze, WhatToInclude whatToInclude, NetworkLevel networkLevel, Int32 maximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(userNameToAnalyze)); Debug.Assert(networkLevel == NetworkLevel.One || networkLevel == NetworkLevel.OnePointFive || networkLevel == NetworkLevel.Two); Debug.Assert(maximumPeoplePerRequest > 0); AssertValid(); const String MethodName = "GetNetworkAsync"; CheckIsBusy(MethodName); // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs(); oGetNetworkAsyncArgs.UserNameToAnalyze = userNameToAnalyze; oGetNetworkAsyncArgs.WhatToInclude = whatToInclude; oGetNetworkAsyncArgs.NetworkLevel = networkLevel; oGetNetworkAsyncArgs.MaximumPeoplePerRequest = maximumPeoplePerRequest; m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs); }
GetNetworkAsync ( String searchTerm, WhatToInclude whatToInclude, Int32 maximumVideos ) { Debug.Assert(!String.IsNullOrEmpty(searchTerm)); Debug.Assert(maximumVideos > 0); AssertValid(); const String MethodName = "GetNetworkAsync"; CheckIsBusy(MethodName); // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs(); oGetNetworkAsyncArgs.SearchTerm = searchTerm; oGetNetworkAsyncArgs.WhatToInclude = whatToInclude; oGetNetworkAsyncArgs.MaximumVideos = maximumVideos; m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs); }
GetVideoNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumVideos > 0); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetVideoNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumVideos, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return(oGraphMLXmlDocument); }
GetNetworkAsync ( String tag, WhatToInclude whatToInclude, NetworkLevel networkLevel, String apiKey ) { Debug.Assert(!String.IsNullOrEmpty(tag)); Debug.Assert(networkLevel == NetworkLevel.One || networkLevel == NetworkLevel.OnePointFive || networkLevel == NetworkLevel.Two); Debug.Assert(!String.IsNullOrEmpty(apiKey)); AssertValid(); const String MethodName = "GetNetworkAsync"; CheckIsBusy(MethodName); // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs(); oGetNetworkAsyncArgs.Tag = tag; oGetNetworkAsyncArgs.NetworkLevel = networkLevel; oGetNetworkAsyncArgs.WhatToInclude = whatToInclude; oGetNetworkAsyncArgs.ApiKey = apiKey; m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs); }
GetNetworkDescription ( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel ) { Debug.Assert(!String.IsNullOrEmpty(sTag)); AssertValid(); NetworkDescriber oNetworkDescriber = new NetworkDescriber(); oNetworkDescriber.AddSentence( "The graph represents the {0} network of Flickr tags related to" + " the tag \"{1}\"." , NetworkLevelToString(eNetworkLevel), sTag ); oNetworkDescriber.AddNetworkTime(NetworkSource); return(oNetworkDescriber.ConcatenateSentences()); }
WhatToIncludeFlagIsSet ( WhatToInclude eORedEnumFlags, WhatToInclude eORedEnumFlagsToCheck ) { return((eORedEnumFlags & eORedEnumFlagsToCheck) != 0); }
GetVideoNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumVideos > 0); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // First, add a vertex for each video matching the search term. HashSet <String> oVideoIDs; Dictionary <String, LinkedList <String> > oCategoryDictionary; AppendVertexXmlNodes(sSearchTerm, eWhatToInclude, iMaximumVideos, oGraphMLXmlDocument, oRequestStatistics, out oVideoIDs, out oCategoryDictionary); // Now add whatever edges were requested. if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges)) { Debug.Assert(oCategoryDictionary != null); ReportProgress("Adding edges for shared categories."); AppendEdgesFromDictionary(oCategoryDictionary, oGraphMLXmlDocument, "Shared category", SharedCategoryID); } oCategoryDictionary = null; if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCommenterEdges)) { AppendSharedResponderEdges(oGraphMLXmlDocument, oVideoIDs, MaximumCommentsPerVideo, "http://gdata.youtube.com/feeds/api/videos/{0}/comments", "commenter", SharedCommenterID, oRequestStatistics); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedVideoResponderEdges)) { AppendSharedResponderEdges(oGraphMLXmlDocument, oVideoIDs, iMaximumVideos, "http://gdata.youtube.com/feeds/api/videos/{0}/responses", "video responder", SharedVideoResponderID, oRequestStatistics); } }
CreateGraphMLXmlDocument ( WhatToInclude eWhatToInclude ) { AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = new GraphMLXmlDocument(false); NodeXLGraphMLUtil.DefineEdgeRelationshipGraphMLAttribute( oGraphMLXmlDocument); if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges)) { oGraphMLXmlDocument.DefineEdgeStringGraphMLAttributes( SharedCategoryID, "Shared Category"); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCommenterEdges)) { oGraphMLXmlDocument.DefineEdgeStringGraphMLAttributes( SharedCommenterID, "Shared Commenter"); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedVideoResponderEdges)) { oGraphMLXmlDocument.DefineEdgeStringGraphMLAttributes( SharedVideoResponderID, "Shared Video Responder"); } oGraphMLXmlDocument.DefineVertexStringGraphMLAttributes( TitleID, "Title", AuthorID, "Author", CreatedDateUtcID, "Created Date (UTC)" ); oGraphMLXmlDocument.DefineGraphMLAttribute(false, RatingID, "Rating", "double", null); oGraphMLXmlDocument.DefineGraphMLAttributes(false, "int", ViewsID, "Views", FavoritedID, "Favorited", CommentsID, "Comments" ); NodeXLGraphMLUtil.DefineVertexImageFileGraphMLAttribute( oGraphMLXmlDocument); NodeXLGraphMLUtil.DefineVertexCustomMenuGraphMLAttributes( oGraphMLXmlDocument); return(oGraphMLXmlDocument); }
GetNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumStatuses, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumStatuses > 0); Debug.Assert(iMaximumStatuses != Int32.MaxValue); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the Twitter user ID and the value is the corresponding // TwitterUser. Dictionary <String, TwitterUser> oUserIDDictionary = new Dictionary <String, TwitterUser>(); // First, append a vertex for each person who has tweeted the search // term. AppendVertexXmlNodesForSearchTerm(sSearchTerm, eWhatToInclude, iMaximumStatuses, oGraphMLXmlDocument, oUserIDDictionary, oRequestStatistics); // Now append a vertex for each person who was mentioned or replied to // by the first set of people, but who didn't tweet the search term // himself. AppendVertexXmlNodesForMentionsAndRepliesTo(eWhatToInclude, oGraphMLXmlDocument, oUserIDDictionary, oRequestStatistics); TwitterSearchNetworkGraphMLUtil.AppendVertexTooltipXmlNodes( oGraphMLXmlDocument, oUserIDDictionary); if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowedEdges)) { // Look at each author's friends, and if a friend has also tweeted // the search term, add an edge between the author and the friend. AppendFriendEdgeXmlNodes(oUserIDDictionary, MaximumFriends, oGraphMLXmlDocument, oRequestStatistics); } AppendRepliesToAndMentionsEdgeXmlNodes(oGraphMLXmlDocument, oUserIDDictionary.Values, TwitterGraphMLUtil.TwitterUsersToUniqueScreenNames( oUserIDDictionary.Values) ); }
GetUserNetworkInternal ( String sUserNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert(!String.IsNullOrEmpty(sUserNameToAnalyze)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the user name and the value is the corresponding GraphML // XML node that represents the user. This is used to prevent the same // user name from being added to the XmlDocument twice. Dictionary <String, XmlNode> oUserNameDictionary = new Dictionary <String, XmlNode>(); // Include friends, subscriptions, both, or neither. Boolean [] abIncludes = new Boolean[] { WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FriendVertices), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SubscriptionVertices), }; for (Int32 i = 0; i < 2; i++) { if (abIncludes[i]) { GetUserNetworkRecursive(sUserNameToAnalyze, eWhatToInclude, (i == 0), eNetworkLevel, iMaximumPeoplePerRequest, 1, oGraphMLXmlDocument, oUserNameDictionary, oRequestStatistics); } } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.AllStatistics)) { AppendAllStatisticGraphMLAttributeValues(oGraphMLXmlDocument, oUserNameDictionary, oRequestStatistics); } }
GetNetwork ( String searchTerm, WhatToInclude whatToInclude, Int32 maximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(searchTerm)); Debug.Assert(maximumPeoplePerRequest > 0); AssertValid(); return(GetSearchNetworkInternal(searchTerm, whatToInclude, maximumPeoplePerRequest)); }
GetNetwork ( String searchTerm, WhatToInclude whatToInclude, Int32 maximumStatuses ) { Debug.Assert(!String.IsNullOrEmpty(searchTerm)); Debug.Assert(maximumStatuses > 0); Debug.Assert(maximumStatuses != Int32.MaxValue); AssertValid(); return(GetNetworkInternal(searchTerm, whatToInclude, maximumStatuses)); }
GetNetworkDescription ( String sUserNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(sUserNameToAnalyze)); Debug.Assert(iMaximumPeoplePerRequest > 0); AssertValid(); NetworkDescriber oNetworkDescriber = new NetworkDescriber(); oNetworkDescriber.AddSentence( "The graph represents the {0} YouTube network of the user with the" + " username \"{1}\"." , NetworkLevelToString(eNetworkLevel), sUserNameToAnalyze ); oNetworkDescriber.AddNetworkTime(NetworkSource); oNetworkDescriber.StartNewParagraph(); if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FriendVertices)) { oNetworkDescriber.AddSentence( "There is a vertex for each friend of the user." ); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SubscriptionVertices)) { oNetworkDescriber.AddSentence( "There is a vertex for each person or channel subscribed to by" + " the user." ); } oNetworkDescriber.AddNetworkLimit(iMaximumPeoplePerRequest, "people"); return(oNetworkDescriber.ConcatenateSentences()); }
GetUserNetworkInternal ( String sScreenName, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPerRequest, String sApiKey ) { Debug.Assert(!String.IsNullOrEmpty(sScreenName)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPerRequest > 0); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.CommenterVertices), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.UserInformation) ); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetUserNetworkInternal(sScreenName, eWhatToInclude, eNetworkLevel, iMaximumPerRequest, sApiKey, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return(oGraphMLXmlDocument); }
GetUserNetworkInternal ( String sUserNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(sUserNameToAnalyze)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.AllStatistics)); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetUserNetworkInternal(sUserNameToAnalyze, eWhatToInclude, eNetworkLevel, iMaximumPeoplePerRequest, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnUnexpectedException(oException, oGraphMLXmlDocument, oRequestStatistics); } String sNetworkTitle = "YouTube User " + sUserNameToAnalyze; OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics, GetNetworkDescription(sUserNameToAnalyze, eWhatToInclude, eNetworkLevel, iMaximumPeoplePerRequest), sNetworkTitle, sNetworkTitle ); return(oGraphMLXmlDocument); }
GetUserNetworkInternal ( String sScreenNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(sScreenNameToAnalyze)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); AssertValid(); BeforeGetNetwork(); Boolean bIncludeLatestStatus = WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.LatestStatuses); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(true, bIncludeLatestStatus); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetUserNetworkInternal(sScreenNameToAnalyze, eWhatToInclude, eNetworkLevel, iMaximumPeoplePerRequest, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return(oGraphMLXmlDocument); }
GetRelatedTagsInternal ( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey ) { Debug.Assert(!String.IsNullOrEmpty(sTag)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SampleThumbnails)); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetRelatedTagsInternal(sTag, eWhatToInclude, eNetworkLevel, sApiKey, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnUnexpectedException(oException, oGraphMLXmlDocument, oRequestStatistics); } String sNetworkTitle = "Flickr Tag " + sTag; OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics, GetNetworkDescription(sTag, eWhatToInclude, eNetworkLevel), sNetworkTitle, sNetworkTitle ); return(oGraphMLXmlDocument); }
GetNetwork ( String screenNameToAnalyze, WhatToInclude whatToInclude, NetworkLevel networkLevel, Int32 maximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(screenNameToAnalyze)); Debug.Assert(networkLevel == NetworkLevel.One || networkLevel == NetworkLevel.OnePointFive || networkLevel == NetworkLevel.Two); Debug.Assert(maximumPeoplePerRequest > 0); AssertValid(); return(GetUserNetworkInternal(screenNameToAnalyze, whatToInclude, networkLevel, maximumPeoplePerRequest)); }
GetNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumStatuses ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumStatuses > 0); Debug.Assert(iMaximumStatuses != Int32.MaxValue); AssertValid(); BeforeGetNetwork(); GraphMLXmlDocument oGraphMLXmlDocument = TwitterSearchNetworkGraphMLUtil.CreateGraphMLXmlDocument(); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumStatuses, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnUnexpectedException(oException, oGraphMLXmlDocument, oRequestStatistics); } OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics, GetNetworkDescription(sSearchTerm, eWhatToInclude, iMaximumStatuses, oGraphMLXmlDocument), SnaTitleCreator.CreateSnaTitle(sSearchTerm, oRequestStatistics), "Twitter Search " + sSearchTerm ); return(oGraphMLXmlDocument); }
GetRelatedTagsInternal ( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey ) { Debug.Assert(!String.IsNullOrEmpty(sTag)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SampleThumbnails)); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetRelatedTagsInternal(sTag, eWhatToInclude, eNetworkLevel, sApiKey, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return(oGraphMLXmlDocument); }
GetRelatedTagsInternal ( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert(!String.IsNullOrEmpty(sTag)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the tag name and the value is the corresponding GraphML // XML node that represents the tag. This is used to prevent the same // tag from being added to the XmlDocument twice. Dictionary <String, XmlNode> oTagDictionary = new Dictionary <String, XmlNode>(); GetRelatedTagsRecursive(sTag, eWhatToInclude, eNetworkLevel, sApiKey, 1, oGraphMLXmlDocument, oTagDictionary, oRequestStatistics); if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SampleThumbnails)) { AppendSampleThumbnails(oTagDictionary, oGraphMLXmlDocument, sApiKey, oRequestStatistics); } }
GetSearchNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumPeoplePerRequest ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumPeoplePerRequest > 0); AssertValid(); BeforeGetNetwork(); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( bIncludeStatistics, false); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetSearchNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumPeoplePerRequest, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return(oGraphMLXmlDocument); }
AppendVertexXmlNodesForMentionsAndRepliesTo ( WhatToInclude eWhatToInclude, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary <String, TwitterUser> oUserIDDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oUserIDDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); ReportProgress("Getting replied to and mentioned users."); // Get the screen names that were mentioned or replied to by the people // who tweeted the search term. String[] asUniqueMentionsAndRepliesToScreenNames = TwitterSearchNetworkGraphMLUtil.GetMentionsAndRepliesToScreenNames( oUserIDDictionary); // Get information about each of those screen names and append vertex // XML nodes for them if necessary. foreach (Dictionary <String, Object> oUserValueDictionary in EnumerateUserValueDictionaries( asUniqueMentionsAndRepliesToScreenNames, false, oRequestStatistics)) { TwitterUser oTwitterUser; TwitterSearchNetworkGraphMLUtil.TryAppendVertexXmlNode( oUserValueDictionary, false, oGraphMLXmlDocument, oUserIDDictionary, out oTwitterUser); } }
GetVideoNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumVideos > 0); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(eWhatToInclude); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetVideoNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumVideos, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnUnexpectedException(oException, oGraphMLXmlDocument, oRequestStatistics); } String sNetworkTitle = "YouTube Video " + sSearchTerm; OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics, GetNetworkDescription(sSearchTerm, eWhatToInclude, iMaximumVideos), sNetworkTitle, sNetworkTitle ); return(oGraphMLXmlDocument); }
//************************************************************************* // Method: GetUserNetworkInternal() // /// <summary> /// Gets a network of Twitter users, given a GraphMLXmlDocument. /// </summary> /// /// <param name="sScreenNameToAnalyze"> /// The screen name of the Twitter user whose network should be analyzed. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument to populate with the requested network. /// </param> //************************************************************************* protected void GetUserNetworkInternal( String sScreenNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sScreenNameToAnalyze) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the screen name and the value is the corresponding // TwitterVertex. This is used to prevent the same screen name from // being added to the XmlDocument twice. Dictionary<String, TwitterVertex> oScreenNameDictionary = new Dictionary<String, TwitterVertex>(); // Include followed, followers, both, or neither. Boolean [] abIncludes = new Boolean[] { WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowedVertices), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowerVertices), }; for (Int32 i = 0; i < 2; i++) { if ( abIncludes[i] ) { GetUserNetworkRecursive(sScreenNameToAnalyze, eWhatToInclude, (i == 0), eNetworkLevel, iMaximumPeoplePerRequest, 1, oGraphMLXmlDocument, oScreenNameDictionary, oRequestStatistics); } } Boolean bIncludeLatestStatus = WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.LatestStatuses); AppendMissingGraphMLAttributeValues(oGraphMLXmlDocument, oScreenNameDictionary, true, bIncludeLatestStatus, oRequestStatistics); AppendRepliesToAndMentionsXmlNodes(oGraphMLXmlDocument, oScreenNameDictionary, WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.RepliesToEdges), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.MentionsEdges) ); }
//************************************************************************* // Method: AppendVertexXmlNodes() // /// <summary> /// Appends a vertex XML node for each video that matches a specified /// search term. /// </summary> /// /// <param name="sSearchTerm"> /// The term to search for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumVideos"> /// Maximum number of videos to request, or Int32.MaxValue for no limit. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument being populated. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> /// /// <param name="oVideoIDs"> /// Where the set of unique video IDs gets stored. /// </param> /// /// <param name="oTagDictionary"> /// If an edge should be included for each pair of videos that share the /// same tag, this gets set to a Dictionary for which the key is a /// lower-case tag and the value is a LinkedList of the video IDs that have /// the tag. Otherwise, it gets set to null. /// </param> //************************************************************************* protected void AppendVertexXmlNodes( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos, GraphMLXmlDocument oGraphMLXmlDocument, RequestStatistics oRequestStatistics, out HashSet<String> oVideoIDs, out Dictionary< String, LinkedList<String> > oTagDictionary ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumVideos> 0); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oRequestStatistics != null); AssertValid(); ReportProgress("Getting a list of videos."); // This is used to skip duplicate videos in the results returned by // YouTube. (I'm not sure why YouTube sometimes returns duplicates, // but it does.) oVideoIDs = new HashSet<String>(); // If an edge should be included for each pair of videos that share the // same tag, the key is a lower-case tag and the value is a LinkedList // of the video IDs that have the tag. if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedTagEdges) ) { oTagDictionary = new Dictionary< String, LinkedList<String> >(); } else { oTagDictionary = null; } String sUrl = String.Format( "http://gdata.youtube.com/feeds/api/videos?q={0}" , EncodeUrlParameter(sSearchTerm) ); // The document consists of an "entry" XML node for each video. foreach ( XmlNode oEntryXmlNode in EnumerateXmlNodes(sUrl, "a:feed/a:entry", iMaximumVideos, false, oRequestStatistics) ) { XmlNamespaceManager oXmlNamespaceManager = CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument); // Use the video ID as the GraphML vertex name. The video title // can't be used because it is not unique. String sVideoID; if ( !XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "media:group/yt:videoid/text()", oXmlNamespaceManager, out sVideoID) || oVideoIDs.Contains(sVideoID) ) { continue; } oVideoIDs.Add(sVideoID); XmlNode oVertexXmlNode = oGraphMLXmlDocument.AppendVertexXmlNode( sVideoID); AppendStringGraphMLAttributeValue(oEntryXmlNode, "a:title/text()", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, TitleID); AppendDoubleGraphMLAttributeValue(oEntryXmlNode, "gd:rating/@average", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, RatingID); AppendInt32GraphMLAttributeValue(oEntryXmlNode, "yt:statistics/@viewCount", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, ViewsID); AppendInt32GraphMLAttributeValue(oEntryXmlNode, "yt:statistics/@favoriteCount", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, FavoritedID); AppendInt32GraphMLAttributeValue(oEntryXmlNode, "gd:comments/gd:feedLink/@countHint", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, CommentsID); AppendYouTubeDateGraphMLAttributeValue(oEntryXmlNode, "a:published/text()", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, CreatedDateUtcID); AppendStringGraphMLAttributeValue(oEntryXmlNode, "media:group/media:thumbnail/@url", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, ImageFileID); if ( AppendStringGraphMLAttributeValue(oEntryXmlNode, "media:group/media:player/@url", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, MenuActionID) ) { oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, MenuTextID, "Play Video in Browser"); } if (oTagDictionary != null) { CollectTags(oEntryXmlNode, sVideoID, oXmlNamespaceManager, oTagDictionary); } } }
//************************************************************************* // Method: GetVideoNetworkInternal() // /// <summary> /// Gets a network of related YouTube videos, given a GraphMLXmlDocument. /// </summary> /// /// <param name="sSearchTerm"> /// The term to search for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumVideos"> /// Maximum number of videos to request, or Int32.MaxValue for no limit. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument to populate with the requested network. /// </param> //************************************************************************* protected void GetVideoNetworkInternal( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumVideos > 0); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // First, add a vertex for each video matching the search term. HashSet<String> oVideoIDs; Dictionary< String, LinkedList<String> > oTagDictionary; AppendVertexXmlNodes(sSearchTerm, eWhatToInclude, iMaximumVideos, oGraphMLXmlDocument, oRequestStatistics, out oVideoIDs, out oTagDictionary); // Now add whatever edges were requested. if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedTagEdges) ) { Debug.Assert(oTagDictionary != null); ReportProgress("Adding edges for shared tags."); AppendEdgesFromDictionary(oTagDictionary, oGraphMLXmlDocument, "Shared tag"); } oTagDictionary = null; if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCommenterEdges) ) { AppendSharedResponderEdges(oGraphMLXmlDocument, oVideoIDs, MaximumCommentsPerVideo, "http://gdata.youtube.com/feeds/api/videos/{0}/comments", "commenter", oRequestStatistics); } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedVideoResponderEdges) ) { AppendSharedResponderEdges(oGraphMLXmlDocument, oVideoIDs, iMaximumVideos, "http://gdata.youtube.com/feeds/api/videos/{0}/responses", "video responder", oRequestStatistics); } }
//************************************************************************* // Method: GetUserNetworkRecursive() // /// <summary> /// Recursively gets a network of Twitter users. /// </summary> /// /// <param name="sScreenName"> /// The screen name to use in this call. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="bIncludeFollowedThisCall"> /// true to include the people followed by the user, false to include the /// people following the user. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="iRecursionLevel"> /// Recursion level for this call. Must be 1 or 2. Gets incremented when /// recursing. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// GraphMLXmlDocument being populated. /// </param> /// /// <param name="oScreenNameDictionary"> /// The key is the screen name in lower case and the value is the /// corresponding TwitterVertex. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> //************************************************************************* protected void GetUserNetworkRecursive( String sScreenName, WhatToInclude eWhatToInclude, Boolean bIncludeFollowedThisCall, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest, Int32 iRecursionLevel, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, TwitterVertex> oScreenNameDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert( !String.IsNullOrEmpty(sScreenName) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oScreenNameDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); /* Here is what this method should do, based on the eNetworkLevel and iRecursionLevel parameters. It's assumed that eWhatToInclude.FollowedFollowerEdge is set. eNetworkLevel |One | OnePointFive | Two ---|------------------| ------------------| ----------------- i 1 |Add all vertices. | Add all vertices. | Add all vertices. R | | | e |Add all edges. | Add all edges. | Add all edges. c | | | u |Do not recurse. | Recurse. | Recurse. r | | | s ---|------------------|-------------------|------------------ i 2 |Impossible. | Do not add | Add all vertices. o | | vertices. | n | | | L | | Add edges only if | Add all edges. e | | vertices are | v | | already included. | e | | | l | | Do not recurse. | Do not recurse. | | | ---|------------------|-------------------|------------------ */ Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel, iRecursionLevel); List<String> oScreenNamesToRecurse = new List<String>(); Boolean bIncludeLatestStatuses = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.LatestStatuses); ReportProgressForFollowedOrFollowing(sScreenName, bIncludeFollowedThisCall); Boolean bThisUserAppended = false; // If the GraphMLXmlDocument already contains at least one vertex node, // then this is the second time that this method has been called, a // partial network has already been obtained, and most errors should // now be skipped. However, if none of the network has been obtained // yet, errors on page 1 should throw an immediate exception. Boolean bSkipMostPage1Errors = oGraphMLXmlDocument.HasVertexXmlNode; // The document consists of a single "users" node with zero or more // "user" child nodes. foreach ( XmlNode oUserXmlNode in EnumerateXmlNodes( GetFollowedOrFollowingUrl(sScreenName, bIncludeFollowedThisCall), "users_list/users/user", null, null, Int32.MaxValue, iMaximumPeoplePerRequest, false, bSkipMostPage1Errors, oRequestStatistics) ) { String sOtherScreenName; if ( !TryGetScreenName(oUserXmlNode, out sOtherScreenName) ) { // Nothing can be done without a screen name. continue; } if (!bThisUserAppended) { // Append a vertex node for this request's user. // // This used to be done after the foreach loop, which avoided // the need for a "bThisUserAppended" flag. That caused the // following bug: If a Twitter error occurred within // EnumerateXmlNodes() after some edges had been added, and the // user decided to import the resulting partial network, the // GraphML might contain edges that referenced "this user" // without containing a vertex for "this user." That is an // illegal state for GraphML, which the ExcelTemplate project // caught and reported as an error. TryAppendVertexXmlNode(sScreenName, null, oGraphMLXmlDocument, oScreenNameDictionary, true, bIncludeLatestStatuses); bThisUserAppended = true; } Boolean bNeedToAppendVertices = GetNeedToAppendVertices( eNetworkLevel, iRecursionLevel); if (bNeedToAppendVertices) { if ( TryAppendVertexXmlNode(sOtherScreenName, oUserXmlNode, oGraphMLXmlDocument, oScreenNameDictionary, true, bIncludeLatestStatuses) && bNeedToRecurse ) { oScreenNamesToRecurse.Add(sOtherScreenName); } } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowedFollowerEdges) ) { if ( bNeedToAppendVertices || oScreenNameDictionary.ContainsKey(sOtherScreenName) ) { XmlNode oEdgeXmlNode; if (bIncludeFollowedThisCall) { oEdgeXmlNode = AppendEdgeXmlNode(oGraphMLXmlDocument, sScreenName, sOtherScreenName, "Followed"); } else { oEdgeXmlNode = AppendEdgeXmlNode(oGraphMLXmlDocument, sOtherScreenName, sScreenName, "Follower"); } AppendRelationshipDateUtcGraphMLAttributeValue( oGraphMLXmlDocument, oEdgeXmlNode, oRequestStatistics); } } } if (bNeedToRecurse) { foreach (String sScreenNameToRecurse in oScreenNamesToRecurse) { GetUserNetworkRecursive(sScreenNameToRecurse, eWhatToInclude, bIncludeFollowedThisCall, eNetworkLevel, iMaximumPeoplePerRequest, 2, oGraphMLXmlDocument, oScreenNameDictionary, oRequestStatistics); } } }
GetNetwork ( String searchTerm, WhatToInclude whatToInclude, Int32 maximumStatuses ) { Debug.Assert( !String.IsNullOrEmpty(searchTerm) ); Debug.Assert(maximumStatuses > 0); Debug.Assert(maximumStatuses != Int32.MaxValue); AssertValid(); return ( GetNetworkInternal(searchTerm, whatToInclude, maximumStatuses) ); }
//************************************************************************* // Method: GetRelatedTagsInternal() // /// <summary> /// Gets the Flickr tags related to a specified tag, given a /// GraphXMLXmlDocument. /// </summary> /// /// <param name="sTag"> /// Tag to get related tags for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="sApiKey"> /// Flickr API key. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument to populate with the requested network. /// </param> //************************************************************************* protected void GetRelatedTagsInternal( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sTag) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert( !String.IsNullOrEmpty(sApiKey) ); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the tag name and the value is the corresponding GraphML // XML node that represents the tag. This is used to prevent the same // tag from being added to the XmlDocument twice. Dictionary<String, XmlNode> oTagDictionary = new Dictionary<String, XmlNode>(); GetRelatedTagsRecursive(sTag, eWhatToInclude, eNetworkLevel, sApiKey, 1, oGraphMLXmlDocument, oTagDictionary, oRequestStatistics); if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SampleThumbnails) ) { AppendSampleThumbnails(oTagDictionary, oGraphMLXmlDocument, sApiKey, oRequestStatistics); } }
GetNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumStatuses, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumStatuses > 0); Debug.Assert(iMaximumStatuses != Int32.MaxValue); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the Twitter user ID and the value is the corresponding // TwitterUser. Dictionary<String, TwitterUser> oUserIDDictionary = new Dictionary<String, TwitterUser>(); // First, append a vertex for each person who has tweeted the search // term. AppendVertexXmlNodesForSearchTerm(sSearchTerm, eWhatToInclude, iMaximumStatuses, oGraphMLXmlDocument, oUserIDDictionary, oRequestStatistics); // Now append a vertex for each person who was mentioned or replied to // by the first set of people, but who didn't tweet the search term // himself. AppendVertexXmlNodesForMentionsAndRepliesTo(eWhatToInclude, oGraphMLXmlDocument, oUserIDDictionary, oRequestStatistics); TwitterSearchNetworkGraphMLUtil.AppendVertexTooltipXmlNodes( oGraphMLXmlDocument, oUserIDDictionary); if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowedEdges) ) { // Look at the people followed by each author, and if a followed // has also tweeted the search term, add an edge between the author // and the followed. AppendFollowedOrFollowingEdgeXmlNodes(oUserIDDictionary, true, MaximumFollowers, oGraphMLXmlDocument, oRequestStatistics); } AppendRepliesToAndMentionsEdgeXmlNodes(oGraphMLXmlDocument, oUserIDDictionary.Values, TwitterGraphMLUtil.TwitterUsersToUniqueScreenNames( oUserIDDictionary.Values), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.RepliesToEdges), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.MentionsEdges), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.NonRepliesToNonMentionsEdges), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.Statuses) ); }
AppendVertexXmlNodesForMentionsAndRepliesTo ( WhatToInclude eWhatToInclude, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, TwitterUser> oUserIDDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oUserIDDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); ReportProgress("Getting replied to and mentioned users."); // Get the screen names that were mentioned or replied to by the people // who tweeted the search term. String[] asUniqueMentionsAndRepliesToScreenNames = TwitterSearchNetworkGraphMLUtil.GetMentionsAndRepliesToScreenNames( oUserIDDictionary); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); // Get information about each of those screen names and append vertex // XML nodes for them if necessary. foreach ( Dictionary<String, Object> oUserValueDictionary in EnumerateUserValueDictionaries( asUniqueMentionsAndRepliesToScreenNames, false, oRequestStatistics) ) { TwitterUser oTwitterUser; TwitterSearchNetworkGraphMLUtil.TryAppendVertexXmlNode( oUserValueDictionary, bIncludeStatistics, false, oGraphMLXmlDocument, oUserIDDictionary, out oTwitterUser); } }
AppendVertexXmlNodesForSearchTerm ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumStatuses, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, TwitterUser> oUserIDDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumStatuses > 0); Debug.Assert(iMaximumStatuses != Int32.MaxValue); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oUserIDDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); Boolean bExpandStatusUrls = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.ExpandedStatusUrls); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); ReportProgress("Getting a list of tweets."); // Get the tweets that contain the search term. Note that multiple // tweets may have the same author. Debug.Assert(m_oTwitterUtil != null); foreach ( Dictionary<String, Object> oStatusValueDictionary in m_oTwitterUtil.EnumerateSearchStatuses( sSearchTerm, iMaximumStatuses, oRequestStatistics, new ReportProgressHandler(this.ReportProgress), new CheckCancellationPendingHandler( this.CheckCancellationPending) ) ) { const String UserKeyName = "user"; if ( !oStatusValueDictionary.ContainsKey(UserKeyName) ) { // This has actually happened--Twitter occasionally sends a // status without user information. continue; } Dictionary<String, Object> oUserValueDictionary = ( Dictionary<String, Object> ) oStatusValueDictionary[UserKeyName]; TwitterUser oTwitterUser; if ( !TwitterSearchNetworkGraphMLUtil. TryAppendVertexXmlNode(oUserValueDictionary, bIncludeStatistics, true, oGraphMLXmlDocument, oUserIDDictionary, out oTwitterUser) ) { continue; } // Parse the status and add it to the user's status collection. if ( !TwitterSearchNetworkGraphMLUtil.TryAddStatusToUser( oStatusValueDictionary, oTwitterUser, bExpandStatusUrls) ) { continue; } } }
GetVideoNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumVideos > 0); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(eWhatToInclude); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetVideoNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumVideos, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnUnexpectedException(oException, oGraphMLXmlDocument, oRequestStatistics); } OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics, GetNetworkDescription(sSearchTerm, eWhatToInclude, iMaximumVideos), "YouTube Video " + sSearchTerm ); return (oGraphMLXmlDocument); }
GetNetworkDescription ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumStatuses, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumStatuses > 0); Debug.Assert(iMaximumStatuses != Int32.MaxValue); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); const String Int32FormatString = "N0"; NetworkDescriber oNetworkDescriber = new NetworkDescriber(); Int32 iVertexXmlNodes = oGraphMLXmlDocument.VertexXmlNodes; oNetworkDescriber.AddSentence( // WARNING: // // If you change this first sentence, you may also have to change // the code in // TwitterSearchNetworkTopItemsCalculator2.GetSearchTerm(), which // attempts to extract the search term from the graph description. "The graph represents a network of {0} Twitter {1} whose recent" + " tweets contained \"{2}\", or who {3} replied to or mentioned" + " in those tweets, taken from a data set limited to a maximum of" + " {4} tweets." , iVertexXmlNodes.ToString(Int32FormatString), StringUtil.MakePlural("user", iVertexXmlNodes), sSearchTerm, iVertexXmlNodes > 1 ? "were" : "was", iMaximumStatuses.ToString(Int32FormatString) ); oNetworkDescriber.AddNetworkTime(NetworkSource); Boolean bIncludeFollowedEdges = WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowedEdges); Boolean bIncludeRepliesToEdges = WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.RepliesToEdges); Boolean bIncludeMentionsEdges = WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.MentionsEdges); Boolean bIncludeNonRepliesToNonMentionsEdges = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.NonRepliesToNonMentionsEdges); if (bIncludeRepliesToEdges && bIncludeMentionsEdges && bIncludeNonRepliesToNonMentionsEdges) { // Every collected tweet has an edge that contains the date of the // tweet, so the range of tweet dates can be determined. oNetworkDescriber.StartNewParagraph(); TweetDateRangeAnalyzer.AddTweetDateRangeToNetworkDescription( oGraphMLXmlDocument, oNetworkDescriber); } if (bIncludeFollowedEdges || bIncludeRepliesToEdges || bIncludeMentionsEdges || bIncludeNonRepliesToNonMentionsEdges) { oNetworkDescriber.StartNewParagraph(); } if (bIncludeFollowedEdges) { oNetworkDescriber.AddSentence( "There is an edge for each follows relationship." ); } if (bIncludeRepliesToEdges) { oNetworkDescriber.AddSentence( "There is an edge for each \"replies-to\" relationship in a" + " tweet." ); } if (bIncludeMentionsEdges) { oNetworkDescriber.AddSentence( "There is an edge for each \"mentions\" relationship in a" + " tweet." ); } if (bIncludeNonRepliesToNonMentionsEdges) { oNetworkDescriber.AddSentence( "There is a self-loop edge for each tweet that is not a" + " \"replies-to\" or \"mentions\"." ); } return ( oNetworkDescriber.ConcatenateSentences() ); }
//************************************************************************* // Method: GetUserNetworkRecursive() // /// <summary> /// Recursively gets a network of YouTube users. /// </summary> /// /// <param name="sUserName"> /// The user name to use in this call. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="bIncludeFriendsThisCall"> /// true to include the user's friends, false to include the people /// subscribed to by the user. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="iRecursionLevel"> /// Recursion level for this call. Must be 1 or 2. Gets incremented when /// recursing. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// GraphMLXmlDocument being populated. /// </param> /// /// <param name="oUserNameDictionary"> /// The key is the user name and the value is the corresponding GraphML XML /// node that represents the user. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> //************************************************************************* protected void GetUserNetworkRecursive( String sUserName, WhatToInclude eWhatToInclude, Boolean bIncludeFriendsThisCall, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest, Int32 iRecursionLevel, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, XmlNode> oUserNameDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert( !String.IsNullOrEmpty(sUserName) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oUserNameDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); /* Here is what this method should do, based on the eNetworkLevel and iRecursionLevel parameters. eNetworkLevel |One | OnePointFive | Two ---|------------------| ------------------| ----------------- i 1 |Add all vertices. | Add all vertices. | Add all vertices. R | | | e |Add all edges. | Add all edges. | Add all edges. c | | | u |Do not recurse. | Recurse. | Recurse. r | | | s ---|------------------|-------------------|------------------ i 2 |Impossible. | Do not add | Add all vertices. o | | vertices. | n | | | L | | Add edges only if | Add all edges. e | | vertices are | v | | already included. | e | | | l | | Do not recurse. | Do not recurse. | | | ---|------------------|-------------------|------------------ */ Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel, iRecursionLevel); List<String> oUserNamesToRecurse = new List<String>(); ReportProgressForFriendsOrSubscriptions(sUserName, bIncludeFriendsThisCall); Boolean bThisUserAppended = false; // If the GraphMLXmlDocument already contains at least one vertex node, // then this is the second time that this method has been called, a // partial network has already been obtained, and most errors should // now be skipped. However, if none of the network has been obtained // yet, errors on page 1 should throw an immediate exception. Boolean bSkipMostPage1Errors = oGraphMLXmlDocument.HasVertexXmlNode; // The document consists of a single "feed" node with zero or more // "entry" child nodes. foreach ( XmlNode oEntryXmlNode in EnumerateXmlNodes( GetFriendsOrSubscriptionsUrl(sUserName, bIncludeFriendsThisCall), "a:feed/a:entry", iMaximumPeoplePerRequest, bSkipMostPage1Errors, oRequestStatistics) ) { XmlNamespaceManager oXmlNamespaceManager = CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument); String sOtherUserName; if ( !XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "yt:username/text()", oXmlNamespaceManager, out sOtherUserName) ) { continue; } if (!bThisUserAppended) { // Append a vertex node for this request's user. // // This used to be done after the foreach loop, which avoided // the need for a "bThisUserAppended" flag. That caused the // following bug: If a YouTube error occurred within // EnumerateXmlNodes() after some edges had been added, and the // user decided to import the resulting partial network, the // GraphML might contain edges that referenced "this user" // without containing a vertex for "this user." That is an // illegal state for GraphML, which the ExcelTemplate project // caught and reported as an error. TryAppendVertexXmlNode(sUserName, null, oGraphMLXmlDocument, oUserNameDictionary); bThisUserAppended = true; } Boolean bNeedToAppendVertices = GetNeedToAppendVertices( eNetworkLevel, iRecursionLevel); if (bNeedToAppendVertices) { if ( TryAppendVertexXmlNode(sOtherUserName, oEntryXmlNode, oGraphMLXmlDocument, oUserNameDictionary) && bNeedToRecurse ) { oUserNamesToRecurse.Add(sOtherUserName); } } if ( bNeedToAppendVertices || oUserNameDictionary.ContainsKey(sOtherUserName) ) { String sRelationship; if (bIncludeFriendsThisCall) { sRelationship = "Friend of"; } else { sRelationship = "Subscribes to"; String sSubscriptionType = null; if ( XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "a:category[@scheme='http://gdata.youtube.com/schemas/" + "2007/subscriptiontypes.cat']/@term", oXmlNamespaceManager, out sSubscriptionType) ) { sRelationship += " " + sSubscriptionType; } } AppendEdgeXmlNode(oGraphMLXmlDocument, sUserName, sOtherUserName, sRelationship); } } if (bNeedToRecurse) { foreach (String sUserNameToRecurse in oUserNamesToRecurse) { GetUserNetworkRecursive(sUserNameToRecurse, eWhatToInclude, bIncludeFriendsThisCall, eNetworkLevel, iMaximumPeoplePerRequest, 2, oGraphMLXmlDocument, oUserNameDictionary, oRequestStatistics); } } }
//************************************************************************* // Method: GetUserNetworkInternal() // /// <summary> /// Gets a network of YouTube users, given a GraphMLXmlDocument. /// </summary> /// /// <param name="sUserNameToAnalyze"> /// The user name of the YouTube user whose network should be analyzed. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument to populate with the requested network. /// </param> //************************************************************************* protected void GetUserNetworkInternal( String sUserNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sUserNameToAnalyze) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); // The key is the user name and the value is the corresponding GraphML // XML node that represents the user. This is used to prevent the same // user name from being added to the XmlDocument twice. Dictionary<String, XmlNode> oUserNameDictionary = new Dictionary<String, XmlNode>(); // Include friends, subscriptions, both, or neither. Boolean [] abIncludes = new Boolean[] { WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FriendVertices), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SubscriptionVertices), }; for (Int32 i = 0; i < 2; i++) { if ( abIncludes[i] ) { GetUserNetworkRecursive(sUserNameToAnalyze, eWhatToInclude, (i == 0), eNetworkLevel, iMaximumPeoplePerRequest, 1, oGraphMLXmlDocument, oUserNameDictionary, oRequestStatistics); } } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.AllStatistics) ) { AppendAllStatisticGraphMLAttributeValues(oGraphMLXmlDocument, oUserNameDictionary, oRequestStatistics); } }
//************************************************************************* // Method: GetUserNetworkInternal() // /// <overloads> /// Gets a network of YouTube users. /// </overloads> /// /// <summary> /// Gets a network of YouTube users. /// </summary> /// /// <param name="sUserNameToAnalyze"> /// The user name of the YouTube user whose network should be analyzed. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <returns> /// An XmlDocument containing the network as GraphML. /// </returns> //************************************************************************* protected XmlDocument GetUserNetworkInternal( String sUserNameToAnalyze, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest ) { Debug.Assert( !String.IsNullOrEmpty(sUserNameToAnalyze) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.AllStatistics) ); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetUserNetworkInternal(sUserNameToAnalyze, eWhatToInclude, eNetworkLevel, iMaximumPeoplePerRequest, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return (oGraphMLXmlDocument); }
//************************************************************************* // Method: GetNetworkAsync() // /// <summary> /// Asynchronously gets a directed network of YouTube users. /// </summary> /// /// <param name="userNameToAnalyze"> /// The user name of the YouTube user whose network should be analyzed. /// </param> /// /// <param name="whatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="networkLevel"> /// Network level to include. /// </param> /// /// <param name="maximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <remarks> /// When the analysis completes, the <see /// cref="HttpNetworkAnalyzerBase.AnalysisCompleted" /> event fires. The /// <see cref="RunWorkerCompletedEventArgs.Result" /> property will return /// an XmlDocument containing the network as GraphML. /// /// <para> /// To cancel the analysis, call <see /// cref="HttpNetworkAnalyzerBase.CancelAsync" />. /// </para> /// /// </remarks> //************************************************************************* public void GetNetworkAsync( String userNameToAnalyze, WhatToInclude whatToInclude, NetworkLevel networkLevel, Int32 maximumPeoplePerRequest ) { Debug.Assert( !String.IsNullOrEmpty(userNameToAnalyze) ); Debug.Assert(networkLevel == NetworkLevel.One || networkLevel == NetworkLevel.OnePointFive || networkLevel == NetworkLevel.Two); Debug.Assert(maximumPeoplePerRequest > 0); AssertValid(); const String MethodName = "GetNetworkAsync"; CheckIsBusy(MethodName); // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs(); oGetNetworkAsyncArgs.UserNameToAnalyze = userNameToAnalyze; oGetNetworkAsyncArgs.WhatToInclude = whatToInclude; oGetNetworkAsyncArgs.NetworkLevel = networkLevel; oGetNetworkAsyncArgs.MaximumPeoplePerRequest = maximumPeoplePerRequest; m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs); }
AppendVertexXmlNodes ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos, GraphMLXmlDocument oGraphMLXmlDocument, RequestStatistics oRequestStatistics, out HashSet <String> oVideoIDs, out Dictionary <String, LinkedList <String> > oCategoryDictionary ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumVideos > 0); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oRequestStatistics != null); AssertValid(); ReportProgress("Getting a list of videos."); // This is used to skip duplicate videos in the results returned by // YouTube. (I'm not sure why YouTube sometimes returns duplicates, // but it does.) oVideoIDs = new HashSet <String>(); // If an edge should be included for each pair of videos that share the // same category, the key is a lower-case category and the value is a // LinkedList of the video IDs that have the category. if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges)) { oCategoryDictionary = new Dictionary <String, LinkedList <String> >(); } else { oCategoryDictionary = null; } String sUrl = String.Format( "http://gdata.youtube.com/feeds/api/videos?q={0}" , EncodeUrlParameter(sSearchTerm) ); // The document consists of an "entry" XML node for each video. foreach (XmlNode oEntryXmlNode in EnumerateXmlNodes(sUrl, "a:feed/a:entry", iMaximumVideos, false, oRequestStatistics)) { XmlNamespaceManager oXmlNamespaceManager = CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument); // Use the video ID as the GraphML vertex name. The video title // can't be used because it is not unique. String sVideoID; if ( !XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "media:group/yt:videoid/text()", oXmlNamespaceManager, out sVideoID) || oVideoIDs.Contains(sVideoID) ) { continue; } oVideoIDs.Add(sVideoID); XmlNode oVertexXmlNode = oGraphMLXmlDocument.AppendVertexXmlNode( sVideoID); AppendStringGraphMLAttributeValue(oEntryXmlNode, "a:title/text()", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, TitleID); AppendStringGraphMLAttributeValue(oEntryXmlNode, "a:author/a:name/text()", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, AuthorID); AppendDoubleGraphMLAttributeValue(oEntryXmlNode, "gd:rating/@average", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, RatingID); AppendInt32GraphMLAttributeValue(oEntryXmlNode, "yt:statistics/@viewCount", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, ViewsID); AppendInt32GraphMLAttributeValue(oEntryXmlNode, "yt:statistics/@favoriteCount", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, FavoritedID); AppendInt32GraphMLAttributeValue(oEntryXmlNode, "gd:comments/gd:feedLink/@countHint", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, CommentsID); AppendYouTubeDateGraphMLAttributeValue(oEntryXmlNode, "a:published/text()", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, CreatedDateUtcID); AppendStringGraphMLAttributeValue(oEntryXmlNode, "media:group/media:thumbnail/@url", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, NodeXLGraphMLUtil.VertexImageFileID); if (AppendStringGraphMLAttributeValue(oEntryXmlNode, "media:group/media:player/@url", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, NodeXLGraphMLUtil.VertexMenuActionID)) { oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, NodeXLGraphMLUtil.VertexMenuTextID, "Play Video in Browser"); } if (oCategoryDictionary != null) { CollectCategories(oEntryXmlNode, sVideoID, oXmlNamespaceManager, oCategoryDictionary); } } }
//************************************************************************* // Method: GetRelatedTagsInternal() // /// <overloads> /// Gets the Flickr tags related to a specified tag. /// </overloads> /// /// <summary> /// Gets the Flickr tags related to a specified tag. /// </summary> /// /// <param name="sTag"> /// Tag to get related tags for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="sApiKey"> /// Flickr API key. /// </param> /// /// <returns> /// An XmlDocument containing the network as GraphML. /// </returns> //************************************************************************* protected XmlDocument GetRelatedTagsInternal( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey ) { Debug.Assert( !String.IsNullOrEmpty(sTag) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert( !String.IsNullOrEmpty(sApiKey) ); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SampleThumbnails) ); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetRelatedTagsInternal(sTag, eWhatToInclude, eNetworkLevel, sApiKey, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return (oGraphMLXmlDocument); }
GetNetworkDescription ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos ) { Debug.Assert(!String.IsNullOrEmpty(sSearchTerm)); Debug.Assert(iMaximumVideos > 0); AssertValid(); NetworkDescriber oNetworkDescriber = new NetworkDescriber(); oNetworkDescriber.AddSentence( "The graph represents the network of YouTube videos whose title," + " keywords, description, categories, or author\'s username" + " contain \"{0}\"." , sSearchTerm ); oNetworkDescriber.AddNetworkTime(NetworkSource); oNetworkDescriber.StartNewParagraph(); oNetworkDescriber.AddNetworkLimit(iMaximumVideos, "videos"); if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges | WhatToInclude.SharedCommenterEdges | WhatToInclude.SharedVideoResponderEdges )) { oNetworkDescriber.StartNewParagraph(); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges)) { oNetworkDescriber.AddSentence( "There is an edge for each pair of videos that have the same" + " category." ); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCommenterEdges)) { oNetworkDescriber.AddSentence( "There is an edge for each pair of videos commented on by the" + " same user." ); } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedVideoResponderEdges)) { oNetworkDescriber.AddSentence( "There is an edge for each pair of videos responded to with" + " another video by the same user." ); } return(oNetworkDescriber.ConcatenateSentences()); }
//************************************************************************* // Method: GetRelatedTagsRecursive() // /// <summary> /// Recursively gets a tag's related tags. /// </summary> /// /// <param name="sTag"> /// Tag to get related tags for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="eNetworkLevel"> /// Network level to include. Must be NetworkLevel.One, OnePointFive, or /// Two. /// </param> /// /// <param name="sApiKey"> /// Flickr API key. /// </param> /// /// <param name="iRecursionLevel"> /// Recursion level for this call. Must be 1 or 2. Gets incremented when /// recursing. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// GraphMLXmlDocument being populated. /// </param> /// /// <param name="oTagDictionary"> /// The key is the tag name and the value is the corresponding GraphML XML /// node that represents the tag. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> //************************************************************************* protected void GetRelatedTagsRecursive( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey, Int32 iRecursionLevel, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, XmlNode> oTagDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert( !String.IsNullOrEmpty(sTag) ); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert( !String.IsNullOrEmpty(sApiKey) ); Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oTagDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); /* Here is what this method should do, based on the eNetworkLevel and iRecursionLevel parameters. eNetworkLevel |One | OnePointFive | Two ---|------------------| ------------------| ----------------- i 1 |Add all vertices. | Add all vertices. | Add all vertices. R | | | e |Add all edges. | Add all edges. | Add all edges. c | | | u |Do not recurse. | Recurse. | Recurse. r | | | s ---|------------------|-------------------|------------------ i 2 |Impossible. | Do not add | Add all vertices. o | | vertices. | n | | | L | | Add edges only if | Add all edges. e | | vertices are | v | | already included. | e | | | l | | Do not recurse. | Do not recurse. | | | ---|------------------|-------------------|------------------ */ Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel, iRecursionLevel); Boolean bNeedToAppendVertices = GetNeedToAppendVertices( eNetworkLevel, iRecursionLevel); ReportProgress("Getting tags related to \"" + sTag + "\"."); String sUrl = GetFlickrMethodUrl( "flickr.tags.getRelated", sApiKey, "&tag=" + UrlUtil.EncodeUrlParameter(sTag) ); XmlDocument oXmlDocument; try { oXmlDocument = GetXmlDocument(sUrl, oRequestStatistics); } catch (Exception oException) { // If the exception is not a WebException or XmlException, or if // none of the network has been obtained yet, throw the exception. if (!ExceptionIsWebOrXml(oException) || !oGraphMLXmlDocument.HasVertexXmlNode) { throw oException; } return; } // The document consists of a single "tags" node with zero or more // "tag" child nodes. String sOtherTag = null; XmlNodeList oTagNodes = oXmlDocument.DocumentElement.SelectNodes( "tags/tag"); if (oTagNodes.Count > 0) { AppendVertexXmlNode(sTag, oGraphMLXmlDocument, oTagDictionary); } foreach (XmlNode oTagNode in oTagNodes) { sOtherTag = XmlUtil2.SelectRequiredSingleNodeAsString(oTagNode, "text()", null); if (bNeedToAppendVertices) { AppendVertexXmlNode(sOtherTag, oGraphMLXmlDocument, oTagDictionary); } if ( bNeedToAppendVertices || oTagDictionary.ContainsKey(sOtherTag) ) { oGraphMLXmlDocument.AppendEdgeXmlNode(sTag, sOtherTag); } } if (bNeedToRecurse) { foreach (XmlNode oTagNode in oTagNodes) { sOtherTag = XmlUtil2.SelectRequiredSingleNodeAsString(oTagNode, "text()", null); GetRelatedTagsRecursive(sOtherTag, eWhatToInclude, eNetworkLevel, sApiKey, 2, oGraphMLXmlDocument, oTagDictionary, oRequestStatistics); } } }
//************************************************************************* // Method: GetNetwork() // /// <summary> /// Synchronously gets a directed network of people who have tweeted a /// specified search term. /// </summary> /// /// <param name="searchTerm"> /// The term to search for. /// </param> /// /// <param name="whatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="maximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <returns> /// An XmlDocument containing the network as GraphML. /// </returns> //************************************************************************* public XmlDocument GetNetwork( String searchTerm, WhatToInclude whatToInclude, Int32 maximumPeoplePerRequest ) { Debug.Assert( !String.IsNullOrEmpty(searchTerm) ); Debug.Assert(maximumPeoplePerRequest > 0); AssertValid(); return ( GetSearchNetworkInternal(searchTerm, whatToInclude, maximumPeoplePerRequest) ); }
GetNetworkInternal ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumStatuses ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumStatuses > 0); Debug.Assert(iMaximumStatuses != Int32.MaxValue); AssertValid(); BeforeGetNetwork(); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); Boolean bIncludeStatuses = WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.Statuses); GraphMLXmlDocument oGraphMLXmlDocument = TwitterSearchNetworkGraphMLUtil.CreateGraphMLXmlDocument( bIncludeStatistics, bIncludeStatuses); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumStatuses, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnUnexpectedException(oException, oGraphMLXmlDocument, oRequestStatistics); } OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics, GetNetworkDescription(sSearchTerm, eWhatToInclude, iMaximumStatuses, oGraphMLXmlDocument), "Twitter Search " + sSearchTerm ); return (oGraphMLXmlDocument); }
//************************************************************************* // Method: AppendFollowedEdgeXmlNodes() // /// <summary> /// Appends an edge XML node for each pair of people who have tweeted a /// specified search term and one follows the other. /// </summary> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// GraphMLXmlDocument being populated. /// </param> /// /// <param name="oScreenNameDictionary"> /// The key is the screen name in lower case and the value is the /// corresponding TwitterVertex. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> //************************************************************************* protected void AppendFollowedEdgeXmlNodes( WhatToInclude eWhatToInclude, Int32 iMaximumPeoplePerRequest, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, TwitterVertex> oScreenNameDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oScreenNameDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); // Look at the people followed by each author, and if a followed has // also tweeted the search term, add an edge between the author and the // followed. foreach (String sScreenName in oScreenNameDictionary.Keys) { ReportProgressForFollowedOrFollowing(sScreenName, true); foreach ( XmlNode oOtherUserXmlNode in EnumerateXmlNodes( GetFollowedOrFollowingUrl(sScreenName, true), "users_list/users/user", null, null, Int32.MaxValue, iMaximumPeoplePerRequest, false, true, oRequestStatistics) ) { String sOtherScreenName; if ( !TryGetScreenName(oOtherUserXmlNode, out sOtherScreenName) ) { continue; } TwitterVertex oOtherTwitterVertex; if ( oScreenNameDictionary.TryGetValue(sOtherScreenName, out oOtherTwitterVertex) ) { XmlNode oOtherVertexXmlNode = oOtherTwitterVertex.VertexXmlNode; XmlNode oEdgeXmlNode = AppendEdgeXmlNode( oGraphMLXmlDocument, sScreenName, sOtherScreenName, "Followed"); AppendRelationshipDateUtcGraphMLAttributeValue( oGraphMLXmlDocument, oEdgeXmlNode, oRequestStatistics); if (bIncludeStatistics && !AppendFromUserXmlNodeCalled(oGraphMLXmlDocument, oOtherVertexXmlNode) ) { // The other vertex node has no statistics. Add them. AppendFromUserXmlNode(oOtherUserXmlNode, oGraphMLXmlDocument, oOtherTwitterVertex, bIncludeStatistics, false); } } } } }
GetRelatedTagsRecursive ( String sTag, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, String sApiKey, Int32 iRecursionLevel, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary <String, XmlNode> oTagDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert(!String.IsNullOrEmpty(sTag)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oTagDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); /* * Here is what this method should do, based on the eNetworkLevel and * iRecursionLevel parameters. * * eNetworkLevel * |One | OnePointFive | Two * ---|------------------| ------------------| ----------------- * i 1 |Add all vertices. | Add all vertices. | Add all vertices. * R | | | * e |Add all edges. | Add all edges. | Add all edges. * c | | | * u |Do not recurse. | Recurse. | Recurse. * r | | | * s ---|------------------|-------------------|------------------ * i 2 |Impossible. | Do not add | Add all vertices. * o | | vertices. | * n | | | * L | | Add edges only if | Add all edges. * e | | vertices are | * v | | already included. | * e | | | * l | | Do not recurse. | Do not recurse. | | | | ---|------------------|-------------------|------------------ */ Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel, iRecursionLevel); Boolean bNeedToAppendVertices = GetNeedToAppendVertices( eNetworkLevel, iRecursionLevel); ReportProgress("Getting tags related to \"" + sTag + "\"."); String sUrl = GetFlickrMethodUrl("flickr.tags.getRelated", sApiKey, "&tag=" + UrlUtil.EncodeUrlParameter(sTag)); XmlDocument oXmlDocument; try { oXmlDocument = GetXmlDocument(sUrl, oRequestStatistics); } catch (Exception oException) { // If the exception is not a WebException or XmlException, or if // none of the network has been obtained yet, throw the exception. if (!HttpSocialNetworkUtil.ExceptionIsWebOrXml(oException) || !oGraphMLXmlDocument.HasVertexXmlNode) { throw oException; } return; } // The document consists of a single "tags" node with zero or more // "tag" child nodes. String sOtherTag = null; XmlNodeList oTagNodes = oXmlDocument.DocumentElement.SelectNodes( "tags/tag"); if (oTagNodes.Count > 0) { AppendVertexXmlNode(sTag, oGraphMLXmlDocument, oTagDictionary); } foreach (XmlNode oTagNode in oTagNodes) { sOtherTag = XmlUtil2.SelectRequiredSingleNodeAsString(oTagNode, "text()", null); if (bNeedToAppendVertices) { AppendVertexXmlNode(sOtherTag, oGraphMLXmlDocument, oTagDictionary); } if (bNeedToAppendVertices || oTagDictionary.ContainsKey(sOtherTag)) { oGraphMLXmlDocument.AppendEdgeXmlNode(sTag, sOtherTag); } } if (bNeedToRecurse) { foreach (XmlNode oTagNode in oTagNodes) { sOtherTag = XmlUtil2.SelectRequiredSingleNodeAsString(oTagNode, "text()", null); GetRelatedTagsRecursive(sOtherTag, eWhatToInclude, eNetworkLevel, sApiKey, 2, oGraphMLXmlDocument, oTagDictionary, oRequestStatistics); } } }
GetUserNetworkInternal ( String sScreenName, WhatToInclude eWhatToInclude, NetworkLevel eNetworkLevel, Int32 iMaximumPerRequest, String sApiKey, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert(!String.IsNullOrEmpty(sScreenName)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPerRequest > 0); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); String sUserID; // Get the user's ID and the correct case of the screen name. FlickrScreenNameToUserID(sScreenName, sApiKey, oRequestStatistics, out sUserID, out sScreenName); // The key is the user ID name and the value is the corresponding // GraphML XML node that represents the user. This is used to prevent // the same user ID from being added to the XmlDocument twice. Dictionary <String, XmlNode> oUserIDDictionary = new Dictionary <String, XmlNode>(); // Include contacts, commenters, both, or neither. Boolean [] abIncludes = new Boolean[] { WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.ContactVertices), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.CommenterVertices), }; for (Int32 i = 0; i < 2; i++) { if (abIncludes[i]) { GetUserNetworkRecursive(sUserID, sScreenName, eWhatToInclude, (i == 0), eNetworkLevel, iMaximumPerRequest, sApiKey, 1, oGraphMLXmlDocument, oUserIDDictionary, oRequestStatistics); } } if (WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.UserInformation)) { AppendUserInformationGraphMLAttributeValues(oGraphMLXmlDocument, oUserIDDictionary, sApiKey, oRequestStatistics); } }
//************************************************************************* // Method: GetNetworkAsync() // /// <summary> /// Asynchronously gets an undirected network of related YouTube videos. /// </summary> /// /// <param name="searchTerm"> /// The term to search for. /// </param> /// /// <param name="whatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="maximumVideos"> /// Maximum number of videos to request, or Int32.MaxValue for no limit. /// </param> /// /// <remarks> /// When the analysis completes, the <see /// cref="HttpNetworkAnalyzerBase.AnalysisCompleted" /> event fires. The /// <see cref="RunWorkerCompletedEventArgs.Result" /> property will return /// an XmlDocument containing the network as GraphML. /// /// <para> /// To cancel the analysis, call <see /// cref="HttpNetworkAnalyzerBase.CancelAsync" />. /// </para> /// /// </remarks> //************************************************************************* public void GetNetworkAsync( String searchTerm, WhatToInclude whatToInclude, Int32 maximumVideos ) { Debug.Assert( !String.IsNullOrEmpty(searchTerm) ); Debug.Assert(maximumVideos > 0); AssertValid(); const String MethodName = "GetNetworkAsync"; CheckIsBusy(MethodName); // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs(); oGetNetworkAsyncArgs.SearchTerm = searchTerm; oGetNetworkAsyncArgs.WhatToInclude = whatToInclude; oGetNetworkAsyncArgs.MaximumVideos = maximumVideos; m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs); }
CreateGraphMLXmlDocument ( WhatToInclude eWhatToInclude ) { AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = new GraphMLXmlDocument(false); NodeXLGraphMLUtil.DefineEdgeRelationshipGraphMLAttribute( oGraphMLXmlDocument); if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges) ) { oGraphMLXmlDocument.DefineEdgeStringGraphMLAttributes( SharedCategoryID, "Shared Category"); } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCommenterEdges) ) { oGraphMLXmlDocument.DefineEdgeStringGraphMLAttributes( SharedCommenterID, "Shared Commenter"); } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedVideoResponderEdges) ) { oGraphMLXmlDocument.DefineEdgeStringGraphMLAttributes( SharedVideoResponderID, "Shared Video Responder"); } oGraphMLXmlDocument.DefineVertexStringGraphMLAttributes( TitleID, "Title", AuthorID, "Author", CreatedDateUtcID, "Created Date (UTC)" ); oGraphMLXmlDocument.DefineGraphMLAttribute(false, RatingID, "Rating", "double", null); oGraphMLXmlDocument.DefineGraphMLAttributes(false, "int", ViewsID, "Views", FavoritedID, "Favorited", CommentsID, "Comments" ); NodeXLGraphMLUtil.DefineVertexImageFileGraphMLAttribute( oGraphMLXmlDocument); NodeXLGraphMLUtil.DefineVertexCustomMenuGraphMLAttributes( oGraphMLXmlDocument); return (oGraphMLXmlDocument); }
//************************************************************************* // Method: GetVideoNetworkInternal() // /// <overloads> /// Gets a network of related YouTube videos. /// </overloads> /// /// <summary> /// Gets a network of related YouTube videos. /// </summary> /// /// <param name="sSearchTerm"> /// The term to search for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumVideos"> /// Maximum number of videos to request, or Int32.MaxValue for no limit. /// </param> /// /// <returns> /// An XmlDocument containing the network as GraphML. /// </returns> //************************************************************************* protected XmlDocument GetVideoNetworkInternal( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumVideos > 0); AssertValid(); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetVideoNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumVideos, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return (oGraphMLXmlDocument); }
//************************************************************************* // Method: GetNetwork() // /// <summary> /// Synchronously gets a directed network of Twitter users. /// </summary> /// /// <param name="screenNameToAnalyze"> /// The screen name of the Twitter user whose network should be analyzed. /// </param> /// /// <param name="whatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="networkLevel"> /// Network level to include. /// </param> /// /// <param name="maximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <returns> /// An XmlDocument containing the network as GraphML. /// </returns> //************************************************************************* public XmlDocument GetNetwork( String screenNameToAnalyze, WhatToInclude whatToInclude, NetworkLevel networkLevel, Int32 maximumPeoplePerRequest ) { Debug.Assert( !String.IsNullOrEmpty(screenNameToAnalyze) ); Debug.Assert(networkLevel == NetworkLevel.One || networkLevel == NetworkLevel.OnePointFive || networkLevel == NetworkLevel.Two); Debug.Assert(maximumPeoplePerRequest > 0); AssertValid(); return ( GetUserNetworkInternal(screenNameToAnalyze, whatToInclude, networkLevel, maximumPeoplePerRequest) ); }
//************************************************************************* // Method: WhatToIncludeFlagIsSet() // /// <summary> /// Checks whether a flag is set in an ORed combination of WhatToInclude /// flags. /// </summary> /// /// <param name="eORedEnumFlags"> /// Zero or more ORed Enum flags. /// </param> /// /// <param name="eORedEnumFlagsToCheck"> /// One or more Enum flags to check. /// </param> /// /// <returns> /// true if any of the <paramref name="eORedEnumFlagsToCheck" /> flags are /// set in <paramref name="eORedEnumFlags" />. /// </returns> //************************************************************************* protected Boolean WhatToIncludeFlagIsSet( WhatToInclude eORedEnumFlags, WhatToInclude eORedEnumFlagsToCheck ) { return ( (eORedEnumFlags & eORedEnumFlagsToCheck) != 0 ); }
//************************************************************************* // Method: GetNetworkAsync() // /// <summary> /// Asynchronously gets an undirected network of Flickr tags related to a /// specified tag. /// </summary> /// /// <param name="tag"> /// Tag to get related tags for. /// </param> /// /// <param name="whatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="networkLevel"> /// Network level to include. /// </param> /// /// <param name="apiKey"> /// Flickr API key. /// </param> /// /// <remarks> /// When the analysis completes, the <see /// cref="HttpNetworkAnalyzerBase.AnalysisCompleted" /> event fires. The /// <see cref="RunWorkerCompletedEventArgs.Result" /> property will return /// an XmlDocument containing the network as GraphML. /// /// <para> /// To cancel the analysis, call <see /// cref="HttpNetworkAnalyzerBase.CancelAsync" />. /// </para> /// /// </remarks> //************************************************************************* public void GetNetworkAsync( String tag, WhatToInclude whatToInclude, NetworkLevel networkLevel, String apiKey ) { Debug.Assert( !String.IsNullOrEmpty(tag) ); Debug.Assert(networkLevel == NetworkLevel.One || networkLevel == NetworkLevel.OnePointFive || networkLevel == NetworkLevel.Two); Debug.Assert( !String.IsNullOrEmpty(apiKey) ); AssertValid(); const String MethodName = "GetNetworkAsync"; CheckIsBusy(MethodName); // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs(); oGetNetworkAsyncArgs.Tag = tag; oGetNetworkAsyncArgs.NetworkLevel = networkLevel; oGetNetworkAsyncArgs.WhatToInclude = whatToInclude; oGetNetworkAsyncArgs.ApiKey = apiKey; m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs); }
//************************************************************************* // Method: AppendVertexXmlNodes() // /// <summary> /// Appends a vertex XML node for each person who has tweeted a specified /// search term. /// </summary> /// /// <param name="sSearchTerm"> /// The term to search for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument being populated. /// </param> /// /// <param name="oScreenNameDictionary"> /// The key is the screen name in lower case and the value is the /// corresponding TwitterVertex. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> //************************************************************************* protected void AppendVertexXmlNodes( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumPeoplePerRequest, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary<String, TwitterVertex> oScreenNameDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oScreenNameDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); // Convert spaces in the search term to a plus sign. // // (Note: Don't try to use Twitter's "show_user" URL parameter in an // attempt to get a "user" XML node for each author. That's not what // the URL parameter does.) String sUrl = String.Format( "http://search.twitter.com/search.atom?q={0}&rpp=100" , UrlUtil.EncodeUrlParameter(sSearchTerm).Replace("%20", "+") ); ReportProgress("Getting a list of tweets."); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); Boolean bIncludeStatuses = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statuses); // The document consists of an "entry" XML node for each tweet that // contains the search term. Multiple tweets may have the same author, // so we have to enumerate until the requested maximum number of unique // authors is reached. foreach ( XmlNode oAuthorNameXmlNode in EnumerateXmlNodes( sUrl, "a:feed/a:entry/a:author/a:name", "a", AtomNamespaceUri, 15, Int32.MaxValue, true, false, oRequestStatistics) ) { if (oScreenNameDictionary.Count == iMaximumPeoplePerRequest) { break; } // The author name is in this format: // // james_laker (James Laker) String sAuthorName; if ( !XmlUtil2.TrySelectSingleNodeAsString(oAuthorNameXmlNode, "text()", null, out sAuthorName) ) { continue; } Int32 iIndexOfSpace = sAuthorName.IndexOf(' '); String sScreenName = sAuthorName; if (iIndexOfSpace != -1) { sScreenName = sAuthorName.Substring(0, iIndexOfSpace); } sScreenName = sScreenName.ToLower(); TwitterVertex oTwitterVertex; if ( !TryAppendVertexXmlNode(sScreenName, null, oGraphMLXmlDocument, oScreenNameDictionary, bIncludeStatistics, false, out oTwitterVertex) ) { // A tweet for the author has already been found. continue; } XmlNode oVertexXmlNode = oTwitterVertex.VertexXmlNode; XmlNode oEntryXmlNode = oAuthorNameXmlNode.ParentNode.ParentNode; XmlNamespaceManager oXmlNamespaceManager = new XmlNamespaceManager( oEntryXmlNode.OwnerDocument.NameTable); oXmlNamespaceManager.AddNamespace("a", AtomNamespaceUri); // Get the image URL and status for the tweet's author. AppendStringGraphMLAttributeValue(oEntryXmlNode, "a:link[@rel='image']/@href", oXmlNamespaceManager, oGraphMLXmlDocument, oVertexXmlNode, ImageFileID); String sStatus; if ( XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "a:title/text()", oXmlNamespaceManager, out sStatus) ) { String sStatusDateUtc; if ( XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "a:published/text()", oXmlNamespaceManager, out sStatusDateUtc) ) { sStatusDateUtc = TwitterDateParser.ParseTwitterDate( sStatusDateUtc); } oTwitterVertex.StatusForAnalysis = sStatus; oTwitterVertex.StatusForAnalysisDateUtc = sStatusDateUtc; if (bIncludeStatuses) { oGraphMLXmlDocument.AppendGraphMLAttributeValue( oVertexXmlNode, StatusID, sStatus); if ( !String.IsNullOrEmpty(sStatusDateUtc) ) { oGraphMLXmlDocument.AppendGraphMLAttributeValue( oVertexXmlNode, StatusDateUtcID, sStatusDateUtc); } } } } }
GetUserNetworkRecursive ( String sUserID, String sScreenName, WhatToInclude eWhatToInclude, Boolean bIncludeContactsThisCall, NetworkLevel eNetworkLevel, Int32 iMaximumPerRequest, String sApiKey, Int32 iRecursionLevel, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary <String, XmlNode> oUserIDDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert(!String.IsNullOrEmpty(sUserID)); Debug.Assert(!String.IsNullOrEmpty(sScreenName)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPerRequest > 0); Debug.Assert(!String.IsNullOrEmpty(sApiKey)); Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oUserIDDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); /* * Here is what this method should do, based on the eNetworkLevel and * iRecursionLevel parameters. * * eNetworkLevel * |One | OnePointFive | Two * ---|------------------| ------------------| ----------------- * i 1 |Add all vertices. | Add all vertices. | Add all vertices. * R | | | * e |Add all edges. | Add all edges. | Add all edges. * c | | | * u |Do not recurse. | Recurse. | Recurse. * r | | | * s ---|------------------|-------------------|------------------ * i 2 |Impossible. | Do not add | Add all vertices. * o | | vertices. | * n | | | * L | | Add edges only if | Add all edges. * e | | vertices are | * v | | already included. | * e | | | * l | | Do not recurse. | Do not recurse. | | | | ---|------------------|-------------------|------------------ */ Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel, iRecursionLevel); Boolean bNeedToAppendVertices = GetNeedToAppendVertices(eNetworkLevel, iRecursionLevel); List <String> oUserIDsToRecurse = new List <String>(); ReportProgressForContactsOrCommenters(sScreenName, bIncludeContactsThisCall); Boolean bThisUserAppended = false; foreach (XmlNode oChildXmlNode in GetContactsOrCommentersEnumerator( sUserID, bIncludeContactsThisCall, iMaximumPerRequest, oGraphMLXmlDocument, sApiKey, oRequestStatistics)) { String sOtherScreenName, sOtherUserID; if ( !XmlUtil2.TrySelectSingleNodeAsString(oChildXmlNode, bIncludeContactsThisCall ? "@username" : "@authorname", null, out sOtherScreenName) || !XmlUtil2.TrySelectSingleNodeAsString(oChildXmlNode, bIncludeContactsThisCall ? "@nsid" : "@author", null, out sOtherUserID) ) { continue; } if (!bThisUserAppended) { // Append a vertex node for this request's user. // // This used to be done after the foreach loop, which avoided // the need for a "bThisUserAppended" flag. That caused the // following bug: If a YouTube error occurred within // EnumerateXmlNodes() after some edges had been added, and the // user decided to import the resulting partial network, the // GraphML might contain edges that referenced "this user" // without containing a vertex for "this user." That is an // illegal state for GraphML, which the ExcelTemplate project // caught and reported as an error. TryAppendVertexXmlNode(sUserID, sScreenName, oGraphMLXmlDocument, oUserIDDictionary); bThisUserAppended = true; } if (bNeedToAppendVertices) { if ( TryAppendVertexXmlNode(sOtherUserID, sOtherScreenName, oGraphMLXmlDocument, oUserIDDictionary) && bNeedToRecurse ) { oUserIDsToRecurse.Add(sOtherUserID); } } if (bNeedToAppendVertices || oUserIDDictionary.ContainsKey(sOtherUserID)) { // Append an edge node and optional attributes. XmlNode oEdgeXmlNode; if (bIncludeContactsThisCall) { oEdgeXmlNode = AppendEdgeXmlNode(oGraphMLXmlDocument, sScreenName, sOtherScreenName, "Contact"); } else { // (Note the swapping of screen names in the commenter // case.) oEdgeXmlNode = AppendEdgeXmlNode(oGraphMLXmlDocument, sOtherScreenName, sScreenName, "Commenter"); UInt32 uCommentDateUtc; if (XmlUtil2.TrySelectSingleNodeAsUInt32(oChildXmlNode, "@datecreate", null, out uCommentDateUtc)) { DateTime oCommentDateUtc = DateTimeUtil2.UnixTimestampToDateTimeUtc( uCommentDateUtc); oGraphMLXmlDocument.AppendGraphMLAttributeValue( oEdgeXmlNode, CommentDateUtcID, ExcelDateTimeUtil.DateTimeToStringLocale1033( oCommentDateUtc, ExcelColumnFormat.DateAndTime) ); } AppendStringGraphMLAttributeValue(oChildXmlNode, "@permalink", null, oGraphMLXmlDocument, oEdgeXmlNode, CommentUrlID); } } } if (bNeedToRecurse) { foreach (String sUserIDToRecurse in oUserIDsToRecurse) { XmlNode oVertexXmlNode = oUserIDDictionary[sUserIDToRecurse]; String sScreenNameToRecurse = GetScreenNameFromVertexXmlNode( oVertexXmlNode); GetUserNetworkRecursive(sUserIDToRecurse, sScreenNameToRecurse, eWhatToInclude, bIncludeContactsThisCall, eNetworkLevel, iMaximumPerRequest, sApiKey, 2, oGraphMLXmlDocument, oUserIDDictionary, oRequestStatistics); } } }
GetUserNetworkRecursive ( String sUserName, WhatToInclude eWhatToInclude, Boolean bIncludeFriendsThisCall, NetworkLevel eNetworkLevel, Int32 iMaximumPeoplePerRequest, Int32 iRecursionLevel, GraphMLXmlDocument oGraphMLXmlDocument, Dictionary <String, XmlNode> oUserNameDictionary, RequestStatistics oRequestStatistics ) { Debug.Assert(!String.IsNullOrEmpty(sUserName)); Debug.Assert(eNetworkLevel == NetworkLevel.One || eNetworkLevel == NetworkLevel.OnePointFive || eNetworkLevel == NetworkLevel.Two); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2); Debug.Assert(oGraphMLXmlDocument != null); Debug.Assert(oUserNameDictionary != null); Debug.Assert(oRequestStatistics != null); AssertValid(); /* * Here is what this method should do, based on the eNetworkLevel and * iRecursionLevel parameters. * * eNetworkLevel * |One | OnePointFive | Two * ---|------------------| ------------------| ----------------- * i 1 |Add all vertices. | Add all vertices. | Add all vertices. * R | | | * e |Add all edges. | Add all edges. | Add all edges. * c | | | * u |Do not recurse. | Recurse. | Recurse. * r | | | * s ---|------------------|-------------------|------------------ * i 2 |Impossible. | Do not add | Add all vertices. * o | | vertices. | * n | | | * L | | Add edges only if | Add all edges. * e | | vertices are | * v | | already included. | * e | | | * l | | Do not recurse. | Do not recurse. | | | | ---|------------------|-------------------|------------------ */ Boolean bNeedToRecurse = GetNeedToRecurse(eNetworkLevel, iRecursionLevel); List <String> oUserNamesToRecurse = new List <String>(); ReportProgressForFriendsOrSubscriptions(sUserName, bIncludeFriendsThisCall); Boolean bThisUserAppended = false; // If the GraphMLXmlDocument already contains at least one vertex node, // then this is the second time that this method has been called, a // partial network has already been obtained, and most errors should // now be skipped. However, if none of the network has been obtained // yet, errors on page 1 should throw an immediate exception. Boolean bSkipMostPage1Errors = oGraphMLXmlDocument.HasVertexXmlNode; // The document consists of a single "feed" node with zero or more // "entry" child nodes. foreach (XmlNode oEntryXmlNode in EnumerateXmlNodes( GetFriendsOrSubscriptionsUrl(sUserName, bIncludeFriendsThisCall), "a:feed/a:entry", iMaximumPeoplePerRequest, bSkipMostPage1Errors, oRequestStatistics)) { XmlNamespaceManager oXmlNamespaceManager = CreateXmlNamespaceManager(oEntryXmlNode.OwnerDocument); String sOtherUserName; if (!XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "yt:username/text()", oXmlNamespaceManager, out sOtherUserName)) { continue; } if (!bThisUserAppended) { // Append a vertex node for this request's user. // // This used to be done after the foreach loop, which avoided // the need for a "bThisUserAppended" flag. That caused the // following bug: If a YouTube error occurred within // EnumerateXmlNodes() after some edges had been added, and the // user decided to import the resulting partial network, the // GraphML might contain edges that referenced "this user" // without containing a vertex for "this user." That is an // illegal state for GraphML, which the ExcelTemplate project // caught and reported as an error. TryAppendVertexXmlNode(sUserName, null, oGraphMLXmlDocument, oUserNameDictionary); bThisUserAppended = true; } Boolean bNeedToAppendVertices = GetNeedToAppendVertices( eNetworkLevel, iRecursionLevel); if (bNeedToAppendVertices) { if ( TryAppendVertexXmlNode(sOtherUserName, oEntryXmlNode, oGraphMLXmlDocument, oUserNameDictionary) && bNeedToRecurse ) { oUserNamesToRecurse.Add(sOtherUserName); } } if (bNeedToAppendVertices || oUserNameDictionary.ContainsKey(sOtherUserName)) { String sRelationship; if (bIncludeFriendsThisCall) { sRelationship = "Friend of"; } else { sRelationship = "Subscribes to"; String sSubscriptionType = null; if (XmlUtil2.TrySelectSingleNodeAsString(oEntryXmlNode, "a:category[@scheme='http://gdata.youtube.com/schemas/" + "2007/subscriptiontypes.cat']/@term", oXmlNamespaceManager, out sSubscriptionType)) { sRelationship += " " + sSubscriptionType; } } NodeXLGraphMLUtil.AppendEdgeXmlNode(oGraphMLXmlDocument, sUserName, sOtherUserName, sRelationship); } } if (bNeedToRecurse) { foreach (String sUserNameToRecurse in oUserNamesToRecurse) { GetUserNetworkRecursive(sUserNameToRecurse, eWhatToInclude, bIncludeFriendsThisCall, eNetworkLevel, iMaximumPeoplePerRequest, 2, oGraphMLXmlDocument, oUserNameDictionary, oRequestStatistics); } } }
//************************************************************************* // Method: GetSearchNetworkInternal() // /// <overloads> /// Gets the network of people who have tweeted a specified search term. /// </overloads> /// /// <summary> /// Gets the network of people who have tweeted a specified search term. /// </summary> /// /// <param name="sSearchTerm"> /// The term to search for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <returns> /// An XmlDocument containing the network as GraphML. /// </returns> //************************************************************************* protected XmlDocument GetSearchNetworkInternal( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumPeoplePerRequest ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumPeoplePerRequest > 0); AssertValid(); BeforeGetNetwork(); Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument( bIncludeStatistics, false); RequestStatistics oRequestStatistics = new RequestStatistics(); try { GetSearchNetworkInternal(sSearchTerm, eWhatToInclude, iMaximumPeoplePerRequest, oRequestStatistics, oGraphMLXmlDocument); } catch (Exception oException) { OnTerminatingException(oException); } OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics); return (oGraphMLXmlDocument); }
//************************************************************************* // Method: GetSearchNetworkInternal() // /// <summary> /// Gets the network of people who have tweeted a specified search term, /// given a GraphMLXmlDocument. /// </summary> /// /// <param name="sSearchTerm"> /// The term to search for. /// </param> /// /// <param name="eWhatToInclude"> /// Specifies what should be included in the network. /// </param> /// /// <param name="iMaximumPeoplePerRequest"> /// Maximum number of people to request for each query, or Int32.MaxValue /// for no limit. /// </param> /// /// <param name="oRequestStatistics"> /// A <see cref="RequestStatistics" /> object that is keeping track of /// requests made while getting the network. /// </param> /// /// <param name="oGraphMLXmlDocument"> /// The GraphMLXmlDocument to populate with the requested network. /// </param> //************************************************************************* protected void GetSearchNetworkInternal( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumPeoplePerRequest, RequestStatistics oRequestStatistics, GraphMLXmlDocument oGraphMLXmlDocument ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumPeoplePerRequest > 0); Debug.Assert(oRequestStatistics != null); Debug.Assert(oGraphMLXmlDocument != null); AssertValid(); if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.Statuses) ) { oGraphMLXmlDocument.DefineGraphMLAttribute(false, StatusID, "Tweet", "string", null); oGraphMLXmlDocument.DefineGraphMLAttribute(false, StatusDateUtcID, "Tweet Date (UTC)", "string", null); } // The key is the screen name and the value is the corresponding // TwitterVertex. This is used to prevent the same screen name from // being added to the XmlDocument twice. Dictionary<String, TwitterVertex> oScreenNameDictionary = new Dictionary<String, TwitterVertex>(); // First, add a vertex for each person who has tweeted the search term. AppendVertexXmlNodes(sSearchTerm, eWhatToInclude, iMaximumPeoplePerRequest, oGraphMLXmlDocument, oScreenNameDictionary, oRequestStatistics); if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.FollowedEdges) ) { // Look at the people followed by each author, and if a followed // has also tweeted the search term, add an edge between the author // and the followed. AppendFollowedEdgeXmlNodes(eWhatToInclude, iMaximumPeoplePerRequest, oGraphMLXmlDocument, oScreenNameDictionary, oRequestStatistics); } Boolean bIncludeStatistics = WhatToIncludeFlagIsSet( eWhatToInclude, WhatToInclude.Statistics); AppendMissingGraphMLAttributeValues(oGraphMLXmlDocument, oScreenNameDictionary, bIncludeStatistics, false, oRequestStatistics); AppendRepliesToAndMentionsXmlNodes(oGraphMLXmlDocument, oScreenNameDictionary, WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.RepliesToEdges), WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.MentionsEdges) ); }
GetNetworkDescription ( String sSearchTerm, WhatToInclude eWhatToInclude, Int32 iMaximumVideos ) { Debug.Assert( !String.IsNullOrEmpty(sSearchTerm) ); Debug.Assert(iMaximumVideos > 0); AssertValid(); NetworkDescriber oNetworkDescriber = new NetworkDescriber(); oNetworkDescriber.AddSentence( "The graph represents the network of YouTube videos whose title," + " keywords, description, categories, or author\'s username" + " contain \"{0}\"." , sSearchTerm ); oNetworkDescriber.AddNetworkTime(NetworkSource); oNetworkDescriber.StartNewParagraph(); oNetworkDescriber.AddNetworkLimit(iMaximumVideos, "videos"); if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges | WhatToInclude.SharedCommenterEdges | WhatToInclude.SharedVideoResponderEdges ) ) { oNetworkDescriber.StartNewParagraph(); } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCategoryEdges) ) { oNetworkDescriber.AddSentence( "There is an edge for each pair of videos that have the same" + " category." ); } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedCommenterEdges) ) { oNetworkDescriber.AddSentence( "There is an edge for each pair of videos commented on by the" + " same user." ); } if ( WhatToIncludeFlagIsSet(eWhatToInclude, WhatToInclude.SharedVideoResponderEdges) ) { oNetworkDescriber.AddSentence( "There is an edge for each pair of videos responded to with" + " another video by the same user." ); } return ( oNetworkDescriber.ConcatenateSentences() ); }