コード例 #1
0
        /// <summary>
        /// Get the last page number of the tally. This may be determined solely
        /// from the thread range info, or might require information from the
        /// provided first page, where we can extract how many pages are in the thread.
        /// </summary>
        /// <param name="quest">The quest being tallied.</param>
        /// <param name="adapter">The forum adapter that handles the quest's thread.</param>
        /// <param name="threadRangeInfo">The range of posts that are wanted in the tally.</param>
        /// <param name="firstPage">The first page of the tally, from which we can get the page range of the thread.</param>
        /// <returns>Returns the last page number of the tally.</returns>
        private async Task <int> GetLastPageNumber(IQuest quest, IForumAdapter2 adapter,
                                                   ThreadRangeInfo threadRangeInfo, Task <HtmlDocument?> firstPage)
        {
            // Check for quick results first.
            if (threadRangeInfo.Pages > 0)
            {
                // If the page range has already been determined, use that.
                return(threadRangeInfo.Pages);
            }

            if (!quest.ReadToEndOfThread && !threadRangeInfo.IsThreadmarkSearchResult)
            {
                // If we're not reading to the end of the thread, just calculate
                // what the last page number will be.  Pages to scan will be the
                // difference in pages +1.
                return(ThreadInfo.GetPageNumberOfPost(quest.EndPost, quest));
            }

            // If we're reading to the end of the thread (end post 0, or based on a threadmark),
            // then we need to load the first page to find out how many pages there are in the thread.
            var page = await firstPage.ConfigureAwait(false);

            if (page == null)
            {
                throw new InvalidOperationException($"Unable to load first page of {quest.ThreadName}");
            }

            return(adapter.GetThreadInfo(page).Pages);
        }
コード例 #2
0
        /// <summary>
        /// Acquire a list of page loading tasks for the pages that are intended
        /// to be tallied.
        /// </summary>
        /// <param name="quest">The quest for which the tally is being run.</param>
        /// <param name="adapter">The forum adapter that handles the quest's thread.</param>
        /// <param name="threadRangeInfo">The range of posts that are wanted in the tally.</param>
        /// <param name="token">A cancellation token.</param>
        /// <returns>Returns a list of page loading tasks.</returns>
        private async Task <List <Task <HtmlDocument?> > > LoadQuestPagesAsync(
            IQuest quest, IForumAdapter2 adapter, ThreadRangeInfo threadRangeInfo, IPageProvider pageProvider, CancellationToken token)
        {
            int firstPageNumber = threadRangeInfo.GetStartPage(quest);

            // Get the first page in order to find out how many pages are in the thread
            // Keep it as a task.
            Task <HtmlDocument?> firstPage = GetFirstPage(firstPageNumber, quest, adapter, pageProvider, token);

            // Get the last page number.
            int lastPageNumber = await GetLastPageNumber(quest, adapter, threadRangeInfo, firstPage).ConfigureAwait(false);

            // Initiate tasks for any remaining pages
            IEnumerable <Task <HtmlDocument?> > remainingPages =
                GetRemainingPages(firstPageNumber, lastPageNumber, quest, adapter, pageProvider, token);

            // Collect all the page load tasks (including the finished first page) to return to caller.
            List <Task <HtmlDocument?> > pagesToLoad = new List <Task <HtmlDocument?> >()
            {
                firstPage
            };

            pagesToLoad.AddRange(remainingPages);

            return(pagesToLoad);
        }
