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);
        }
Example #2
0
 /// <summary>
 /// Creates a new <see cref="SelectionCandidate"/> ranker.
 /// </summary>
 /// <param name="stabilityPolicy">Implementations at this stability level or higher are preferred. Lower levels are used only if there is no other choice.</param>
 /// <param name="networkUse">Controls how liberally network access is attempted.</param>
 /// <param name="languages">The preferred languages for the implementation.</param>
 /// <param name="isCached">Used to determine which implementations are already cached in the <see cref="IImplementationStore"/>.</param>
 public SelectionCandidateComparer(Stability stabilityPolicy, NetworkLevel networkUse, LanguageSet languages, Predicate <Implementation> isCached)
 {
     _stabilityPolicy = stabilityPolicy;
     _networkUse      = networkUse;
     _languages       = languages ?? throw new ArgumentNullException(nameof(languages));
     _isCached        = isCached ?? throw new ArgumentNullException(nameof(isCached));
 }
        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());
        }
        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);
        }
Example #5
0
        NetworkLevelToString
        (
            NetworkLevel eNetworkLevel
        )
        {
            AssertValid();

            String sNetworkLevel = String.Empty;

            switch (eNetworkLevel)
            {
            case NetworkLevel.One:

                sNetworkLevel = "1";
                break;

            case NetworkLevel.OnePointFive:

                sNetworkLevel = "1.5";
                break;

            case NetworkLevel.Two:

                sNetworkLevel = "2";
                break;

            case NetworkLevel.TwoPointFive:

                sNetworkLevel = "2.5";
                break;

            case NetworkLevel.Three:

                sNetworkLevel = "3";
                break;

            case NetworkLevel.ThreePointFive:

                sNetworkLevel = "3.5";
                break;

            case NetworkLevel.Four:

                sNetworkLevel = "4";
                break;

            case NetworkLevel.FourPointFive:

                sNetworkLevel = "4.5";
                break;

            default:

                Debug.Assert(false);
                break;
            }

            return(sNetworkLevel + "-level");
        }
        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);
            }
        }
Example #7
0
        private void Update()
        {
            if (_session == null)
            {
                return;
            }

            var result = _session.Process();

            switch (result.Type)
            {
            case ProcessingResult.ResultType.None:
                break;

            case ProcessingResult.ResultType.Error:
                Debug.LogError($"{result.SessionError} {result.ServerErrorId}");
                break;

            case ProcessingResult.ResultType.Joined:
                Debug.Log("Waiting for players...");
                break;

            case ProcessingResult.ResultType.Started:
                Debug.Log("Session started");
                var inputSource = new KeyboardInputSource(Keyboard.current, Key.W, Key.S);
                var viewFactory = new LevelViewFactory(_assetLoader, new UiSystem(_assetLoader, _canvas));
                _level = new NetworkLevel(inputSource, viewFactory, this, result.StartMessage);
                break;

            case ProcessingResult.ResultType.Active:
                if (!_level.HandleReceivedInputs(_session.ReceivedInputs))
                {
                    Debug.LogError(SessionError.ProtocolError);
                    break;
                }

                var(inputs, optMsgFinish) = _level.Tick();
                var optError = _session.SendMessages(inputs, optMsgFinish);
                if (optError.HasValue)
                {
                    Debug.LogError(optError.Value);
                }
                break;

            case ProcessingResult.ResultType.Finished:
                var msgFinish = result.FinishMessage;
                var frames    = string.Join(", ", msgFinish.Frames);
                var hashes    = string.Join(", ", msgFinish.Hashes);
                var(frame, simulations) = _level.SimulationStats;
                Debug.Log($"Session finished at [{frames}] with state [{hashes}]\nSimulations: {frame} / {simulations}");
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        NetworkLevel myScript = (NetworkLevel)target;

        if (GUILayout.Button("Toggle Pause"))
        {
            myScript.TogglePause();
        }
    }
 private void Awake()
 {
     if (Instance)
     {
         Destroy(this);
     }
     else
     {
         Instance = this;
     }
     paused.OnValueChanged += OnPause;
     ready.OnValueChanged  += OnReady;
 }
        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());
        }
Example #11
0
        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);
        }
