From 1fc0030ae846d9164e8b5098390b14eabd573a47 Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Wed, 19 Jun 2019 20:41:38 -0700 Subject: [PATCH 1/3] Team composition --- Data/DataModel/Team.cs | 2 + ServerCore/Pages/Swag/Report.cshtml | 2 + ServerCore/Pages/Teams/Index.cshtml | 15 ++- .../Pages/Teams/TeamCompositionSummary.cshtml | 83 ++++++++++++++ .../Teams/TeamCompositionSummary.cshtml.cs | 104 ++++++++++++++++++ 5 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 ServerCore/Pages/Teams/TeamCompositionSummary.cshtml create mode 100644 ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs diff --git a/Data/DataModel/Team.cs b/Data/DataModel/Team.cs index 2a4cc291..771ec55d 100644 --- a/Data/DataModel/Team.cs +++ b/Data/DataModel/Team.cs @@ -9,6 +9,8 @@ public class Team { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } + + public int EventID { get; set; } [Required] public virtual Event Event { get; set; } diff --git a/ServerCore/Pages/Swag/Report.cshtml b/ServerCore/Pages/Swag/Report.cshtml index 435ce0bf..d271449a 100644 --- a/ServerCore/Pages/Swag/Report.cshtml +++ b/ServerCore/Pages/Swag/Report.cshtml @@ -8,6 +8,8 @@ ViewData["PlayRoute"] = "../Swag/Report"; } +Back to team list +

Lunch and T-Shirt Report

diff --git a/ServerCore/Pages/Teams/Index.cshtml b/ServerCore/Pages/Teams/Index.cshtml index f4beb370..d8e7aa0a 100644 --- a/ServerCore/Pages/Teams/Index.cshtml +++ b/ServerCore/Pages/Teams/Index.cshtml @@ -52,15 +52,18 @@ Create New }

-@if (Model.Event.IsInternEvent) -{ -
- Intern T-shirts and Lunch -
-}

Registered Teams: @Model.Teams.Count/@Model.Event.MaxNumberOfTeams, Registered Players: @Model.PlayerCount/@(Model.Event.MaxNumberOfTeams * Model.Event.MaxTeamSize)

@if (Model.EventRole == ModelBases.EventRole.admin) { +
+ Summaries: + Team Composition @("|") + @if (Model.Event.IsInternEvent) + { + Intern T-shirts and Lunch @("|") + } +
+
Email all team primary contacts from the bulk mailer }
diff --git a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml new file mode 100644 index 00000000..81b92692 --- /dev/null +++ b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml @@ -0,0 +1,83 @@ +@page "/{eventId}/{eventRole}/Teams/TeamCompositionSummary" +@model ServerCore.Pages.Teams.TeamCompositionSummaryModel +@{ + ViewData["Title"] = "Team Index"; + ViewData["AdminRoute"] = "/Teams/TeamCompositionSummary"; + ViewData["AuthorRoute"] = "/Teams/Index"; + ViewData["PlayRoute"] = "/Teams/Index"; +} + + + + + + + TeamCompositionSummary + + + + Back to team list +
+

Team Composition Summary