コード例 #3
0
        /// <summary>
        /// Collects the posts out of a quest based on the quest's configuration.
        /// </summary>
        /// <param name="quest">The quest to read.</param>
        /// <param name="pageProvider">The page provider to use to read this quest.</param>
        /// <param name="token">The cancellation token.</param>
        /// <returns>Returns a list of posts extracted from the quest.</returns>
        private async Task <(string threadTitle, List <Post> posts)> ReadQuestAsyncImpl(
            IQuest quest, IPageProvider pageProvider, CancellationToken token)
        {
            logger.LogDebug($"Reading quest {quest.DisplayName} with ForumReader.");

            IForumAdapter2 adapter = await forumAdapterFactory.CreateForumAdapterAsync(quest, pageProvider, token).ConfigureAwait(false);

            logger.LogDebug($"Forum adapter created for {quest.DisplayName}.");

            SyncQuestWithForumAdapter(quest, adapter);

            logger.LogDebug($"Quest {quest.DisplayName} synced with forum adapter.");

            ThreadRangeInfo rangeInfo = await GetStartInfoAsync(quest, adapter, pageProvider, token).ConfigureAwait(false);

            logger.LogDebug($"Range info acquired for {quest.DisplayName}. ({rangeInfo})");

            List <Task <HtmlDocument?> > loadingPages = await LoadQuestPagesAsync(quest, adapter, rangeInfo, pageProvider, token).ConfigureAwait(false);

            logger.LogDebug($"Got {loadingPages.Count} pages loading {quest.DisplayName}.");

            var(threadInfo, posts2) = await GetPostsFromPagesAsync(loadingPages, quest, adapter, rangeInfo).ConfigureAwait(false);

            logger.LogDebug($"Got {posts2.Count} posts for quest {quest.DisplayName}.");

            List <Post> filteredPosts = FilterPosts(posts2, quest, threadInfo, rangeInfo);

            logger.LogDebug($"Filtered to {filteredPosts.Count} posts for quest {quest.DisplayName}.");

            return(threadInfo.Title, filteredPosts);
        }
コード例 #4
0
        /// <summary>
        /// Update the quest with information from the forum adapter.
        /// </summary>
        /// <param name="quest">The quest to sync up.</param>
        /// <param name="adapter">The forum adapter created for the quest.</param>
        private void SyncQuestWithForumAdapter(IQuest quest, IForumAdapter2 adapter)
        {
            if (quest.PostsPerPage == 0)
            {
                quest.PostsPerPage = adapter.GetDefaultPostsPerPage(quest.ThreadUri);
            }

            if (adapter.GetHasRssThreadmarksFeed(quest.ThreadUri) == BoolEx.True && quest.UseRSSThreadmarks == BoolEx.Unknown)
            {
                quest.UseRSSThreadmarks = BoolEx.True;
            }
        }
コード例 #5
0
        /// <summary>
        /// Gets the first page of the desired tally.
        /// </summary>
        /// <param name="firstPageNumber">The page number of the first page.</param>
        /// <param name="quest">The quest being tallied.</param>
        /// <param name="adapter">The forum adapter that handles the quest's thread.</param>
        /// <param name="token">A cancellation token.</param>
        /// <returns>Returns the thread page that starts the tally.</returns>
        private async Task <HtmlDocument?> GetFirstPage(
            int firstPageNumber, IQuest quest, IForumAdapter2 adapter,
            IPageProvider pageProvider, CancellationToken token)
        {
            string firstPageUrl = adapter.GetUrlForPage(quest, firstPageNumber);

            // Make sure to bypass the cache, since it may have changed since the last load.
            HtmlDocument?page = await pageProvider.GetHtmlDocumentAsync(
                firstPageUrl, $"Page {firstPageNumber}",
                CachingMode.BypassCache, ShouldCache.Yes,
                SuppressNotifications.No, token)
                                .ConfigureAwait(false);

            return(page);
        }
