@page "/teams/assignment" @attribute [Authorize] @using Core.Calculation @using Core.Validation @using Microsoft.EntityFrameworkCore @using WebApp.Models @using EventAssignment = Core.Calculation.EventAssignment @inject AppDbContext Context @inject IDialogService DialogService @inject NavigationManager NavigationManager @inject ValidationService ValidationService Event Assignment - TSA Chapter Organizer Assignment Optimized team assignments based on the student event rankings Edit Student Event Rankings Event Count LOE Assignment Requirements @item.Student.FirstName @item.EventDefinition.ShortName @if (item.Requirement == Requirement.Include) { } @if (item.Requirement == Requirement.Exclude) { } Two Team Events @item.ShortName Omitted Events @item.ShortName Solve Students Student Event Count LOE Sum Warnings @context.Student.FirstName @context.EventCount @context.TotalLevelOfEffort @{ var allStudentEvents = context.Student.EventRankings .OrderBy(e => e.Rank) .Select(e => e.EventDefinition) .Concat(context.Events) .Distinct(); } @foreach (var e in allStudentEvents .OrderBy(e => context.Student.EventRankings .Find(ser => ser.EventDefinition == e)?.Rank ?? 10)) { var eventRank = context.Student.EventRankings.Find(er => er.EventDefinition == e)?.Rank; var isAssigned = context.Events.Contains(e); var color = AppIcons.RankedEventColor(eventRank ?? 0); var style = string.Empty; if (isAssigned) { style += "border-color:black; border-width:thin;"; if (eventRank.HasValue) { style += $"background:{color};"; if (eventRank == 1) style += $"color:black"; } else style += $"background:{Colors.Gray.Lighten3};"; } else { if (eventRank.HasValue) style += $"border-color:{color}; border-width:medium; color:{Colors.Gray.Lighten1};"; } @e.ShortName  @AppIcons.EventAttributes(e) @{ var isIncluded = _assignmentRequirements .Find(ar => ar.EventDefinition == e && ar.Student == context.Student && ar.Requirement == Requirement.Include) == null; var isExcluded = _assignmentRequirements .Find(ar => ar.EventDefinition == e && ar.Student == context.Student && ar.Requirement == Requirement.Exclude) == null; } @if (isIncluded) { } else { } @if (isExcluded) { } else { } } Teams Team R, # [LB-UB] @{ var thresholds = _eventAssignmentThresholds.First(e => e.Event == context.Event); } @context.Event.Name  @thresholds.StudentRankingCount, [@thresholds.LowerBound-@thresholds.UpperBound] × @thresholds.TeamCount @if (_eventTwoTeams.Contains(context.Event)) { } else if (thresholds.TeamCount != 2 && context.Event.EventFormat == EventFormat.Team) { } Solution status: @_solutionStatus Loading... @if (_hasExistingTeams) { Cannot save: There are existing teams. Please delete all teams before saving a new assignment. View Teams } Save @code { public bool TestSwitch { get; set; } = false; private readonly AssignmentParameters _parameters = new() { RequireOnSite = false, RequireRegional = false }; private ValidationService _validationService = new ValidationService(ValidationConfiguration.Default); private List? _events; private List? _students; private List _eventAssignmentThresholds = []; MudTable _teamData; private Team[] _teams = []; MudTable _statisticData; private List _statistics = []; MudTable _assignmentRequirementData; private List _assignmentRequirements = []; MudTable _eventTwoTeamData; private List _eventTwoTeams = []; MudTable _eventOmittedData; private List _eventOmitted = []; private string _solutionStatus = string.Empty; private bool _isSolving = false; private bool _hasExistingTeams = false; protected override async Task OnInitializedAsync() { _events = await Context.Events .OrderBy(e => e.Name) .ToListAsync(); _students = await Context.Students .Where(e => e.FirstName != "test") .Include(e => e.EventRankings) .ThenInclude(e => e.EventDefinition) .Where(e => e.EventRankings.Any()) .OrderBy(e => e.FirstName).ToListAsync(); // Check if there are existing teams _hasExistingTeams = await Context.Teams.AnyAsync(); } private async Task AddTeam() { //Context.TimeSlots.Add(Team); await Context.SaveChangesAsync(); //NavigationManager.NavigateTo("/teams"); } private void Solve() { _teamData.ReloadServerData(); } private async Task> SolveAssignments(TableState arg1, CancellationToken arg2) { _isSolving = true; UpdateValidationConfig(); var eventAssignment = new EventAssignment(_events, _students, _parameters); foreach (var requirement in _assignmentRequirements) { eventAssignment.AddAssignmentRequirement(requirement); } eventAssignment.RemoveEvents(_eventOmitted); eventAssignment.AllowTwoTeams(_eventTwoTeams); var solution = await eventAssignment.Solve(); _solutionStatus = solution.Status; _statistics = StudentEventStatistics.Generate(solution.Teams) .OrderByDescending(s => s.Student.Grade + s.Student.TsaYear) .ThenBy(s => s.Student.FirstName).ToList(); _eventAssignmentThresholds = solution.AssignmentThresholds; await _statisticData.ReloadServerData(); _isSolving = false; await InvokeAsync(StateHasChanged); // let the UI know that the solution has been found _teams = solution.Teams; return new TableData { Items = _teams }; } private async Task> ReloadStatistics(TableState arg1, CancellationToken arg2) { return new TableData {Items = _statistics}; } private void UpdateValidationConfig() { var config = ValidationConfiguration.FromAssignmentParameters(_parameters); _validationService = new ValidationService(config); } private List GetStatisticsWarnings(StudentEventStatistics stats) { return _validationService.ValidateStudentStatistics(stats, ValidationContext.StudentAssignment); } private async Task> ReloadAssignmentRequirements(TableState arg1, CancellationToken arg2) { return new TableData { Items = _assignmentRequirements }; } private async Task> ReloadEventTwoTeam(TableState arg1, CancellationToken arg2) { return new TableData { Items = _eventTwoTeams }; } private async Task> ReloadOmittedEvents(TableState arg1, CancellationToken arg2) { return new TableData { Items = _eventOmitted }; } private void RequireEvent(EventDefinition evt, Student student, Requirement requirement) { _assignmentRequirements.Add(new AssignmentRequirement(evt, student, requirement)); _assignmentRequirementData.ReloadServerData(); } private void RemoveRequireEvent(EventDefinition evt, Student student, Requirement requirement) { var assignmentRequirement = _assignmentRequirements .Find(ar => ar.EventDefinition == evt && ar.Student == student && ar.Requirement == requirement); if (assignmentRequirement != null) RemoveRequireEvent(assignmentRequirement); } private void RemoveRequireEvent(AssignmentRequirement assignmentRequirement) { _assignmentRequirements.Remove(assignmentRequirement); _assignmentRequirementData.ReloadServerData(); } private void AddTwoTeam(EventDefinition evt) { _eventTwoTeams.Add(evt); _eventTwoTeamData.ReloadServerData(); } private void RemoveTwoTeam(EventDefinition evt) { _eventTwoTeams.Remove(evt); _eventTwoTeamData.ReloadServerData(); } private void AddOmitted(EventDefinition evt) { _eventOmitted.Add(evt); _eventOmittedData.ReloadServerData(); } private void RemoveOmitted(EventDefinition evt) { _eventOmitted.Remove(evt); _eventOmittedData.ReloadServerData(); } public async Task SaveTeams() { var result = await DialogService .ShowMessageBox("Save Teams", (MarkupString)$"Are you sure want to save these teams? Current teams will be erased. This cannot be undone.", yesText: "Yes", noText: "Cancel"); if (result == true) { await Context.Teams.ExecuteDeleteAsync(); await Context.Teams.AddRangeAsync(_teams); await Context.SaveChangesAsync(); NavigationManager.NavigateTo("/teams"); } } }