@model ManuscriptScanReviewViewModel @{ ViewData["Title"] = "Review manuscript scan"; var probableCharacters = Model.CharacterCandidates .Where(candidate => string.Equals(candidate.Category, "ProbableCharacter", StringComparison.OrdinalIgnoreCase)) .ToList(); var possibleCharacters = Model.CharacterCandidates .Where(candidate => string.Equals(candidate.Category, "PossibleCharacter", StringComparison.OrdinalIgnoreCase)) .ToList(); var relationshipTitles = Model.CharacterCandidates .Where(candidate => string.Equals(candidate.Category, "RelationshipTitle", StringComparison.OrdinalIgnoreCase)) .ToList(); var primaryCharacters = probableCharacters .Concat(possibleCharacters) .Concat(relationshipTitles) .OrderBy(candidate => candidate.ReviewName) .ThenBy(candidate => candidate.Name) .ToList(); var detectedAliasTargets = primaryCharacters .OrderBy(candidate => candidate.ReviewName) .ToList(); var visibleCharacterCount = probableCharacters.Count + possibleCharacters.Count + relationshipTitles.Count; var isCharacterStep = string.Equals(ViewData["ScanReviewStep"] as string, "characters", StringComparison.OrdinalIgnoreCase); const int characterPageSize = 10; var primaryCharacterPageCount = Math.Max(1, (int)Math.Ceiling(primaryCharacters.Count / (double)characterPageSize)); var characterReviewPageCount = primaryCharacterPageCount; var requestedCharacterPage = ViewData["CharacterReviewPage"] is int requestedPage ? requestedPage : 1; var characterPage = Math.Clamp(requestedCharacterPage, 1, characterReviewPageCount); var currentCharacters = primaryCharacters.Skip((characterPage - 1) * characterPageSize).Take(characterPageSize).ToList(); var currentStep = isCharacterStep ? 7 + characterPage : 7; var totalSteps = 7 + characterReviewPageCount + 2; var characterPageTitle = $"Review Characters {characterPage} of {primaryCharacterPageCount}"; var duplicateCanonicalNames = primaryCharacters .Where(candidate => candidate.Include && string.IsNullOrWhiteSpace(candidate.AliasTargetTemporaryCharacterKey)) .Select(candidate => candidate.ReviewName.Trim()) .Where(name => !string.IsNullOrWhiteSpace(name)) .GroupBy(name => name, StringComparer.OrdinalIgnoreCase) .Where(group => group.Count() > 1) .Select(group => group.Key) .ToHashSet(StringComparer.OrdinalIgnoreCase); var includedPrimaryKeys = primaryCharacters .Where(candidate => candidate.Include && string.IsNullOrWhiteSpace(candidate.AliasTargetTemporaryCharacterKey)) .Select(candidate => candidate.TemporaryCharacterKey) .ToHashSet(StringComparer.Ordinal); var invalidCharacterKeys = primaryCharacters .Where(candidate => (candidate.Include && string.IsNullOrWhiteSpace(candidate.ReviewName)) || (candidate.Include && string.IsNullOrWhiteSpace(candidate.AliasTargetTemporaryCharacterKey) && duplicateCanonicalNames.Contains(candidate.ReviewName.Trim())) || (candidate.Include && !string.IsNullOrWhiteSpace(candidate.AliasTargetTemporaryCharacterKey) && !includedPrimaryKeys.Contains(candidate.AliasTargetTemporaryCharacterKey))) .Select(candidate => candidate.TemporaryCharacterKey) .ToHashSet(StringComparer.Ordinal); }
@Html.AntiForgeryToken()

Step @currentStep of @totalSteps

@(isCharacterStep ? characterPageTitle : "Review Chapters")

@(isCharacterStep ? "Review this page of detected names and aliases, then continue through the wizard." : "Confirm the chapter and scene structure before reviewing detected characters.")

@if (TempData["OnboardingReviewMessage"] is string reviewMessage) {
@reviewMessage
} @if (!ViewData.ModelState.IsValid) { }
Chapters @Model.SelectedChapterCount / @Model.ChapterCount
Scenes @Model.SelectedSceneCount / @Model.SceneCount
Characters @Model.SelectedCharacterCount / @visibleCharacterCount
Words @Model.SelectedWordCount.ToString("N0")
@if (!isCharacterStep) {
}
@if (isCharacterStep) { @if (characterPage <= 1) { Back to chapters } else { } @if (characterPage < characterReviewPageCount) { } else { } } else { Back to setup }