Improve formatting for Event Create

This commit is contained in:
2025-09-25 09:58:56 -04:00
parent 7b1a52d5b3
commit 227dbcf336
9 changed files with 135 additions and 190 deletions
+1
View File
@@ -85,6 +85,7 @@ public class TeamScheduler
model.Add(x[ts.Item1, s] != x[ts.Item2, s]);
var solver = new CpSolver();
solver.StringParameters = "max_time_in_seconds:2.0";
var cpSolverStatus = solver.Solve(model);
Debug.WriteLine($"Solver status: {cpSolverStatus}");
+12
View File
@@ -29,4 +29,16 @@ public class TeamSchedulerSolution(
where gs.Key.Count() > 1
select Tuple.Create(gs.First(), gs.Key);
}
public static Student[] GetStudentsNotInTimSlot(Team[] timeSlot, Student[] students)
{
var studentsInTimeSlot = timeSlot.SelectMany(ts => ts.Students).Distinct();
return
(from allStudent in students
where studentsInTimeSlot.FirstOrDefault(e => e.Equals(allStudent)) == null
select allStudent
).ToArray();
}
}
+12
View File
@@ -5,8 +5,19 @@ namespace Core.Entities;
public class EventDefinition
{
public int Id { get; set; }
[Required]
[StringLength(100, MinimumLength = 2)]
[Display(Name = "Event Name")]
public string Name { get; set; }
[Required]
[StringLength(40, MinimumLength = 2)]
[Display(Name = "Event Short Name")]
public string? ShortName { get; set; }
[Required]
public EventFormat EventFormat { get; set; }
[Range(1, 6)]
@@ -55,6 +66,7 @@ public class EventDefinition
public string? Documentation { get; set; }
[Required]
public string Eligibility { get; set; }
public string? Theme { get; set; }
public string? Description { get; set; }
@@ -1,134 +1,63 @@
@page "/events/create"
@using Microsoft.EntityFrameworkCore
@using Core.Entities
@using Data
@inject AppDbContext context
@inject NavigationManager NavigationManager
<PageTitle>Create</PageTitle>
<PageTitle>Create Event - TSA Chapter Organizer</PageTitle>
<h1>Create</h1>
<MudText Typo="Typo.h3">Create</MudText>
<MudText Typo="Typo.h4">Event</MudText>
<MudDivider />
<h2>EventDefinition</h2>
<hr />
<div class="row">
<div class="col-md-4">
<EditForm method="post" Model="EventDefinition" OnValidSubmit="AddEventDefinition" FormName="create" Enhance>
<EditForm Model="EventDefinition" OnValidSubmit="OnValidSubmit" Enhance>
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert"/>
<div class="mb-3">
<label for="name" class="form-label">Name:</label>
<InputText id="name" @bind-Value="EventDefinition.Name" class="form-control" />
<ValidationMessage For="() => EventDefinition.Name" class="text-danger" />
</div>
<div class="mb-3">
<label for="shortname" class="form-label">ShortName:</label>
<InputText id="shortname" @bind-Value="EventDefinition.ShortName" class="form-control" />
<ValidationMessage For="() => EventDefinition.ShortName" class="text-danger" />
</div>
<div class="mb-3">
<label for="eventformat" class="form-label">EventFormat:</label>
<InputSelect @bind-Value="@EventDefinition.EventFormat">
@foreach (var format in Enum.GetValues(typeof(EventFormat)))
{
<option value="@format">@(@format.ToString())</option>
}
</InputSelect>
<ValidationMessage For="() => EventDefinition.EventFormat" class="text-danger" />
</div>
<div class="mb-3">
<label for="minteamsize" class="form-label">MinTeamSize:</label>
<InputNumber id="minteamsize" @bind-Value="EventDefinition.MinTeamSize" class="form-control" />
<ValidationMessage For="() => EventDefinition.MinTeamSize" class="text-danger" />
</div>
<div class="mb-3">
<label for="maxteamsize" class="form-label">MaxTeamSize:</label>
<InputNumber id="maxteamsize" @bind-Value="EventDefinition.MaxTeamSize" class="form-control" />
<ValidationMessage For="() => EventDefinition.MaxTeamSize" class="text-danger" />
</div>
<div class="mb-3">
<label for="semifinalistactivity" class="form-label">SemifinalistActivity:</label>
<InputText id="semifinalistactivity" @bind-Value="EventDefinition.SemifinalistActivity" class="form-control" />
<ValidationMessage For="() => EventDefinition.SemifinalistActivity" class="text-danger" />
</div>
<div class="mb-3">
<label for="notes" class="form-label">Notes:</label>
<InputText id="notes" @bind-Value="EventDefinition.Notes" class="form-control" />
<ValidationMessage For="() => EventDefinition.Notes" class="text-danger" />
</div>
<div class="mb-3">
<label for="maxteamcountstate" class="form-label">MaxTeamCountState:</label>
<InputNumber id="maxteamcountstate" @bind-Value="EventDefinition.MaxTeamCountState" class="form-control" />
<ValidationMessage For="() => EventDefinition.MaxTeamCountState" class="text-danger" />
</div>
<div class="mb-3">
<label for="regionalevent" class="form-label">RegionalEvent:</label>
<InputCheckbox id="regionalevent" @bind-Value="EventDefinition.RegionalEvent" class="form-check-input" />
<ValidationMessage For="() => EventDefinition.RegionalEvent" class="text-danger" />
</div>
<div class="mb-3">
<label for="regionalpresubmit" class="form-label">RegionalPresubmit:</label>
<InputCheckbox id="regionalpresubmit" @bind-Value="EventDefinition.RegionalPresubmit" class="form-check-input" />
<ValidationMessage For="() => EventDefinition.RegionalPresubmit" class="text-danger" />
</div>
<div class="mb-3">
<label for="statepresubmission" class="form-label">StatePresubmission:</label>
<InputCheckbox id="statepresubmission" @bind-Value="EventDefinition.StatePresubmission" class="form-check-input" />
<ValidationMessage For="() => EventDefinition.StatePresubmission" class="text-danger" />
</div>
<div class="mb-3">
<label for="statepretesting" class="form-label">StatePretesting:</label>
<InputCheckbox id="statepretesting" @bind-Value="EventDefinition.StatePretesting" class="form-check-input" />
<ValidationMessage For="() => EventDefinition.StatePretesting" class="text-danger" />
</div>
<div class="mb-3">
<label for="statepreliminaryround" class="form-label">StatePreliminaryRound:</label>
<InputCheckbox id="statepreliminaryround" @bind-Value="EventDefinition.StatePreliminaryRound" class="form-check-input" />
<ValidationMessage For="() => EventDefinition.StatePreliminaryRound" class="text-danger" />
</div>
<div class="mb-3">
<label for="documentation" class="form-label">Documentation:</label>
<InputText id="documentation" @bind-Value="EventDefinition.Documentation" class="form-control" />
<ValidationMessage For="() => EventDefinition.Documentation" class="text-danger" />
</div>
<div class="mb-3">
<label for="eligibility" class="form-label">Eligibility:</label>
<InputText id="eligibility" @bind-Value="EventDefinition.Eligibility" class="form-control" />
<ValidationMessage For="() => EventDefinition.Eligibility" class="text-danger" />
</div>
<div class="mb-3">
<label for="theme" class="form-label">Theme:</label>
<InputText id="theme" @bind-Value="EventDefinition.Theme" class="form-control" />
<ValidationMessage For="() => EventDefinition.Theme" class="text-danger" />
</div>
<div class="mb-3">
<label for="description" class="form-label">Description:</label>
<InputText id="description" @bind-Value="EventDefinition.Description" class="form-control" />
<ValidationMessage For="() => EventDefinition.Description" class="text-danger" />
</div>
<div class="mb-3">
<label for="levelofeffort" class="form-label">LevelOfEffort:</label>
<InputNumber id="levelofeffort" @bind-Value="EventDefinition.LevelOfEffort" class="form-control" />
<ValidationMessage For="() => EventDefinition.LevelOfEffort" class="text-danger" />
</div>
<button type="submit" class="btn btn-primary">Create</button>
</EditForm>
</div>
</div>
<MudGrid>
<MudItem xs="12" sm="7">
<MudPaper Class="pa-4">
<MudTextField T="string" Label="Event Name" @bind-Value="EventDefinition.Name" For="@(() => EventDefinition.Name)"></MudTextField>
<MudTextField T="string" Label="Short Name" @bind-Value="EventDefinition.ShortName" For="@(() => EventDefinition.ShortName)"></MudTextField>
<label for="@EventDefinition.EventFormat" class="form-label">Format:</label>
<MudRadioGroup T="EventFormat" @bind-Value="@EventDefinition.EventFormat">
@* <MudRadio T="EventFormat" Value="EventFormat.Team">Team</MudRadio>
<MudRadio T="EventFormat" Value="EventFormat.Individual">Individual</MudRadio> *@
@foreach (EventFormat format in Enum.GetValues(typeof(EventFormat)))
{
<MudRadio T="EventFormat" value="@format">@(format.ToString())</MudRadio>
}
</MudRadioGroup>
<ValidationMessage For="() => EventDefinition.EventFormat" class="text-danger" />
<MudTextField T="string" Label="Theme" AutoGrow="true" @bind-Value="EventDefinition.Theme" For="@(() => EventDefinition.Theme)"></MudTextField>
<MudTextField T="string" Label="Documentation" @bind-Value="EventDefinition.Documentation" For="@(() => EventDefinition.Documentation)"></MudTextField>
<MudNumericField T="int?" Label="Level of Effort" @bind-Value="EventDefinition.LevelOfEffort" For="@(() => EventDefinition.LevelOfEffort)"></MudNumericField>
<MudNumericField T="int" Label="Minimum Team Size" @bind-Value="EventDefinition.MinTeamSize" For="@(() => EventDefinition.MinTeamSize)"></MudNumericField>
<MudNumericField T="int" Label="Maxiumum Team Size" @bind-Value="EventDefinition.MaxTeamSize" For="@(() => EventDefinition.MaxTeamSize)"></MudNumericField>
<MudNumericField T="int" Label="Maxiumum Team Count at State" @bind-Value="EventDefinition.MaxTeamCountState" For="@(() => EventDefinition.MaxTeamCountState)"></MudNumericField>
<MudTextField T="string" Label="Semifinalist Activity" @bind-Value="EventDefinition.SemifinalistActivity" For="@(() => EventDefinition.SemifinalistActivity)"></MudTextField>
<MudCheckBox T="bool" Label="Regional Event" @bind-Value="EventDefinition.RegionalEvent" For="@(() => EventDefinition.RegionalEvent)"></MudCheckBox>
<MudCheckBox T="bool" Label="Requires Presubmission" @bind-Value="EventDefinition.StatePresubmission" For="@(() => EventDefinition.StatePresubmission)"></MudCheckBox>
<MudCheckBox T="bool" Label="Preliminary Round" @bind-Value="EventDefinition.StatePreliminaryRound" For="@(() => EventDefinition.StatePreliminaryRound)"></MudCheckBox>
<MudTextField T="string" Label="Eligibility" @bind-Value="EventDefinition.Eligibility" For="@(() => EventDefinition.Eligibility)"></MudTextField>
</MudPaper>
</MudItem>
</MudGrid>
<MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="events">Back</MudButton>
<MudButton ButtonType="ButtonType.Submit" StartIcon="@Icons.Material.Filled.Save">Save</MudButton>
</EditForm>
<div>
<a href="/events">Back to List</a>
</div>
@code {
[SupplyParameterFromForm]
private EventDefinition EventDefinition { get; set; } = new();
// To protect from overposting attacks, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.
private async Task AddEventDefinition()
private void OnValidSubmit()
{
context.Events.Add(EventDefinition);
await context.SaveChangesAsync();
context.SaveChanges();
NavigationManager.NavigateTo("/events");
}
}
@@ -1,7 +1,5 @@
@page "/events/delete"
@using Microsoft.EntityFrameworkCore
@using Core.Entities
@using Data
@inject AppDbContext context
@inject NavigationManager NavigationManager
@@ -1,7 +1,5 @@
@page "/events/details"
@using Microsoft.EntityFrameworkCore
@using Core.Entities
@using Data
@inject AppDbContext context
@inject NavigationManager NavigationManager
@@ -8,50 +8,34 @@
<MudText Typo="Typo.h4">Student</MudText>
<MudDivider />
<div class="row">
<div class="col-md-4">
<EditForm method="post" Model="Student" OnValidSubmit="AddStudent" FormName="create" Enhance>
<EditForm Model="Student" OnValidSubmit="OnValidSubmit" Enhance>
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" role="alert"/>
<div class="mb-3">
<label for="firstname" class="form-label">First Name:</label>
<InputText id="firstname" @bind-Value="Student.FirstName" class="form-control" />
<ValidationMessage For="() => Student.FirstName" class="text-danger" />
</div>
<div class="mb-3">
<label for="lastname" class="form-label">Last Name:</label>
<InputText id="lastname" @bind-Value="Student.LastName" class="form-control" />
<ValidationMessage For="() => Student.LastName" class="text-danger" />
</div>
<div class="mb-3">
<label for="grade" class="form-label">Grade:</label>
<InputNumber id="grade" @bind-Value="Student.Grade" class="form-control" />
<ValidationMessage For="() => Student.Grade" class="text-danger" />
</div>
<div class="mb-3">
<label for="tsayear" class="form-label">TSA Year:</label>
<InputNumber id="tsayear" @bind-Value="Student.TsaYear" class="form-control" />
<ValidationMessage For="() => Student.TsaYear" class="text-danger" />
</div>
<button type="submit" class="btn btn-primary">Create</button>
</EditForm>
</div>
</div>
<div>
<a href="/students">Back to List</a>
</div>
<MudGrid>
<MudItem xs="12" sm="7">
<MudPaper Class="pa-4">
<MudTextField T="string" Label="First Name" @bind-Value="Student.FirstName" For="@(() => Student.FirstName)"></MudTextField>
<MudTextField T="string" Label="Last Name" @bind-Value="Student.LastName" For="@(() => Student.LastName)"></MudTextField>
<MudTextField T="string" Label="Email Adress" @bind-Value="Student.Email" For="@(() => Student.Email)"></MudTextField>
<MudTextField T="string" Label="Phone Number" @bind-Value="Student.PhoneNumber" For="@(() => Student.PhoneNumber)"></MudTextField>
<MudTextField T="int" Label="Grade" @bind-Value="Student.Grade" For="@(() => Student.Grade)"></MudTextField>
<MudTextField T="int" Label="TSA Year" @bind-Value="Student.TsaYear" For="@(() => Student.TsaYear)"></MudTextField>
</MudPaper>
</MudItem>
</MudGrid>
<MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="students">Back</MudButton>
<MudButton ButtonType="ButtonType.Submit" StartIcon="@Icons.Material.Filled.Save">Save</MudButton>
</EditForm>
@code {
[SupplyParameterFromForm]
private Student Student { get; set; } = new() { TsaYear = 1 };
private async Task AddStudent()
private void OnValidSubmit(EditContext context)
{
Context.Students.Add(Student);
await Context.SaveChangesAsync();
Context.SaveChanges();
NavigationManager.NavigateTo("/students");
}
}
@@ -21,39 +21,19 @@ else
<MudTh>Name</MudTh>
<MudTh>Grade</MudTh>
<MudTh>TSA Year</MudTh>
<MudTh>1st</MudTh>
<MudTh>2nd</MudTh>
<MudTh>3rd</MudTh>
<MudTh>4th</MudTh>
<MudTh>5th</MudTh>
<MudTh>6th</MudTh>
<MudTh>7th</MudTh>
<MudTh>8th</MudTh>
<MudTh>9th</MudTh>
<MudTh>10th</MudTh>
<MudTh></MudTh>
<MudTh>Warnings</MudTh>
@for (var i = 1; i <= 10; i++)
{
var ii = i;
<MudTh>@AppIcons.GetOrdinal(ii)</MudTh>
}
</HeaderContent>
<RowTemplate>
<MudTd>@context.FirstName</MudTd>
<MudTh>@context.Grade</MudTh>
<MudTh>@context.TsaYear</MudTh>
@for (var i = 1; i <= 10; i++)
{
var st = context.EventRankings.FirstOrDefault(e => e.Rank == i);
<MudTd Class="@($"event-rank-{i})")">
@if (st != null)
{
<span>@st.EventDefinition.ShortName&nbsp;
@if(st.EventDefinition.EventFormat == EventFormat.Individual) { <span>ⓘ</span>}
@if(st.EventDefinition.RegionalEvent) {<span>ⓡ</span>}
@if(st.EventDefinition.OnSiteActivity) {<span>ⓐ</span>}
</span>
}
</MudTd>
}
<MudTd><MudButton StartIcon="@Icons.Material.Filled.TableChart" Href="@($"students/event-ranking-edit/{context.Id}")">Edit</MudButton></MudTd>
<MudTd>
@if (!context.RankedEvents.Any(re => re.OnSiteActivity))
@@ -76,6 +56,22 @@ else
</MudTooltip>
}
</MudTd>
@for (var i = 1; i <= 10; i++)
{
var ii = i;
var st = context.EventRankings.FirstOrDefault(e => e.Rank == i);
<MudTd Class="@($"event-rank-{ii})")">
@if (st != null)
{
<span>@st.EventDefinition.ShortName&nbsp;
@if(st.EventDefinition.EventFormat == EventFormat.Individual) { <span>ⓘ</span>}
@if(st.EventDefinition.RegionalEvent) {<span>ⓡ</span>}
@if(st.EventDefinition.OnSiteActivity) {<span>ⓐ</span>}
</span>
}
</MudTd>
}
</RowTemplate>
</MudTable>
<MudTable Items="_eventStudentRankings" Hover="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info">
@@ -16,19 +16,31 @@
</HeaderContent>
<RowTemplate>
<MudTd>
@{
var ol = TeamSchedulerSolution.GetStudentTeamOverlaps(context);}
@foreach (var t in context)
{
<MudItem>
@t.ToString() -
@string.Join(", ", t.Students)
@string.Join(", ", t.Students.Select(s => s.FirstName + " " + (ol.Any(o => o.Item1.Equals(s)) ? "*" : "" )) )
</MudItem>
}
@foreach (var overlap in TeamSchedulerSolution.GetStudentTeamOverlaps(context))
@* @foreach (var overlap in ol)
{
<MudItem>
@string.Join(", ", overlap.Item1)
</MudItem>
} *@
@{ var notInTimeSLot = TeamSchedulerSolution.GetStudentsNotInTimSlot(context, _students); }
@if (notInTimeSLot.Any()) {
<MudItem>
<i>
Not scheduled: @string.Join(", ", notInTimeSLot.Select(s => s.FirstName))
</i>
</MudItem>
}
</MudTd>
</RowTemplate>
</MudTable>
@@ -93,9 +105,12 @@
);
var mustIncludeTeams = _teams;
mustIncludeTeams = mustIncludeTeams.Where(t => t.Event.EventFormat == EventFormat.Team).ToArray();
mustIncludeTeams = mustIncludeTeams.Where(t => t.Event.EventFormat != EventFormat.Individual).ToArray();
mustIncludeTeams = mustIncludeTeams.Where(t => !t.ToString().Contains("#1")).ToArray();
//mustIncludeTeams = mustIncludeTeams.Where(t => !t.ToString().Contains("Photo")).ToArray();
var teamScheduler = new TeamScheduler(mustIncludeTeams, 3);
var teamScheduler = new TeamScheduler(mustIncludeTeams, scheduleOptions.TimeSlots);
// teamScheduler
// .ScheduleSeparate(