From 7c777f48884201f6b0f851000733d1f51da20139 Mon Sep 17 00:00:00 2001 From: Lambert Wang Date: Wed, 23 Jan 2019 20:25:17 -0800 Subject: [PATCH 1/3] Added ability for admins to release and restrict email mode --- .../ModelBases/PuzzleStatePerTeamPageModel.cs | 45 +++++++-- ServerCore/Pages/Events/Map.cshtml.cs | 99 +++++++++++++++---- ServerCore/Pages/Puzzles/Status.cshtml | 26 ++++- ServerCore/Pages/Puzzles/Status.cshtml.cs | 15 +++ ServerCore/Pages/Teams/Status.cshtml | 24 +++++ ServerCore/Pages/Teams/Status.cshtml.cs | 16 +++ ServerCore/PuzzleStateHelper.cs | 10 +- 7 files changed, 207 insertions(+), 28 deletions(-) diff --git a/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs b/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs index 732fd9cd..71b72195 100644 --- a/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs +++ b/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs @@ -14,9 +14,10 @@ public abstract class PuzzleStatePerTeamPageModel : EventSpecificPageModel { public SortOrder? Sort { get; set; } - public PuzzleStatePerTeamPageModel(PuzzleServerContext serverContext, UserManager userManager) : base(serverContext, userManager) - { - } + public PuzzleStatePerTeamPageModel( + PuzzleServerContext serverContext, + UserManager userManager) + : base(serverContext, userManager) { } public IList PuzzleStatePerTeam { get; set; } @@ -29,7 +30,13 @@ public async Task InitializeModelAsync(Puzzle puzzle, Team team, return NotFound(); } - IQueryable statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, puzzle, team, EventRole == EventRole.admin ? null : LoggedInUser); + IQueryable statesQ = PuzzleStateHelper.GetFullReadOnlyQuery( + _context, + Event, + puzzle, + team, + EventRole == EventRole.admin ? null : LoggedInUser); + Sort = sort; switch(sort ?? DefaultSort) @@ -86,12 +93,38 @@ public async Task InitializeModelAsync(Puzzle puzzle, Team team, public async Task SetUnlockStateAsync(Puzzle puzzle, Team team, bool value) { - await PuzzleStateHelper.SetUnlockStateAsync(_context, Event, puzzle, team, value ? (DateTime?)DateTime.UtcNow : null, EventRole == EventRole.admin ? null : LoggedInUser); + await PuzzleStateHelper.SetUnlockStateAsync( + _context, + Event, + puzzle, + team, + value ? (DateTime?)DateTime.UtcNow : null, + EventRole == EventRole.admin ? null : LoggedInUser); + } 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); + await PuzzleStateHelper.SetSolveStateAsync( + _context, + Event, + puzzle, + team, + value ? (DateTime?)DateTime.UtcNow : null, + EventRole == EventRole.admin ? null : LoggedInUser); + + } + + public async Task SetEmailModeAsync(Puzzle puzzle, Team team, bool value) + { + await PuzzleStateHelper.SetEmailOnlyModeAsync( + _context, + Event, + puzzle, + team, + value, + EventRole == EventRole.admin ? null : LoggedInUser); + } public enum SortOrder diff --git a/ServerCore/Pages/Events/Map.cshtml.cs b/ServerCore/Pages/Events/Map.cshtml.cs index 2eff1e5e..43810817 100644 --- a/ServerCore/Pages/Events/Map.cshtml.cs +++ b/ServerCore/Pages/Events/Map.cshtml.cs @@ -21,9 +21,9 @@ public class MapModel : EventSpecificPageModel public StateStats[,] StateMap { get; private set; } - public MapModel(PuzzleServerContext serverContext, UserManager userManager) : base(serverContext, userManager) - { - } + public MapModel(PuzzleServerContext serverContext, + UserManager userManager) + : base(serverContext, userManager) { } public async Task OnGetAsync() { @@ -32,14 +32,22 @@ public async Task OnGetAsync() if (EventRole == EventRole.admin) { - puzzles = await _context.Puzzles.Where(p => p.Event == Event).Select(p => new PuzzleStats() { Puzzle = p }).ToListAsync(); + puzzles = await _context.Puzzles.Where(p => p.Event == Event) + .Select(p => new PuzzleStats() { Puzzle = p }) + .ToListAsync(); + } else { - puzzles = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser).Select(p => new PuzzleStats() { Puzzle = p }).ToListAsync(); + puzzles = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) + .Select(p => new PuzzleStats() { Puzzle = p }) + .ToListAsync(); + } - List teams = await _context.Teams.Where(t => t.Event == Event).Select(t => new TeamStats() { Team = t }).ToListAsync(); + List teams = await _context.Teams.Where(t => t.Event == Event) + .Select(t => new TeamStats() { Team = t }) + .ToListAsync(); // build an ID-based lookup for puzzles and teams Dictionary puzzleLookup = new Dictionary(); @@ -49,17 +57,30 @@ public async Task OnGetAsync() teams.ForEach(t => teamLookup[t.Team.ID] = t); // tabulate solve counts and team scores - List states = await PuzzleStateHelper.GetSparseQuery(_context, Event, null, null, EventRole == EventRole.admin ? null : LoggedInUser).ToListAsync(); + List states = await PuzzleStateHelper.GetSparseQuery( + _context, + Event, + null, + null, + EventRole == EventRole.admin ? null : LoggedInUser).ToListAsync(); + List stateList = new List(states.Count); foreach (PuzzleStatePerTeam state in states) { // TODO: Is it more performant to prefilter the states if an author, or is this sufficient? - if (!puzzleLookup.TryGetValue(state.PuzzleID, out PuzzleStats puzzle) || !teamLookup.TryGetValue(state.TeamID, out TeamStats team)) + if (!puzzleLookup.TryGetValue(state.PuzzleID, out PuzzleStats puzzle) || + !teamLookup.TryGetValue(state.TeamID, out TeamStats team)) { continue; } - stateList.Add(new StateStats() { Puzzle = puzzle, Team = team, UnlockedTime = state.UnlockedTime, SolvedTime = state.SolvedTime }); + stateList.Add(new StateStats() { + Puzzle = puzzle, + Team = team, + UnlockedTime = state.UnlockedTime, + SolvedTime = state.SolvedTime, + LockedOut = state.IsEmailOnlyMode + }); if (state.SolvedTime != null) { @@ -75,17 +96,26 @@ public async Task OnGetAsync() } // sort puzzles by solve count, add the sort index to the lookup - puzzles = puzzles.OrderByDescending(p => p.SolveCount).ThenBy(p => p.Puzzle.Name).ToList(); + puzzles = puzzles.OrderByDescending(p => p.SolveCount) + .ThenBy(p => p.Puzzle.Name) + .ToList(); + for (int i = 0; i < puzzles.Count; i++) { puzzles[i].SortOrder = i; } // sort teams by metameta/score, add the sort index to the lookup - teams = teams.OrderBy(t => t.FinalMetaSolveTime).ThenByDescending(t => t.Score).ThenBy(t => t.Team.Name).ToList(); + teams = teams.OrderBy(t => t.FinalMetaSolveTime) + .ThenByDescending(t => t.Score) + .ThenBy(t => t.Team.Name) + .ToList(); + for (int i = 0; i < teams.Count; i++) { - if (i == 0 || teams[i].FinalMetaSolveTime != teams[i - 1].FinalMetaSolveTime || teams[i].Score != teams[i - 1].Score) + if (i == 0 || + teams[i].FinalMetaSolveTime != teams[i - 1].FinalMetaSolveTime || + teams[i].Score != teams[i - 1].Score) { teams[i].Rank = i + 1; } @@ -129,12 +159,29 @@ public class StateStats public TeamStats Team { get; set; } public DateTime? UnlockedTime { get; set; } public DateTime? SolvedTime { get; set; } + public Boolean LockedOut { get; set; } public string DisplayText { get { - return SolvedTime != null ? "C" : UnlockedTime != null ? "U" : "L"; + if (LockedOut) + { + return "E"; + } + + if (SolvedTime != null) + { + return "C"; + } + + if (UnlockedTime != null) + { + return "U"; + } + + return "L"; + } } @@ -142,7 +189,22 @@ public int DisplayHue { get { - return SolvedTime != null ? 120 : UnlockedTime != null ? 60 : 0; + if (LockedOut) + { + return 0; + } + + if (SolvedTime != null) + { + return 120; + } + + if (UnlockedTime != null) + { + return 60; + } + + return 0; } } @@ -155,15 +217,14 @@ public int DisplayLightness int minutes = (int)((DateTime.UtcNow - SolvedTime.Value).TotalMinutes); return 75 - (Math.Min(minutes, 236) >> 2); } - else if (UnlockedTime != null) + + if (UnlockedTime != null) { int minutes = (int)((DateTime.UtcNow - UnlockedTime.Value).TotalMinutes); return 75 - (Math.Min(minutes, 236) >> 2); } - else - { - return 100; - } + + return 100; } } } diff --git a/ServerCore/Pages/Puzzles/Status.cshtml b/ServerCore/Pages/Puzzles/Status.cshtml index 8baf60e2..dfe913f3 100644 --- a/ServerCore/Pages/Puzzles/Status.cshtml +++ b/ServerCore/Pages/Puzzles/Status.cshtml @@ -36,6 +36,9 @@ @Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].Notes) + + @Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].IsEmailOnlyMode) + @@ -43,7 +46,7 @@ { - @Html.DisplayFor(modelItem => item.Team.Name) + @Html.DisplayFor(modelItem => item.Team.Name) @if (item.UnlockedTime == null) @@ -73,6 +76,16 @@ @Html.DisplayFor(modelItem => item.Notes) + + @if (item.IsEmailOnlyMode == true) + { + Release + } + else + { + Restrict + } + } @@ -98,5 +111,16 @@ + + + + Release all + + + Restrict all + + + + diff --git a/ServerCore/Pages/Puzzles/Status.cshtml.cs b/ServerCore/Pages/Puzzles/Status.cshtml.cs index bc33be19..467b6d3d 100644 --- a/ServerCore/Pages/Puzzles/Status.cshtml.cs +++ b/ServerCore/Pages/Puzzles/Status.cshtml.cs @@ -63,6 +63,21 @@ public async Task OnGetSolveStateAsync(int puzzleId, int? teamId, await SetSolveStateAsync(puzzle, team, value); + // redirect without the solve info to keep the URL clean + return RedirectToPage(new { puzzleId, sort }); + } + public async Task OnGetEmailModeAsync(int puzzleId, int? teamId, bool value, string sort) + { + var puzzle = await _context.Puzzles.FirstAsync(m => m.ID == puzzleId); + var team = teamId == null ? null : await _context.Teams.FirstAsync(m => m.ID == teamId.Value); + + if (puzzle == null) + { + return NotFound(); + } + + await SetEmailModeAsync(puzzle, team, value); + // redirect without the solve info to keep the URL clean return RedirectToPage(new { puzzleId, sort }); } diff --git a/ServerCore/Pages/Teams/Status.cshtml b/ServerCore/Pages/Teams/Status.cshtml index 644b217d..55cb1a5d 100644 --- a/ServerCore/Pages/Teams/Status.cshtml +++ b/ServerCore/Pages/Teams/Status.cshtml @@ -35,6 +35,9 @@ @Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].Notes) + + @Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].IsEmailOnlyMode) + @@ -72,6 +75,16 @@ @Html.DisplayFor(modelItem => item.Notes) + + @if (item.IsEmailOnlyMode == true) + { + Release + } + else + { + Restrict + } + } @@ -97,6 +110,17 @@ + + + + Release all + + + Restrict all + + + +
diff --git a/ServerCore/Pages/Teams/Status.cshtml.cs b/ServerCore/Pages/Teams/Status.cshtml.cs index 3223f50d..887362e9 100644 --- a/ServerCore/Pages/Teams/Status.cshtml.cs +++ b/ServerCore/Pages/Teams/Status.cshtml.cs @@ -67,5 +67,21 @@ public async Task OnGetSolveStateAsync(int teamId, int? puzzleId, // redirect without the solve info to keep the URL clean return RedirectToPage(new { teamId, sort }); } + + public async Task OnGetEmailModeAsync(int teamId, int? puzzleId, bool value, string sort) + { + var puzzle = puzzleId == null ? null : await _context.Puzzles.FirstAsync(m => m.ID == puzzleId); + var team = await _context.Teams.FirstAsync(m => m.ID == teamId); + + if (team == null) + { + return NotFound(); + } + + await SetEmailModeAsync(puzzle, team, value); + + // redirect without the solve info to keep the URL clean + return RedirectToPage(new { teamId, sort }); + } } } diff --git a/ServerCore/PuzzleStateHelper.cs b/ServerCore/PuzzleStateHelper.cs index ffc41e35..63e64d09 100644 --- a/ServerCore/PuzzleStateHelper.cs +++ b/ServerCore/PuzzleStateHelper.cs @@ -62,7 +62,8 @@ from teamstate in tmp.DefaultIfEmpty() UnlockedTime = teamstate == null ? null : teamstate.UnlockedTime, SolvedTime = teamstate == null ? null : teamstate.SolvedTime, Printed = teamstate == null ? false : teamstate.Printed, - Notes = teamstate == null ? null : teamstate.Notes + Notes = teamstate == null ? null : teamstate.Notes, + IsEmailOnlyMode = teamstate == null ? false : teamstate.IsEmailOnlyMode }; } @@ -83,7 +84,8 @@ from teamstate in tmp.DefaultIfEmpty() UnlockedTime = teamstate == null ? null : teamstate.UnlockedTime, SolvedTime = teamstate == null ? null : teamstate.SolvedTime, Printed = teamstate == null ? false : teamstate.Printed, - Notes = teamstate == null ? null : teamstate.Notes + Notes = teamstate == null ? null : teamstate.Notes, + IsEmailOnlyMode = teamstate == null ? false : teamstate.IsEmailOnlyMode }; } #pragma warning restore IDE0031 @@ -250,6 +252,10 @@ public static async Task SetEmailOnlyModeAsync( for (int i = 0; i < states.Count; i++) { states[i].IsEmailOnlyMode = value; + if (value == true) + { + states[i].WrongSubmissionCountBuffer += 50; + } } await context.SaveChangesAsync(); From c89366af6c4fefe78714a7d530e3c2be82a69f52 Mon Sep 17 00:00:00 2001 From: Lambert Wang Date: Wed, 27 Feb 2019 18:12:17 -0800 Subject: [PATCH 2/3] Blank lines --- ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs | 4 ---- ServerCore/Pages/Events/Map.cshtml.cs | 3 --- 2 files changed, 7 deletions(-) diff --git a/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs b/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs index 71b72195..474d0b40 100644 --- a/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs +++ b/ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs @@ -36,7 +36,6 @@ public async Task InitializeModelAsync(Puzzle puzzle, Team team, puzzle, team, EventRole == EventRole.admin ? null : LoggedInUser); - Sort = sort; switch(sort ?? DefaultSort) @@ -100,7 +99,6 @@ await PuzzleStateHelper.SetUnlockStateAsync( team, value ? (DateTime?)DateTime.UtcNow : null, EventRole == EventRole.admin ? null : LoggedInUser); - } public async Task SetSolveStateAsync(Puzzle puzzle, Team team, bool value) @@ -112,7 +110,6 @@ await PuzzleStateHelper.SetSolveStateAsync( team, value ? (DateTime?)DateTime.UtcNow : null, EventRole == EventRole.admin ? null : LoggedInUser); - } public async Task SetEmailModeAsync(Puzzle puzzle, Team team, bool value) @@ -124,7 +121,6 @@ await PuzzleStateHelper.SetEmailOnlyModeAsync( team, value, EventRole == EventRole.admin ? null : LoggedInUser); - } public enum SortOrder diff --git a/ServerCore/Pages/Events/Map.cshtml.cs b/ServerCore/Pages/Events/Map.cshtml.cs index a46fce40..187618ac 100644 --- a/ServerCore/Pages/Events/Map.cshtml.cs +++ b/ServerCore/Pages/Events/Map.cshtml.cs @@ -35,14 +35,12 @@ public async Task OnGetAsync() puzzles = await _context.Puzzles.Where(p => p.Event == Event) .Select(p => new PuzzleStats() { Puzzle = p }) .ToListAsync(); - } else { puzzles = await UserEventHelper.GetPuzzlesForAuthorAndEvent(_context, Event, LoggedInUser) .Select(p => new PuzzleStats() { Puzzle = p }) .ToListAsync(); - } List teams = await _context.Teams.Where(t => t.Event == Event) @@ -188,7 +186,6 @@ public string DisplayText } return "L"; - } } From 578d7a4f595e947d7e31af1ea17a3a717d02755a Mon Sep 17 00:00:00 2001 From: Lambert Wang Date: Wed, 27 Feb 2019 18:13:04 -0800 Subject: [PATCH 3/3] Blank line --- ServerCore/Pages/Puzzles/Status.cshtml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ServerCore/Pages/Puzzles/Status.cshtml.cs b/ServerCore/Pages/Puzzles/Status.cshtml.cs index 467b6d3d..7a7548bc 100644 --- a/ServerCore/Pages/Puzzles/Status.cshtml.cs +++ b/ServerCore/Pages/Puzzles/Status.cshtml.cs @@ -66,6 +66,7 @@ public async Task OnGetSolveStateAsync(int puzzleId, int? teamId, // redirect without the solve info to keep the URL clean return RedirectToPage(new { puzzleId, sort }); } + public async Task OnGetEmailModeAsync(int puzzleId, int? teamId, bool value, string sort) { var puzzle = await _context.Puzzles.FirstAsync(m => m.ID == puzzleId);