Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability for admins to release and restrict email mode #214

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 39 additions & 6 deletions ServerCore/ModelBases/PuzzleStatePerTeamPageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ public abstract class PuzzleStatePerTeamPageModel : EventSpecificPageModel
{
public SortOrder? Sort { get; set; }

public PuzzleStatePerTeamPageModel(PuzzleServerContext serverContext, UserManager<IdentityUser> userManager) : base(serverContext, userManager)
{
}
public PuzzleStatePerTeamPageModel(
PuzzleServerContext serverContext,
UserManager<IdentityUser> userManager)
: base(serverContext, userManager) { }

public IList<PuzzleStatePerTeam> PuzzleStatePerTeam { get; set; }

Expand All @@ -29,7 +30,13 @@ public async Task<IActionResult> InitializeModelAsync(Puzzle puzzle, Team team,
return NotFound();
}

IQueryable<PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, puzzle, team, EventRole == EventRole.admin ? null : LoggedInUser);
IQueryable<PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(
_context,
Event,
puzzle,
team,
EventRole == EventRole.admin ? null : LoggedInUser);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

Sort = sort;

switch(sort ?? DefaultSort)
Expand Down Expand Up @@ -86,12 +93,38 @@ public async Task<IActionResult> 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);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

}

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);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

}

public async Task SetEmailModeAsync(Puzzle puzzle, Team team, bool value)
{
await PuzzleStateHelper.SetEmailOnlyModeAsync(
_context,
Event,
puzzle,
team,
value,
EventRole == EventRole.admin ? null : LoggedInUser);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

}

public enum SortOrder
Expand Down
99 changes: 80 additions & 19 deletions ServerCore/Pages/Events/Map.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public class MapModel : EventSpecificPageModel

public StateStats[,] StateMap { get; private set; }

public MapModel(PuzzleServerContext serverContext, UserManager<IdentityUser> userManager) : base(serverContext, userManager)
{
}
public MapModel(PuzzleServerContext serverContext,
UserManager<IdentityUser> userManager)
: base(serverContext, userManager) { }

public async Task<IActionResult> OnGetAsync()
{
Expand All @@ -32,14 +32,22 @@ public async Task<IActionResult> 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();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

}
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();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

}

List<TeamStats> teams = await _context.Teams.Where(t => t.Event == Event).Select(t => new TeamStats() { Team = t }).ToListAsync();
List<TeamStats> 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<int, PuzzleStats> puzzleLookup = new Dictionary<int, PuzzleStats>();
Expand All @@ -49,17 +57,30 @@ public async Task<IActionResult> OnGetAsync()
teams.ForEach(t => teamLookup[t.Team.ID] = t);

// tabulate solve counts and team scores
List<PuzzleStatePerTeam> states = await PuzzleStateHelper.GetSparseQuery(_context, Event, null, null, EventRole == EventRole.admin ? null : LoggedInUser).ToListAsync();
List<PuzzleStatePerTeam> states = await PuzzleStateHelper.GetSparseQuery(
_context,
Event,
null,
null,
EventRole == EventRole.admin ? null : LoggedInUser).ToListAsync();

List<StateStats> stateList = new List<StateStats>(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)
{
Expand All @@ -75,17 +96,26 @@ public async Task<IActionResult> 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;
}
Expand Down Expand Up @@ -129,20 +159,52 @@ 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";

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

}
}

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;
}
}

Expand All @@ -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;
}
}
}
Expand Down
26 changes: 25 additions & 1 deletion ServerCore/Pages/Puzzles/Status.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@
<th>
@Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].Notes)
</th>
<th>
@Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].IsEmailOnlyMode)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.PuzzleStatePerTeam)
{
<tr>
<td>
<a asp-page="../Teams/Status" asp-route-id="@item.Team.ID">@Html.DisplayFor(modelItem => item.Team.Name)</a>
<a asp-page="../Teams/Status" asp-route-teamId="@item.Team.ID">@Html.DisplayFor(modelItem => item.Team.Name)</a>
</td>
<td>
@if (item.UnlockedTime == null)
Expand Down Expand Up @@ -73,6 +76,16 @@
<td>
@Html.DisplayFor(modelItem => item.Notes)
</td>
<td>
@if (item.IsEmailOnlyMode == true)
{
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-teamId="@item.Team.ID" asp-route-puzzleid="@Model.Puzzle.ID" asp-route-value="false" onclick="return confirm('Are you sure you want to release @(item.Team.Name) from email-only mode?')">Release</a>
}
else
{
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-teamId="@item.Team.ID" asp-route-puzzleid="@Model.Puzzle.ID" asp-route-value="true" onclick="return confirm('Are you sure you want to restrict @(item.Team.Name) to email-only mode?')">Restrict</a>
}
</td>
</tr>
}

Expand All @@ -98,5 +111,16 @@
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-puzzleId="@Model.Puzzle.ID" asp-route-value="false" onclick="return confirm('Are you sure you want to release all teams from email-only mode?')">Release all</a>
</td>
<td>
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-puzzleId="@Model.Puzzle.ID" asp-route-value="true" onclick="return confirm('Are you sure you want to restrict all teams to email-only mode?')">Restrict all</a>
</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
15 changes: 15 additions & 0 deletions ServerCore/Pages/Puzzles/Status.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ public async Task<IActionResult> 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<IActionResult> OnGetEmailModeAsync(int puzzleId, int? teamId, bool value, string sort)
lambertwang-zz marked this conversation as resolved.
Show resolved Hide resolved
{
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 });
}
Expand Down
24 changes: 24 additions & 0 deletions ServerCore/Pages/Teams/Status.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
<th>
@Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].Notes)
</th>
<th>
@Html.DisplayNameFor(model => model.PuzzleStatePerTeam[0].IsEmailOnlyMode)
</th>
</tr>
</thead>
<tbody>
Expand Down Expand Up @@ -72,6 +75,16 @@
<td>
@Html.DisplayFor(modelItem => item.Notes)
</td>
<td>
@if (item.IsEmailOnlyMode == true)
{
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-teamId="@Model.Team.ID" asp-route-puzzleid="@item.Puzzle.ID" asp-route-value="false" onclick="return confirm('Are you sure you want to release @(item.Puzzle.Name) from email-only mode?')">Release</a>
}
else
{
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-teamId="@Model.Team.ID" asp-route-puzzleid="@item.Puzzle.ID" asp-route-value="true" onclick="return confirm('Are you sure you want to restrict @(item.Puzzle.Name) to email-only mode?')">Restrict</a>
}
</td>
</tr>
}

Expand All @@ -97,6 +110,17 @@
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-teamId="@Model.Team.ID" asp-route-value="false" onclick="return confirm('Are you sure you want to release all puzzles from email-only mode?')">Release all</a>
</td>
<td>
<a asp-page-handler="EmailMode" asp-route-sort="@Model.Sort" asp-route-teamId="@Model.Team.ID" asp-route-value="true" onclick="return confirm('Are you sure you want to restrict all puzzles to email-only mode?')">Restrict all</a>
</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<div>
Expand Down
16 changes: 16 additions & 0 deletions ServerCore/Pages/Teams/Status.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,21 @@ public async Task<IActionResult> OnGetSolveStateAsync(int teamId, int? puzzleId,
// redirect without the solve info to keep the URL clean
return RedirectToPage(new { teamId, sort });
}

public async Task<IActionResult> 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 });
}
}
}
Loading