Example #13
0
        GetNeedToAppendVertices
        (
            NetworkLevel eNetworkLevel,
            Int32 iRecursionLevel
        )
        {
            Debug.Assert(eNetworkLevel == NetworkLevel.One ||
                         eNetworkLevel == NetworkLevel.OnePointFive ||
                         eNetworkLevel == NetworkLevel.Two);

            Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2);
            AssertValid();

            return(
                (eNetworkLevel != NetworkLevel.OnePointFive ||
                 iRecursionLevel == 1)
                );
        }
        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);
        }
Example #15
0
        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);
        }
Example #16
0
        GetNeedToRecurse
        (
            NetworkLevel eNetworkLevel,
            Int32 iRecursionLevel
        )
        {
            Debug.Assert(eNetworkLevel == NetworkLevel.One ||
                         eNetworkLevel == NetworkLevel.OnePointFive ||
                         eNetworkLevel == NetworkLevel.Two);

            Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2);
            AssertValid();

            return(
                iRecursionLevel == 1
                &&
                (eNetworkLevel == NetworkLevel.OnePointFive ||
                 eNetworkLevel == NetworkLevel.Two)
                );
        }
Example #17
0
        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));
        }
        GetTwitterUserNetworkConfiguration
        (
            out String screenNameToAnalyze,
            out TwitterUserNetworkAnalyzer.WhatToInclude whatToInclude,
            out NetworkLevel networkLevel,
            out Int32 maximumPeoplePerRequest,
            out String networkFileFolderPath,
            out NetworkFileFormats networkFileFormats,
            out Boolean automateNodeXLWorkbook
        )
        {
            AssertValid();
            Debug.Assert(m_oNetworkConfigurationXmlDocument != null);
            Debug.Assert(GetNetworkType() == NetworkType.TwitterUser);

            networkFileFolderPath = null;

            XmlNode oTwitterUserNetworkConfigurationNode =
                XmlUtil2.SelectRequiredSingleNode(
                    m_oNetworkConfigurationXmlDocument,
                    "/NetworkConfiguration/TwitterUserNetworkConfiguration",
                    null);

            screenNameToAnalyze = XmlUtil2.SelectRequiredSingleNodeAsString(
                oTwitterUserNetworkConfigurationNode,
                "ScreenNameToAnalyze/text()", null);

            whatToInclude =
                GetRequiredEnumValue <TwitterUserNetworkAnalyzer.WhatToInclude>(
                    oTwitterUserNetworkConfigurationNode, "WhatToInclude/text()",
                    "WhatToInclude");

            networkLevel = GetRequiredEnumValue <NetworkLevel>(
                oTwitterUserNetworkConfigurationNode, "NetworkLevel/text()",
                "NetworkLevel");

            GetTwitterCommonConfiguration(oTwitterUserNetworkConfigurationNode,
                                          out maximumPeoplePerRequest, out networkFileFolderPath,
                                          out networkFileFormats, out automateNodeXLWorkbook);
        }
Example #19
0
        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);
            }
        }
        /// <summary>
        /// Creates a new <see cref="SelectionCandidate"/> ranker.
        /// </summary>
        /// <param name="config">Used to retrieve global configuration.</param>
        /// <param name="isCached">Used to determine which implementations are already cached in the <see cref="IStore"/>.</param>
        /// <param name="stabilityPolicy">Implementations at this stability level or higher are preferred. Lower levels are used only if there is no other choice. Must not be <see cref="Stability.Unset"/>!</param>
        /// <param name="culture">The user's culture, used to determine preferred languages.</param>
        public SelectionCandidateComparer([NotNull] Config config, [NotNull] Predicate <Implementation> isCached, Stability stabilityPolicy, [NotNull] CultureInfo culture)
        {
            #region Sanity check
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (isCached == null)
            {
                throw new ArgumentNullException("isCached");
            }
            if (culture == null)
            {
                throw new ArgumentNullException("culture");
            }
            #endregion

            _networkUse      = config.NetworkUse;
            _isCached        = isCached;
            _stabilityPolicy = (stabilityPolicy == Stability.Unset)
                ? (config.HelpWithTesting ? Stability.Testing : Stability.Stable)
                : stabilityPolicy;
            _culture = culture;
        }