コード例 #6
0
        /// <summary>
        /// Gets all posts from the provided pages list.
        /// </summary>
        /// <param name="loadingPages">The pages that are being loaded for the tally.</param>
        /// <param name="quest">The quest being tallied.</param>
        /// <param name="adapter">The forum adapter that handles the quest's thread.</param>
        /// <returns>Returns all posts extracted from all pages provided,
        /// and the thread title.</returns>
        private async Task <(ThreadInfo threadInfo, List <Post> posts)> GetPostsFromPagesAsync(
            List <Task <HtmlDocument?> > loadingPages,
            IQuest quest, IForumAdapter2 adapter,
            ThreadRangeInfo threadRangeInfo)
        {
            ThreadInfo? threadInfo = null;
            List <Post> postsList  = new List <Post>();
            int         pageNumber = threadRangeInfo.GetStartPage(quest) - 1;
            bool        incomplete = false;

            foreach (var loadingPage in loadingPages)
            {
                var page = await loadingPage.ConfigureAwait(false);

                pageNumber++;

                if (page == null)
                {
                    incomplete = true;
                    continue;
                }

                if (threadInfo == null)
                {
                    threadInfo = adapter.GetThreadInfo(page);
                }

                postsList.AddRange(adapter.GetPosts(page, quest, pageNumber));
            }

            if (incomplete)
            {
                InvalidOperationException e = new InvalidOperationException("Unable to load all pages.");
                e.Data["Application"] = true;
                throw e;
            }

            if (threadInfo == null)
            {
                threadInfo = new ThreadInfo("Unknown", "Unknown", 0);
            }

            return(threadInfo, postsList);
        }
コード例 #7
0
        /// <summary>
        /// Gets a collection of all pages that need to be loaded for the tally,
        /// other than the first page (which was already loaded).
        /// </summary>
        /// <param name="firstPageNumber">The first page number of the tally.</param>
        /// <param name="lastPageNumber">The last page number of the tally.</param>
        /// <param name="quest">The quest being tallied.</param>
        /// <param name="adapter">The forum adapter that handles the quest's thread.</param>
        /// <param name="token">The cancellation token.</param>
        /// <returns>Returns a collection of pages being loaded.</returns>
        private IEnumerable <Task <HtmlDocument?> > GetRemainingPages(
            int firstPageNumber, int lastPageNumber,
            IQuest quest, IForumAdapter2 adapter,
            IPageProvider pageProvider, CancellationToken token)
        {
            if (lastPageNumber <= firstPageNumber)
            {
                yield break;
            }

            for (int pageNum = firstPageNumber + 1; pageNum <= lastPageNumber; pageNum++)
            {
                var pageUrl     = adapter.GetUrlForPage(quest, pageNum);
                var shouldCache = (pageNum == lastPageNumber) ? ShouldCache.No : ShouldCache.Yes;

                yield return(pageProvider.GetHtmlDocumentAsync(
                                 pageUrl, $"Page {pageNum}",
                                 CachingMode.UseCache, shouldCache,
                                 SuppressNotifications.No, token));
            }
        }
コード例 #8
0
ファイル: TallyOutput.cs プロジェクト: MizMahem/NetTally
        public TallyOutput(
            IVoteCounter counter,
            RankVoteCounterFactory rankVoteCounterFactory,
            ForumAdapterFactory forumAdapterFactory,
            IGeneralOutputOptions options)
        {
            voteCounter   = counter;
            outputOptions = options;

            rankVoteCounter = rankVoteCounterFactory.CreateRankVoteCounter(options.RankVoteCounterMethod);

            if (voteCounter.Quest != null)
            {
                quest        = voteCounter.Quest;
                forumAdapter = forumAdapterFactory.CreateForumAdapter(quest.ForumType, quest.ThreadUri);
            }
            else
            {
                quest        = new Quest();
                forumAdapter = forumAdapterFactory.CreateForumAdapter(ForumType.Unknown, Quest.InvalidThreadUri);
            }
        }
コード例 #9
0
        /// <summary>
        /// Gets the thread range info (page and post numbers) based on the quest configuration.
        /// May load pages (such as for checking threadmarks), so will use the ViewModel's page provider.
        /// </summary>
        /// <param name="quest">The quest we're getting thread info for.</param>
        /// <param name="adapter">The quest's forum adapter.</param>
        /// <param name="token">The cancellation token.</param>
        /// <returns>Returns the quest's thread range info.</returns>
        private async Task <ThreadRangeInfo> GetStartInfoAsync(IQuest quest, IForumAdapter2 adapter, IPageProvider pageProvider, CancellationToken token)
        {
            ThreadRangeInfo rangeInfo = await adapter.GetQuestRangeInfoAsync(quest, pageProvider, token).ConfigureAwait(false);

            return(rangeInfo);
        }