FormatYouTubeDate ( String sYouTubeDate ) { Debug.Assert(sYouTubeDate != null); DateTime oYouTubeDate; // For some odd reason, YouTube returns dates in the format // "2008-04-29T21:42:41.000-07:00" for the "published" date of a // contact (which is the contact's join date), but uses the format // "2006-05-14T07:54:03.000Z" for the "published" date of a video. // Cover both cases. foreach ( String sTimeZoneSuffix in new String [] {"zzz", "Z"} ) { if ( DateTime.TryParseExact(sYouTubeDate, "yyyy-MM-ddTHH:mm:ss.fff" + sTimeZoneSuffix, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out oYouTubeDate) ) { return ( ExcelDateTimeUtil.DateTimeToStringLocale1033( oYouTubeDate, ExcelColumnFormat.Date) ); } } return (sYouTubeDate); }
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); } } }