Example #22
0
        /// <summary>
        /// Creates a new <see cref="SelectionCandidate"/> ranker.
        /// </summary>
        /// <param name="config">Used to retrieve global configuration.</param>
        /// <param name="isCached">Used to determine which implementations are already cached in the <see cref="IStore"/>.</param>
        /// <param name="stabilityPolicy">Implementations at this stability level or higher are preferred. Lower levels are used only if there is no other choice. Must not be <see cref="Stability.Unset"/>!</param>
        /// <param name="languages">The preferred languages for the implementation.</param>
        public SelectionCandidateComparer([NotNull] Config config, [NotNull] Predicate <Implementation> isCached, Stability stabilityPolicy, [NotNull] LanguageSet languages)
        {
            #region Sanity check
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            if (isCached == null)
            {
                throw new ArgumentNullException(nameof(isCached));
            }
            if (languages == null)
            {
                throw new ArgumentNullException(nameof(languages));
            }
            #endregion

            _networkUse      = config.NetworkUse;
            _isCached        = isCached;
            _stabilityPolicy = (stabilityPolicy == Stability.Unset)
                ? (config.HelpWithTesting ? Stability.Testing : Stability.Stable)
                : stabilityPolicy;
            _languages = languages;
        }
    GetNetworkDescription
    (
        String sScreenNameToAnalyze,
        WhatToInclude eWhatToInclude,
        NetworkLevel eNetworkLevel,
        Int32 iMaximumPeoplePerRequest
    )
    {
        Debug.Assert( !String.IsNullOrEmpty(sScreenNameToAnalyze) );
        Debug.Assert(iMaximumPeoplePerRequest > 0);
        AssertValid();

        NetworkDescriber oNetworkDescriber = new NetworkDescriber();

        oNetworkDescriber.AddSentence(

            "The graph represents the {0} Twitter network of the user with the"
            + " username \"{1}\"."
            ,
            NetworkLevelToString(eNetworkLevel),
            sScreenNameToAnalyze
            );

        oNetworkDescriber.AddNetworkTime(NetworkSource);

        oNetworkDescriber.StartNewParagraph();

        Boolean bFollowedVertices = WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.FollowedVertices);

        Boolean bFollowerVertices = WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.FollowerVertices);

        if (bFollowedVertices)
        {
            oNetworkDescriber.AddSentence(
                "There is a vertex for each person followed by the user."
                );
        }

        if (bFollowerVertices)
        {
            oNetworkDescriber.AddSentence(
                "There is a vertex for each person following the user."
                );
        }

        oNetworkDescriber.AddNetworkLimit(iMaximumPeoplePerRequest, "people");

        if ( WhatToIncludeFlagIsSet(eWhatToInclude,

            WhatToInclude.FollowedFollowerEdges
            |
            WhatToInclude.RepliesToEdges
            |
            WhatToInclude.MentionsEdges
            ) )
        {
            oNetworkDescriber.StartNewParagraph();
        }

        if ( WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.FollowedFollowerEdges) )
        {
            if (bFollowedVertices)
            {
                oNetworkDescriber.AddSentence(
                    "There is an edge for each followed relationship."
                    );
            }

            if (bFollowerVertices)
            {
                oNetworkDescriber.AddSentence(
                    "There is an edge for each following relationship."
                    );
            }
        }

        if ( WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.RepliesToEdges) )
        {
            oNetworkDescriber.AddSentence(
                "There is an edge for each \"replies-to\" relationship in the"
                + " user's latest tweet."
                );
        }

        if ( WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.MentionsEdges) )
        {
            oNetworkDescriber.AddSentence(
                "There is an edge for each \"mentions\" relationship in the"
                + " user's latest tweet."
                );
        }

        return ( oNetworkDescriber.ConcatenateSentences() );
    }
        //*************************************************************************
        //  Method: DoDataExchange()
        //
        /// <summary>
        /// Transfers data between the dialog's fields and its controls.
        /// </summary>
        ///
        /// <param name="bFromControls">
        /// true to transfer data from the dialog's controls to its fields, false
        /// for the other direction.
        /// </param>
        ///
        /// <returns>
        /// true if the transfer was successful.
        /// </returns>
        //*************************************************************************
        protected override Boolean DoDataExchange(
            Boolean bFromControls
            )
        {
            if (bFromControls)
            {
            // Validate the controls.

            if ( !ValidateRequiredTextBox(txbScreenNameToAnalyze,
                    "Enter the screen name of a Twitter user.",
                    out m_sScreenNameToAnalyze) )
            {
                return (false);
            }

            m_bIncludeFollowedVertices = m_bIncludeFollowerVertices = false;

            if (radIncludeFollowedVertices.Checked)
            {
                m_bIncludeFollowedVertices = true;
            }
            else if (radIncludeFollowerVertices.Checked)
            {
                m_bIncludeFollowerVertices = true;
            }
            else
            {
                m_bIncludeFollowedVertices = m_bIncludeFollowerVertices = true;
            }

            m_bIncludeFollowedFollowerEdges =
                chkIncludeFollowedFollowerEdges.Checked;

            m_bIncludeRepliesToEdges = chkIncludeRepliesToEdges.Checked;
            m_bIncludeMentionsEdges = chkIncludeMentionsEdges.Checked;
            m_eNetworkLevel = usrNetworkLevel.Level;
            m_bIncludeLatestStatuses = chkIncludeLatestStatuses.Checked;
            m_iMaximumPeoplePerRequest = usrLimitToN.N;
            }
            else
            {
            txbScreenNameToAnalyze.Text = m_sScreenNameToAnalyze;

            if (m_bIncludeFollowedVertices)
            {
                if (m_bIncludeFollowerVertices)
                {
                    radIncludeFollowedAndFollower.Checked = true;
                }
                else
                {
                    radIncludeFollowedVertices.Checked = true;
                }
            }
            else
            {
                radIncludeFollowerVertices.Checked = true;
            }

            chkIncludeFollowedFollowerEdges.Checked =
                m_bIncludeFollowedFollowerEdges;

            chkIncludeRepliesToEdges.Checked = m_bIncludeRepliesToEdges;
            chkIncludeMentionsEdges.Checked = m_bIncludeMentionsEdges;
            usrNetworkLevel.Level = m_eNetworkLevel;
            chkIncludeLatestStatuses.Checked = m_bIncludeLatestStatuses;
            usrLimitToN.N = m_iMaximumPeoplePerRequest;

            EnableControls();
            }

            return (true);
        }
    GetNetworkInternal
    (
        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();

        sScreenNameToAnalyze = sScreenNameToAnalyze.ToLower();

        // The unique screen names of the people in the network.

        HashSet<String> oUniqueScreenNames = new HashSet<String>();

        // The key is the Twitter user ID and the value is the corresponding
        // TwitterUser.  This is used by AppendOnePointFiveDegreeNetwork() to
        // determine which Twitter users are in the 1-degree network.

        Dictionary<String, TwitterUser> oUserIDDictionary =
            new Dictionary<String, TwitterUser>();

        // The screen names of people followed by (or following) sScreenName.

        HashSet<String> oOneDegreeOtherScreenNames = new HashSet<String>();

        // Include followed, followers, both, or neither.

        Boolean [] abFollowedFollowerFlags = new Boolean[] {

            WhatToIncludeFlagIsSet(eWhatToInclude,
                WhatToInclude.FollowedVertices),

            WhatToIncludeFlagIsSet(eWhatToInclude,
                WhatToInclude.FollowerVertices),
            };

        for (Int32 i = 0; i < 2; i++)
        {
            if ( abFollowedFollowerFlags[i] )
            {
                // Always start with a 1-degree network.

                AppendOneDegreeNetwork(sScreenNameToAnalyze, eWhatToInclude,
                    (i == 0), iMaximumPeoplePerRequest, oGraphMLXmlDocument,
                    oUniqueScreenNames, oUserIDDictionary,
                    oOneDegreeOtherScreenNames, oRequestStatistics);
            }
        }

        for (Int32 i = 0; i < 2; i++)
        {
            if ( abFollowedFollowerFlags[i] )
            {
                if (eNetworkLevel == NetworkLevel.OnePointFive)
                {
                    // Append edges to make it a 1.5-degree network.

                    AppendOnePointFiveDegreeNetwork(sScreenNameToAnalyze,
                        eWhatToInclude, (i == 0), iMaximumPeoplePerRequest,
                        oGraphMLXmlDocument, oUserIDDictionary,
                        oOneDegreeOtherScreenNames, oRequestStatistics);
                }
                else if (eNetworkLevel == NetworkLevel.Two)
                {
                    // Append vertices and edges to make it a 2-degree network.

                    AppendTwoDegreeNetwork(sScreenNameToAnalyze,
                        eWhatToInclude, (i == 0), iMaximumPeoplePerRequest,
                        oGraphMLXmlDocument, oUniqueScreenNames,
                        oUserIDDictionary, oOneDegreeOtherScreenNames,
                        oRequestStatistics);
                }
            }
        }

        AppendRepliesToAndMentionsEdgeXmlNodes(oGraphMLXmlDocument,
            oUserIDDictionary.Values, oUniqueScreenNames,

            WhatToIncludeFlagIsSet(eWhatToInclude,
                WhatToInclude.RepliesToEdges),

            WhatToIncludeFlagIsSet(eWhatToInclude,
                WhatToInclude.MentionsEdges),

            false, 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);
                }
            }
        }
