356 lines
12 KiB
C#
356 lines
12 KiB
C#
using System.Diagnostics;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using Core.Calculation;
|
|
using Core.Entities;
|
|
using Core.Parsers;
|
|
using Core.Utility;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Web.Models;
|
|
|
|
namespace Web.Controllers
|
|
{
|
|
public partial class HomeController : Controller
|
|
{
|
|
private const string ContentDirectory = @"C:\Users\james\source\TSA Chapter Organizer\Tests\Parsers\TestInput\";
|
|
|
|
private CompetitiveEvent[] GetCompetitiveEvents()
|
|
{
|
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - Event Definitions.csv");
|
|
var eventRankingsParser = new EventDefinitionParser(fileInfo);
|
|
return eventRankingsParser.Parse();
|
|
}
|
|
|
|
private Student[] GetStudents(IList<CompetitiveEvent> events)
|
|
{
|
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - Nationals Student Event Rankings.csv");
|
|
var eventRankingsParser = new StudentParser(fileInfo);
|
|
return eventRankingsParser.Parse(events);
|
|
}
|
|
|
|
public Team[] GetTeams(IList<CompetitiveEvent> competitiveEvents, IList<Student> students)
|
|
{
|
|
var studentEventRankingsCsv = "2024-25 RMS TSA student & event - Nationals Teams.csv";
|
|
|
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
|
var eventRankingsParser = new TeamParser(fileInfo);
|
|
var teams = eventRankingsParser.Parse(competitiveEvents, students);
|
|
|
|
foreach (var student in students)
|
|
{
|
|
student.Teams = teams.Where(t => t.Students.Contains(student)).ToList();
|
|
}
|
|
return teams;
|
|
}
|
|
|
|
public AssignmentAssumption[] GetAssignmentAssumptions(IList<CompetitiveEvent> competitiveEvents, IList<Student> students)
|
|
{
|
|
var assumptionsCsv = "2024-25 RMS TSA student & event - assumptions.csv";
|
|
|
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, assumptionsCsv);
|
|
var assumptionParser = new AssignmentAssumptionParser(fileInfo);
|
|
var assumptions = assumptionParser.Parse(competitiveEvents, students);
|
|
|
|
return assumptions;
|
|
}
|
|
|
|
public IDictionary<CompetitiveEvent, List<EventOccurrence>> GetStateEventOccurrences(IList<CompetitiveEvent> competitiveEvents)
|
|
{
|
|
var eventTimesFilename = "2025 TN TSA State Competition Event Times.txt";
|
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
|
|
var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
|
|
return parser.Parse();
|
|
}
|
|
|
|
public IDictionary<CompetitiveEvent, List<EventOccurrence>> GetNationalEventOccurrences(IList<CompetitiveEvent> competitiveEvents)
|
|
{
|
|
var eventTimesFilename = "2025 TSA Nationals Competition Event Times.txt";
|
|
var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
|
|
var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
|
|
var nationalEventOccurrences = parser.Parse();
|
|
|
|
var locationPrefixes = new[] { "Cheekwood", "Ryman", "Lincoln", "Canal", "Online", "Magnolia", "Tennessee", "Bayou", "Hermitage", "Belmont", "Davidson", "Washington", "Belle Meade"};
|
|
var oredLocations = string.Join("|", locationPrefixes);
|
|
var regex = new Regex($"^(.*)((?:{oredLocations}).*)");
|
|
|
|
foreach (var occurrence in nationalEventOccurrences)
|
|
{
|
|
foreach (var eventOccurrence in occurrence.Value)
|
|
{
|
|
if (!string.IsNullOrEmpty(eventOccurrence.Location))
|
|
continue;
|
|
var match = regex.Match(eventOccurrence.Name);
|
|
if (!match.Success)
|
|
continue;
|
|
eventOccurrence.Name = match.Groups[1].Value.Trim();
|
|
eventOccurrence.Location = match.Groups[2].Value.Trim();
|
|
}
|
|
}
|
|
return nationalEventOccurrences;
|
|
|
|
}
|
|
|
|
private readonly ILogger<HomeController> _logger;
|
|
|
|
public HomeController(ILogger<HomeController> logger)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
public IActionResult Index()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
public IActionResult Events()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents().Where(e => e.Name != "Chapter Team").ToArray();
|
|
return View(competitiveEvents);
|
|
}
|
|
|
|
public IActionResult StudentEventHandout()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
var teams = GetTeams(competitiveEvents, students);
|
|
|
|
return View(Tuple.Create(students));
|
|
}
|
|
|
|
public IActionResult StudentEvents()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
|
|
var eventStudentPicksArray = DataProcessing.GetEventStudentPicks(competitiveEvents, students);
|
|
var assignmentParameters = new AssignmentParameters
|
|
{
|
|
EffortUpperBound = 9,
|
|
RequireOnSite = true,
|
|
RequireRegional = true,
|
|
TeamSizeLimit = 4
|
|
};
|
|
|
|
var eventAssignment = new EventAssigner(competitiveEvents, students, assignmentParameters);
|
|
|
|
var assignmentAssumptions = GetAssignmentAssumptions(competitiveEvents, students);
|
|
|
|
foreach (var assumption in assignmentAssumptions)
|
|
{
|
|
switch (assumption.Assumption)
|
|
{
|
|
case Assumption.Exclude:
|
|
eventAssignment.ExcludeFromEvent(assumption.EventAssignment);
|
|
break;
|
|
case Assumption.Include:
|
|
eventAssignment.AssignToEvent(assumption.EventAssignment);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
eventAssignment.RemoveEvent(new List<CompetitiveEvent>
|
|
{
|
|
competitiveEvents.First(e => e.Name == "Chapter Team")
|
|
});
|
|
|
|
eventAssignment.IncludedEvents(new List<CompetitiveEvent>
|
|
{
|
|
//competitiveEvents.First(e => e.Name == "System Control Technology")
|
|
});
|
|
|
|
|
|
var eventAssignmentsList = eventAssignment.Solve();
|
|
return View(Tuple.Create(competitiveEvents, students, eventStudentPicksArray, eventAssignmentsList, assignmentParameters));
|
|
}
|
|
|
|
public IActionResult Teams()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
var teams = GetTeams(competitiveEvents, students);
|
|
|
|
//teams = teams.Where(t => t.Event.RegionalEvent).ToArray();
|
|
|
|
return View(Tuple.Create(teams));
|
|
}
|
|
|
|
public IActionResult Regionals()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
var teams = GetTeams(competitiveEvents, students);
|
|
|
|
teams = teams.Where(t => t.Event.RegionalEvent).ToArray();
|
|
var enumerable = students.Where(s => !teams.SelectMany(ts => ts.Students).Contains(s)).ToArray();
|
|
|
|
return View(Tuple.Create(teams, enumerable));
|
|
}
|
|
|
|
public IActionResult State()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
var teams = GetTeams(competitiveEvents, students);
|
|
var eventOccurrences = GetStateEventOccurrences(competitiveEvents);
|
|
|
|
// Filter out pre-conference
|
|
eventOccurrences =
|
|
(from kv in eventOccurrences
|
|
let newV = kv.Value.Where(eo => !eo.Name.Contains("Pre-Conference"))
|
|
select Tuple.Create(kv.Key, newV))
|
|
.ToDictionary(s => s.Item1, s => s.Item2.ToList());
|
|
|
|
return View(Tuple.Create(teams, students, eventOccurrences));
|
|
}
|
|
public IActionResult Nationals()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
var teams = GetTeams(competitiveEvents, students);
|
|
var eventOccurrences = GetNationalEventOccurrences(competitiveEvents);
|
|
|
|
// Filter out pre-conference
|
|
eventOccurrences =
|
|
(from kv in eventOccurrences
|
|
let newV = kv.Value.Where(eo => !eo.Name.Contains("Pre-Conference"))
|
|
select Tuple.Create(kv.Key, newV))
|
|
.ToDictionary(s => s.Item1, s => s.Item2.ToList());
|
|
|
|
return View(Tuple.Create(teams, students, eventOccurrences));
|
|
}
|
|
|
|
public IActionResult TeamGrid()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
var teams = GetTeams(competitiveEvents, students);
|
|
|
|
return View(Tuple.Create(teams, students));
|
|
}
|
|
|
|
public IActionResult Students()
|
|
{
|
|
var competitiveEvents = GetCompetitiveEvents();
|
|
var students = GetStudents(competitiveEvents);
|
|
GetTeams(competitiveEvents, students);
|
|
|
|
return View(Tuple.Create(students));
|
|
}
|
|
|
|
public static Func<T, bool> And<T>(params Func<T, bool>[] predicates)
|
|
{
|
|
return t => predicates.All(predicate => predicate(t));
|
|
}
|
|
|
|
public IActionResult Schedule()
|
|
{
|
|
var scheduleOptions =
|
|
new ScheduleOptions(
|
|
timeSlots: 3,
|
|
mustIncludeEvents:new []
|
|
{
|
|
"Medical Technology", "Electrical Applications" //, "RegionalTeam",
|
|
,"Dragster", "Flight"
|
|
},
|
|
extended: new string[]
|
|
{
|
|
"Invention", "Construction Challenge", "Mechanical", "Mass", "Micro"
|
|
//"STEM"
|
|
//"Community", "Vlogging"// "Microcontroller"
|
|
},
|
|
omittedEvents: new string[]
|
|
{
|
|
"Vlogging", "Junior", "Community Service Video", "Digital Photography",
|
|
"STEM"
|
|
|
|
//"Leadership",// "Electrical", //"Construction"
|
|
// "Forensic",
|
|
//"CAD"
|
|
//"I&I Team 1", "I&I Team 2"//, "Website Design",
|
|
},
|
|
absentStudents: new string[]
|
|
{
|
|
//"Eliam"
|
|
},
|
|
reverse: true,
|
|
slotPush:0);
|
|
|
|
var events = GetCompetitiveEvents();
|
|
var students = GetStudents(events);
|
|
|
|
var allTeams = GetTeams(events, students);
|
|
|
|
var omittedEvents = allTeams.Where(t => scheduleOptions.OmittedEvents?.Any(s => t.Name.Contains(s)) == true).ToArray();
|
|
allTeams = allTeams.Where(t => !omittedEvents.Contains(t)).ToArray();
|
|
|
|
bool RegionalPredicate(Team t) => t.Event.RegionalEvent;
|
|
bool TeamPredicate(Team t) => t.Event.Format is EventFormat.Team;
|
|
bool RegionalTeamPredicate(Team t) => RegionalPredicate(t) && TeamPredicate(t);
|
|
bool HighEffort(Team t) => t.Event.LevelOfEffort == 3;
|
|
bool LowEffort(Team t) => t.Event.LevelOfEffort == 1;
|
|
|
|
bool RegionalTeamSomeEffortPredicate(Team t) => RegionalTeamPredicate(t) && !LowEffort(t);
|
|
|
|
bool IndividualPredicate(Team t) => t.Event.Format is EventFormat.Individual;
|
|
bool negativePredicate(Team t) => false;
|
|
|
|
var mustIncludeTeams =
|
|
from e in events
|
|
from t in allTeams
|
|
where t.Event == e &&
|
|
(scheduleOptions.MustIncludeEvents?.Any(s => s == "RegionalTeam") == true && RegionalTeamSomeEffortPredicate(t)
|
|
|| scheduleOptions.MustIncludeEvents?.Any( t.Event.Name.Contains) == true
|
|
|| HighEffort(t))
|
|
select t;
|
|
|
|
Debug.WriteLine("Must Include: " + string.Join(", ", mustIncludeTeams.Select(t => t.ToStringWithIndividualAndRegional())));
|
|
Debug.WriteLine("Omitted: " + string.Join(", ", omittedEvents.Select(t => t.ToStringWithIndividualAndRegional())));
|
|
|
|
var teamScheduler = new TeamScheduler(mustIncludeTeams.ToArray(), scheduleOptions.TimeSlots);
|
|
var schedule = teamScheduler.Solve();
|
|
|
|
//schedule = schedule.OrderByDescending(ts => new Random(schedule.GetHashCode() + 1).Next()).ToArray();
|
|
|
|
if (scheduleOptions.SlotPush != 0)
|
|
{
|
|
schedule = schedule.Skip(scheduleOptions.SlotPush).Concat(schedule.Take(scheduleOptions.SlotPush))
|
|
.ToArray();
|
|
}
|
|
if (scheduleOptions.Reverse)
|
|
schedule = schedule.Reverse().ToArray();
|
|
|
|
////// extend schedules
|
|
var extendedTeams =
|
|
from t in allTeams
|
|
where
|
|
scheduleOptions.MustIncludeEvents?.Any(s => scheduleOptions.ExtendedTeams?.Any(et => t.Name.Contains(et)) == true) == true
|
|
select t;
|
|
|
|
foreach (var extendedTeam in extendedTeams)
|
|
{
|
|
schedule = new UnassignedStudentScheduler(allTeams, schedule).AddAdditionalTimeSlot(extendedTeam);
|
|
}
|
|
|
|
schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.LevelOfEffort);
|
|
schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
|
|
schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
|
|
//schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.AnyNotMeetingAlready);
|
|
|
|
var unassignedStudents = UnassignedStudentScheduler.UnassignedStudents(students, schedule).ToArray();
|
|
|
|
return View(Tuple.Create(schedule, unassignedStudents, scheduleOptions));
|
|
}
|
|
|
|
public IActionResult Privacy()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
|
public IActionResult Error()
|
|
{
|
|
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
|
}
|
|
}
|
|
} |