+
+
+ + + + + + + + + + + + @foreach (var teamComp in Model.TeamCompositions) + { + + + + + + + + + } + +
+ Team Name + + Total + + Employee Count + + Intern Count + + Non-Microsoft Count + + Possible Employee Aliases +
+ @teamComp.TeamName + + @teamComp.Total + + @teamComp.MicrosoftNonInternCount + + @teamComp.InternCount + + @teamComp.NonMicrosoftCount + + @teamComp.PossibleEmployeeAliases.Count + @if (teamComp.PossibleEmployeeAliases.Count > 0) + { +
+ @string.Format("({0})", string.Join("; ", teamComp.PossibleEmployeeAliases)) + } +
+ + + diff --git a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs new file mode 100644 index 00000000..ab1d5028 --- /dev/null +++ b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ServerCore.DataModel; +using ServerCore.Helpers; +using ServerCore.ModelBases; + +namespace ServerCore.Pages.Teams +{ + /// + /// Model for the author/admin's view of the feedback items for a specific puzzle + /// /used for viewing and aggregating feedback for a specific puzzle + /// + [Authorize(Policy = "IsEventAdmin")] + public class TeamCompositionSummaryModel : EventSpecificPageModel + { + // AlphaNumeric or dashes only + public Regex aliasRegex = new Regex("^[a-zA-Z0-9-]*$"); + + public TeamCompositionSummaryModel(PuzzleServerContext serverContext, UserManager userManager) : base(serverContext, userManager) + { + } + + public class TeamComposition + { + public TeamComposition(int teamId) + { + this.TeamID = teamId; + this.PossibleEmployeeAliases = new List(); + } + + public int TeamID { get; } + public string TeamName { get; set; } + public int MicrosoftNonInternCount { get; set; } + public int InternCount { get; set; } + public int NonMicrosoftCount { get; set; } + public int Total { get; set; } + public List PossibleEmployeeAliases { get; set; } + } + + public IEnumerable TeamCompositions { get; set; } + + public IActionResult OnGet() + { + TeamCompositions = _context.Teams.Where(team => team.EventID == Event.ID) + .Join( + _context.TeamMembers, + team => team.ID, + teamMember => teamMember.Team.ID, + (team, teamMember) => new IntermediateTeamMember() { TeamID = team.ID, TeamName = team.Name, TeamMember = teamMember.Member }) + .GroupBy(intermediate => intermediate.TeamID) + .Select(this.MapToTeamComposition); + + return Page(); + } + + private TeamComposition MapToTeamComposition(IGrouping group) + { + TeamComposition toReturn = new TeamComposition(group.Key); + + foreach (IntermediateTeamMember groupMember in group) + { + toReturn.TeamName = groupMember.TeamName; + toReturn.Total += 1; + + string email = groupMember.TeamMember.Email; + string alias = groupMember.TeamMember.EmployeeAlias; + if (email.EndsWith("@microsoft.com")) + { + if (email.StartsWith("t-")) + { + toReturn.InternCount += 1; + } + else + { + toReturn.MicrosoftNonInternCount += 1; + } + } + else if (!string.IsNullOrEmpty(alias) && aliasRegex.IsMatch(alias)) + { + toReturn.PossibleEmployeeAliases.Add(alias); + } + else + { + toReturn.NonMicrosoftCount += 1; + } + } + + return toReturn; + } + + private class IntermediateTeamMember + { + public int TeamID { get; set; } + public string TeamName { get; set; } + public PuzzleUser TeamMember { get; set; } + } + } +} \ No newline at end of file From 7abc7cdfa0f28cdf11b4cd70ec3790758e4acc08 Mon Sep 17 00:00:00 2001 From: Jessica Hoang Date: Sat, 22 Jun 2019 13:15:26 -0700 Subject: [PATCH 2/3] Added sort criteria --- .../Pages/Teams/TeamCompositionSummary.cshtml | 23 +++++- .../Teams/TeamCompositionSummary.cshtml.cs | 72 +++++++++++++++++-- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml index 81b92692..8948b662 100644 --- a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml +++ b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml @@ -17,12 +17,31 @@ td, th { text-align:center; } + + .sortButton { + padding: 0px 20px; + } Back to team list

Team Composition Summary

+
+
+ Sort by: + + + + + + + + + +
+
+
@@ -58,7 +77,7 @@ @teamComp.Total diff --git a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs index ab1d5028..24930ab8 100644 --- a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs +++ b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs @@ -1,13 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using ServerCore.DataModel; -using ServerCore.Helpers; using ServerCore.ModelBases; namespace ServerCore.Pages.Teams @@ -22,6 +20,23 @@ public class TeamCompositionSummaryModel : EventSpecificPageModel // AlphaNumeric or dashes only public Regex aliasRegex = new Regex("^[a-zA-Z0-9-]*$"); + public enum SortEnum + { + NonMicrosoftAndPossibleAliases, + InternCount, + EmployeeCount, + NonMicrosoftCount, + PossibleEmployeeAliasas, + Total, + Title + } + + public enum SortDirectionEnum + { + Descend, + Ascend + } + public TeamCompositionSummaryModel(PuzzleServerContext serverContext, UserManager userManager) : base(serverContext, userManager) { } @@ -36,7 +51,7 @@ public TeamComposition(int teamId) public int TeamID { get; } public string TeamName { get; set; } - public int MicrosoftNonInternCount { get; set; } + public int EmployeeCount { get; set; } public int InternCount { get; set; } public int NonMicrosoftCount { get; set; } public int Total { get; set; } @@ -45,8 +60,15 @@ public TeamComposition(int teamId) public IEnumerable TeamCompositions { get; set; } - public IActionResult OnGet() + public SortEnum SortBy { get; set; } + + public SortDirectionEnum SortDirection { get; set; } + + public IActionResult OnGet(SortEnum? sortBy, SortDirectionEnum? sortDirection) { + SortBy = sortBy ?? SortEnum.NonMicrosoftAndPossibleAliases; + SortDirection = sortDirection ?? SortDirectionEnum.Descend; + TeamCompositions = _context.Teams.Where(team => team.EventID == Event.ID) .Join( _context.TeamMembers, @@ -56,9 +78,45 @@ public IActionResult OnGet() .GroupBy(intermediate => intermediate.TeamID) .Select(this.MapToTeamComposition); + if (SortDirection == SortDirectionEnum.Ascend) + { + TeamCompositions = TeamCompositions.OrderBy(this.GetSortSelector()); + } + else + { + TeamCompositions = TeamCompositions.OrderByDescending(this.GetSortSelector()); + } + return Page(); } + public IActionResult OnPost(SortEnum sortBy, SortDirectionEnum sortDirection) + { + return RedirectToPage("./TeamCompositionSummary", new { sortBy = sortBy, sortDirection = sortDirection }); + } + + private Func GetSortSelector() + { + switch (SortBy) + { + case SortEnum.EmployeeCount: + return teamComp => teamComp.EmployeeCount.ToString(); + case SortEnum.InternCount: + return teamComp => teamComp.InternCount.ToString(); + case SortEnum.NonMicrosoftAndPossibleAliases: + return teamComp => (teamComp.NonMicrosoftCount + teamComp.PossibleEmployeeAliases.Count).ToString(); + case SortEnum.NonMicrosoftCount: + return teamComp => teamComp.NonMicrosoftCount.ToString(); + case SortEnum.PossibleEmployeeAliasas: + return teamComp => teamComp.PossibleEmployeeAliases.Count.ToString(); + case SortEnum.Total: + return teamComp => teamComp.Total.ToString(); + case SortEnum.Title: + default: + return teamComp => teamComp.TeamName; + } + } + private TeamComposition MapToTeamComposition(IGrouping group) { TeamComposition toReturn = new TeamComposition(group.Key); @@ -78,7 +136,7 @@ private TeamComposition MapToTeamComposition(IGrouping Date: Sat, 22 Jun 2019 13:31:53 -0700 Subject: [PATCH 3/3] fix line length --- .../Teams/TeamCompositionSummary.cshtml.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs index 24930ab8..86cbd4dc 100644 --- a/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs +++ b/ServerCore/Pages/Teams/TeamCompositionSummary.cshtml.cs @@ -37,7 +37,10 @@ public enum SortDirectionEnum Ascend } - public TeamCompositionSummaryModel(PuzzleServerContext serverContext, UserManager userManager) : base(serverContext, userManager) + public TeamCompositionSummaryModel( + PuzzleServerContext serverContext, + UserManager userManager) + : base(serverContext, userManager) { } @@ -74,7 +77,12 @@ public IActionResult OnGet(SortEnum? sortBy, SortDirectionEnum? sortDirection) _context.TeamMembers, team => team.ID, teamMember => teamMember.Team.ID, - (team, teamMember) => new IntermediateTeamMember() { TeamID = team.ID, TeamName = team.Name, TeamMember = teamMember.Member }) + (team, teamMember) => new IntermediateTeamMember() + { + TeamID = team.ID, + TeamName = team.Name, + TeamMember = teamMember.Member + }) .GroupBy(intermediate => intermediate.TeamID) .Select(this.MapToTeamComposition); @@ -92,7 +100,12 @@ public IActionResult OnGet(SortEnum? sortBy, SortDirectionEnum? sortDirection) public IActionResult OnPost(SortEnum sortBy, SortDirectionEnum sortDirection) { - return RedirectToPage("./TeamCompositionSummary", new { sortBy = sortBy, sortDirection = sortDirection }); + return RedirectToPage( + "./TeamCompositionSummary", + new { + sortBy = sortBy, + sortDirection = sortDirection + }); } private Func GetSortSelector()
- @teamComp.MicrosoftNonInternCount + @teamComp.EmployeeCount @teamComp.InternCount @@ -70,7 +89,7 @@ @teamComp.PossibleEmployeeAliases.Count @if (teamComp.PossibleEmployeeAliases.Count > 0) { -
+
@string.Format("({0})", string.Join("; ", teamComp.PossibleEmployeeAliases)) }