Example #27
0
        GetTwitterUserNetwork
        (
            DateTime oStartTime,
            String sNetworkConfigurationFilePath,
            NetworkConfigurationFileParser oNetworkConfigurationFileParser,
            out XmlDocument oXmlDocument,
            out String sNetworkFileFolderPath,
            out NetworkFileFormats eNetworkFileFormats,
            out Boolean bAutomateNodeXLWorkbook
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sNetworkConfigurationFilePath));
            Debug.Assert(oNetworkConfigurationFileParser != null);

            oXmlDocument            = null;
            sNetworkFileFolderPath  = null;
            eNetworkFileFormats     = NetworkFileFormats.None;
            bAutomateNodeXLWorkbook = false;

            String sScreenNameToAnalyze = null;

            TwitterUserNetworkAnalyzer.WhatToInclude eWhatToInclude =
                TwitterUserNetworkAnalyzer.WhatToInclude.None;

            NetworkLevel eNetworkLevel            = NetworkLevel.One;
            Int32        iMaximumPeoplePerRequest = Int32.MaxValue;

            try
            {
                oNetworkConfigurationFileParser.GetTwitterUserNetworkConfiguration(
                    out sScreenNameToAnalyze, out eWhatToInclude,
                    out eNetworkLevel, out iMaximumPeoplePerRequest,
                    out sNetworkFileFolderPath, out eNetworkFileFormats,
                    out bAutomateNodeXLWorkbook);
            }
            catch (XmlException oXmlException)
            {
                // (This call exits the program.)

                OnNetworkConfigurationFileException(oXmlException);
            }

            TwitterUserNetworkAnalyzer oTwitterUserNetworkAnalyzer =
                new TwitterUserNetworkAnalyzer();

            oTwitterUserNetworkAnalyzer.ProgressChanged +=
                new ProgressChangedEventHandler(
                    HttpNetworkAnalyzer_ProgressChanged);

            Console.WriteLine(
                "Getting the Twitter User network specified in \"{0}\".  The"
                + " screen name is \"{1}\"."
                ,
                sNetworkConfigurationFilePath,
                sScreenNameToAnalyze
                );

            try
            {
                oXmlDocument = oTwitterUserNetworkAnalyzer.GetNetwork(
                    sScreenNameToAnalyze, eWhatToInclude, eNetworkLevel,
                    iMaximumPeoplePerRequest);
            }
            catch (PartialNetworkException oPartialNetworkException)
            {
                oXmlDocument = OnGetNetworkPartialNetworkException(oStartTime,
                                                                   oPartialNetworkException, sNetworkConfigurationFilePath,
                                                                   sNetworkFileFolderPath, oTwitterUserNetworkAnalyzer);
            }
            catch (Exception oException)
            {
                // (This call exits the program.)

                OnGetNetworkOtherException(oStartTime, oException,
                                           sNetworkConfigurationFilePath, sNetworkFileFolderPath,
                                           oTwitterUserNetworkAnalyzer);
            }
        }
    NetworkLevelToString
    (
        NetworkLevel eNetworkLevel
    )
    {
        AssertValid();

        String sNetworkLevel = String.Empty;

        switch (eNetworkLevel)
        {
            case NetworkLevel.One:

                sNetworkLevel = "1";
                break;

            case NetworkLevel.OnePointFive:

                sNetworkLevel = "1.5";
                break;

            case NetworkLevel.Two:

                sNetworkLevel = "2";
                break;

            case NetworkLevel.TwoPointFive:

                sNetworkLevel = "2.5";
                break;

            case NetworkLevel.Three:

                sNetworkLevel = "3";
                break;

            case NetworkLevel.ThreePointFive:

                sNetworkLevel = "3.5";
                break;

            case NetworkLevel.Four:

                sNetworkLevel = "4";
                break;

            case NetworkLevel.FourPointFive:

                sNetworkLevel = "4.5";
                break;

            default:

                Debug.Assert(false);
                break;
        }

        return (sNetworkLevel + "-level");
    }
        //*************************************************************************
        //  Method: GetNeedToAppendVertices()
        //
        /// <summary>
        /// Determines whether a method getting a recursive network needs to
        /// add vertices for a specified network and recursion level.
        /// </summary>
        ///
        /// <param name="eNetworkLevel">
        /// Network level to include.  Must be NetworkLevel.One, OnePointFive, or
        /// Two.
        /// </param>
        ///
        /// <param name="iRecursionLevel">
        /// Recursion level for the current call.  Must be 1 or 2.
        /// </param>
        ///
        /// <returns>
        /// true if the caller needs to add vertices for the specified network and
        /// recursion levels.
        /// </returns>
        ///
        /// <remarks>
        /// This is meant for network analyzers that analyze a recursive network.
        /// Call this from the method that uses recursion to get the different
        /// network levels, and use the return value to determine whether to add
        /// vertices for the current network and recursion levels.
        /// </remarks>
        //*************************************************************************
        protected Boolean GetNeedToAppendVertices(
            NetworkLevel eNetworkLevel,
            Int32 iRecursionLevel
            )
        {
            Debug.Assert(eNetworkLevel == NetworkLevel.One ||
            eNetworkLevel == NetworkLevel.OnePointFive ||
            eNetworkLevel == NetworkLevel.Two);

            Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2);
            AssertValid();

            return (
            (eNetworkLevel != NetworkLevel.OnePointFive ||
            iRecursionLevel == 1)
            );
        }
    GetNetworkAsync
    (
        String screenName,
        WhatToInclude whatToInclude,
        NetworkLevel networkLevel,
        Int32 maximumPerRequest,
        String apiKey
    )
    {
        Debug.Assert( !String.IsNullOrEmpty(screenName) );

        Debug.Assert(networkLevel == NetworkLevel.One ||
            networkLevel == NetworkLevel.OnePointFive ||
            networkLevel == NetworkLevel.Two);

        Debug.Assert(maximumPerRequest > 0);
        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.ScreenName = screenName;
        oGetNetworkAsyncArgs.WhatToInclude = whatToInclude;
        oGetNetworkAsyncArgs.NetworkLevel = networkLevel;
        oGetNetworkAsyncArgs.MaximumPerRequest = maximumPerRequest;
        oGetNetworkAsyncArgs.ApiKey = apiKey;

        m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs);
    }
        //*************************************************************************
        //  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);
            }
            }
        }
    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: 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: 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) );
        }
    GetNetworkInternal
    (
        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(bIncludeLatestStatus);

        RequestStatistics oRequestStatistics = new RequestStatistics();

        try
        {
            GetNetworkInternal(sScreenNameToAnalyze, eWhatToInclude,
                eNetworkLevel, iMaximumPeoplePerRequest, oRequestStatistics,
                oGraphMLXmlDocument);
        }
        catch (Exception oException)
        {
            OnUnexpectedException(oException, oGraphMLXmlDocument,
                oRequestStatistics);
        }

        OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics,

            GetNetworkDescription(sScreenNameToAnalyze, eWhatToInclude,
                eNetworkLevel, iMaximumPeoplePerRequest),

            "Twitter User " + sScreenNameToAnalyze
            );

        return (oGraphMLXmlDocument);
    }
        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: GetNeedToRecurse()
        //
        /// <summary>
        /// Determines whether a method getting a recursive network needs to
        /// recurse.
        /// </summary>
        ///
        /// <param name="eNetworkLevel">
        /// Network level to include.  Must be NetworkLevel.One, OnePointFive, or
        /// Two.
        /// </param>
        ///
        /// <param name="iRecursionLevel">
        /// Recursion level for the current call.  Must be 1 or 2.
        /// </param>
        ///
        /// <returns>
        /// true if the caller needs to recurse.
        /// </returns>
        ///
        /// <remarks>
        /// This is meant for network analyzers that analyze a recursive network.
        /// Call this from the method that uses recursion to get the different
        /// network levels, and use the return value to determine whether to
        /// recurse.
        /// </remarks>
        //*************************************************************************
        protected Boolean GetNeedToRecurse(
            NetworkLevel eNetworkLevel,
            Int32 iRecursionLevel
            )
        {
            Debug.Assert(eNetworkLevel == NetworkLevel.One ||
            eNetworkLevel == NetworkLevel.OnePointFive ||
            eNetworkLevel == NetworkLevel.Two);

            Debug.Assert(iRecursionLevel == 1 || iRecursionLevel == 2);
            AssertValid();

            return (
            iRecursionLevel == 1
            &&
            (eNetworkLevel == NetworkLevel.OnePointFive ||
            eNetworkLevel == NetworkLevel.Two)
            );
        }
    GetNetworkDescription
    (
        String sScreenName,
        WhatToInclude eWhatToInclude,
        NetworkLevel eNetworkLevel,
        Int32 iMaximumPerRequest
    )
    {
        Debug.Assert( !String.IsNullOrEmpty(sScreenName) );
        Debug.Assert(iMaximumPerRequest > 0);
        AssertValid();

        NetworkDescriber oNetworkDescriber = new NetworkDescriber();

        oNetworkDescriber.AddSentence(

            "The graph represents the {0} Flickr network of the user with the"
            + " screen name \"{1}\"."
            ,
            NetworkLevelToString(eNetworkLevel),
            sScreenName
            );

        oNetworkDescriber.AddNetworkTime(NetworkSource);

        oNetworkDescriber.StartNewParagraph();

        if ( WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.ContactVertices) )
        {
            oNetworkDescriber.AddSentence(
                "There is a vertex for each contact of the user."
                );
        }

        if ( WhatToIncludeFlagIsSet(eWhatToInclude,
            WhatToInclude.CommenterVertices) )
        {
            oNetworkDescriber.AddSentence(
                "There is a vertex for each person who commented on the user's"
                + " photos."
                );
        }

        oNetworkDescriber.AddNetworkLimit(iMaximumPerRequest, "people");

        return ( oNetworkDescriber.ConcatenateSentences() );
    }
        //*************************************************************************
        //  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);
        }
        //*************************************************************************
        //  Method: DoDataExchange()
        //
        /// <summary>
        /// Transfers data between the dialog's fields and its controls.
        /// </summary>
        ///
        /// <param name="bFromControls">
        /// true to transfer data from the dialog's controls to its fields, false
        /// for the other direction.
        /// </param>
        ///
        /// <returns>
        /// true if the transfer was successful.
        /// </returns>
        //*************************************************************************
        protected override Boolean DoDataExchange(
            Boolean bFromControls
            )
        {
            if (bFromControls)
            {
            // Validate the controls.

            if (
                !ValidateRequiredTextBox(txbScreenName,
                "Enter the screen name of a Flickr user.", out m_sScreenName)
                ||
                !usrFlickrApiKey.Validate()
                )
            {
                return (false);
            }

            m_bIncludeContactVertices = m_bIncludeCommenterVertices = false;

            if (radIncludeContactVertices.Checked)
            {
                m_bIncludeContactVertices = true;
            }
            else if (radIncludeCommenterVertices.Checked)
            {
                m_bIncludeCommenterVertices = true;
            }
            else
            {
                m_bIncludeContactVertices = m_bIncludeCommenterVertices =
                    true;
            }

            m_bIncludeUserInformation = chkIncludeUserInformation.Checked;
            m_eNetworkLevel = usrNetworkLevel.Level;
            m_iMaximumPeoplePerRequest = usrLimitToN.N;
            m_sApiKey = usrFlickrApiKey.ApiKey;
            }
            else
            {
            txbScreenName.Text = m_sScreenName;

            if (m_bIncludeContactVertices)
            {
                if (m_bIncludeCommenterVertices)
                {
                    radIncludeContactsAndCommenters.Checked = true;
                }
                else
                {
                    radIncludeContactVertices.Checked = true;
                }
            }
            else
            {
                radIncludeCommenterVertices.Checked = true;
            }

            chkIncludeUserInformation.Checked = m_bIncludeUserInformation;
            usrNetworkLevel.Level = m_eNetworkLevel;
            usrLimitToN.N = m_iMaximumPeoplePerRequest;
            usrFlickrApiKey.ApiKey = m_sApiKey;

            EnableControls();
            }

            return (true);
        }
        //*************************************************************************
        //  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: 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);
        }
    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() );
    }
        //*************************************************************************
        //  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);
            }
        }
    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)
        {
            OnUnexpectedException(oException, oGraphMLXmlDocument,
                oRequestStatistics);
        }

        OnNetworkObtained(oGraphMLXmlDocument, oRequestStatistics,

            GetNetworkDescription(sScreenName, eWhatToInclude, eNetworkLevel,
                iMaximumPerRequest),

            "Flickr User " + sScreenName
            );

        return (oGraphMLXmlDocument);
    }
        //*************************************************************************
        //  Method: DoDataExchange()
        //
        /// <summary>
        /// Transfers data between the dialog's fields and its controls.
        /// </summary>
        ///
        /// <param name="bFromControls">
        /// true to transfer data from the dialog's controls to its fields, false
        /// for the other direction.
        /// </param>
        ///
        /// <returns>
        /// true if the transfer was successful.
        /// </returns>
        //*************************************************************************
        protected override Boolean DoDataExchange(
            Boolean bFromControls
            )
        {
            if (bFromControls)
            {
            // Validate the controls.

            String sTag;

            if (
                !ValidateRequiredTextBox(txbTag,
                    "Enter a Flickr tag.",
                    out sTag)
                ||
                !usrFlickrApiKey.Validate()
                )
            {
                return (false);
            }

            m_sTag = sTag;
            m_eNetworkLevel = usrNetworkLevel.Level;
            m_bIncludeSampleThumbnails = chkIncludeSampleThumbnails.Checked;
            m_sApiKey = usrFlickrApiKey.ApiKey;
            }
            else
            {
            txbTag.Text = m_sTag;
            usrNetworkLevel.Level = m_eNetworkLevel;
            chkIncludeSampleThumbnails.Checked = m_bIncludeSampleThumbnails;
            usrFlickrApiKey.ApiKey = m_sApiKey;

            EnableControls();
            }

            return (true);
        }
    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() );
    }
