public async Task SetSolveStateAsync(Puzzle puzzle, Team team, bool value) { await PuzzleStateHelper.SetSolveStateAsync( _context, Event, puzzle, team, value?(DateTime?)DateTime.UtcNow : null, EventRole == EventRole.admin?null : LoggedInUser); }
public async Task <IActionResult> OnPostAsync(int teamId) { Team = await _context.Teams.FindAsync(teamId); var mergeIntoTeam = await _context.Teams.FindAsync(MergeIntoID); if (Team == null || mergeIntoTeam == null || Team.Event != Event || mergeIntoTeam.Event != Event) { return(NotFound()); } List <string> memberEmails = null; List <string> mergeIntoMemberEmails = null; using (var transaction = await _context.Database.BeginTransactionAsync(IsolationLevel.Serializable)) { var members = await _context.TeamMembers.Where(tm => tm.Team.ID == teamId).ToListAsync(); memberEmails = await _context.TeamMembers.Where(tm => tm.Team.ID == teamId).Select(tm => tm.Member.Email).ToListAsync(); mergeIntoMemberEmails = await _context.TeamMembers.Where(tm => tm.Team.ID == MergeIntoID).Select(m => m.Member.Email).ToListAsync(); var states = await PuzzleStateHelper.GetSparseQuery(_context, Team.Event, null, Team).ToListAsync(); var mergeIntoStates = await PuzzleStateHelper.GetSparseQuery(_context, Team.Event, null, mergeIntoTeam).ToDictionaryAsync(s => s.PuzzleID); // copy all the team members over foreach (var member in members) { member.Team = mergeIntoTeam; } // also copy puzzle solves over foreach (var state in states) { if (state.SolvedTime != null && mergeIntoStates.TryGetValue(state.PuzzleID, out var mergeIntoState) && mergeIntoState.SolvedTime == null) { await PuzzleStateHelper.SetSolveStateAsync(_context, Team.Event, mergeIntoState.Puzzle, mergeIntoTeam, state.UnlockedTime); } } await TeamHelper.DeleteTeamAsync(_context, Team, sendEmail : false); await _context.SaveChangesAsync(); transaction.Commit(); } MailHelper.Singleton.SendPlaintextWithoutBcc(memberEmails.Union(mergeIntoMemberEmails), $"{Event.Name}: Team '{Team.Name}' has been merged into '{mergeIntoTeam.Name}'", $"These two teams have been merged into one superteam. Please welcome your new teammates!"); return(RedirectToPage("./Index")); }
public async Task <IActionResult> OnPostAsync(int?puzzleId) { // Handle no radio button selected if (Result == FreeformResult.None) { return(RedirectToPage()); } bool accepted = (Result == FreeformResult.Accepted); if (String.IsNullOrWhiteSpace(FreeformResponse)) { FreeformResponse = Result.ToString(); } Submission submission; using (var transaction = _context.Database.BeginTransaction()) { bool isAdmin = (EventRole == EventRole.admin); submission = await(from Submission sub in _context.Submissions join PuzzleAuthors author in _context.PuzzleAuthors on sub.PuzzleID equals author.PuzzleID where sub.ID == SubmissionID && sub.FreeformAccepted == null && sub.Puzzle.EventID == Event.ID && (isAdmin || author.Author == LoggedInUser) select sub).FirstOrDefaultAsync(); if (submission != null) { submission.FreeformAccepted = accepted; submission.FreeformResponse = FreeformResponse; submission.FreeformJudge = LoggedInUser; submission.FreeformFavorited = Favorite; if (accepted) { await PuzzleStateHelper.SetSolveStateAsync(_context, Event, submission.Puzzle, submission.Team, DateTime.UtcNow); } await _context.SaveChangesAsync(); await transaction.CommitAsync(); } } if (submission != null) { MailHelper.Singleton.SendPlaintextOneAddress(submission.Submitter.Email, $"{submission.Puzzle.Name} Submission {Result}", $"Your submission for {submission.Puzzle.Name} has been {Result} with the response: {FreeformResponse}"); } return(RedirectToPage()); }
public async Task <IActionResult> OnPostAsync(int puzzleId, string submissionText) { if (String.IsNullOrWhiteSpace(submissionText)) { ModelState.AddModelError("submissionText", "Your answer cannot be empty"); } SubmissionText = submissionText; if (DateTime.UtcNow < Event.EventBegin) { return(NotFound("The event hasn't started yet!")); } await SetupContext(puzzleId); if (!ModelState.IsValid) { return(Page()); } // Don't allow submissions if the team is locked out. if (PuzzleState.IsTeamLockedOut) { return(Page()); } // Don't allow submissions if team is in email only mode. if (PuzzleState.IsEmailOnlyMode) { return(Page()); } // Don't allow submissions after the answer has been found. if (PuzzleState.SolvedTime != null) { return(Page()); } // Don't accept posted submissions when a puzzle is causing lockout if (PuzzlesCausingGlobalLockout.Count != 0 && !PuzzlesCausingGlobalLockout.Contains(Puzzle)) { return(Page()); } // Soft enforcement of duplicates to give a friendly message in most cases DuplicateSubmission = (from sub in Submissions where sub.SubmissionText == ServerCore.DataModel.Response.FormatSubmission(submissionText) select sub).Any(); if (DuplicateSubmission) { return(Page()); } // Create submission and add it to list Submission submission = new Submission { TimeSubmitted = DateTime.UtcNow, Puzzle = PuzzleState.Puzzle, Team = PuzzleState.Team, Submitter = LoggedInUser, AllowFreeformSharing = AllowFreeformSharing }; string submissionTextToCheck = ServerCore.DataModel.Response.FormatSubmission(submissionText); if (Puzzle.IsFreeform) { submission.UnformattedSubmissionText = submissionText; } else { submission.SubmissionText = submissionText; } submission.Response = await _context.Responses.Where( r => r.Puzzle.ID == puzzleId && submissionTextToCheck == r.SubmittedText) .FirstOrDefaultAsync(); Submissions.Add(submission); // Update puzzle state if submission was correct if (submission.Response != null && submission.Response.IsSolution) { await PuzzleStateHelper.SetSolveStateAsync(_context, Event, submission.Puzzle, submission.Team, submission.TimeSubmitted); AnswerToken = submission.SubmissionText; } else if (!Puzzle.IsFreeform && submission.Response == null && Event.IsAnswerSubmissionActive) { // We also determine if the puzzle should be set to email-only mode. if (IsPuzzleSubmissionLimitReached( Event, Submissions, PuzzleState)) { await PuzzleStateHelper.SetEmailOnlyModeAsync(_context, Event, submission.Puzzle, submission.Team, true); var authors = await _context.PuzzleAuthors.Where((pa) => pa.Puzzle == submission.Puzzle).Select((pa) => pa.Author.Email).ToListAsync(); MailHelper.Singleton.SendPlaintextBcc(authors, $"{Event.Name}: Team {submission.Team.Name} is in email mode for {submission.Puzzle.Name}", ""); } else { // If the submission was incorrect and not a partial solution, // we will do the lockout computations now. DateTime?lockoutExpiryTime = ComputeLockoutExpiryTime( Event, Submissions, PuzzleState); if (lockoutExpiryTime != null) { await PuzzleStateHelper.SetLockoutExpiryTimeAsync(_context, Event, submission.Puzzle, submission.Team, lockoutExpiryTime); } } } _context.Submissions.Add(submission); await _context.SaveChangesAsync(); SubmissionViews.Add(new SubmissionView() { Submission = submission, Response = submission.Response, SubmitterName = LoggedInUser.Name, IsFreeform = Puzzle.IsFreeform }); return(Page()); }
public async Task <IActionResult> OnPostUnlock(int puzzleId, int unlockId) { Puzzle puzzle = await(from puzz in _context.Puzzles where puzz.ID == puzzleId select puzz).FirstOrDefaultAsync(); if (puzzle == null) { return(NotFound()); } // Restrict this page to whistle stop non-puzzles if (puzzle.MinutesOfEventLockout == 0 || puzzle.IsPuzzle) { return(NotFound()); } Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); using (var transaction = _context.Database.BeginTransaction()) { var puzzleStateQuery = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team); PuzzleStatePerTeam prereqState = await(from pspt in puzzleStateQuery where pspt.PuzzleID == puzzle.ID select pspt).FirstOrDefaultAsync(); if (prereqState == null) { return(NotFound()); } // Only move forward if the prereq is open and unsolved if (prereqState.UnlockedTime == null || prereqState.SolvedTime != null) { return(NotFound("Your team has already chosen and can't choose again")); } PuzzleStatePerTeam unlockState = await(from pspt in puzzleStateQuery where pspt.PuzzleID == unlockId select pspt).FirstOrDefaultAsync(); if (unlockState == null) { return(NotFound()); } // The chosen puzzle must be locked (and unsolved) if (unlockState.UnlockedTime != null || unlockState.SolvedTime != null) { return(NotFound("You've already chosen this puzzle")); } // Ensure the puzzle is actually one of the unlock options Prerequisites prereq = await(from pre in _context.Prerequisites where pre.PrerequisiteID == puzzleId && pre.PuzzleID == unlockId select pre).FirstOrDefaultAsync(); if (prereq == null) { return(NotFound()); } await PuzzleStateHelper.SetSolveStateAsync(_context, Event, puzzle, team, DateTime.UtcNow); await PuzzleStateHelper.SetUnlockStateAsync(_context, Event, unlockState.Puzzle, team, DateTime.UtcNow); transaction.Commit(); } Puzzle puzzleToUnlock = await(from p in _context.Puzzles where p.ID == unlockId select p).FirstOrDefaultAsync(); string puzzleUrl; if (puzzleToUnlock.CustomURL != null) { puzzleUrl = PuzzleHelper.GetFormattedUrl(puzzleToUnlock, Event.ID); } else { puzzleUrl = puzzleToUnlock.PuzzleFile.UrlString; } return(Redirect(puzzleUrl)); }
public async Task <IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return(Page()); } using (var transaction = _context.Database.BeginTransaction()) { // // Add the event and save, so the event gets an ID. // Event.TeamRegistrationBegin = DateTime.UtcNow; Event.TeamRegistrationEnd = Event.AnswerSubmissionEnd; Event.TeamNameChangeEnd = Event.AnswerSubmissionEnd; Event.TeamMembershipChangeEnd = Event.AnswerSubmissionEnd; Event.TeamMiscDataChangeEnd = Event.AnswerSubmissionEnd; Event.TeamDeleteEnd = Event.AnswerSubmissionEnd; Event.AnswersAvailableBegin = Event.AnswerSubmissionEnd; Event.StandingsAvailableBegin = DateTime.UtcNow; Event.LockoutIncorrectGuessLimit = 5; Event.LockoutIncorrectGuessPeriod = 1; Event.LockoutDurationMultiplier = 2; Event.MaxSubmissionCount = 50; Event.MaxNumberOfTeams = 120; Event.MaxExternalsPerTeam = 9; Event.MaxTeamSize = 12; _context.Events.Add(Event); await _context.SaveChangesAsync(); // // Add start puzzle, three module puzzles, and one module meta (marked as the final event puzzle for this demo) // Puzzle start = new Puzzle { Name = "!!!Get Hopping!!!", Event = Event, IsPuzzle = false, IsGloballyVisiblePrerequisite = true, Description = "Start the event", }; _context.Puzzles.Add(start); Puzzle easy = new Puzzle { Name = "Bunny Slope", Event = Event, IsPuzzle = true, SolveValue = 10, HintCoinsForSolve = 1, Group = "Thumper's Stumpers", OrderInGroup = 1, MinPrerequisiteCount = 1, Description = "Bunsweeper", }; _context.Puzzles.Add(easy); Puzzle intermediate = new Puzzle { Name = "Rabbit Run (automatically solves in ~3 mins)", Event = Event, IsPuzzle = true, SolveValue = 10, HintCoinsForSolve = 2, Group = "Thumper's Stumpers", OrderInGroup = 2, MinPrerequisiteCount = 1, MinutesToAutomaticallySolve = 3, Description = "Rabbit's Cube", }; _context.Puzzles.Add(intermediate); Puzzle hard = new Puzzle { Name = "Hare-Raising", Event = Event, IsPuzzle = true, SolveValue = 10, HintCoinsForSolve = 3, Group = "Thumper's Stumpers", OrderInGroup = 3, MinPrerequisiteCount = 1, Description = "Lateral Leaping", }; _context.Puzzles.Add(hard); Puzzle meta = new Puzzle { Name = "Lagomorph Meta", Event = Event, IsPuzzle = true, IsMetaPuzzle = true, IsFinalPuzzle = true, SolveValue = 100, Group = "Thumper's Stumpers", OrderInGroup = 99, MinPrerequisiteCount = 2, Description = "Word Hutch", }; _context.Puzzles.Add(meta); Puzzle other = new Puzzle { Name = "Rabbit Season", Event = Event, IsPuzzle = true, SolveValue = 10, Group = "Daffy's Delights", OrderInGroup = 1, MinPrerequisiteCount = 1, Description = "Hip Hop Identification", CustomURL = "https://www.bing.com/images/search?q=%22rabbit%22", }; _context.Puzzles.Add(other); Puzzle cheat = new Puzzle { Name = "You're Despicable (cheat code)", Event = Event, IsPuzzle = true, IsCheatCode = true, SolveValue = -1, Group = "Daffy's Delights", OrderInGroup = 2, MinPrerequisiteCount = 1, Description = "Duck Konundrum", }; _context.Puzzles.Add(cheat); Puzzle lockIntro = new Puzzle { Name = "Wouldn't you know... (whistle stop intro)", Event = Event, IsPuzzle = true, SolveValue = 0, Group = "Roger's Railway", OrderInGroup = 1, MinPrerequisiteCount = 1, Description = "Whistle Hop Intro", }; _context.Puzzles.Add(lockIntro); Puzzle lockPuzzle = new Puzzle { Name = "...Locked! (whistle stop, lasts 5 minutes)", Event = Event, IsPuzzle = true, SolveValue = 0, Group = "Roger's Railway", OrderInGroup = 2, MinPrerequisiteCount = 1, MinutesOfEventLockout = 5, Description = "Whistle Hop", }; _context.Puzzles.Add(lockPuzzle); Puzzle kitchenSyncPuzzle = new Puzzle { Name = "Kitchen Sync", Event = Event, IsPuzzle = true, SolveValue = 10, Group = "Sync Test", OrderInGroup = 1, MinPrerequisiteCount = 1 }; _context.Puzzles.Add(kitchenSyncPuzzle); Puzzle heatSyncPuzzle = new Puzzle { Name = "Heat Sync", Event = Event, IsPuzzle = true, SolveValue = 10, Group = "Sync Test", OrderInGroup = 2, MinPrerequisiteCount = 1 }; _context.Puzzles.Add(heatSyncPuzzle); Puzzle lipSyncPuzzle = new Puzzle { Name = "Lip Sync", Event = Event, IsPuzzle = true, SolveValue = 10, Group = "Sync Test", OrderInGroup = 3, MinPrerequisiteCount = 1 }; _context.Puzzles.Add(lipSyncPuzzle); Puzzle syncTestMetapuzzle = new Puzzle { Name = "Sync Test", Event = Event, IsPuzzle = true, IsMetaPuzzle = true, SolveValue = 50, Group = "Sync Test", OrderInGroup = 99, MinPrerequisiteCount = 1, MaxAnnotationKey = 400 }; _context.Puzzles.Add(syncTestMetapuzzle); await _context.SaveChangesAsync(); // // Add responses, PARTIAL is a partial, ANSWER is the answer. // _context.Responses.Add(new Response() { Puzzle = easy, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = easy, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = intermediate, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = intermediate, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = hard, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = hard, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = meta, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = meta, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = other, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = other, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = cheat, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = cheat, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = lockIntro, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = lockIntro, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = lockPuzzle, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = lockPuzzle, SubmittedText = "ANSWER", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = kitchenSyncPuzzle, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = kitchenSyncPuzzle, SubmittedText = "SYNC", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = heatSyncPuzzle, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = heatSyncPuzzle, SubmittedText = "OR", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = lipSyncPuzzle, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = lipSyncPuzzle, SubmittedText = "SWIM", ResponseText = "Correct!", IsSolution = true }); _context.Responses.Add(new Response() { Puzzle = syncTestMetapuzzle, SubmittedText = "PARTIAL", ResponseText = "Keep going..." }); _context.Responses.Add(new Response() { Puzzle = syncTestMetapuzzle, SubmittedText = "SYNCORSWIM", ResponseText = "Correct!", IsSolution = true }); string hint1Description = "Tell me about the rabbits, George."; string hint1Content = "O.K. Some day – we’re gonna get the jack together and we’re gonna have a little house and a couple of acres an’ a cow and some pigs and..."; string hint2Description = "Go on... George. How I get to tend the rabbits."; string hint2Content = "Well, we’ll have a big vegetable patch and a rabbit-hutch and chickens."; _context.Hints.Add(new Hint() { Puzzle = easy, Description = hint1Description, DisplayOrder = 0, Cost = 0, Content = hint1Content }); _context.Hints.Add(new Hint() { Puzzle = easy, Description = hint2Description, DisplayOrder = 1, Cost = 1, Content = hint2Content }); _context.Hints.Add(new Hint() { Puzzle = intermediate, Description = hint1Description, DisplayOrder = 0, Cost = 0, Content = hint1Content }); _context.Hints.Add(new Hint() { Puzzle = intermediate, Description = hint2Description, DisplayOrder = 1, Cost = 1, Content = hint2Content }); _context.Hints.Add(new Hint() { Puzzle = hard, Description = hint1Description, DisplayOrder = 0, Cost = 0, Content = hint1Content }); _context.Hints.Add(new Hint() { Puzzle = hard, Description = hint2Description, DisplayOrder = 1, Cost = 1, Content = hint2Content }); _context.Hints.Add(new Hint() { Puzzle = meta, Description = hint1Description, DisplayOrder = 0, Cost = 0, Content = hint1Content }); _context.Hints.Add(new Hint() { Puzzle = meta, Description = hint2Description, DisplayOrder = 1, Cost = 1, Content = hint2Content }); await _context.SaveChangesAsync(); // // Set up prequisite links. // The first two depend on start puzzle, then the third depends on one of the first two, then the meta depends on two of the first three. // _context.Prerequisites.Add(new Prerequisites() { Puzzle = easy, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = intermediate, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = hard, Prerequisite = easy }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = hard, Prerequisite = intermediate }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = meta, Prerequisite = easy }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = meta, Prerequisite = intermediate }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = meta, Prerequisite = hard }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = other, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = cheat, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = lockIntro, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = lockPuzzle, Prerequisite = lockIntro }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = kitchenSyncPuzzle, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = heatSyncPuzzle, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = lipSyncPuzzle, Prerequisite = start }); _context.Prerequisites.Add(new Prerequisites() { Puzzle = syncTestMetapuzzle, Prerequisite = start }); await _context.SaveChangesAsync(); // // Create puzzle pieces. // _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 0, 1, "xxx xxxx'x x xXxxx!", 3, "What Crocodile Dundee might say")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 1, 2, "xxx xxxx xxxx xxx Xxxxx", 1, "In <i>Hey Diddle Diddle</i>, what the dish did")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 1, 3, "xxXx", 1, "It smells")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 2, 4, "xxX", 4, "You reach things by extending it")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 2, 5, "Xxx xxxxx", 1, "Result of the <i>Exxon Valdez</i> crash")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 2, 6, "xxxxX", 2, "What you make out of turkey drippings")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 3, 7, "xxxXx xxx", 4, "Another name for a slow cooker")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 4, 8, "xXxxxx", 3, "The index is one of them")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 4, 9, "xxxxX", 2, "It can suffer when you play tennis")); _context.Pieces.Add(MakePiece(syncTestMetapuzzle, 4, 10, "xxxxxxX xxxxxxx", 2, "What Chekov asked strangers the location of in Star Trek IV, garnering suspicion due to his Russian accent")); await _context.SaveChangesAsync(); // // Create teams. Can we add players to these? // Team team1 = new Team { Name = "Team Bugs", Event = Event }; _context.Teams.Add(team1); Team team2 = new Team { Name = "Team Babs", Event = Event }; _context.Teams.Add(team2); Team team3 = new Team { Name = "Team Buster", Event = Event }; _context.Teams.Add(team3); Team teamLoneWolf = null; if (AddCreatorToLoneWolfTeam) { teamLoneWolf = new Team { Name = "Lone Wolf", Event = Event }; _context.Teams.Add(teamLoneWolf); } var demoCreatorUser = await PuzzleUser.GetPuzzleUserForCurrentUser(_context, User, _userManager); if (demoCreatorUser != null) { // // Event admin/author // _context.EventAdmins.Add(new EventAdmins() { Event = Event, Admin = demoCreatorUser }); _context.EventAuthors.Add(new EventAuthors() { Event = Event, Author = demoCreatorUser }); // // Puzzle author (for Thumper module only) // _context.PuzzleAuthors.Add(new PuzzleAuthors() { Puzzle = easy, Author = demoCreatorUser }); _context.PuzzleAuthors.Add(new PuzzleAuthors() { Puzzle = intermediate, Author = demoCreatorUser }); _context.PuzzleAuthors.Add(new PuzzleAuthors() { Puzzle = hard, Author = demoCreatorUser }); _context.PuzzleAuthors.Add(new PuzzleAuthors() { Puzzle = meta, Author = demoCreatorUser }); } // TODO: Files (need to know how to detect whether local blob storage is configured) // Is there a point to adding Feedback or is that quick/easy enough to demo by hand? await _context.SaveChangesAsync(); if (teamLoneWolf != null) { _context.TeamMembers.Add(new TeamMembers() { Team = teamLoneWolf, Member = demoCreatorUser }); } // line up all hints var teams = await _context.Teams.Where((t) => t.Event == Event).ToListAsync(); var hints = await _context.Hints.Where((h) => h.Puzzle.Event == Event).ToListAsync(); var puzzles = await _context.Puzzles.Where(p => p.Event == Event).ToListAsync(); foreach (Team team in teams) { foreach (Hint hint in hints) { _context.HintStatePerTeam.Add(new HintStatePerTeam() { Hint = hint, Team = team }); } foreach (Puzzle puzzle in puzzles) { _context.PuzzleStatePerTeam.Add(new PuzzleStatePerTeam() { PuzzleID = puzzle.ID, TeamID = team.ID }); } } await _context.SaveChangesAsync(); // // Mark the start puzzle as solved if we were asked to. // if (StartTheEvent) { await PuzzleStateHelper.SetSolveStateAsync(_context, Event, start, null, DateTime.UtcNow); } transaction.Commit(); } return(RedirectToPage("./Index")); }
public async Task <IActionResult> OnPostAsync(int puzzleId) { if (!this.Event.IsAnswerSubmissionActive) { return(RedirectToPage("/Submissions/Index", new { puzzleid = puzzleId })); } await SetupContext(puzzleId); if (!ModelState.IsValid) { return(Page()); } // Don't allow submissions after the answer has been found. if (PuzzleState.SolvedTime != null) { return(Page()); } // Create submission and add it to list Submission submission = new Submission { SubmissionText = SubmissionText, TimeSubmitted = DateTime.UtcNow, Puzzle = PuzzleState.Puzzle, Team = PuzzleState.Team, Submitter = LoggedInUser, }; submission.Response = await _context.Responses.Where( r => r.Puzzle.ID == puzzleId && submission.SubmissionText == r.SubmittedText) .FirstOrDefaultAsync(); Submissions.Add(submission); // Update puzzle state if submission was correct if (submission.Response != null && submission.Response.IsSolution) { await PuzzleStateHelper.SetSolveStateAsync(_context, Event, submission.Puzzle, submission.Team, submission.TimeSubmitted); } else if (submission.Response == null) { // We also determine if the puzzle should be set to email-only mode. if (IsPuzzleSubmissionLimitReached( Event, Submissions, PuzzleState)) { await PuzzleStateHelper.SetEmailOnlyModeAsync(_context, Event, submission.Puzzle, submission.Team, true); } else { // If the submission was incorrect and not a partial solution, // we will do the lockout computations now. DateTime?lockoutExpiryTime = ComputeLockoutExpiryTime( Event, Submissions, PuzzleState); if (lockoutExpiryTime != null) { await PuzzleStateHelper.SetLockoutExpiryTimeAsync(_context, Event, submission.Puzzle, submission.Team, lockoutExpiryTime); } } } _context.Submissions.Add(submission); await _context.SaveChangesAsync(); return(RedirectToPage( "/Submissions/Index", new { puzzleid = puzzleId })); }
/// <summary> /// Evaulates player submissions then either saves them to the database or returns an error to the caller /// </summary> public static async Task <SubmissionResponse> EvaluateSubmission(PuzzleServerContext context, PuzzleUser loggedInUser, Event thisEvent, int puzzleId, string submissionText, bool allowFreeformSharing) { //Query data needed to process submission Team team = await UserEventHelper.GetTeamForPlayer(context, thisEvent, loggedInUser); Puzzle puzzle = await context.Puzzles.Where( (p) => p.ID == puzzleId).FirstOrDefaultAsync(); PuzzleStatePerTeam puzzleState = await(PuzzleStateHelper .GetFullReadOnlyQuery( context, thisEvent, puzzle, team)) .FirstAsync(); List <Puzzle> puzzlesCausingGlobalLockout = await PuzzleStateHelper.PuzzlesCausingGlobalLockout(context, thisEvent, team).ToListAsync(); // Return early for cases when there's obviously nothing we can do with the submission // The submission text is empty if (String.IsNullOrWhiteSpace(submissionText)) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.EmptySubmission }); } // The puzzle is locked if (puzzle == null || puzzleState.UnlockedTime == null) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.PuzzleLocked }); } // The user or team isn't known if (loggedInUser == null || team == null) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.Unauthorized }); } // The event hasn't started yet if (DateTime.UtcNow < thisEvent.EventBegin) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.Unauthorized }); } // The team is locked out if (puzzleState.IsTeamLockedOut || puzzleState.IsEmailOnlyMode) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.TeamLockedOut }); } // The puzzle has already been solved if (puzzleState.SolvedTime != null) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.AlreadySolved }); } // The team is under a global lockout if (puzzlesCausingGlobalLockout.Count != 0 && !puzzlesCausingGlobalLockout.Contains(puzzle)) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.TeamLockedOut }); } List <SubmissionView> submissionViews = await(from sub in context.Submissions join user in context.PuzzleUsers on sub.Submitter equals user join r in context.Responses on sub.Response equals r into responses from response in responses.DefaultIfEmpty() where sub.Team == team && sub.Puzzle == puzzle orderby sub.TimeSubmitted select new SubmissionView() { Submission = sub, Response = response, SubmitterName = user.Name, FreeformReponse = sub.FreeformResponse, IsFreeform = puzzle.IsFreeform }).ToListAsync(); List <Submission> submissions = new List <Submission>(submissionViews.Count); foreach (SubmissionView submissionView in submissionViews) { submissions.Add(submissionView.Submission); } // The submission is a duplicate bool duplicateSubmission = (from sub in submissions where sub.SubmissionText == Response.FormatSubmission(submissionText) select sub).Any(); if (duplicateSubmission) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.DuplicateSubmission }); } // Create submission and add it to list Submission submission = new Submission { TimeSubmitted = DateTime.UtcNow, Puzzle = puzzleState.Puzzle, Team = puzzleState.Team, Submitter = loggedInUser, AllowFreeformSharing = allowFreeformSharing }; string submissionTextToCheck = Response.FormatSubmission(submissionText); if (puzzle.IsFreeform) { submission.UnformattedSubmissionText = submissionText; } else { submission.SubmissionText = submissionText; } submission.Response = await context.Responses.Where( r => r.Puzzle.ID == puzzleId && submissionTextToCheck == r.SubmittedText) .FirstOrDefaultAsync(); submissions.Add(submission); // Update puzzle state if submission was correct if (submission.Response != null && submission.Response.IsSolution) { await PuzzleStateHelper.SetSolveStateAsync(context, thisEvent, submission.Puzzle, submission.Team, submission.TimeSubmitted); } else if (!puzzle.IsFreeform && submission.Response == null && thisEvent.IsAnswerSubmissionActive) { // We also determine if the puzzle should be set to email-only mode. if (IsPuzzleSubmissionLimitReached( thisEvent, submissions, puzzleState)) { await PuzzleStateHelper.SetEmailOnlyModeAsync(context, thisEvent, submission.Puzzle, submission.Team, true); var authors = await context.PuzzleAuthors.Where((pa) => pa.Puzzle == submission.Puzzle).Select((pa) => pa.Author.Email).ToListAsync(); MailHelper.Singleton.SendPlaintextBcc(authors, $"{thisEvent.Name}: Team {submission.Team.Name} is in email mode for {submission.Puzzle.Name}", ""); } else { // If the submission was incorrect and not a partial solution, // we will do the lockout computations now. DateTime?lockoutExpiryTime = ComputeLockoutExpiryTime( thisEvent, submissions, puzzleState); if (lockoutExpiryTime != null) { await PuzzleStateHelper.SetLockoutExpiryTimeAsync(context, thisEvent, submission.Puzzle, submission.Team, lockoutExpiryTime); } } } context.Submissions.Add(submission); await context.SaveChangesAsync(); // Send back responses for cases where the database has been updated // Correct response if (submission.Response != null && submission.Response.IsSolution) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.Correct, CompleteResponse = submission.Response }); } // Freeform response if (puzzle.IsFreeform) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.Freeform, FreeformResponse = submission.FreeformResponse }); } // Partial response if (submission.Response != null && !submission.Response.IsSolution) { return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.Partial, CompleteResponse = submission.Response }); } // Default to incorrect return(new SubmissionResponse() { ResponseCode = SubmissionResponseCode.Incorrect }); }