Compare commits
10
Commits
551688f6fe
...
ea21406309
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea21406309 | ||
|
|
2119de05a8 | ||
|
|
5708d846d1 | ||
|
|
d777b4f67d | ||
|
|
c42cc43399 | ||
|
|
dd8921f6fe | ||
|
|
054166d7af | ||
|
|
349ec09cd7 | ||
|
|
0ff45f9310 | ||
|
|
3964a61794 |
@@ -14,9 +14,9 @@ public class TeamScheduler
|
|||||||
|
|
||||||
private readonly List<Tuple<int,int>> _scheduleSeparateTeams = [];
|
private readonly List<Tuple<int,int>> _scheduleSeparateTeams = [];
|
||||||
|
|
||||||
public TeamScheduler(Team[] teams, int numTimeSlots)
|
public TeamScheduler(IEnumerable<Team> teams, int numTimeSlots)
|
||||||
{
|
{
|
||||||
_teamObjects = teams;
|
_teamObjects = teams.ToArray();
|
||||||
_studentObjects = teams.SelectMany(t => t.Students).Distinct().ToList();
|
_studentObjects = teams.SelectMany(t => t.Students).Distinct().ToList();
|
||||||
|
|
||||||
_students = Enumerable.Range(0, _studentObjects.Count).ToArray();
|
_students = Enumerable.Range(0, _studentObjects.Count).ToArray();
|
||||||
@@ -31,7 +31,7 @@ public class TeamScheduler
|
|||||||
_scheduleSeparateTeams.Add(Tuple.Create(one,two));
|
_scheduleSeparateTeams.Add(Tuple.Create(one,two));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TeamScheduler CreateInstance(Team[] teams, int numTimeSlots)
|
public static TeamScheduler CreateInstance(IEnumerable<Team> teams, int numTimeSlots)
|
||||||
{
|
{
|
||||||
return new TeamScheduler(teams, numTimeSlots);
|
return new TeamScheduler(teams, numTimeSlots);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,11 @@ public class TeamSchedulerSolution(
|
|||||||
).ToArray();
|
).ToArray();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Team[] StudentUnassignedTeams(Student student)
|
||||||
|
{
|
||||||
|
var meetingTeams = TimeSlots.SelectMany(t => t);
|
||||||
|
return
|
||||||
|
student.Teams.Where(e => !meetingTeams.Contains(e)).ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ public class UnassignedStudentScheduler
|
|||||||
{
|
{
|
||||||
_teams = teams;
|
_teams = teams;
|
||||||
_students = teams.SelectMany(t => t.Students).Distinct().ToArray();
|
_students = teams.SelectMany(t => t.Students).Distinct().ToArray();
|
||||||
_timeSlots = timeslots.Select(ts => ts.Select(t => t.Clone()).ToArray()).ToArray();
|
_timeSlots = timeslots.Select(ts => ts.Select(t => t.Clone()).ToList()).ToArray<IList<Team>>();
|
||||||
}
|
}
|
||||||
public static IEnumerable<Student> UnassignedStudents(IList<Student> students, IList<Team> timeSlot)
|
public static IEnumerable<Student> UnassignedStudents(IList<Student> students, IList<Team> timeSlot)
|
||||||
=> students.Where(s => !timeSlot.SelectMany(t => t.Students).Contains(s));
|
=> students.Where(s => !timeSlot.SelectMany(t => t.Students).Contains(s));
|
||||||
@@ -20,9 +20,9 @@ public class UnassignedStudentScheduler
|
|||||||
public static IEnumerable<Student>[] UnassignedStudents(IList<Student> students, IList<Team>[] schedule)
|
public static IEnumerable<Student>[] UnassignedStudents(IList<Student> students, IList<Team>[] schedule)
|
||||||
=> schedule.Select(ts => UnassignedStudents(students, ts)).ToArray();
|
=> schedule.Select(ts => UnassignedStudents(students, ts)).ToArray();
|
||||||
|
|
||||||
public TeamSchedulerSolution ScheduleStrategy(UnassignedScheduleStrategy scheduleStrategy)
|
public Team[] ScheduleStrategy(UnassignedScheduleStrategy scheduleStrategy)
|
||||||
{
|
{
|
||||||
var ss = scheduleStrategy switch
|
var assignments = scheduleStrategy switch
|
||||||
{
|
{
|
||||||
UnassignedScheduleStrategy.BiggestGroup => ScheduleStrategy(GetAvailableTeams_BiggestGroup),
|
UnassignedScheduleStrategy.BiggestGroup => ScheduleStrategy(GetAvailableTeams_BiggestGroup),
|
||||||
UnassignedScheduleStrategy.IndividualEvents => ScheduleStrategy(GetAvailableTeams_Individual),
|
UnassignedScheduleStrategy.IndividualEvents => ScheduleStrategy(GetAvailableTeams_Individual),
|
||||||
@@ -32,14 +32,15 @@ public class UnassignedStudentScheduler
|
|||||||
_ => throw new ArgumentOutOfRangeException(nameof(scheduleStrategy), scheduleStrategy, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(scheduleStrategy), scheduleStrategy, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
return new TeamSchedulerSolution(ss, "Success?");
|
return assignments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Team[][] ScheduleStrategy(Func<IEnumerable<Team>, IEnumerable<Student>, IEnumerable<Team>> availableTeamSelector)
|
public Team[] ScheduleStrategy(Func<IEnumerable<Team>, IEnumerable<Student>, IEnumerable<Team>> availableTeamSelector)
|
||||||
{
|
{
|
||||||
// Find stuff for unassigned students in each timeslot
|
// Find stuff for unassigned students in each timeslot
|
||||||
var scheduledTeams = _timeSlots.SelectMany(list => list).Distinct().ToList();
|
var scheduledTeams = _timeSlots.SelectMany(list => list).Distinct().ToList();
|
||||||
foreach (var slot in _timeSlots)
|
var additions = new List<Team>();
|
||||||
|
foreach (var slot in _timeSlots)
|
||||||
{
|
{
|
||||||
var unassigned = UnassignedStudents(_students, slot).ToList();
|
var unassigned = UnassignedStudents(_students, slot).ToList();
|
||||||
while (unassigned.Count > 0)
|
while (unassigned.Count > 0)
|
||||||
@@ -54,20 +55,21 @@ public class UnassignedStudentScheduler
|
|||||||
|
|
||||||
slot.Add(teamToAdd);
|
slot.Add(teamToAdd);
|
||||||
scheduledTeams.Add(teamToAdd);
|
scheduledTeams.Add(teamToAdd);
|
||||||
|
additions.Add(teamToAdd);
|
||||||
|
|
||||||
foreach (var student in teamToAdd.Students)
|
foreach (var student in teamToAdd.Students)
|
||||||
unassigned.Remove(student);
|
unassigned.Remove(student);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _timeSlots.Select(e => e.ToArray()).ToArray();
|
return additions.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
// find teams where several unassigned students can work together
|
// find teams where several unassigned students can work together
|
||||||
private IEnumerable<Team> GetAvailableTeams_BiggestGroup(
|
private IEnumerable<Team> GetAvailableTeams_BiggestGroup(
|
||||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||||
_teams
|
_teams
|
||||||
.Where(t => scheduledTeams.All(st => st.Identifier != t.Identifier))
|
.Where(t => scheduledTeams.All(st => st.Id != t.Id))
|
||||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||||
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
|
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
|
||||||
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
||||||
@@ -78,7 +80,7 @@ public class UnassignedStudentScheduler
|
|||||||
private IEnumerable<Team> GetAvailableTeams_Individual(
|
private IEnumerable<Team> GetAvailableTeams_Individual(
|
||||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||||
_teams
|
_teams
|
||||||
.Where(t => scheduledTeams.All(st => st.Identifier != t.Identifier))
|
.Where(t => scheduledTeams.All(st => st.Id != t.Id))
|
||||||
.Where(t => t.Event.EventFormat == EventFormat.Individual || t.Students.Count == 1)
|
.Where(t => t.Event.EventFormat == EventFormat.Individual || t.Students.Count == 1)
|
||||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||||
.Where(t => t.Students.Count > 0);
|
.Where(t => t.Students.Count > 0);
|
||||||
@@ -87,7 +89,7 @@ public class UnassignedStudentScheduler
|
|||||||
private IEnumerable<Team> GetAvailableTeams_AnyNotMeetingAlready(
|
private IEnumerable<Team> GetAvailableTeams_AnyNotMeetingAlready(
|
||||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||||
_teams
|
_teams
|
||||||
.Where(t => scheduledTeams.All(st => st.Identifier != t.Identifier))
|
.Where(t => scheduledTeams.All(st => st.Id != t.Id))
|
||||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||||
.Where(t => t.Students.Count > 0);
|
.Where(t => t.Students.Count > 0);
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ public class UnassignedStudentScheduler
|
|||||||
private IEnumerable<Team> GetAvailableTeams_LevelOfEffort(
|
private IEnumerable<Team> GetAvailableTeams_LevelOfEffort(
|
||||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||||
_teams
|
_teams
|
||||||
.Where(t => scheduledTeams.All(st => st.Identifier != t.Identifier))
|
.Where(t => scheduledTeams.All(st => st.Id != t.Id))
|
||||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||||
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
|
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
|
||||||
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ namespace Core.Entities;
|
|||||||
public class Team
|
public class Team
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
public EventDefinition Event { get; set; }
|
public EventDefinition Event { get; set; }
|
||||||
|
|
||||||
public List<Student> Students { get; set; } = [];
|
public List<Student> Students { get; set; } = [];
|
||||||
@@ -47,11 +48,11 @@ public class Team
|
|||||||
{
|
{
|
||||||
var studentsToOmitList = studentsToOmit.ToList();
|
var studentsToOmitList = studentsToOmit.ToList();
|
||||||
var omittedStudents = Students.Where(studentsToOmitList.Contains).ToList();
|
var omittedStudents = Students.Where(studentsToOmitList.Contains).ToList();
|
||||||
if (!omittedStudents.Any())
|
if (omittedStudents.Count == 0)
|
||||||
return new Team{Captain = Captain, Event = Event, Students = Students.ToList(), Identifier = Identifier};
|
return new Team{Captain = Captain, Event = Event, Students = Students.ToList(), Identifier = Identifier, Id = Id};
|
||||||
|
|
||||||
var remainingStudents = Students.Where(s => !studentsToOmitList.Contains(s)).ToList();
|
var remainingStudents = Students.Where(s => !studentsToOmitList.Contains(s)).ToList();
|
||||||
return new PartialTeam { Event = Event, Students = remainingStudents, OmittedStudents = omittedStudents};
|
return new PartialTeam { Event = Event, Students = remainingStudents, OmittedStudents = omittedStudents, Identifier = Identifier, Id = Id};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Team Clone() => CloneWithOmittedStudents([]);
|
public Team Clone() => CloneWithOmittedStudents([]);
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ namespace Data
|
|||||||
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
//protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
//{
|
||||||
var dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ChapterOrganizer.db");
|
// var dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ChapterOrganizer.db");
|
||||||
optionsBuilder.UseSqlite($"Data Source={dbPath}");
|
// optionsBuilder.UseSqlite($"Data Source={dbPath}");
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EventDefinitionConfiguration : IEntityTypeConfiguration<EventDefinition>
|
public class EventDefinitionConfiguration : IEntityTypeConfiguration<EventDefinition>
|
||||||
|
|||||||
Binary file not shown.
@@ -18,7 +18,7 @@ public class EventAssignmentTests
|
|||||||
var eventAssignment = new EventAssignment(events, students, new AssignmentParameters());
|
var eventAssignment = new EventAssignment(events, students, new AssignmentParameters());
|
||||||
var solution = eventAssignment.Solve().Result;
|
var solution = eventAssignment.Solve().Result;
|
||||||
|
|
||||||
var teamWriter = new TeamWriter(solution.Teams, @"c:\temp\teams.csv");
|
//var teamWriter = new TeamWriter(solution.Teams, @"c:\temp\teams.csv");
|
||||||
teamWriter.Write();
|
//teamWriter.Write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,8 @@ public class TeamSchedulerTest
|
|||||||
solution = teamScheduler.Solve();
|
solution = teamScheduler.Solve();
|
||||||
}
|
}
|
||||||
|
|
||||||
solution = new UnassignedStudentScheduler(allTeams, solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
|
//solution = new UnassignedStudentScheduler(allTeams, solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
|
||||||
solution = new UnassignedStudentScheduler(allTeams, solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
|
//solution = new UnassignedStudentScheduler(allTeams, solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
|
||||||
|
|
||||||
var i = 1;
|
var i = 1;
|
||||||
foreach (var slot in solution.TimeSlots)
|
foreach (var slot in solution.TimeSlots)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public static class TestEntityHandler
|
|||||||
|
|
||||||
public static FileInfo GetEventOccurrenceFileInfo()
|
public static FileInfo GetEventOccurrenceFileInfo()
|
||||||
{
|
{
|
||||||
return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Events Times.txt");
|
return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Event Times.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Student[] GetStudents(IList<EventDefinition> events)
|
public static Student[] GetStudents(IList<EventDefinition> events)
|
||||||
@@ -35,7 +35,7 @@ public static class TestEntityHandler
|
|||||||
public static Team[] GetTeams(IList<EventDefinition> events, IList<Student> students)
|
public static Team[] GetTeams(IList<EventDefinition> events, IList<Student> students)
|
||||||
{
|
{
|
||||||
//var studentEventRankingsCsv = "Student Event Rankings.csv";
|
//var studentEventRankingsCsv = "Student Event Rankings.csv";
|
||||||
var studentEventRankingsCsv = "2023-24 RMS TSA student & event - TimeSlots.csv";
|
var studentEventRankingsCsv = "2023-24 RMS TSA student & event - Teams.csv";
|
||||||
|
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
||||||
var eventRankingsParser = new TeamParser(fileInfo);
|
var eventRankingsParser = new TeamParser(fileInfo);
|
||||||
@@ -50,14 +50,14 @@ public static class TestEntityHandler
|
|||||||
|
|
||||||
public static AssignmentRequirement[] GetEventRequirements(IList<EventDefinition> events, IList<Student> students)
|
public static AssignmentRequirement[] GetEventRequirements(IList<EventDefinition> events, IList<Student> students)
|
||||||
{
|
{
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - assumptions.csv");
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - assumptions.csv");
|
||||||
var assumptionParser = new AssignmentRequirementParser(fileInfo);
|
var assumptionParser = new AssignmentRequirementParser(fileInfo);
|
||||||
return assumptionParser.Parse(events, students);
|
return assumptionParser.Parse(events, students);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StudentEventRanking[] GetStudentEventRankings(Student[] students, EventDefinition[] events)
|
public static StudentEventRanking[] GetStudentEventRankings(Student[] students, EventDefinition[] events)
|
||||||
{
|
{
|
||||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA - Student Event Rankings.csv");
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - Student Event Rankings.csv");
|
||||||
|
|
||||||
var rankingParser = new StudentEventRankingParser(fileInfo);
|
var rankingParser = new StudentEventRankingParser(fileInfo);
|
||||||
return rankingParser.Parse(students, events);
|
return rankingParser.Parse(students, events);
|
||||||
|
|||||||
Binary file not shown.
@@ -2,13 +2,22 @@
|
|||||||
|
|
||||||
namespace WebApp
|
namespace WebApp
|
||||||
{
|
{
|
||||||
public class ChapterSettings
|
public class StateContainer
|
||||||
{
|
{
|
||||||
public required string Name { get; set; }
|
private int[]? _scheduledTeams;
|
||||||
public required string ShortName { get; set; }
|
|
||||||
public required string RegionalId { get; set; }
|
public int[] UserId
|
||||||
public required string StateId { get; set; }
|
{
|
||||||
public required string NationalId { get; set; }
|
get => _scheduledTeams ?? [];
|
||||||
public required string CompetitionYear { get; set; }
|
set
|
||||||
|
{
|
||||||
|
_scheduledTeams = value;
|
||||||
|
NotifyStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action? OnChange;
|
||||||
|
|
||||||
|
private void NotifyStateChanged() => OnChange?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
|
public sealed class ClipboardService
|
||||||
|
{
|
||||||
|
private readonly IJSRuntime _jsRuntime;
|
||||||
|
|
||||||
|
public ClipboardService(IJSRuntime jsRuntime)
|
||||||
|
{
|
||||||
|
_jsRuntime = jsRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<string> ReadTextAsync()
|
||||||
|
{
|
||||||
|
return _jsRuntime.InvokeAsync<string>("navigator.clipboard.readText");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask WriteTextAsync(string text)
|
||||||
|
{
|
||||||
|
return _jsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<MudNavLink Href="/teams/printout" Icon="@Icons.Material.Filled.Print">Print out</MudNavLink>
|
<MudNavLink Href="/teams/printout" Icon="@Icons.Material.Filled.Print">Print out</MudNavLink>
|
||||||
<MudNavLink Href="/teams/handout" Icon="@Icons.Material.Filled.Print">Handout</MudNavLink>
|
<MudNavLink Href="/teams/handout" Icon="@Icons.Material.Filled.Print">Handout</MudNavLink>
|
||||||
</MudNavGroup>
|
</MudNavGroup>
|
||||||
<MudNavLink Href="/meeting-schedule/scheduler" Icon="@AppIcons.Scheduler">Schedule</MudNavLink>
|
<MudNavLink Href="/meeting-schedule/" Icon="@AppIcons.Scheduler">Schedule</MudNavLink>
|
||||||
<MudNavGroup Title="Team Building" Icon="@Icons.Material.Filled.GroupAdd" Expanded="true">
|
<MudNavGroup Title="Team Building" Icon="@Icons.Material.Filled.GroupAdd" Expanded="true">
|
||||||
<MudNavLink Href="/students/event-ranking" Icon="@AppIcons.EventRank">Event Ranking</MudNavLink>
|
<MudNavLink Href="/students/event-ranking" Icon="@AppIcons.EventRank">Event Ranking</MudNavLink>
|
||||||
<MudNavLink Href="/teams/assignment" Icon="@AppIcons.TeamAssignment">Team Assignment</MudNavLink>
|
<MudNavLink Href="/teams/assignment" Icon="@AppIcons.TeamAssignment">Team Assignment</MudNavLink>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@using WebApp.Models
|
@using WebApp.Models
|
||||||
|
@*
|
||||||
<MudTooltip Text="Level of Effort">@AppIcons.LevelOfEffortIcon(EventDefinition.LevelOfEffort)</MudTooltip>
|
<MudTooltip Text="Level of Effort">@AppIcons.LevelOfEffortIcon(EventDefinition.LevelOfEffort)</MudTooltip>
|
||||||
|
|
||||||
@if(EventDefinition.EventFormat == EventFormat.Individual) {
|
@if(EventDefinition.EventFormat == EventFormat.Individual) {
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
}
|
}
|
||||||
@if (EventDefinition.OnSiteActivity)
|
@if (EventDefinition.OnSiteActivity)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="On-site Activity">@AppIcons.OnSiteActivity</MudTooltip>
|
<MudTooltip Text="On-site Activity"> @AppIcons.OnSiteActivity</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (EventDefinition.RegionalEvent)
|
@if (EventDefinition.RegionalEvent)
|
||||||
@@ -18,6 +18,28 @@
|
|||||||
@if (EventDefinition.Presubmission)
|
@if (EventDefinition.Presubmission)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="Presubmission Event">@AppIcons.PresubmissionEvent</MudTooltip>
|
<MudTooltip Text="Presubmission Event">@AppIcons.PresubmissionEvent</MudTooltip>
|
||||||
|
} *@
|
||||||
|
|
||||||
|
|
||||||
|
@AppIcons.LevelOfEffortIcon(EventDefinition.LevelOfEffort)
|
||||||
|
|
||||||
|
@if (EventDefinition.EventFormat == EventFormat.Individual)
|
||||||
|
{
|
||||||
|
@AppIcons.IndividualEvent
|
||||||
|
}
|
||||||
|
@if (EventDefinition.OnSiteActivity)
|
||||||
|
{
|
||||||
|
@AppIcons.OnSiteActivity
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (EventDefinition.RegionalEvent)
|
||||||
|
{
|
||||||
|
@AppIcons.RegionalEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (EventDefinition.Presubmission)
|
||||||
|
{
|
||||||
|
@AppIcons.PresubmissionEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,327 @@
|
|||||||
|
@using System.Text
|
||||||
|
@using Core.Calculation
|
||||||
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
@page "/meeting-schedule"
|
||||||
|
@inject IConfiguration Configuration
|
||||||
|
@inject AppDbContext Context
|
||||||
|
@inject ClipboardService ClipboardService
|
||||||
|
|
||||||
|
<PageTitle>@Configuration["ChapterSettings:Shortname"] TSA Schedule @Configuration["ChapterSettings:CompetitionYear"]</PageTitle>
|
||||||
|
|
||||||
|
<MudText Typo="Typo.h3">@Configuration["ChapterSettings:Shortname"] TSA Schedule @Configuration["ChapterSettings:CompetitionYear"]</MudText>
|
||||||
|
|
||||||
|
|
||||||
|
<MudPaper Class="pa-4 mt-5">
|
||||||
|
@* <MudText>Include: @string.Join(", ", _scheduledTeams) </MudText> *@
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="6" lg="9">
|
||||||
|
<MudText Typo="Typo.h4">Time Slots</MudText>
|
||||||
|
<MudNumericField @bind-Value="_parameters.TimeSlots"
|
||||||
|
Label="Time Slots" Min="1" Max="4"></MudNumericField>
|
||||||
|
<MudButton Class="ma-3" OnClick="Solve" Variant="Variant.Filled" Color="Color.Primary" Disabled="@_isSolving">Solve</MudButton>
|
||||||
|
<MudIconButton OnClick="CopyToClipboard" Icon="@Icons.Material.Filled.ContentCopy">
|
||||||
|
</MudIconButton>
|
||||||
|
<MudTable T="Team[]" ServerData="SolveSchedule" @ref="_solutionData">
|
||||||
|
<HeaderContent>
|
||||||
|
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd>
|
||||||
|
|
||||||
|
@{
|
||||||
|
var overlaps
|
||||||
|
= TeamSchedulerSolution.GetStudentTeamOverlaps(context).ToArray();
|
||||||
|
}
|
||||||
|
@foreach (var team in context.OrderBy(e => e.ToString()))
|
||||||
|
{
|
||||||
|
var removed = !_scheduledTeams.Contains(team);
|
||||||
|
<MudItem>
|
||||||
|
<MudLink Typo="Typo.body1"
|
||||||
|
Class="d-flex align-center"
|
||||||
|
Color="Color.Default"
|
||||||
|
OnClick="@(() => ToggleRequiredTeam(team))">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Clear"
|
||||||
|
Size="Size.Small"
|
||||||
|
Class="@(removed ? "" : "d-none")"></MudIcon>
|
||||||
|
@team -
|
||||||
|
@foreach (var student in team.Students)
|
||||||
|
{
|
||||||
|
var overlap = overlaps.Any(o => o.Item1.Equals(student));
|
||||||
|
var color = overlap ? Color.Warning : Color.Default;
|
||||||
|
|
||||||
|
<MudText
|
||||||
|
Typo="Typo.body2"
|
||||||
|
Class="d-inline-block ml-3"
|
||||||
|
Color="color">
|
||||||
|
@student.FirstName@(overlap ? "*" : "")
|
||||||
|
</MudText>
|
||||||
|
}
|
||||||
|
</MudLink>
|
||||||
|
|
||||||
|
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@{
|
||||||
|
var unscheduled = TeamSchedulerSolution.GetStudentsNotInTimSlot(context, _students);
|
||||||
|
}
|
||||||
|
@if (unscheduled.Any())
|
||||||
|
{
|
||||||
|
|
||||||
|
<MudItem>Unscheduled</MudItem>
|
||||||
|
foreach (var student in unscheduled)
|
||||||
|
{
|
||||||
|
<MudItem Class="">
|
||||||
|
<MudText Typo="Typo.body1" HtmlTag="i">@student.FirstName </MudText>
|
||||||
|
@{
|
||||||
|
var pa = _possibleAdditions.ToArray();
|
||||||
|
}
|
||||||
|
@foreach (var unassignedTeam in _solution.StudentUnassignedTeams(student))
|
||||||
|
{
|
||||||
|
var color = pa.Contains(unassignedTeam) ? Color.Error : Color.Default;
|
||||||
|
var added = _scheduledTeams.Contains(unassignedTeam);
|
||||||
|
<MudLink Typo="Typo.body2"
|
||||||
|
Class="d-flex align-center ml-3"
|
||||||
|
Color="@color"
|
||||||
|
OnClick="@(() => ToggleRequiredTeam(unassignedTeam))">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Check"
|
||||||
|
Size="Size.Small"
|
||||||
|
Class="@(added ? "" : "d-none")"></MudIcon>
|
||||||
|
@unassignedTeam
|
||||||
|
</MudLink>
|
||||||
|
}
|
||||||
|
</MudItem>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
</MudTable>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="6" lg="3">
|
||||||
|
|
||||||
|
<MudStack>
|
||||||
|
|
||||||
|
<MudButton OnClick="() => AddHighLevelOfEffort()">Add High Effort</MudButton>
|
||||||
|
<MudButton OnClick="() => AddRegionals()">Add Regionals</MudButton>
|
||||||
|
|
||||||
|
<MudButton OnClick="() => RemoveIndividual()">Remove Individual</MudButton>
|
||||||
|
<MudButton OnClick="() => RemoveLowLevelOfEffort()">Remove Low Effort</MudButton>
|
||||||
|
<MudButton OnClick="() => Invert()">Invert</MudButton>
|
||||||
|
|
||||||
|
<MudItem>@string.Join(", ", (_possibleAdditions ?? []).Select(e => e.ToString()))</MudItem>
|
||||||
|
|
||||||
|
<MudToggleGroup T="Team"
|
||||||
|
SelectionMode="SelectionMode.MultiSelection"
|
||||||
|
@bind-Values="_scheduledTeams"
|
||||||
|
Vertical="true"
|
||||||
|
CheckMark>
|
||||||
|
@foreach (var team in _teams.OrderBy(e => e.Event.Name))
|
||||||
|
{
|
||||||
|
<MudToggleItem Value="@team" Style="font-size: .75rem;">
|
||||||
|
<MudTooltip Text="@string.Join(", ", team.Students.Select(s => s.FirstName))">
|
||||||
|
<div class="d-flex align-center justify-space-between flex-wrap">
|
||||||
|
<MudText Class="ellipsis">@team.ToString()</MudText>
|
||||||
|
<EventAttributes EventDefinition="@team.Event"></EventAttributes>
|
||||||
|
</div>
|
||||||
|
</MudTooltip>
|
||||||
|
</MudToggleItem>
|
||||||
|
}
|
||||||
|
</MudToggleGroup>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
</MudGrid>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private Team[]? _teams;
|
||||||
|
private Student[]? _students;
|
||||||
|
MudTable<Team[]> _solutionData;
|
||||||
|
private TeamSchedulerSolution _solution;
|
||||||
|
private TeamSchedulerOptions _parameters;
|
||||||
|
bool _isSolving;
|
||||||
|
private IEnumerable<Team> _scheduledTeams = [];
|
||||||
|
private IEnumerable<Team> _possibleAdditions = [];
|
||||||
|
|
||||||
|
private async Task AddRegionals()
|
||||||
|
{
|
||||||
|
_scheduledTeams
|
||||||
|
= _teams.Where(e => e.Event.RegionalEvent).Concat(_scheduledTeams).Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddHighLevelOfEffort()
|
||||||
|
{
|
||||||
|
_scheduledTeams
|
||||||
|
= _teams.Where(e => e.Event.LevelOfEffort >= 3).Concat(_scheduledTeams).Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveIndividual()
|
||||||
|
{
|
||||||
|
_scheduledTeams
|
||||||
|
= _scheduledTeams.Where(t => t.Event.EventFormat != EventFormat.Individual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveLowLevelOfEffort()
|
||||||
|
{
|
||||||
|
_scheduledTeams
|
||||||
|
= _scheduledTeams.Where(t => t.Event.LevelOfEffort > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Invert()
|
||||||
|
{
|
||||||
|
var rt = _scheduledTeams.ToArray();
|
||||||
|
_scheduledTeams
|
||||||
|
= _teams.Where(t => !rt.Contains(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleRequiredTeam(Team unassignedTeam)
|
||||||
|
{
|
||||||
|
if (_scheduledTeams.Contains(unassignedTeam))
|
||||||
|
_scheduledTeams = _scheduledTeams.Where(t => t != unassignedTeam);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_scheduledTeams = _scheduledTeams.Concat(new[] { unassignedTeam });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_parameters =
|
||||||
|
new TeamSchedulerOptions(
|
||||||
|
timeSlots: 2,
|
||||||
|
mustIncludeEvents:
|
||||||
|
[
|
||||||
|
// "Medical Technology", "Electrical Applications" , "RegionalTeam",
|
||||||
|
// ,"Dragster", "Flight"
|
||||||
|
],
|
||||||
|
extended:
|
||||||
|
[
|
||||||
|
// "Invention", "Construction Challenge", "Mechanical", "Mass", "Micro"
|
||||||
|
//"STEM"
|
||||||
|
//"Community", "Vlogging"// "Microcontroller"
|
||||||
|
],
|
||||||
|
omittedEvents:
|
||||||
|
[
|
||||||
|
// "Vlogging", "Junior", "Community Service Video", "Digital Photography",
|
||||||
|
// "STEM"
|
||||||
|
|
||||||
|
//"Leadership",// "Electrical", //"Construction"
|
||||||
|
// "Forensic",
|
||||||
|
//"CAD"
|
||||||
|
//"I&I Team 1", "I&I Team 2"//, "Website Design",
|
||||||
|
],
|
||||||
|
absentStudents:
|
||||||
|
[
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
_teams
|
||||||
|
= await Context.Teams
|
||||||
|
.Include(e => e.Event)
|
||||||
|
.Include(e => e.Students)
|
||||||
|
.OrderBy(e => e.Event.Name)
|
||||||
|
.ThenBy(e => e.Identifier)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
_students =
|
||||||
|
await Context.Students
|
||||||
|
.Include(e => e.Teams)
|
||||||
|
.ThenInclude(e => e.Captain)
|
||||||
|
.Include(e => e.EventRankings)
|
||||||
|
.ThenInclude(e => e.EventDefinition)
|
||||||
|
.OrderBy(e => e.FirstName).ToArrayAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<TableData<Team[]>> SolveSchedule(TableState arg1, CancellationToken arg2)
|
||||||
|
{
|
||||||
|
_isSolving = true;
|
||||||
|
var teamScheduler = new TeamScheduler(_scheduledTeams, _parameters.TimeSlots);
|
||||||
|
|
||||||
|
// teamScheduler
|
||||||
|
// .ScheduleSeparate(
|
||||||
|
// _teams.First(e => e.Event.Name.Contains("Data Science")),
|
||||||
|
// _teams.First(e => e.Event.Name.Contains("Microcontroller Design"))
|
||||||
|
// );
|
||||||
|
|
||||||
|
_solution = teamScheduler.Solve();
|
||||||
|
|
||||||
|
var loe = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.LevelOfEffort);
|
||||||
|
var biggest = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
|
||||||
|
var individual = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
|
||||||
|
var anyNotMeetingAlready = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.AnyNotMeetingAlready);
|
||||||
|
|
||||||
|
_possibleAdditions = loe;
|
||||||
|
if (!_possibleAdditions.Any())
|
||||||
|
_possibleAdditions = biggest;
|
||||||
|
if (!_possibleAdditions.Any())
|
||||||
|
_possibleAdditions = anyNotMeetingAlready;
|
||||||
|
if (!_possibleAdditions.Any())
|
||||||
|
_possibleAdditions = individual;
|
||||||
|
|
||||||
|
await InvokeAsync(StateHasChanged); // let the UI know that the solution has been found
|
||||||
|
|
||||||
|
_isSolving = false;
|
||||||
|
return new TableData<Team[]> { Items = _solution.TimeSlots};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Solve()
|
||||||
|
{
|
||||||
|
_solutionData.ReloadServerData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task CopyToClipboard()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var timeslot in _solution.TimeSlots)
|
||||||
|
{
|
||||||
|
var overlaps
|
||||||
|
= TeamSchedulerSolution.GetStudentTeamOverlaps(timeslot).Select(e => e.Item1).ToArray();
|
||||||
|
foreach (var scheduledTeam in timeslot.OrderBy(e => e.ToString()))
|
||||||
|
{
|
||||||
|
var t = scheduledTeam.ToString();
|
||||||
|
var s =
|
||||||
|
string.Join(", ",
|
||||||
|
scheduledTeam.Students
|
||||||
|
.OrderBy(e => e == scheduledTeam.Captain)
|
||||||
|
.ThenBy(e => e.FirstName)
|
||||||
|
.Select(e => e.FirstName + (overlaps.Contains(e) ? "*": "")));
|
||||||
|
|
||||||
|
if (scheduledTeam.Event.EventFormat is EventFormat.Individual)
|
||||||
|
sb.Append(t);
|
||||||
|
else
|
||||||
|
sb.Append($"{t} - {s}");
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
}
|
||||||
|
var unscheduled = TeamSchedulerSolution.GetStudentsNotInTimSlot(timeslot, _students);
|
||||||
|
|
||||||
|
if (unscheduled.Any())
|
||||||
|
{
|
||||||
|
sb.Append("--Unscheduled");
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
foreach (var student in unscheduled)
|
||||||
|
{
|
||||||
|
var s = student.FirstName;
|
||||||
|
var unassignedTeams = _solution.StudentUnassignedTeams(student);
|
||||||
|
var t = string.Join(", ", unassignedTeams.Select(e => e.ToString()));
|
||||||
|
|
||||||
|
sb.Append($"{s} - {t}");
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ClipboardService.WriteTextAsync(sb.ToString());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine("Cannot write text to clipboard");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
@using Core.Calculation
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
|
||||||
@page "/meeting-schedule/scheduler"
|
|
||||||
@inject IConfiguration Configuration
|
|
||||||
@inject AppDbContext Context
|
|
||||||
|
|
||||||
<PageTitle>@Configuration["ChapterSettings:Shortname"] TSA Schedule @Configuration["ChapterSettings:CompetitionYear"]</PageTitle>
|
|
||||||
|
|
||||||
<MudText Typo="Typo.h3">@Configuration["ChapterSettings:Shortname"] TSA Schedule @Configuration["ChapterSettings:CompetitionYear"]</MudText>
|
|
||||||
|
|
||||||
|
|
||||||
<MudPaper Class="pa-4 mt-5">
|
|
||||||
<MudGrid>
|
|
||||||
<MudItem Style="width:160px;">
|
|
||||||
<MudNumericField @bind-Value="_parameters.TimeSlots"
|
|
||||||
Label="Time Slots" Min="1" Max="4"></MudNumericField>
|
|
||||||
</MudItem>
|
|
||||||
@foreach (var evt in _options)
|
|
||||||
{
|
|
||||||
<MudItem Style="width:200px">
|
|
||||||
|
|
||||||
<MudButtonGroup>
|
|
||||||
<MudTooltip Text="Require">
|
|
||||||
<MudToggleIconButton @bind-Toggled="@evt.Require"
|
|
||||||
Icon="@Icons.Material.Filled.CheckBoxOutlineBlank"
|
|
||||||
ToggledIcon="@Icons.Material.Filled.CheckBox"
|
|
||||||
ToggledColor="@Color.Success"
|
|
||||||
Disabled="@evt.Omit"
|
|
||||||
Size="Size.Small"
|
|
||||||
title="@(evt.Require ? "Required" : "Optional")" />
|
|
||||||
</MudTooltip>
|
|
||||||
<MudTooltip Text="Extend">
|
|
||||||
<MudToggleIconButton @bind-Toggled="@evt.Extend"
|
|
||||||
Icon="@Icons.Material.Filled.PlusOne"
|
|
||||||
ToggledIcon="@Icons.Material.Filled.PlusOne"
|
|
||||||
ToggledColor="@Color.Success"
|
|
||||||
Disabled="@evt.Omit"
|
|
||||||
Size="Size.Small"
|
|
||||||
title="@(evt.Extend ? "Extend" : "Single")" />
|
|
||||||
</MudTooltip>
|
|
||||||
<MudTooltip Text="Extend">
|
|
||||||
<MudToggleIconButton @bind-Toggled="@evt.Omit"
|
|
||||||
Icon="@Icons.Material.Filled.EventBusy"
|
|
||||||
ToggledIcon="@Icons.Material.Filled.EventBusy"
|
|
||||||
ToggledColor="@Color.Error"
|
|
||||||
Size="Size.Small"
|
|
||||||
title="@(evt.Omit ? "Omit" : "Optional")" />
|
|
||||||
</MudTooltip>
|
|
||||||
</MudButtonGroup>
|
|
||||||
<MudTooltip Text="@string.Join(", ", evt.Team.Students.Select(e => e.FirstName))">
|
|
||||||
<MudText>@evt.Team.ToString()</MudText>
|
|
||||||
</MudTooltip>
|
|
||||||
</MudItem>
|
|
||||||
}
|
|
||||||
@* <MudItem>
|
|
||||||
<MudTooltip Text="Require at least one On-Site Event">
|
|
||||||
<MudSwitch @bind-Value="_parameters" Color="Color.Info"
|
|
||||||
Label="On-Site" />
|
|
||||||
</MudTooltip>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem>
|
|
||||||
<MudTooltip Text="Require at least one Regional Event">
|
|
||||||
<MudSwitch @bind-Value="_parameters.RequireRegional" Color="Color.Info"
|
|
||||||
Label="Regional" />
|
|
||||||
</MudTooltip>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem>
|
|
||||||
<MudStack Style="width:100px;">
|
|
||||||
<MudTooltip Text="Student Event Count Assignment Range">
|
|
||||||
<MudInputLabel>Event Count</MudInputLabel>
|
|
||||||
</MudTooltip>
|
|
||||||
<MudNumericField @bind-Value="_parameters.EventsLowerBound"
|
|
||||||
Label="At Least" Min="2" Max="4"></MudNumericField>
|
|
||||||
|
|
||||||
<MudNumericField @bind-Value="_parameters.EventsUpperBound"
|
|
||||||
Label="Up to" Min="3" Max="5"></MudNumericField>
|
|
||||||
</MudStack>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem>
|
|
||||||
<MudStack Style="width:100px;">
|
|
||||||
<MudTooltip Text="Student Level of Effort Range">
|
|
||||||
<MudInputLabel>LOE</MudInputLabel>
|
|
||||||
</MudTooltip>
|
|
||||||
<MudNumericField @bind-Value="_parameters.EffortLowerBound"
|
|
||||||
Label="At Least" Min="4" Max="7"></MudNumericField>
|
|
||||||
|
|
||||||
<MudNumericField @bind-Value="_parameters.EffortUpperBound"
|
|
||||||
Label="Up to" Min="7" Max="12"></MudNumericField>
|
|
||||||
</MudStack>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem>
|
|
||||||
<MudInputLabel>Assignment Requirements</MudInputLabel>
|
|
||||||
<MudTable T="AssignmentRequirement" ServerData="ReloadAssignmentRequirements" @ref="_assignmentRequirementData">
|
|
||||||
|
|
||||||
<RowTemplate Context="item">
|
|
||||||
<MudTd Class="align-center">
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.RemoveCircle" Size="Size.Small"
|
|
||||||
OnClick="() => RemoveRequireEvent(item)"></MudIconButton>
|
|
||||||
</MudTd>
|
|
||||||
<MudTd Class="align-center">
|
|
||||||
@item.Student.FirstName
|
|
||||||
@item.EventDefinition.ShortName
|
|
||||||
@if (item.Requirement == Requirement.Include)
|
|
||||||
{
|
|
||||||
<MudIcon Class="ml-3" Icon="@Icons.Material.Filled.ThumbUp" Size="Size.Small"></MudIcon>
|
|
||||||
}
|
|
||||||
@if (item.Requirement == Requirement.Exclude)
|
|
||||||
{
|
|
||||||
<MudIcon Class="ml-3" Icon="@Icons.Material.Filled.ThumbDownAlt" Size="Size.Small"></MudIcon>
|
|
||||||
}
|
|
||||||
</MudTd>
|
|
||||||
</RowTemplate>
|
|
||||||
</MudTable>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem>
|
|
||||||
<MudInputLabel>Two Team Events</MudInputLabel>
|
|
||||||
<MudTable T="EventDefinition" ServerData="ReloadEventTwoTeam" @ref="_eventTwoTeamData">
|
|
||||||
|
|
||||||
<RowTemplate Context="item">
|
|
||||||
<MudTd Class="align-center">
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.RemoveCircle" Size="Size.Small"
|
|
||||||
OnClick="() => RemoveTwoTeam(item)"></MudIconButton>
|
|
||||||
</MudTd>
|
|
||||||
<MudTd Class="align-center">@item.ShortName</MudTd>
|
|
||||||
</RowTemplate>
|
|
||||||
</MudTable>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem>
|
|
||||||
<MudInputLabel>Omitted Events</MudInputLabel>
|
|
||||||
<MudTable T="EventDefinition" ServerData="ReloadOmittedEvents" @ref="_eventOmittedData">
|
|
||||||
|
|
||||||
<RowTemplate Context="item">
|
|
||||||
<MudTd Class="align-center">
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.RemoveCircle" Size="Size.Small"
|
|
||||||
OnClick="() => RemoveOmitted(item)"></MudIconButton>
|
|
||||||
</MudTd>
|
|
||||||
<MudTd Class="align-center">@item.ShortName</MudTd>
|
|
||||||
</RowTemplate>
|
|
||||||
</MudTable>
|
|
||||||
</MudItem> *@
|
|
||||||
</MudGrid>
|
|
||||||
<MudButton Class="ma-3" OnClick="Solve" Variant="Variant.Filled" Color="Color.Primary" Disabled="@_isSolving">Solve</MudButton>
|
|
||||||
</MudPaper>
|
|
||||||
|
|
||||||
|
|
||||||
<MudItem xs="12" lg="4">
|
|
||||||
<MudText Typo="Typo.h4">Time Slots</MudText>
|
|
||||||
<MudTable T="Team[]" ServerData="SolveSchedule" @ref="_solutionData">
|
|
||||||
<HeaderContent>
|
|
||||||
|
|
||||||
</HeaderContent>
|
|
||||||
<RowTemplate>
|
|
||||||
<MudTd>
|
|
||||||
@{
|
|
||||||
var ol = TeamSchedulerSolution.GetStudentTeamOverlaps(context);}
|
|
||||||
@foreach (var t in context)
|
|
||||||
{
|
|
||||||
<MudItem>
|
|
||||||
@t.ToString() -
|
|
||||||
@string.Join(", ", t.Students.Select(s => s.FirstName + " " + (ol.Any(o => o.Item1.Equals(s)) ? "*" : "" )) )
|
|
||||||
</MudItem>
|
|
||||||
}
|
|
||||||
@* @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>
|
|
||||||
</MudItem>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private Team[]? _teams;
|
|
||||||
private Student[]? _students;
|
|
||||||
MudTable<Team[]> _solutionData;
|
|
||||||
private TeamSchedulerSolution _solution;
|
|
||||||
private TeamSchedulerOptions _parameters;
|
|
||||||
bool _isSolving = false;
|
|
||||||
|
|
||||||
public class TeamOptions
|
|
||||||
{
|
|
||||||
public Team Team { get; set; }
|
|
||||||
public bool Require { get; set; } = false;
|
|
||||||
public bool Omit { get; set; } = false;
|
|
||||||
public bool Extend { get; set; } = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TeamOptions[]? _options;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
_parameters =
|
|
||||||
new TeamSchedulerOptions(
|
|
||||||
timeSlots: 4,
|
|
||||||
mustIncludeEvents:
|
|
||||||
[
|
|
||||||
// "Medical Technology", "Electrical Applications" , "RegionalTeam",
|
|
||||||
// ,"Dragster", "Flight"
|
|
||||||
],
|
|
||||||
extended:
|
|
||||||
[
|
|
||||||
// "Invention", "Construction Challenge", "Mechanical", "Mass", "Micro"
|
|
||||||
//"STEM"
|
|
||||||
//"Community", "Vlogging"// "Microcontroller"
|
|
||||||
],
|
|
||||||
omittedEvents:
|
|
||||||
[
|
|
||||||
// "Vlogging", "Junior", "Community Service Video", "Digital Photography",
|
|
||||||
// "STEM"
|
|
||||||
|
|
||||||
//"Leadership",// "Electrical", //"Construction"
|
|
||||||
// "Forensic",
|
|
||||||
//"CAD"
|
|
||||||
//"I&I Team 1", "I&I Team 2"//, "Website Design",
|
|
||||||
],
|
|
||||||
absentStudents:
|
|
||||||
[
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
_teams
|
|
||||||
= await Context.Teams
|
|
||||||
.Include(e => e.Event)
|
|
||||||
.Include(e => e.Students)
|
|
||||||
.OrderBy(e => e.Event.Name)
|
|
||||||
.ThenBy(e => e.Identifier)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
_students =
|
|
||||||
await Context.Students
|
|
||||||
.Include(e => e.Teams)
|
|
||||||
.ThenInclude(e => e.Captain)
|
|
||||||
.Include(e => e.EventRankings)
|
|
||||||
.ThenInclude(e => e.EventDefinition)
|
|
||||||
.OrderBy(e => e.FirstName).ToArrayAsync();
|
|
||||||
|
|
||||||
_options = _teams
|
|
||||||
.Select(e => new TeamOptions { Team = e })
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TableData<Team[]>> SolveSchedule(TableState arg1, CancellationToken arg2)
|
|
||||||
{
|
|
||||||
//_isSolving = true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var mustIncludeTeams = _teams;
|
|
||||||
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);
|
|
||||||
|
|
||||||
// teamScheduler
|
|
||||||
// .ScheduleSeparate(
|
|
||||||
// _teams.First(e => e.Event.Name.Contains("Data Science")),
|
|
||||||
// _teams.First(e => e.Event.Name.Contains("Microcontroller Design"))
|
|
||||||
// );
|
|
||||||
|
|
||||||
_solution = teamScheduler.Solve();
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged); // let the UI know that the solution has been found
|
|
||||||
|
|
||||||
return new TableData<Team[]> { Items = _solution.TimeSlots};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void Solve()
|
|
||||||
{
|
|
||||||
_solutionData.ReloadServerData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,20 +39,20 @@ else
|
|||||||
@if (!context.RankedEvents.Any(re => re.OnSiteActivity))
|
@if (!context.RankedEvents.Any(re => re.OnSiteActivity))
|
||||||
{
|
{
|
||||||
<MudTooltip Text="No On-Site Activity">
|
<MudTooltip Text="No On-Site Activity">
|
||||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.LocalActivity"></MudIcon>
|
<MudText Style="font-weight:bolder" Color="Color.Warning">@AppIcons.OnSiteActivity</MudText>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
@if (!context.RankedEvents.Any(re => re.RegionalEvent))
|
@if (!context.RankedEvents.Any(re => re.RegionalEvent))
|
||||||
{
|
{
|
||||||
<MudTooltip Text="No Regional Event">
|
<MudTooltip Text="No Regional Event">
|
||||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.PinDrop"></MudIcon>
|
<MudText Style="font-weight:bolder" Color="Color.Warning">@AppIcons.RegionalEvent</MudText>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (context.RankedEvents.All(re => re.EventFormat != EventFormat.Individual))
|
@if (context.RankedEvents.All(re => re.EventFormat != EventFormat.Individual))
|
||||||
{
|
{
|
||||||
<MudTooltip Text="No Individual Event">
|
<MudTooltip Text="No Individual Event">
|
||||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.Person"></MudIcon>
|
<MudText Style="font-weight:bolder" Color="Color.Warning">@AppIcons.IndividualEvent</MudText>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
@@ -64,9 +64,7 @@ else
|
|||||||
@if (st != null)
|
@if (st != null)
|
||||||
{
|
{
|
||||||
<span>@st.EventDefinition.ShortName
|
<span>@st.EventDefinition.ShortName
|
||||||
@if(st.EventDefinition.EventFormat == EventFormat.Individual) { <span>ⓘ</span>}
|
<EventAttributes EventDefinition="@st.EventDefinition"></EventAttributes>
|
||||||
@if(st.EventDefinition.RegionalEvent) {<span>ⓡ</span>}
|
|
||||||
@if(st.EventDefinition.OnSiteActivity) {<span>ⓐ</span>}
|
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@using WebApp.Models
|
@using WebApp.Models
|
||||||
@using EventAssignment = Core.Calculation.EventAssignment
|
@using EventAssignment = Core.Calculation.EventAssignment
|
||||||
@inject AppDbContext Context
|
@inject AppDbContext Context
|
||||||
|
@inject IDialogService DialogService
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
<PageTitle>Event Assignment - TSA Chapter Organizer</PageTitle>
|
<PageTitle>Event Assignment - TSA Chapter Organizer</PageTitle>
|
||||||
@@ -135,13 +136,13 @@
|
|||||||
<MudTd>@if (!context.HasOnSiteActivity)
|
<MudTd>@if (!context.HasOnSiteActivity)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="No On-Site Activity">
|
<MudTooltip Text="No On-Site Activity">
|
||||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.LocalActivity"></MudIcon>
|
<MudText Style="font-weight:bolder" Color="Color.Warning">@AppIcons.OnSiteActivity</MudText>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
@if (!context.HasRegionalEvent)
|
@if (!context.HasRegionalEvent)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="No Regional Event">
|
<MudTooltip Text="No Regional Event">
|
||||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.PinDrop"></MudIcon>
|
<MudText Style="font-weight:bolder" Color="Color.Warning">@AppIcons.RegionalEvent</MudText>
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
@@ -238,7 +239,7 @@
|
|||||||
</MudTable>
|
</MudTable>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
<MudItem xs="12" lg="4">
|
<MudItem xs="12" lg="4">
|
||||||
<MudText Typo="Typo.h4">TimeSlots</MudText>
|
<MudText Typo="Typo.h4">Teams</MudText>
|
||||||
<MudTable T="Team" ServerData="SolveAssignments" @ref="_teamData">
|
<MudTable T="Team" ServerData="SolveAssignments" @ref="_teamData">
|
||||||
<ColGroup>
|
<ColGroup>
|
||||||
<col style="width: 200px;" />
|
<col style="width: 200px;" />
|
||||||
@@ -254,7 +255,7 @@
|
|||||||
}
|
}
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<b>@context.Event.Name</b>
|
<b>@context.Event.Name</b>
|
||||||
<span>@AppIcons.EventEffort(context.Event) @AppIcons.EventAttributes(context.Event)</span>
|
<EventAttributes EventDefinition="context.Event"></EventAttributes>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd Style="white-space:nowrap">
|
<MudTd Style="white-space:nowrap">
|
||||||
|
|
||||||
@@ -444,11 +445,19 @@
|
|||||||
|
|
||||||
public async Task SaveTeams()
|
public async Task SaveTeams()
|
||||||
{
|
{
|
||||||
var teams = await Context.Teams.ExecuteDeleteAsync();
|
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");
|
||||||
|
|
||||||
await Context.Teams.AddRangeAsync(_teams);
|
if (result == true)
|
||||||
await Context.SaveChangesAsync();
|
{
|
||||||
|
await Context.Teams.ExecuteDeleteAsync();
|
||||||
|
await Context.Teams.AddRangeAsync(_teams);
|
||||||
|
await Context.SaveChangesAsync();
|
||||||
|
|
||||||
NavigationManager.NavigateTo("/teams");
|
NavigationManager.NavigateTo("/teams");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,15 @@ else
|
|||||||
<MudSelectItem T="Student" Value="@student">@student.Name</MudSelectItem>
|
<MudSelectItem T="Student" Value="@student">@student.Name</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudTextField T="Student" Label="Captain" @bind-Value="Team.Captain" For="@(() => Team.Captain)" Required="false" Clearable="true"></MudTextField>
|
<MudStack>
|
||||||
|
<MudText>Captain</MudText>
|
||||||
|
<MudToggleGroup T="Student" SelectionMode="SelectionMode.ToggleSelection" @bind-Value="Team.Captain" CheckMark>
|
||||||
|
@foreach (var student in _selectedStudents.OrderBy(e => e.FirstName))
|
||||||
|
{
|
||||||
|
<MudToggleItem Value="@(student)" Text="@student.Name" />
|
||||||
|
}
|
||||||
|
</MudToggleGroup>
|
||||||
|
</MudStack>
|
||||||
<MudTextField T="string?" Label="Identifier" @bind-Value="Team.Identifier" For="@(() => Team.Identifier)" Required="false" Clearable="true"></MudTextField>
|
<MudTextField T="string?" Label="Identifier" @bind-Value="Team.Identifier" For="@(() => Team.Identifier)" Required="false" Clearable="true"></MudTextField>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
@page "/teams"
|
@page "/teams"
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using WebApp.Models
|
|
||||||
@inject AppDbContext Context
|
@inject AppDbContext Context
|
||||||
@inject IDialogService DialogService
|
@inject IDialogService DialogService
|
||||||
@inject ISnackbar Snackbar
|
@inject ISnackbar Snackbar
|
||||||
@@ -31,11 +30,6 @@
|
|||||||
<TeamStudents Team="@context.Item"></TeamStudents>
|
<TeamStudents Team="@context.Item"></TeamStudents>
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
</TemplateColumn>
|
</TemplateColumn>
|
||||||
@* <TemplateColumn Title="Grade (TSA Year)" SortBy="e => e.Grade" Sortable="true">
|
|
||||||
<CellTemplate>
|
|
||||||
@context.Item.Grade (@context.Item.TsaYear)
|
|
||||||
</CellTemplate>
|
|
||||||
</TemplateColumn> *@
|
|
||||||
<TemplateColumn>
|
<TemplateColumn>
|
||||||
<CellTemplate>
|
<CellTemplate>
|
||||||
<CrudActions
|
<CrudActions
|
||||||
@@ -47,7 +41,7 @@
|
|||||||
</TemplateColumn>
|
</TemplateColumn>
|
||||||
</Columns>
|
</Columns>
|
||||||
<PagerContent>
|
<PagerContent>
|
||||||
<MudDataGridPager T="Student"></MudDataGridPager>
|
<MudDataGridPager T="Team"></MudDataGridPager>
|
||||||
</PagerContent>
|
</PagerContent>
|
||||||
</MudDataGrid>
|
</MudDataGrid>
|
||||||
|
|
||||||
|
|||||||
@@ -33,14 +33,14 @@ else
|
|||||||
@context.ToString()
|
@context.ToString()
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
@AppIcons.EventEffort(context.Event)
|
<EventAttributes EventDefinition="context.Event"></EventAttributes>
|
||||||
@AppIcons.EventAttributes(context.Event)
|
|
||||||
</MudTd>
|
</MudTd>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var students
|
var students
|
||||||
= context.Students
|
= context.Students
|
||||||
.OrderBy(s => s.EventRankings.Find(e => e.EventDefinition == context.Event)?.Rank ?? int.MaxValue)
|
.OrderByDescending(s => s == context.Captain)
|
||||||
|
.ThenBy(s => s.EventRankings.Find(e => e.EventDefinition == context.Event)?.Rank ?? int.MaxValue)
|
||||||
.ThenByDescending(e => e.Grade)
|
.ThenByDescending(e => e.Grade)
|
||||||
.ThenBy(e => e.FirstName)
|
.ThenBy(e => e.FirstName)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
@@ -54,8 +54,8 @@ else
|
|||||||
var rank = student.EventRankings
|
var rank = student.EventRankings
|
||||||
.Find(e => e.EventDefinition == context.Event)?.Rank ?? int.MaxValue;
|
.Find(e => e.EventDefinition == context.Event)?.Rank ?? int.MaxValue;
|
||||||
|
|
||||||
<MudTd Class="@($"event-rank-{rank}")">
|
<MudTd Class="@(EventRankClass(rank))">
|
||||||
@student.Name
|
@student.Name @if(context?.Captain == student) {<span> (Cpt)</span>}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -117,10 +117,9 @@ else
|
|||||||
{
|
{
|
||||||
var rank = context.EventRankings
|
var rank = context.EventRankings
|
||||||
.Find(e => e.EventDefinition == team.Event)?.Rank ?? int.MaxValue;
|
.Find(e => e.EventDefinition == team.Event)?.Rank ?? int.MaxValue;
|
||||||
<MudTh Class="@($"event-rank-{rank}")">
|
<MudTh Class="@(EventRankClass(rank))">
|
||||||
@team.ToString()
|
@team.ToString()
|
||||||
@AppIcons.EventEffort(team.Event)
|
<EventAttributes EventDefinition="team.Event"></EventAttributes>
|
||||||
@AppIcons.EventAttributes(team.Event)
|
|
||||||
|
|
||||||
@if (rank == int.MaxValue)
|
@if (rank == int.MaxValue)
|
||||||
{
|
{
|
||||||
@@ -181,7 +180,7 @@ else
|
|||||||
<RowTemplate>
|
<RowTemplate>
|
||||||
@{ var rank = context.EventRankings
|
@{ var rank = context.EventRankings
|
||||||
.Find(e => e.EventDefinition == team.Event)?.Rank ?? int.MaxValue; }
|
.Find(e => e.EventDefinition == team.Event)?.Rank ?? int.MaxValue; }
|
||||||
<MudTd Class="@($"event-rank-{rank}")">
|
<MudTd Class="@(EventRankClass(rank))">
|
||||||
@team.ToString()
|
@team.ToString()
|
||||||
@AppIcons.EventEffort(team.Event)
|
@AppIcons.EventEffort(team.Event)
|
||||||
@AppIcons.EventAttributes(team.Event)
|
@AppIcons.EventAttributes(team.Event)
|
||||||
@@ -205,6 +204,14 @@ else
|
|||||||
private Team[]? _teams;
|
private Team[]? _teams;
|
||||||
private int _maxTeamSize;
|
private int _maxTeamSize;
|
||||||
private Student[]? _students;
|
private Student[]? _students;
|
||||||
|
private bool _rankColorEnabled;
|
||||||
|
|
||||||
|
private string EventRankClass(int rank)
|
||||||
|
{
|
||||||
|
if (!_rankColorEnabled)
|
||||||
|
return "";
|
||||||
|
return "event-rank-" + rank;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
@student.FirstName
|
@student.FirstName
|
||||||
@if (captain)
|
@if (captain)
|
||||||
{
|
{
|
||||||
<span> *</span>
|
<span> (Cpt)</span>
|
||||||
}
|
}
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<MudSelect
|
||||||
|
T="Team"
|
||||||
|
MultiSelection="true"
|
||||||
|
@bind-SelectedValues="@SelectedTeams"
|
||||||
|
ToStringFunc="e => e.ToString()"
|
||||||
|
Label="@Label">
|
||||||
|
|
||||||
|
@foreach (var evt in Teams.OrderBy(e => e.ToString()))
|
||||||
|
{
|
||||||
|
<MudSelectItem T="Team" Value="@evt">@evt.ToString()</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public Team[]? Teams { get; set; }
|
||||||
|
[Parameter] public required IEnumerable<Team> SelectedTeams { get; set; }
|
||||||
|
[Parameter] public string Label { get; set; } = "Teams";
|
||||||
|
}
|
||||||
+1
-1
@@ -29,4 +29,4 @@ RUN dotnet publish "./WebApp.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:
|
|||||||
FROM base AS final
|
FROM base AS final
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=publish /app/publish .
|
COPY --from=publish /app/publish .
|
||||||
ENTRYPOINT ["dotnet", "WebApp.dll"]
|
ENTRYPOINT ["dotnet", "tsa-chapter-organizer.dll"]
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Data;
|
using Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MudBlazor.Services;
|
using MudBlazor.Services;
|
||||||
|
using WebApp;
|
||||||
using WebApp.Components;
|
using WebApp.Components;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
@@ -19,6 +20,11 @@ builder.Services.AddQuickGridEntityFrameworkAdapter();
|
|||||||
|
|
||||||
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<ClipboardService>();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<StateContainer>(); // Server- side
|
||||||
|
builder.Services.AddSingleton<StateContainer>();//Client-side
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
|
|||||||
+2
-1
@@ -13,4 +13,5 @@
|
|||||||
<ProjectGuid>039e1539-eda8-4f4e-acc0-b8292827a3a9</ProjectGuid>
|
<ProjectGuid>039e1539-eda8-4f4e-acc0-b8292827a3a9</ProjectGuid>
|
||||||
<_TargetId>DockerCustomContainerRegistry</_TargetId>
|
<_TargetId>DockerCustomContainerRegistry</_TargetId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
<!-- https://stackoverflow.com/questions/59713258/how-to-customize-the-docker-image-name-in-vs2019 -->
|
||||||
|
|||||||
@@ -55,6 +55,16 @@
|
|||||||
},
|
},
|
||||||
"publishAllPorts": true,
|
"publishAllPorts": true,
|
||||||
"useSSL": true
|
"useSSL": true
|
||||||
|
},
|
||||||
|
"Scheduler": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "meeting-schedule",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "https://localhost:7235;http://localhost:5013"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<UserSecretsId>73972335-ec46-4ad6-a959-8ebe0b06147d</UserSecretsId>
|
<UserSecretsId>73972335-ec46-4ad6-a959-8ebe0b06147d</UserSecretsId>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<PackageId>tsa-chapter-organizer</PackageId>
|
<PackageId>$(AssemblyName)</PackageId>
|
||||||
<Title>TSA Chapter Organizer</Title>
|
<Title>TSA Chapter Organizer</Title>
|
||||||
|
<AssemblyName>tsa-chapter-organizer</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SQLiteDefault": "Data Source=ChapterOrganizer.db"
|
"SQLiteDefault": "Data Source=data.db"
|
||||||
},
|
},
|
||||||
"Name" : "Test" ,
|
"Name" : "Test" ,
|
||||||
"ChapterSettings": {
|
"ChapterSettings": {
|
||||||
|
|||||||
Reference in New Issue
Block a user