Example #48
0
        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);
                }
            }
        }
        //*************************************************************************
        //  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);
        }
        //*************************************************************************
        //  Method: DoDataExchange()
        //
        /// <summary>
        /// Transfers data between the dialog's fields and its controls.
        /// </summary>
        ///
        /// <param name="bFromControls">
        /// true to transfer data from the dialog's controls to its fields, false
        /// for the other direction.
        /// </param>
        ///
        /// <returns>
        /// true if the transfer was successful.
        /// </returns>
        //*************************************************************************
        protected override Boolean DoDataExchange(
            Boolean bFromControls
            )
        {
            if (bFromControls)
            {
            // Validate the controls.

            if ( !ValidateUserNameTextBox(txbUserName, out m_sUserName) )
            {
                return (false);
            }

            m_bIncludeFriendVertices = m_bIncludeSubscriptionVertices = false;

            if (radIncludeFriendVertices.Checked)
            {
                m_bIncludeFriendVertices = true;
            }
            else if (radIncludeSubscriptionVertices.Checked)
            {
                m_bIncludeSubscriptionVertices = true;
            }
            else
            {
                m_bIncludeFriendVertices = m_bIncludeSubscriptionVertices =
                    true;
            }

            m_eNetworkLevel = usrNetworkLevel.Level;
            m_bIncludeAllStatistics = chkIncludeAllStatistics.Checked;
            m_iMaximumPeoplePerRequest = usrLimitToN.N;
            }
            else
            {
            txbUserName.Text = m_sUserName;

            if (m_bIncludeFriendVertices)
            {
                if (m_bIncludeSubscriptionVertices)
                {
                    radIncludeFollowedAndFollower.Checked = true;
                }
                else
                {
                    radIncludeFriendVertices.Checked = true;
                }
            }
            else
            {
                radIncludeSubscriptionVertices.Checked = true;
            }

            usrNetworkLevel.Level = m_eNetworkLevel;
            chkIncludeAllStatistics.Checked = m_bIncludeAllStatistics;
            usrLimitToN.N = m_iMaximumPeoplePerRequest;

            EnableControls();
            }

            return (true);
        }
        //*************************************************************************
        //  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);
        }
Example #52
0
        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);
                }
            }
        }
        //*************************************************************************
        //  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);
            }
        }
Example #54
0
        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: 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);
            }
            }
        }
Example #56
0
        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);
            }
        }
    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 = NodeXLGraphMLUtil.AppendEdgeXmlNode(
                        oGraphMLXmlDocument, sScreenName, sOtherScreenName,
                        "Contact");
                }
                else
                {
                    // (Note the swapping of screen names in the commenter
                    // case.)

                    oEdgeXmlNode = NodeXLGraphMLUtil.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);
            }
        }
    }