Add Blazor WebApp and rework data handling to utilize Entity Framework
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
using Core.Entities;
|
||||
|
||||
namespace Core.Calculation;
|
||||
|
||||
public class DataProcessing
|
||||
{
|
||||
public static EventStudentPicks[] GetEventStudentPicks(IList<CompetitiveEvent> events, IList<Student> students)
|
||||
{
|
||||
return
|
||||
students.SelectMany(
|
||||
student => student.RankedEventPicks.Select((e, i) => (e, student, i + 1)))
|
||||
.OrderBy(tuple => tuple.Item3)
|
||||
.ThenByDescending(tuple => tuple.student.Grade + tuple.student.TsaYear)
|
||||
.GroupBy(tuple => tuple.e)
|
||||
.OrderBy(tuples => tuples.Key.Name)
|
||||
.Select(tuples =>
|
||||
new EventStudentPicks(tuples.Key, tuples.Select(tuple => Tuple.Create(tuple.student, tuple.Item3)).ToList())
|
||||
).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using Core.Entities;
|
||||
using Google.OrTools.Sat;
|
||||
using IntVar = Google.OrTools.Sat.IntVar;
|
||||
|
||||
namespace Core.Calculation
|
||||
{
|
||||
public class EventAssigner
|
||||
{
|
||||
private readonly IList<CompetitiveEvent> _events;
|
||||
private readonly IList<Student> _students;
|
||||
private readonly AssignmentParameters _parameters;
|
||||
private readonly int[] _allEvents;
|
||||
private readonly int[] _allStudents;
|
||||
// how many students have picked each event?
|
||||
private readonly int[] _eventPickCounts;
|
||||
private IList<EventAssignment> _preAssigned = new List<EventAssignment>();
|
||||
private IList<EventAssignment> _prohibited = new List<EventAssignment>();
|
||||
private IList<CompetitiveEvent> _droppedEvents;
|
||||
private IList<CompetitiveEvent> _includedEvents;
|
||||
|
||||
public EventAssigner(IList<CompetitiveEvent> events, IList<Student> students, AssignmentParameters parameters)
|
||||
{
|
||||
_events = events;
|
||||
_students = students;
|
||||
_parameters = parameters;
|
||||
_allEvents = Enumerable.Range(0, _events.Count).ToArray();
|
||||
_allStudents = Enumerable.Range(0, _students.Count).ToArray();
|
||||
_eventPickCounts = new int[_allEvents.Length];
|
||||
_preAssigned = new List<EventAssignment>();
|
||||
_droppedEvents = new List<CompetitiveEvent>();
|
||||
_includedEvents = new List<CompetitiveEvent>();
|
||||
for (var i = 0; i < _events.Count; i++)
|
||||
{
|
||||
var e = _events[i];
|
||||
_eventPickCounts[i] = _students.Count(s => s.RankedEventPicks.Contains(e));
|
||||
}
|
||||
}
|
||||
|
||||
public void AssignToEvent(EventAssignment preAssigned)
|
||||
{
|
||||
_preAssigned.Add(preAssigned);
|
||||
}
|
||||
|
||||
|
||||
public void ExcludeFromEvent(EventAssignment prohibited)
|
||||
{
|
||||
_prohibited.Add(prohibited);
|
||||
}
|
||||
|
||||
public void RemoveEvent(IList<CompetitiveEvent> events)
|
||||
{
|
||||
_droppedEvents = events;
|
||||
}
|
||||
public void IncludedEvents(IList<CompetitiveEvent> events)
|
||||
{
|
||||
_includedEvents = events;
|
||||
}
|
||||
|
||||
public class SolutionPrinter : CpSolverSolutionCallback
|
||||
{
|
||||
private readonly IList<CompetitiveEvent> _events;
|
||||
private readonly IList<Student> _students;
|
||||
private readonly BoolVar[,] _eventAssignment;
|
||||
|
||||
public SolutionPrinter(IList<CompetitiveEvent> events, IList<Student> students, BoolVar[,] eventAssignment)
|
||||
{
|
||||
_events = events;
|
||||
_students = students;
|
||||
_eventAssignment = eventAssignment;
|
||||
}
|
||||
public override void OnSolutionCallback()
|
||||
{
|
||||
Console.WriteLine($"Solution ");
|
||||
foreach (var evt in Enumerable.Range(0,_events.Count))
|
||||
{
|
||||
foreach (var student in Enumerable.Range(0, _students.Count))
|
||||
{
|
||||
if (Value(_eventAssignment[evt, student]) == 1L)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Team[] Solve()
|
||||
{
|
||||
|
||||
// Model.
|
||||
var model = new CpModel();
|
||||
|
||||
// Variables.
|
||||
var x = new BoolVar[_allEvents.Length, _allStudents.Length];
|
||||
foreach (var e in _allEvents)
|
||||
foreach (var s in _allStudents)
|
||||
x[e, s] = model.NewBoolVar($"eventAssignments[{e},{s}]");
|
||||
|
||||
foreach (var preAssignment in _preAssigned)
|
||||
{
|
||||
var e = _events.IndexOf(preAssignment.Event);
|
||||
var s = _students.IndexOf(preAssignment.Student);
|
||||
model.AddAssumption(x[e, s]);
|
||||
}
|
||||
|
||||
foreach (var prohibit in _prohibited)
|
||||
{
|
||||
var e = _events.IndexOf(prohibit.Event);
|
||||
var s = _students.IndexOf(prohibit.Student);
|
||||
|
||||
var prohibitVar = new List<IntVar> { x[e, s] };
|
||||
|
||||
model.AddLinearConstraint(LinearExpr.Sum(prohibitVar), 0, 0);
|
||||
prohibitVar.Clear();
|
||||
}
|
||||
|
||||
// Limit the capacity of each event
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
var evt = _events[e];
|
||||
var eventPickCounts = _eventPickCounts[e];
|
||||
|
||||
var evtMinTeamSize = evt.MinTeamSize;
|
||||
var evtMaxTeamSize = evt.MaxTeamSize;
|
||||
|
||||
var teamDivs = eventPickCounts / (evtMinTeamSize * 1.0);
|
||||
if (_includedEvents.Contains(evt))
|
||||
teamDivs = 1;
|
||||
|
||||
//var teamsCount = (int)Math.Ceiling(teamDivs);
|
||||
var teamCount = (int)Math.Round(teamDivs);
|
||||
if (teamCount > evt.MaxTeamCountState)
|
||||
teamCount = evt.MaxTeamCountState;
|
||||
|
||||
// limit to one team for group events
|
||||
if (_parameters.LimitTeamsToOne && evt.Format is EventFormat.Team && teamCount > 1) teamCount = 1;
|
||||
|
||||
if (evt.Name == "Tech Bowl")
|
||||
teamCount = 1;
|
||||
|
||||
var eventCapacity = new List<IntVar>();
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
eventCapacity.Add(x[e, s]);
|
||||
}
|
||||
|
||||
if (_droppedEvents != null && _droppedEvents.Contains(evt))
|
||||
teamCount = 0;
|
||||
|
||||
var lb = evtMinTeamSize * teamCount;
|
||||
var ub = Math.Min(evtMaxTeamSize * teamCount, _parameters.TeamSizeLimit);
|
||||
model.AddLinearConstraint(LinearExpr.Sum(eventCapacity), lb, ub);
|
||||
Debug.WriteLine($"{evt.Name,30}\t{evt.Format,-10}\t{lb} - {ub}");
|
||||
|
||||
model.Minimize(LinearExpr.Sum(eventCapacity));
|
||||
eventCapacity.Clear();
|
||||
}
|
||||
|
||||
// Limit the number of events a student is assigned
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var student = _students[s];
|
||||
|
||||
var studentCapacity = new List<IntVar>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
studentCapacity.Add(x[e, s]);
|
||||
}
|
||||
|
||||
model.AddLinearConstraint(LinearExpr.Sum(studentCapacity), _parameters.AssignmentLowerBound, _parameters.AssignmentUpperBound);
|
||||
studentCapacity.Clear();
|
||||
}
|
||||
|
||||
var eventEffortCoefficients = GetEventEffortCoefficients(_events);
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var effortVar = new BoolVar[_allEvents.Length];
|
||||
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
effortVar[e] = x[e, s];
|
||||
}
|
||||
var student = _students[s];
|
||||
var experienceOffset = 0;
|
||||
switch (student.TsaYear)
|
||||
{
|
||||
case 1: experienceOffset = 1; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
var ub = _parameters.EffortUpperBound - experienceOffset;
|
||||
var lb = _parameters.EffortLowerBound;
|
||||
if (ub <= lb)
|
||||
lb = ub - 1;
|
||||
|
||||
model.Add(LinearExpr.WeightedSum(effortVar, eventEffortCoefficients) >= lb);
|
||||
model.Add(LinearExpr.WeightedSum(effortVar, eventEffortCoefficients) <= ub);
|
||||
}
|
||||
|
||||
// each student should be assigned at least one on site activity event
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var onSiteActivity = new List<ILiteral>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
if (_events[e].OnSiteActivity)
|
||||
onSiteActivity.Add(x[e, s]);
|
||||
}
|
||||
|
||||
if (_parameters.RequireOnSite)
|
||||
model.AddAtLeastOne(onSiteActivity);
|
||||
onSiteActivity.Clear();
|
||||
}
|
||||
|
||||
// students should have at maximum one individual event
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var individualEvent = new List<ILiteral>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
if (_events[e].Format == EventFormat.Individual)
|
||||
individualEvent.Add(x[e, s]);
|
||||
}
|
||||
|
||||
model.AddAtMostOne(individualEvent);
|
||||
individualEvent.Clear();
|
||||
}
|
||||
|
||||
// students should have at maximum regional events
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var regionalEvent = new List<ILiteral>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
if (_events[e].RegionalEvent)
|
||||
regionalEvent.Add(x[e, s]);
|
||||
}
|
||||
|
||||
if (_parameters.RequireRegional)
|
||||
model.AddLinearConstraint(LinearExpr.Sum(regionalEvent), 1, 2);
|
||||
regionalEvent.Clear();
|
||||
}
|
||||
|
||||
var maximizePicks = LinearExpr.NewBuilder();
|
||||
// optimize student selections
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var eventPickCoefficients = GetEventPickCoefficients(_students[s].RankedEventPicks, _events);
|
||||
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
maximizePicks.AddTerm(x[e, s], eventPickCoefficients[e]);
|
||||
}
|
||||
}
|
||||
model.Maximize(maximizePicks);
|
||||
|
||||
var solver = new CpSolver();
|
||||
var cpSolverStatus = solver.Solve(model);
|
||||
|
||||
// print solver status
|
||||
Console.WriteLine($"Solver status: {cpSolverStatus}");
|
||||
|
||||
var eventAssignmentsList = new List<Team>();
|
||||
|
||||
if (cpSolverStatus == CpSolverStatus.Optimal || cpSolverStatus == CpSolverStatus.Feasible)
|
||||
{
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
var students = new List<Student>();
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
if (solver.BooleanValue(x[e, s]))
|
||||
{
|
||||
students.Add(_students[s]);
|
||||
//Console.WriteLine($"{_events[evt].Name} : {_students[s].Name}");
|
||||
}
|
||||
}
|
||||
if (students.Count > 0)
|
||||
eventAssignmentsList.Add(new Team(_events[e].Name, _events[e], students));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Console.WriteLine("No solution found.");
|
||||
}
|
||||
|
||||
return eventAssignmentsList.ToArray();
|
||||
}
|
||||
|
||||
private long[] GetEventPickCoefficients(IList<CompetitiveEvent> rankedEvents, IEnumerable<CompetitiveEvent> events)
|
||||
{
|
||||
var eventPickCount = rankedEvents.Count;
|
||||
|
||||
return
|
||||
events.Select(e =>
|
||||
{
|
||||
var eventPickIndex = rankedEvents.IndexOf(e);
|
||||
return eventPickIndex switch
|
||||
{
|
||||
0 => eventPickCount + 1,
|
||||
1 => eventPickCount + 0,
|
||||
> 1 => eventPickCount ,
|
||||
_ => 0L
|
||||
};
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
|
||||
private long[] GetEventEffortCoefficients(IEnumerable<CompetitiveEvent> events)
|
||||
{
|
||||
return
|
||||
events.Select(e => e.Name == "Tech Bowl" ? 0 : e.LevelOfEffort.HasValue ? e.LevelOfEffort.Value : 10L).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Core.Entities;
|
||||
using Google.OrTools.Sat;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using IntVar = Google.OrTools.Sat.IntVar;
|
||||
|
||||
namespace Core.Calculation
|
||||
{
|
||||
public class EventAssignment
|
||||
{
|
||||
private readonly IList<EventDefinition> _events;
|
||||
private readonly IList<Student> _students;
|
||||
private readonly AssignmentParameters _parameters;
|
||||
private readonly int[] _allEvents;
|
||||
private readonly int[] _allStudents;
|
||||
// how many students have picked each eventDefinition?
|
||||
private readonly int[] _eventPickCounts;
|
||||
private IList<AssignmentRequirement> _assignmentRequirements = new List<AssignmentRequirement>();
|
||||
private IList<EventDefinition> _droppedEvents = new List<EventDefinition>();
|
||||
private IList<EventDefinition> _includedEvents = new List<EventDefinition>();
|
||||
private IList<EventDefinition> _twoTeams = new List<EventDefinition>();
|
||||
|
||||
public EventAssignment(IList<EventDefinition> events, IList<Student> students, AssignmentParameters parameters)
|
||||
{
|
||||
_events = events;
|
||||
_students = students;
|
||||
_parameters = parameters;
|
||||
_allEvents = Enumerable.Range(0, _events.Count).ToArray();
|
||||
_allStudents = Enumerable.Range(0, _students.Count).ToArray();
|
||||
_eventPickCounts = new int[_allEvents.Length];
|
||||
for (var i = 0; i < _events.Count; i++)
|
||||
{
|
||||
var e = _events[i];
|
||||
|
||||
_eventPickCounts[i] = _students.Count(s => s.EventRankings.Count(er => er.EventDefinition == e) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddAssignmentRequirement(AssignmentRequirement assignmentRequirement)
|
||||
{
|
||||
_assignmentRequirements.Add(assignmentRequirement);
|
||||
}
|
||||
|
||||
public void RemoveEvents(IList<EventDefinition> events)
|
||||
{
|
||||
_droppedEvents = events;
|
||||
}
|
||||
|
||||
public void IncludedEvents(IList<EventDefinition> events)
|
||||
{
|
||||
_includedEvents = events;
|
||||
}
|
||||
|
||||
public void AllowTwoTeams(IList<EventDefinition> events)
|
||||
{
|
||||
_twoTeams = events;
|
||||
}
|
||||
|
||||
|
||||
public async Task<EventAssignmentSolution> Solve()
|
||||
{
|
||||
Debug.WriteLine(_parameters);
|
||||
|
||||
// Model.
|
||||
var model = new CpModel();
|
||||
|
||||
// Variables.
|
||||
var x = new BoolVar[_allEvents.Length, _allStudents.Length];
|
||||
foreach (var e in _allEvents)
|
||||
foreach (var s in _allStudents)
|
||||
x[e, s] = model.NewBoolVar($"eventAssignments[{e},{s}]");
|
||||
|
||||
AddAssignmentRequirements(model, x);
|
||||
|
||||
var assignmentThresholdsList = AddEventAssignmentThresholds(model, x);
|
||||
|
||||
LimitStudentAssignment(model, x);
|
||||
|
||||
// set the range for level of effort
|
||||
var eventEffortCoefficients = GetEventEffortCoefficients(_events);
|
||||
SetLevelOfEffort(model, x, eventEffortCoefficients);
|
||||
|
||||
// each student should be assigned at least one on site activity eventDefinition
|
||||
if (_parameters.RequireOnSite)
|
||||
RequireOnSiteActivity(model, x);
|
||||
|
||||
// students should have at maximum one individual eventDefinition
|
||||
LimitIndividualEvent(model, x);
|
||||
|
||||
// students should have at least one regional event
|
||||
if (_parameters.RequireRegional)
|
||||
RequireRegionalEvent(model, x);
|
||||
|
||||
OptimizeStudentEventRankings(model, x);
|
||||
|
||||
Debug.WriteLine("Starting optimization");
|
||||
var solver = new CpSolver();
|
||||
var cpSolverStatus = await Task.Run(() => solver.Solve(model));
|
||||
|
||||
// print solver status
|
||||
Debug.WriteLine($"Solver status: {cpSolverStatus}");
|
||||
|
||||
var eventAssignmentsList = GetEventAssignments(x, solver, cpSolverStatus);
|
||||
|
||||
var eventAssignmentSolution =
|
||||
new EventAssignmentSolution
|
||||
(
|
||||
eventAssignmentsList.ToArray(),
|
||||
cpSolverStatus.ToString(),
|
||||
assignmentThresholdsList
|
||||
);
|
||||
|
||||
return eventAssignmentSolution;
|
||||
}
|
||||
|
||||
private List<Team> GetEventAssignments(BoolVar[,] x, CpSolver solver, CpSolverStatus cpSolverStatus)
|
||||
{
|
||||
var eventAssignmentsList = new List<Team>();
|
||||
|
||||
if (cpSolverStatus == CpSolverStatus.Optimal || cpSolverStatus == CpSolverStatus.Feasible)
|
||||
{
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
var students = new List<Student>();
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
if (solver.BooleanValue(x[e, s]))
|
||||
{
|
||||
students.Add(_students[s]);
|
||||
|
||||
}
|
||||
}
|
||||
if (students.Count > 0)
|
||||
eventAssignmentsList.Add(new Team { TeamId = _events[e].Name, Event = _events[e], Students = students});
|
||||
}
|
||||
}
|
||||
|
||||
return eventAssignmentsList;
|
||||
}
|
||||
|
||||
private void OptimizeStudentEventRankings(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
var maximizePicks = LinearExpr.NewBuilder();
|
||||
|
||||
// optimize student event rankings
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var eventPickCoefficients = GetEventPickCoefficients(_events, _students[s].EventRankings);
|
||||
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
maximizePicks.AddTerm(x[e, s], eventPickCoefficients[e]);
|
||||
}
|
||||
}
|
||||
model.Maximize(maximizePicks);
|
||||
}
|
||||
|
||||
private void RequireRegionalEvent(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var regionalEvent = new List<ILiteral>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
if (_events[e].RegionalEvent)
|
||||
regionalEvent.Add(x[e, s]);
|
||||
}
|
||||
|
||||
//if (_parameters.RequireRegional)
|
||||
model.AddLinearConstraint(LinearExpr.Sum(regionalEvent), 1, 2);
|
||||
regionalEvent.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void LimitIndividualEvent(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var individualEvent = new List<ILiteral>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
if (_events[e].EventFormat == EventFormat.Individual)
|
||||
individualEvent.Add(x[e, s]);
|
||||
}
|
||||
|
||||
model.AddAtMostOne(individualEvent);
|
||||
individualEvent.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void RequireOnSiteActivity(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var onSiteActivity = new List<ILiteral>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
if (_events[e].OnSiteActivity)
|
||||
onSiteActivity.Add(x[e, s]);
|
||||
}
|
||||
|
||||
//if (_parameters.RequireOnSite)
|
||||
model.AddAtLeastOne(onSiteActivity);
|
||||
onSiteActivity.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLevelOfEffort(CpModel model, BoolVar[,] x, long[] eventEffortCoefficients)
|
||||
{
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var effortVar = new BoolVar[_allEvents.Length];
|
||||
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
effortVar[e] = x[e, s];
|
||||
}
|
||||
var student = _students[s];
|
||||
var experienceOffset = 0;
|
||||
switch (student.TsaYear)
|
||||
{
|
||||
case 1: experienceOffset = 1; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
var ub = _parameters.EffortUpperBound - experienceOffset;
|
||||
var lb = _parameters.EffortLowerBound;
|
||||
if (ub <= lb)
|
||||
lb = ub - 1;
|
||||
|
||||
model.Add(LinearExpr.WeightedSum(effortVar, eventEffortCoefficients) >= lb);
|
||||
model.Add(LinearExpr.WeightedSum(effortVar, eventEffortCoefficients) <= ub);
|
||||
}
|
||||
}
|
||||
|
||||
private void LimitStudentAssignment(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
// Limit the number of events a student is assigned
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
var studentCapacity = new List<IntVar>();
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
studentCapacity.Add(x[e, s]);
|
||||
}
|
||||
|
||||
model.AddLinearConstraint(
|
||||
LinearExpr.Sum(studentCapacity), _parameters.EventsLowerBound, _parameters.EventsUpperBound);
|
||||
studentCapacity.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private List<EventAssignmentThresholds> AddEventAssignmentThresholds(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
var assignmentThresholdsList = new List<EventAssignmentThresholds>();
|
||||
// Limit the capacity of each event
|
||||
foreach (var e in _allEvents)
|
||||
{
|
||||
var evt = _events[e];
|
||||
var eventPickCounts = _eventPickCounts[e];
|
||||
|
||||
var evtMinTeamSize = evt.MinTeamSize;
|
||||
var evtMaxTeamSize = evt.MaxTeamSize;
|
||||
|
||||
var teamDivs = eventPickCounts / (evtMinTeamSize * 1.25);
|
||||
if (_includedEvents.Contains(evt))
|
||||
teamDivs = 1;
|
||||
|
||||
//var teamsCount = (int)Math.Ceiling(teamDivs);
|
||||
var teamCount = (int)Math.Round(teamDivs);
|
||||
if (teamCount > evt.MaxTeamCountState)
|
||||
teamCount = evt.MaxTeamCountState;
|
||||
|
||||
// limit to one team for group events
|
||||
if (_parameters.LimitTeamsToOne
|
||||
&& evt.EventFormat is EventFormat.Team
|
||||
&& teamCount > 1
|
||||
&& !_twoTeams.Contains(evt)
|
||||
)
|
||||
teamCount = 1;
|
||||
|
||||
if (evt.Name == "Tech Bowl")
|
||||
teamCount = 1;
|
||||
|
||||
var eventCapacity = new List<IntVar>();
|
||||
foreach (var s in _allStudents)
|
||||
{
|
||||
eventCapacity.Add(x[e, s]);
|
||||
}
|
||||
|
||||
if (_droppedEvents != null && _droppedEvents.Contains(evt))
|
||||
teamCount = 0;
|
||||
|
||||
var lb = evtMinTeamSize * teamCount;
|
||||
var ub = Math.Min(evtMaxTeamSize * teamCount, _parameters.TeamSizeLimit * teamCount);
|
||||
|
||||
assignmentThresholdsList.Add(
|
||||
new EventAssignmentThresholds
|
||||
{
|
||||
Event = evt,
|
||||
TeamCount = teamCount,
|
||||
LowerBound = evtMinTeamSize,
|
||||
UpperBound = evtMaxTeamSize,
|
||||
StudentRankingCount = eventPickCounts
|
||||
});
|
||||
|
||||
|
||||
model.AddLinearConstraint(LinearExpr.Sum(eventCapacity), lb, ub);
|
||||
|
||||
Debug.WriteLine($"{evt.Name,30}\t{evt.EventFormat,-10}\t{lb} - {ub}");
|
||||
|
||||
model.Minimize(LinearExpr.Sum(eventCapacity));
|
||||
eventCapacity.Clear();
|
||||
}
|
||||
|
||||
return assignmentThresholdsList;
|
||||
}
|
||||
|
||||
private void AddAssignmentRequirements(CpModel model, BoolVar[,] x)
|
||||
{
|
||||
foreach (var includedAssignment in _assignmentRequirements.Where(e => e.Requirement == Requirement.Include))
|
||||
{
|
||||
var e = _events.IndexOf(includedAssignment.EventDefinition);
|
||||
var s = _students.IndexOf(includedAssignment.Student);
|
||||
model.AddAssumption(x[e, s]);
|
||||
}
|
||||
|
||||
foreach (var excludedAssignment in _assignmentRequirements.Where(e => e.Requirement == Requirement.Exclude))
|
||||
{
|
||||
var e = _events.IndexOf(excludedAssignment.EventDefinition);
|
||||
var s = _students.IndexOf(excludedAssignment.Student);
|
||||
|
||||
var prohibitVar = new List<IntVar> { x[e, s] };
|
||||
|
||||
model.AddLinearConstraint(LinearExpr.Sum(prohibitVar), 0, 0);
|
||||
prohibitVar.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static long[] GetEventPickCoefficients(IList<EventDefinition> events, List<StudentEventRanking> eventRankings)
|
||||
{
|
||||
return
|
||||
events.Select(e =>
|
||||
{
|
||||
var eventRank = eventRankings.FirstOrDefault(er => er.EventDefinition == e);
|
||||
return
|
||||
eventRank == null
|
||||
? 0L
|
||||
: StudentEventRanking.MaxRank - eventRank.Rank; // inverse
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private long[] GetEventEffortCoefficients(IEnumerable<EventDefinition> events)
|
||||
{
|
||||
return
|
||||
events.Select(
|
||||
e =>
|
||||
//e.Name == "Tech Bowl"
|
||||
// ? 0 // Tech bowl gets requires no effort?
|
||||
//:
|
||||
e.LevelOfEffort ?? 10L
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
public class SolutionPrinter : CpSolverSolutionCallback
|
||||
{
|
||||
private readonly IList<EventDefinition> _events;
|
||||
private readonly IList<Student> _students;
|
||||
private readonly BoolVar[,] _eventAssignment;
|
||||
|
||||
public SolutionPrinter(IList<EventDefinition> events, IList<Student> students, BoolVar[,] eventAssignment)
|
||||
{
|
||||
_events = events;
|
||||
_students = students;
|
||||
_eventAssignment = eventAssignment;
|
||||
}
|
||||
public override void OnSolutionCallback()
|
||||
{
|
||||
Console.WriteLine($"Solution ");
|
||||
foreach (var evt in Enumerable.Range(0, _events.Count))
|
||||
{
|
||||
foreach (var student in Enumerable.Range(0, _students.Count))
|
||||
{
|
||||
if (Value(_eventAssignment[evt, student]) == 1L)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Core.Entities;
|
||||
|
||||
namespace Core.Calculation;
|
||||
|
||||
public class EventAssignmentSolution(
|
||||
Team[] teams,
|
||||
string status,
|
||||
List<EventAssignmentThresholds> assignmentThresholds)
|
||||
{
|
||||
public Team[] Teams { get; set; } = teams;
|
||||
public string Status { get; set; } = status;
|
||||
public List<EventAssignmentThresholds> AssignmentThresholds { get; } = assignmentThresholds;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Core.Entities;
|
||||
|
||||
namespace Core.Calculation;
|
||||
|
||||
public class EventAssignmentThresholds
|
||||
{
|
||||
public EventDefinition Event { get; set; }
|
||||
public int TeamCount { get; set; }
|
||||
public int LowerBound { get; set; }
|
||||
public int UpperBound { get; set; }
|
||||
public int StudentRankingCount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Core.Calculation;
|
||||
|
||||
public enum UnassignedScheduleStrategy
|
||||
{
|
||||
BiggestGroup,
|
||||
IndividualEvents,
|
||||
AnyNotMeetingAlready,
|
||||
LevelOfEffort,
|
||||
Any
|
||||
}
|
||||
@@ -2,15 +2,6 @@
|
||||
|
||||
namespace Core.Calculation;
|
||||
|
||||
public enum UnassignedScheduleStrategy
|
||||
{
|
||||
BiggestGroup,
|
||||
IndividualEvents,
|
||||
AnyNotMeetingAlready,
|
||||
LevelOfEffort,
|
||||
Any
|
||||
}
|
||||
|
||||
public class UnassignedStudentScheduler
|
||||
{
|
||||
private readonly IList<Student> _students;
|
||||
@@ -86,21 +77,21 @@ public class UnassignedStudentScheduler
|
||||
_teams
|
||||
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 1) //|| t.Event.Format is EventFormat.Individual
|
||||
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
|
||||
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
||||
.OrderByDescending(t => t.Students.Count); // select descending greatest number of students assigned
|
||||
//.ThenBy(t => Random.Shared.Next()); // todo: sort by student historic record of event assignment
|
||||
//.ThenBy(t => Random.Shared.Next()); // todo: sort by student historic record of eventDefinition assignment
|
||||
|
||||
// find individual events unassigned students can work on
|
||||
private IEnumerable<Team> GetAvailableTeams_Individual(
|
||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||
_teams
|
||||
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
|
||||
.Where(t => t.Event.Format == EventFormat.Individual || t.Students.Count == 1)
|
||||
.Where(t => t.Event.EventFormat == EventFormat.Individual || t.Students.Count == 1)
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 0);
|
||||
|
||||
// find any unassigned event students can work on
|
||||
// find any unassigned eventDefinition students can work on
|
||||
private IEnumerable<Team> GetAvailableTeams_AnyNotMeetingAlready(
|
||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||
_teams
|
||||
@@ -121,7 +112,7 @@ public class UnassignedStudentScheduler
|
||||
_teams
|
||||
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 1) //|| t.Event.Format is EventFormat.Individual
|
||||
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
|
||||
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
||||
.OrderByDescending(t => t.Event.LevelOfEffort); // select descending greatest number of students assigned
|
||||
|
||||
|
||||
+3
-2
@@ -1,13 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
||||
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
||||
<PackageReference Include="FuzzySharp" Version="2.0.2" />
|
||||
<PackageReference Include="Google.OrTools" Version="9.7.2996" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class AssignmentAssumption(CompetitiveEvent @event, Student student, Assumption assumption)
|
||||
{
|
||||
public CompetitiveEvent Event { get; } = @event;
|
||||
public Student Student { get; } = student;
|
||||
public Assumption Assumption { get; } = assumption;
|
||||
|
||||
public EventAssignment EventAssignment => new(@event, student);
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
public class AssignmentParameters(
|
||||
int effortLowerBound = 6,
|
||||
int effortUpperBound = 8,
|
||||
int assignmentLowerBound = 2,
|
||||
int assignmentUpperBound = 4,
|
||||
int eventsLowerBound = 2,
|
||||
int eventsUpperBound = 4,
|
||||
int teamSizeLimit = 4,
|
||||
bool limitTeamsToOne = true,
|
||||
bool requireRegional = true,
|
||||
@@ -12,11 +12,20 @@
|
||||
{
|
||||
public int EffortLowerBound { get; set; } = effortLowerBound;
|
||||
public int EffortUpperBound { get; set; } = effortUpperBound;
|
||||
public int AssignmentLowerBound { get; set; } = assignmentLowerBound;
|
||||
public int AssignmentUpperBound { get; set; } = assignmentUpperBound;
|
||||
public int EventsLowerBound { get; set; } = eventsLowerBound;
|
||||
public int EventsUpperBound { get; set; } = eventsUpperBound;
|
||||
public int TeamSizeLimit { get; set; } = teamSizeLimit;
|
||||
public bool LimitTeamsToOne { get; set; } = limitTeamsToOne;
|
||||
public bool RequireRegional { get; set; } = requireRegional;
|
||||
public bool RequireOnSite { get; set; } = requireOnSite;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Team Size Limit: {TeamSizeLimit}" + Environment.NewLine +
|
||||
$"Require Regional: {RequireRegional}" + Environment.NewLine +
|
||||
$"Require On-site: {RequireOnSite}" + Environment.NewLine +
|
||||
$"Events Range: [{EventsLowerBound}-{EventsUpperBound}]" + Environment.NewLine +
|
||||
$"Effort Range: [{EffortLowerBound}-{EffortUpperBound}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class AssignmentRequirement(EventDefinition eventDefinition, Student student, Requirement requirement)
|
||||
{
|
||||
public EventDefinition EventDefinition { get; } = eventDefinition;
|
||||
public Student Student { get; } = student;
|
||||
public Requirement Requirement { get; } = requirement;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class CompetitiveEvent
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string ShortName { get; set; }
|
||||
public EventFormat Format { get; set; }
|
||||
public int MinTeamSize { get; set; }
|
||||
public int MaxTeamSize { get; set; }
|
||||
|
||||
public string TeamSize =>
|
||||
MinTeamSize == MaxTeamSize
|
||||
? MinTeamSize.ToString()
|
||||
: $"{MinTeamSize.ToString()}-{MaxTeamSize.ToString()}";
|
||||
|
||||
public string SemifinalistActivity { get; set; }
|
||||
|
||||
public bool InterviewOrPresentation
|
||||
=> SemifinalistActivity.Contains("Interview") || SemifinalistActivity.Contains("Presentation");
|
||||
|
||||
public bool OnSiteActivity
|
||||
=> SemifinalistActivity.Contains("Challenge")
|
||||
|| SemifinalistActivity.Contains("Race")
|
||||
|| SemifinalistActivity.Contains("Speech")
|
||||
|| SemifinalistActivity.Contains("Test")
|
||||
|| SemifinalistActivity.Contains("Flight")
|
||||
|| SemifinalistActivity.Contains("Debate")
|
||||
|| SemifinalistActivity.Contains("Photography")
|
||||
|| SemifinalistActivity.Contains("Build")
|
||||
|| Name.Contains("Chapter")
|
||||
|| Name.Contains("Essay")
|
||||
|| SemifinalistActivity.Contains("Fly");
|
||||
|
||||
public string RegionalNotes { get; set; }
|
||||
|
||||
public int MaxTeamCountState { get; set; }
|
||||
public bool RegionalEvent { get; set; }
|
||||
|
||||
public bool RegionalPresubmit { get; set; }
|
||||
public bool StatePresubmission { get; set; }
|
||||
public bool StatePretesting { get; set; }
|
||||
public bool StatePreliminaryRound { get; set; }
|
||||
|
||||
public string Documentation { get; set; }
|
||||
|
||||
public string Eligibility { get; set; }
|
||||
public string Theme { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int? LevelOfEffort { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public static readonly CompetitiveEvent GeneralSchedule = new(){Name = "General Schedule"};
|
||||
public static readonly CompetitiveEvent VotingDelegates = new(){Name = "Voting Delegates"};
|
||||
|
||||
|
||||
public string EventAttributes ()
|
||||
{
|
||||
var st = new List<string>();
|
||||
|
||||
if (Format is EventFormat.Individual)
|
||||
st.Add( "Ind.");
|
||||
if (RegionalEvent)
|
||||
st.Add( "Reg.");
|
||||
|
||||
return string.Join(", ", st);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class EventAssignment
|
||||
public class EventAssignment(EventDefinition eventDefinition, Student student)
|
||||
{
|
||||
public CompetitiveEvent Event { get; }
|
||||
public Student Student { get; }
|
||||
|
||||
public EventAssignment(CompetitiveEvent @event, Student student)
|
||||
{
|
||||
Event = @event;
|
||||
Student = student;
|
||||
}
|
||||
public EventDefinition EventDefinition { get; } = eventDefinition;
|
||||
public Student Student { get; } = student;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Core.Entities;
|
||||
|
||||
public class EventDefinition
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? ShortName { get; set; }
|
||||
public EventFormat EventFormat { get; set; }
|
||||
|
||||
[Range(1, 6)]
|
||||
public int MinTeamSize { get; set; }
|
||||
|
||||
[Range(1, 6)]
|
||||
public int MaxTeamSize { get; set; }
|
||||
|
||||
public string TeamSize =>
|
||||
MinTeamSize == MaxTeamSize
|
||||
? MinTeamSize.ToString()
|
||||
: $"{MinTeamSize.ToString()}-{MaxTeamSize.ToString()}";
|
||||
|
||||
public string? SemifinalistActivity { get; set; }
|
||||
|
||||
public bool InterviewOrPresentation
|
||||
=> SemifinalistActivity != null && (SemifinalistActivity.Contains("Interview") || SemifinalistActivity.Contains("Presentation"));
|
||||
|
||||
public bool OnSiteActivity
|
||||
=> SemifinalistActivity != null
|
||||
&& (SemifinalistActivity.Contains("Challenge")
|
||||
|| SemifinalistActivity.Contains("Race")
|
||||
|| SemifinalistActivity.Contains("Speech")
|
||||
|| SemifinalistActivity.Contains("Test")
|
||||
|| SemifinalistActivity.Contains("Flight")
|
||||
|| Name.Contains("Flight")
|
||||
|| SemifinalistActivity.Contains("Debate")
|
||||
|| SemifinalistActivity.Contains("Photography")
|
||||
|| SemifinalistActivity.Contains("Build")
|
||||
|| Name.Contains("Chapter")
|
||||
|| Name.Contains("Podcast"));
|
||||
|
||||
public string? Notes { get; set; }
|
||||
|
||||
[Range(1, 3)]
|
||||
public int MaxTeamCountState { get; set; }
|
||||
public bool RegionalEvent { get; set; }
|
||||
|
||||
public bool RegionalPresubmit { get; set; }
|
||||
public bool StatePresubmission { get; set; }
|
||||
public bool StatePretesting { get; set; }
|
||||
public bool StatePreliminaryRound { get; set; }
|
||||
|
||||
public string? Documentation { get; set; }
|
||||
|
||||
public string Eligibility { get; set; }
|
||||
public string? Theme { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int? LevelOfEffort { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public static readonly EventDefinition GeneralSchedule = new(){Name = "General Schedule"};
|
||||
public static readonly EventDefinition VotingDelegates = new(){Name = "Voting Delegates"};
|
||||
|
||||
|
||||
public string EventAttributes ()
|
||||
{
|
||||
var st = new List<string>();
|
||||
|
||||
if (EventFormat is EventFormat.Individual)
|
||||
st.Add( "Ind.");
|
||||
if (RegionalEvent)
|
||||
st.Add( "Reg.");
|
||||
|
||||
return string.Join(", ", st);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class EventStudentPicks
|
||||
{
|
||||
public CompetitiveEvent Event { get; }
|
||||
public IList<Tuple<Student,int>> StudentPicks { get; }
|
||||
|
||||
public EventStudentPicks(CompetitiveEvent @event, IList<Tuple<Student, int>> studentPicks)
|
||||
{
|
||||
Event = @event;
|
||||
StudentPicks = studentPicks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Core.Entities;
|
||||
|
||||
public enum OfficerRole
|
||||
{
|
||||
President=1,
|
||||
[Display(Name = "Vice President")]
|
||||
VicePresident=2,
|
||||
Secretary=3,
|
||||
Treasurer=4,
|
||||
Reporter=5,
|
||||
[Display(Name = "Sergeant at Arms")]
|
||||
SergeantAtArms =6
|
||||
}
|
||||
@@ -2,17 +2,12 @@
|
||||
|
||||
public class PartialTeam : Team
|
||||
{
|
||||
public IList<Student> OmittedStudents { get; }
|
||||
|
||||
public PartialTeam(string name, CompetitiveEvent @event, IList<Student> students, IList<Student> omittedStudents) : base(name, @event, students)
|
||||
{
|
||||
OmittedStudents = omittedStudents;
|
||||
}
|
||||
public IList<Student> OmittedStudents { get; set; }
|
||||
|
||||
public override Team CloneWithOmittedStudents(IEnumerable<Student> studentsToOmit)
|
||||
{
|
||||
var remainingStudents = Students.Where(s => !studentsToOmit.Contains(s)).ToList();
|
||||
var omittedStudents = OmittedStudents.Union(Students.Where(studentsToOmit.Contains)).Distinct().ToList();
|
||||
return new PartialTeam(Name, Event, remainingStudents, omittedStudents );
|
||||
return new PartialTeam{TeamId = Name, Event = Event, Students = remainingStudents, OmittedStudents = omittedStudents };
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public enum Assumption
|
||||
public enum Requirement
|
||||
{
|
||||
Include,
|
||||
Exclude
|
||||
+61
-58
@@ -1,68 +1,70 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static System.Text.RegularExpressions.Regex;
|
||||
|
||||
namespace Core.Entities;
|
||||
|
||||
public class Student
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Id { get; set; }
|
||||
|
||||
public string LastNameFirstName
|
||||
[Required]
|
||||
[StringLength(50,MinimumLength = 2)]
|
||||
[Display(Name = "First Name")]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(50, MinimumLength = 2)]
|
||||
[Display(Name = "Last Name")]
|
||||
public string LastName { get; set; }
|
||||
|
||||
[Range(5,12)]
|
||||
[Display(Name = "Grade")]
|
||||
public int Grade { get; set; }
|
||||
|
||||
[EmailAddress]
|
||||
[Display(Name = "Email Address")]
|
||||
public string? Email { get; set; }
|
||||
|
||||
[Phone]
|
||||
[Display(Name = "Phone Number")]
|
||||
public string? PhoneNumber { get; set; }
|
||||
|
||||
[Display(Name = "TSA Year")]
|
||||
public int TsaYear { get; set; }
|
||||
|
||||
[Display(Name = "State Id")]
|
||||
public string? StateId { get; set; }
|
||||
|
||||
[Display(Name = "Regional Id")]
|
||||
public string? RegionalId { get; set; }
|
||||
|
||||
[Display(Name = "National Id")]
|
||||
public string? NationalId { get; set; }
|
||||
|
||||
[Display(Name = "Officer Role")]
|
||||
public OfficerRole? OfficerRole { get; set; }
|
||||
|
||||
public List<Team> Teams { get; set; } = null;
|
||||
public List<EventDefinition> RankedEvents { get; } = [];
|
||||
public List<StudentEventRanking> EventRankings { get; } = [];
|
||||
|
||||
|
||||
public string Name => FirstNameLastName;
|
||||
|
||||
public string LastNameFirstName => $"{LastName}, {FirstName}";
|
||||
|
||||
public string FirstNameLastName => $"{FirstName} {LastName}";
|
||||
|
||||
public static Tuple<string, string> ParseNameParts(string fullName)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Name.Contains(',')) return Name;
|
||||
var match = Regex.Match(Name, @"(.*)\s(.*)");
|
||||
var match = Match(fullName, @"(.*),\s*(.*)");
|
||||
if (match.Success)
|
||||
return $"{match.Groups[2].Value}, {match.Groups[2].Value} ";
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string FirstNameLastName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Name.Contains(',')) return Name;
|
||||
var match = Regex.Match(Name, @"(.*),\s*(.*)");
|
||||
if (match.Success)
|
||||
return $"{match.Groups[2].Value} {match.Groups[1].Value}";
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
public string FirstName
|
||||
{
|
||||
get
|
||||
{
|
||||
var match = Regex.Match(LastNameFirstName, @"(.*),\s*(.*)");
|
||||
if (match.Success)
|
||||
return $"{match.Groups[2].Value}";
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
public int Grade { get; }
|
||||
public string StateID { get; }
|
||||
public string RegionalID { get; }
|
||||
public string NationalID { get; }
|
||||
public int TsaYear { get; }
|
||||
public string Officer { get; }
|
||||
public IList<CompetitiveEvent> RankedEventPicks { get; }
|
||||
|
||||
public ICollection<Team> Teams { get; set; }
|
||||
|
||||
public Student(string name, int grade, int tsaYear, string officer, IList<CompetitiveEvent> rankedEventPicks,
|
||||
string stateID, string regionalID, string nationalId)
|
||||
{
|
||||
Name = name;
|
||||
Grade = grade;
|
||||
TsaYear = tsaYear;
|
||||
Officer = officer;
|
||||
RankedEventPicks = rankedEventPicks;
|
||||
StateID=stateID;
|
||||
RegionalID = regionalID;
|
||||
NationalID = nationalId;
|
||||
return Tuple.Create(match.Groups[2].Value, match.Groups[1].Value);
|
||||
match = Match(fullName, @"(.*)\s*(.*)");
|
||||
return
|
||||
match.Success
|
||||
? Tuple.Create(match.Groups[1].Value, match.Groups[2].Value)
|
||||
: new Tuple<string, string>(fullName, string.Empty);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@@ -70,5 +72,6 @@ public class Student
|
||||
return FirstName;
|
||||
}
|
||||
|
||||
public bool VotingDelegate => Officer.Contains("Pres");
|
||||
public bool VotingDelegate => OfficerRole is Entities.OfficerRole.President or Entities.OfficerRole.VicePresident;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class StudentEventRanking
|
||||
{
|
||||
public Student Student { get; set; } = null!;
|
||||
|
||||
public EventDefinition EventDefinition { get; set; } = null!;
|
||||
public int Rank { get; set; }
|
||||
|
||||
public const int MaxRank = 6;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Core.Entities;
|
||||
|
||||
public class StudentEventStatistics
|
||||
{
|
||||
public Student Student { get; set; }
|
||||
public List<EventDefinition> Events { get; set; } = [];
|
||||
|
||||
public int? TotalLevelOfEffort => Events.Sum(e => e.LevelOfEffort);
|
||||
|
||||
public int EventCount => Events.Count;
|
||||
|
||||
public bool HasRegionalEvent => Events.Any(e => e.RegionalEvent);
|
||||
|
||||
public bool HasOnSiteActivity => Events.Any(e => e.OnSiteActivity);
|
||||
|
||||
public static List<StudentEventStatistics> Generate(ICollection<Team> teams)
|
||||
{
|
||||
Dictionary<Student, StudentEventStatistics> statistics = [];
|
||||
|
||||
foreach (var team in teams)
|
||||
{
|
||||
foreach (var student in team.Students)
|
||||
{
|
||||
if (!statistics.ContainsKey(student))
|
||||
statistics.Add(student, new StudentEventStatistics(){Student = student});
|
||||
statistics[student].Events.Add(team.Event);
|
||||
}
|
||||
}
|
||||
|
||||
return statistics.Values.ToList();
|
||||
}
|
||||
}
|
||||
+32
-38
@@ -1,38 +1,42 @@
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Core.Entities;
|
||||
|
||||
public class Team
|
||||
{
|
||||
public string Name { get; }
|
||||
public CompetitiveEvent Event { get; }
|
||||
public IList<Student> Students { get; }
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Team Name")]
|
||||
public string Name { get; set; }
|
||||
public EventDefinition Event { get; set; }
|
||||
public List<Student> Students { get; set; } = [];
|
||||
|
||||
public Student? Captain { get; set; }
|
||||
|
||||
public string TeamNumber { get; set; }
|
||||
[Display(Name = "Team Id")]
|
||||
public string? TeamId { get; set; }
|
||||
|
||||
public string RegionalTimeSlot { get; set; }
|
||||
//public string? RegionalTimeSlot { get; set; }
|
||||
|
||||
public Tuple<DateTime,DateTime?>? RegionalTimeSlotObj
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(RegionalTimeSlot))
|
||||
return null;
|
||||
var times = Regex.Matches(RegionalTimeSlot, @"(.*)\s*-\s*(.*)");
|
||||
if (times.Count == 0)
|
||||
return Tuple.Create(P(RegionalTimeSlot), (DateTime?)null);
|
||||
var match = times[0];
|
||||
if (!match.Success)
|
||||
return Tuple.Create(P(RegionalTimeSlot), (DateTime?)null);
|
||||
// public Tuple<DateTime,DateTime?>? RegionalTimeSlotObj
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
|
||||
return Tuple.Create(P(match.Groups[1].Value), (DateTime?)P(match.Groups[2].Value));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// if (string.IsNullOrEmpty(RegionalTimeSlot))
|
||||
// return null;
|
||||
// var times = Regex.Matches(RegionalTimeSlot, @"(.*)\s*-\s*(.*)");
|
||||
// if (times.Count == 0)
|
||||
// return Tuple.Create(P(RegionalTimeSlot), (DateTime?)null);
|
||||
// var match = times[0];
|
||||
// if (!match.Success)
|
||||
// return Tuple.Create(P(RegionalTimeSlot), (DateTime?)null);
|
||||
|
||||
// return Tuple.Create(P(match.Groups[1].Value), (DateTime?)P(match.Groups[2].Value));
|
||||
// return null;
|
||||
// }
|
||||
//}
|
||||
|
||||
private DateTime P(string s)
|
||||
{
|
||||
@@ -42,28 +46,18 @@ public class Team
|
||||
return dt;
|
||||
}
|
||||
|
||||
public Team(string name, CompetitiveEvent @event, IList<Student> students, Student? captain = null, string teamNumber = null, string regionalTimeSlot = null)
|
||||
{
|
||||
Name = name;
|
||||
Event = @event;
|
||||
Students = students;
|
||||
Captain = captain;
|
||||
TeamNumber = teamNumber;
|
||||
RegionalTimeSlot = regionalTimeSlot;
|
||||
}
|
||||
|
||||
public virtual Team CloneWithOmittedStudents(IEnumerable<Student> studentsToOmit)
|
||||
{
|
||||
var studentsToOmitList = studentsToOmit.ToList();
|
||||
var omittedStudents = Students.Where(studentsToOmitList.Contains).ToList();
|
||||
if (!omittedStudents.Any())
|
||||
return new Team(Name, Event, Students.ToList(), Captain);
|
||||
return new Team{Captain = Captain, Event = Event, Students = Students.ToList(), TeamId = Name};
|
||||
|
||||
var remainingStudents = Students.Where(s => !studentsToOmitList.Contains(s)).ToList();
|
||||
return new PartialTeam(Name, Event, remainingStudents, omittedStudents);
|
||||
return new PartialTeam { Name = Name, Event = Event, Students = remainingStudents, OmittedStudents = omittedStudents};
|
||||
}
|
||||
|
||||
public Team Clone() => CloneWithOmittedStudents(Array.Empty<Student>());
|
||||
public Team Clone() => CloneWithOmittedStudents([]);
|
||||
|
||||
public static int GetStudentTeamOverlapCount(IList<Team>[] timeSlots)
|
||||
{
|
||||
@@ -92,7 +86,7 @@ public class Team
|
||||
|
||||
public string ToStringWithIndividualAndRegional()
|
||||
{
|
||||
var ind = Event.Format is EventFormat.Individual ? " (Ind.)" : string.Empty;
|
||||
var ind = Event.EventFormat is EventFormat.Individual ? " (Ind.)" : string.Empty;
|
||||
var regional= Event.RegionalEvent ? " (Reg.)" : string.Empty;
|
||||
//var regional= Event.RegionalEvent ? " (Reg.)" : string.Empty;
|
||||
|
||||
|
||||
+7
-7
@@ -2,15 +2,15 @@
|
||||
|
||||
namespace Core.Parsers;
|
||||
|
||||
public class AssignmentAssumptionParser : CsvParserBase
|
||||
public class AssignmentRequirementParser : CsvParserBase
|
||||
{
|
||||
public AssignmentAssumptionParser(FileSystemInfo csvFile, bool ignoreBlankLines = true) : base(csvFile, ignoreBlankLines)
|
||||
public AssignmentRequirementParser(FileSystemInfo csvFile, bool ignoreBlankLines = true) : base(csvFile, ignoreBlankLines)
|
||||
{
|
||||
}
|
||||
|
||||
public AssignmentAssumption[] Parse(ICollection<CompetitiveEvent> events, ICollection<Student> students)
|
||||
public AssignmentRequirement[] Parse(ICollection<EventDefinition> events, ICollection<Student> students)
|
||||
{
|
||||
var assumptions = new List<AssignmentAssumption>();
|
||||
var assumptions = new List<AssignmentRequirement>();
|
||||
|
||||
CsvReader.Read();
|
||||
CsvReader.ReadHeader();
|
||||
@@ -27,7 +27,7 @@ public class AssignmentAssumptionParser : CsvParserBase
|
||||
|
||||
var evt = events.FirstOrDefault(e => e.ShortName == eventShortName);
|
||||
if (evt == null)
|
||||
throw new Exception($"Could not find event named {eventShortName}");
|
||||
throw new Exception($"Could not find eventDefinition named {eventShortName}");
|
||||
for (int i = 0; i <= studentArray.Length; i++)
|
||||
{
|
||||
var field = CsvReader.GetField(i + 1);
|
||||
@@ -35,11 +35,11 @@ public class AssignmentAssumptionParser : CsvParserBase
|
||||
{
|
||||
case "x":
|
||||
case "X":
|
||||
assumptions.Add(new AssignmentAssumption(evt, studentArray[i], Assumption.Exclude));
|
||||
assumptions.Add(new AssignmentRequirement(evt, studentArray[i], Requirement.Exclude));
|
||||
break;
|
||||
case "i":
|
||||
case "I":
|
||||
assumptions.Add(new AssignmentAssumption(evt, studentArray[i], Assumption.Include));
|
||||
assumptions.Add(new AssignmentRequirement(evt, studentArray[i], Requirement.Include));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -10,9 +10,13 @@ public class CsvParserBase : IDisposable
|
||||
//private readonly MemoryStream _memoryStream;
|
||||
protected readonly CsvReader CsvReader;
|
||||
|
||||
protected CsvParserBase(FileSystemInfo csvFile, bool ignoreBlankLines)
|
||||
protected CsvParserBase(FileSystemInfo csvFile, bool ignoreBlankLines) : this(OpenCsv(csvFile), ignoreBlankLines)
|
||||
{
|
||||
_reader = OpenCsv(csvFile);
|
||||
}
|
||||
|
||||
protected CsvParserBase(StreamReader reader, bool ignoreBlankLines)
|
||||
{
|
||||
_reader = reader;
|
||||
CsvReader = InitCsvReader(_reader, ignoreBlankLines);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,13 @@ public class EventDefinitionParser : CsvParserBase
|
||||
{
|
||||
}
|
||||
|
||||
public CompetitiveEvent[] Parse()
|
||||
public EventDefinitionParser(StreamReader reader, bool ignoreBlankLines = true) : base(reader, ignoreBlankLines)
|
||||
{
|
||||
var events = new List<CompetitiveEvent>();
|
||||
}
|
||||
|
||||
public EventDefinition[] Parse()
|
||||
{
|
||||
var events = new List<EventDefinition>();
|
||||
|
||||
CsvReader.Read();
|
||||
CsvReader.ReadHeader();
|
||||
@@ -23,7 +27,7 @@ public class EventDefinitionParser : CsvParserBase
|
||||
continue;
|
||||
var shortName = CsvReader.GetField("Short Name");
|
||||
|
||||
Enum.TryParse(CsvReader.GetField("Format"), out EventFormat format);
|
||||
Enum.TryParse(CsvReader.GetField("EventFormat"), out EventFormat format);
|
||||
|
||||
var teamSize = CsvReader.GetField("Team Size");
|
||||
if (string.IsNullOrEmpty(teamSize))
|
||||
@@ -47,18 +51,18 @@ public class EventDefinitionParser : CsvParserBase
|
||||
var levelOfEffort = CsvReader.GetField<int?>("Level of Effort");
|
||||
//var regionalTeams = CsvReader.GetField<int>("Regional Teams");
|
||||
|
||||
var competitiveEvent = new CompetitiveEvent
|
||||
var competitiveEvent = new EventDefinition
|
||||
{
|
||||
Name = name.Trim(),
|
||||
ShortName = shortName.Trim(),
|
||||
Format = format,
|
||||
EventFormat = format,
|
||||
MaxTeamCountState = stateTeams,
|
||||
MinTeamSize = min,
|
||||
MaxTeamSize = max,
|
||||
SemifinalistActivity = semifinalistActivity,
|
||||
RegionalEvent = !string.IsNullOrEmpty(regionalCount),
|
||||
RegionalPresubmit = regionalPresubmit.Trim() == "TRUE",
|
||||
RegionalNotes = regionalNotes,
|
||||
Notes = regionalNotes,
|
||||
Documentation= documentation,
|
||||
StatePresubmission = statePresubmission.Trim() == "TRUE",
|
||||
StatePretesting = statePretesting.Trim() == "TRUE",
|
||||
|
||||
@@ -7,9 +7,9 @@ namespace Core.Parsers;
|
||||
public class EventOccurrenceParser
|
||||
{
|
||||
private FileSystemInfo _txtFile;
|
||||
private ICollection<CompetitiveEvent> _events;
|
||||
private ICollection<EventDefinition> _events;
|
||||
|
||||
public EventOccurrenceParser(FileSystemInfo txtFile, ICollection<CompetitiveEvent> events)
|
||||
public EventOccurrenceParser(FileSystemInfo txtFile, ICollection<EventDefinition> events)
|
||||
{
|
||||
_events = events;
|
||||
_txtFile = txtFile;
|
||||
@@ -28,10 +28,10 @@ public class EventOccurrenceParser
|
||||
|
||||
private readonly Regex _timeLocationRegex = new(@"(?<Time>.*(?>[AaPp]\.?[Mm]\.?))(?<Location>[\s\t].*)?");
|
||||
|
||||
public IDictionary<CompetitiveEvent, List<EventOccurrence>> Parse()
|
||||
public IDictionary<EventDefinition, List<EventOccurrence>> Parse()
|
||||
{
|
||||
var occurrences = new Dictionary<CompetitiveEvent, List<EventOccurrence>>();
|
||||
CompetitiveEvent currentEvent = null;
|
||||
var occurrences = new Dictionary<EventDefinition, List<EventOccurrence>>();
|
||||
EventDefinition currentEventDefinition = null;
|
||||
|
||||
var lines = File.ReadLines(_txtFile.FullName);
|
||||
foreach (var line in lines)
|
||||
@@ -49,25 +49,25 @@ public class EventOccurrenceParser
|
||||
select e).FirstOrDefault();
|
||||
if (evt == null)
|
||||
continue;
|
||||
currentEvent = evt;
|
||||
currentEventDefinition = evt;
|
||||
continue;
|
||||
}
|
||||
if (line == "General Schedule")
|
||||
{
|
||||
currentEvent = CompetitiveEvent.GeneralSchedule;
|
||||
currentEventDefinition = EventDefinition.GeneralSchedule;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line == "Voting Delegates")
|
||||
{
|
||||
currentEvent = CompetitiveEvent.VotingDelegates;
|
||||
currentEventDefinition = EventDefinition.VotingDelegates;
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentEvent == null)
|
||||
if (currentEventDefinition == null)
|
||||
continue;
|
||||
|
||||
var occurrenceName = match.Groups["Name"].Captures[0].Value;
|
||||
@@ -103,9 +103,9 @@ public class EventOccurrenceParser
|
||||
Location = location
|
||||
};
|
||||
|
||||
if (!occurrences.ContainsKey(currentEvent))
|
||||
occurrences.Add(currentEvent, []);
|
||||
occurrences[currentEvent].Add(eventOccurrence);
|
||||
if (!occurrences.ContainsKey(currentEventDefinition))
|
||||
occurrences.Add(currentEventDefinition, []);
|
||||
occurrences[currentEventDefinition].Add(eventOccurrence);
|
||||
}
|
||||
|
||||
return occurrences;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using Core.Entities;
|
||||
using FuzzySharp;
|
||||
|
||||
namespace Core.Parsers;
|
||||
|
||||
public class StudentEventRankingParser : CsvParserBase
|
||||
{
|
||||
public StudentEventRankingParser(FileSystemInfo csvFile, bool ignoreBlankLines = true) : base(csvFile, ignoreBlankLines)
|
||||
{
|
||||
}
|
||||
|
||||
public StudentEventRanking[] Parse(ICollection<Student> students, ICollection<EventDefinition> events)
|
||||
{
|
||||
var rankings = new List<StudentEventRanking>();
|
||||
|
||||
CsvReader.Read();
|
||||
CsvReader.ReadHeader();
|
||||
|
||||
while (CsvReader.Read())
|
||||
{
|
||||
var name = CsvReader.GetField("Student Name");
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
|
||||
var student = students.FirstOrDefault(s => Fuzz.Ratio(s.FirstNameLastName, name) > 90);
|
||||
if (student == null)
|
||||
continue;
|
||||
|
||||
|
||||
var competitiveEvents = new List<EventDefinition>(6);
|
||||
|
||||
for (var i = 1; i <= 6; i++)
|
||||
{
|
||||
var eventName = CsvReader.GetField(i.ToString());
|
||||
if (string.IsNullOrEmpty(eventName) || eventName == "") continue;
|
||||
|
||||
eventName = eventName.Trim();
|
||||
|
||||
if (eventName == "I&I")
|
||||
eventName = "Inventions & Innovations";
|
||||
if (eventName == "Med Tech")
|
||||
eventName = "Medical Technology";
|
||||
if (eventName.StartsWith("Challenging Tech"))
|
||||
eventName = "Challenging Technology Issues";
|
||||
|
||||
var matches =
|
||||
(from e in events
|
||||
let rat = Fuzz.Ratio(e.Name, eventName)
|
||||
where rat > 90
|
||||
orderby rat descending
|
||||
select e).ToList();
|
||||
|
||||
if (!matches.Any())
|
||||
{
|
||||
matches =
|
||||
(from e in events
|
||||
where e.Name.StartsWith(eventName)
|
||||
select e).ToList();
|
||||
}
|
||||
|
||||
var competitiveEvent = matches.FirstOrDefault();
|
||||
if (competitiveEvent == null)
|
||||
{
|
||||
|
||||
//todo: throw new ArgumentException($"Event named '{eventName}' not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
rankings.Add(new StudentEventRanking{
|
||||
Student = student, EventDefinition = competitiveEvent,
|
||||
Rank = i});
|
||||
}
|
||||
}
|
||||
|
||||
return rankings.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Core.Entities;
|
||||
using FuzzySharp;
|
||||
|
||||
namespace Core.Parsers;
|
||||
|
||||
public class StudentParser : CsvParserBase
|
||||
@@ -9,7 +7,11 @@ public class StudentParser : CsvParserBase
|
||||
{
|
||||
}
|
||||
|
||||
public Student[] Parse(ICollection<CompetitiveEvent> events)
|
||||
public StudentParser(StreamReader reader, bool ignoreBlankLines = true) : base(reader, ignoreBlankLines)
|
||||
{
|
||||
}
|
||||
|
||||
public Student[] Parse()
|
||||
{
|
||||
var s = new List<Student>();
|
||||
|
||||
@@ -22,65 +24,26 @@ public class StudentParser : CsvParserBase
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
|
||||
var stateID = CsvReader.GetField("State ID").Trim();
|
||||
var regionalID = CsvReader.GetField("Regional ID").Trim();
|
||||
var nationalID = CsvReader.GetField("National ID").Trim();
|
||||
var gr = CsvReader.GetField("Grade");
|
||||
var (firstName, lastName) = Student.ParseNameParts(name);
|
||||
|
||||
|
||||
var stateId = CsvReader.GetField("State ID")?.Trim();
|
||||
var regionalId = CsvReader.GetField("Regional ID")?.Trim();
|
||||
var nationalId = CsvReader.GetField("National ID")?.Trim();
|
||||
var grade = CsvReader.GetField("Grade");
|
||||
var tsaYearsStr = CsvReader.GetField("TSA year");
|
||||
var tsaYear = int.Parse(tsaYearsStr?[..1] ?? "1");
|
||||
var officer = CsvReader.GetField("Officer");
|
||||
|
||||
var competitiveEvents = new List<CompetitiveEvent>(6);
|
||||
|
||||
for (var i = 1; i <= 6; i++)
|
||||
var student = new Student
|
||||
{
|
||||
var eventName = CsvReader.GetField(i.ToString());
|
||||
if (string.IsNullOrEmpty(eventName) || eventName == "") continue;
|
||||
|
||||
eventName = eventName.Trim();
|
||||
|
||||
if (eventName == "I&I")
|
||||
eventName = "Inventions & Innovations";
|
||||
if (eventName == "Med Tech")
|
||||
eventName = "Medical Technology";
|
||||
if (eventName.StartsWith("Challenging Tech"))
|
||||
eventName = "Challenging Technology Issues";
|
||||
|
||||
var matches =
|
||||
(from e in events
|
||||
let rat = Fuzz.Ratio(e.Name, eventName)
|
||||
where rat > 90
|
||||
orderby rat descending
|
||||
select e).ToList();
|
||||
|
||||
if (!matches.Any())
|
||||
{
|
||||
matches =
|
||||
(from e in events
|
||||
where e.Name.StartsWith(eventName)
|
||||
select e).ToList();
|
||||
}
|
||||
|
||||
var competitiveEvent = matches.FirstOrDefault();
|
||||
if (competitiveEvent == null)
|
||||
{
|
||||
|
||||
//todo: throw new ArgumentException($"Event named '{eventName}' not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
competitiveEvents.Add(competitiveEvent);
|
||||
}
|
||||
|
||||
if (!competitiveEvents.Any())
|
||||
continue;
|
||||
|
||||
var student = new Student(
|
||||
name.Trim(),
|
||||
Convert.ToInt32(gr),
|
||||
tsaYear,
|
||||
officer?.Trim(),
|
||||
competitiveEvents, stateID, regionalID, nationalID);
|
||||
FirstName = firstName,
|
||||
LastName = lastName,
|
||||
Grade = Convert.ToInt32(grade),
|
||||
TsaYear = tsaYear,
|
||||
StateId = stateId,
|
||||
RegionalId = regionalId,
|
||||
NationalId = nationalId
|
||||
};
|
||||
s.Add(student);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace Core.Parsers
|
||||
{
|
||||
}
|
||||
|
||||
public Team[] Parse(ICollection<CompetitiveEvent> events, ICollection<Student> students)
|
||||
public Team[] Parse(ICollection<EventDefinition> events, ICollection<Student> students)
|
||||
{
|
||||
var teams = new List<Team>();
|
||||
|
||||
@@ -139,22 +139,21 @@ namespace Core.Parsers
|
||||
var teamNumber = string.Empty;
|
||||
if (teamName.EndsWith("Team 2"))
|
||||
teamNumber = "12227-2";
|
||||
else if (@event.Format == EventFormat.Team)
|
||||
else if (@event.EventFormat == EventFormat.Team)
|
||||
teamNumber = "2227";
|
||||
|
||||
if (teamStudents.Count > 0)
|
||||
{
|
||||
if (@event.Format is EventFormat.Team)
|
||||
if (@event.EventFormat is EventFormat.Team)
|
||||
{
|
||||
teams.Add(new Team(teamName, @event, teamStudents, captain, teamNumber,
|
||||
regionalTimeSlot: regionalTimeSlot));
|
||||
teams.Add(new Team { Event = @event, Students = teamStudents, Captain = captain, Name = teamName, TeamId = teamNumber});
|
||||
}
|
||||
else if (@event.Format is EventFormat.Individual)
|
||||
else if (@event.EventFormat is EventFormat.Individual)
|
||||
{
|
||||
foreach (var student in teamStudents)
|
||||
{
|
||||
teams.Add(new Team($"{teamName} - {student.FirstName}", @event,
|
||||
new List<Student> { student }, student, teamNumber, regionalTimeSlot));
|
||||
teams.Add(new Team{Name = $"{teamName} - {student.FirstName}", Event = @event,
|
||||
Students = new List<Student> { student }, Captain = student, TeamId = teamNumber});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Reflection;
|
||||
using Core.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace Data
|
||||
{
|
||||
public class AppDbContext : DbContext
|
||||
{
|
||||
public DbSet<EventDefinition> Events { get; set; }
|
||||
public DbSet<Student> Students { get; set; }
|
||||
public DbSet<Team> Teams { get; set; }
|
||||
public DbSet<StudentEventRanking> StudentEventRanking { get; set; }
|
||||
|
||||
public AppDbContext()
|
||||
{
|
||||
}
|
||||
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
var dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ChapterOrganizer.db");
|
||||
optionsBuilder.UseSqlite($"Data Source={dbPath}");
|
||||
}
|
||||
}
|
||||
|
||||
public class EventDefinitionConfiguration : IEntityTypeConfiguration<EventDefinition>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<EventDefinition> builder)
|
||||
{
|
||||
builder.HasKey(u => u.Id);
|
||||
|
||||
builder.HasIndex(u => u.Name);
|
||||
builder.Property(u => u.Name).HasMaxLength(128);
|
||||
|
||||
//builder.HasMany(u => u.Roles)
|
||||
// .WithOne()
|
||||
// .HasForeignKey(r => r.Id)
|
||||
// .OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public class StudentConfiguration : IEntityTypeConfiguration<Student>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Student> builder)
|
||||
{
|
||||
builder.HasKey(u => u.Id);
|
||||
//builder.Property(s => s.Grade);
|
||||
|
||||
builder
|
||||
.HasMany(e => e.RankedEvents)
|
||||
.WithMany()
|
||||
.UsingEntity<StudentEventRanking>()
|
||||
.HasOne<EventDefinition>(e => e.EventDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
public class TeamConfiguration : IEntityTypeConfiguration<Team>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Team> builder)
|
||||
{
|
||||
builder.HasKey(u => u.Id);
|
||||
|
||||
builder.HasMany(e => e.Students)
|
||||
.WithMany(e => e.Teams);
|
||||
|
||||
builder.HasOne(e => e.Captain);
|
||||
}
|
||||
}
|
||||
public class StudentEventRankingConfiguration : IEntityTypeConfiguration<StudentEventRanking>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<StudentEventRanking> builder)
|
||||
{
|
||||
//builder.HasKey(u => u.EventDefinitionId);
|
||||
//builder.HasKey(u => u.StudentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,167 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250825173042_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TeamNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.Navigation("Captain");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Events",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 128, nullable: false),
|
||||
ShortName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
EventFormat = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
MinTeamSize = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
MaxTeamSize = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SemifinalistActivity = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Notes = table.Column<string>(type: "TEXT", nullable: true),
|
||||
MaxTeamCountState = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
RegionalEvent = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
RegionalPresubmit = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
StatePresubmission = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
StatePretesting = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
StatePreliminaryRound = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
Documentation = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Eligibility = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Theme = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Description = table.Column<string>(type: "TEXT", nullable: true),
|
||||
LevelOfEffort = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Events", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Students",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Students", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Teams",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
CaptainId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
TeamNumber = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Teams", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Teams_Students_CaptainId",
|
||||
column: x => x.CaptainId,
|
||||
principalTable: "Students",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "StudentTeam",
|
||||
columns: table => new
|
||||
{
|
||||
StudentsId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
TeamsId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_StudentTeam", x => new { x.StudentsId, x.TeamsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_StudentTeam_Students_StudentsId",
|
||||
column: x => x.StudentsId,
|
||||
principalTable: "Students",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_StudentTeam_Teams_TeamsId",
|
||||
column: x => x.TeamsId,
|
||||
principalTable: "Teams",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Events_Name",
|
||||
table: "Events",
|
||||
column: "Name");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_StudentTeam_TeamsId",
|
||||
table: "StudentTeam",
|
||||
column: "TeamsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Teams_CaptainId",
|
||||
table: "Teams",
|
||||
column: "CaptainId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Events");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "StudentTeam");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Teams");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Students");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250826155947_AddStudentEventRanking")]
|
||||
partial class AddStudentEventRanking
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.Property<int>("EventDefinitionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StudentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EventDefinitionId", "StudentId");
|
||||
|
||||
b.HasIndex("StudentId");
|
||||
|
||||
b.ToTable("StudentEventRanking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TeamNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventDefinitionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Student", "Student")
|
||||
.WithMany("EventRankings")
|
||||
.HasForeignKey("StudentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EventDefinition");
|
||||
|
||||
b.Navigation("Student");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.Navigation("Captain");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Navigation("EventRankings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddStudentEventRanking : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "StudentEventRanking",
|
||||
columns: table => new
|
||||
{
|
||||
EventDefinitionId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
StudentId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Rank = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_StudentEventRanking", x => new { x.EventDefinitionId, x.StudentId });
|
||||
table.ForeignKey(
|
||||
name: "FK_StudentEventRanking_Events_EventDefinitionId",
|
||||
column: x => x.EventDefinitionId,
|
||||
principalTable: "Events",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_StudentEventRanking_Students_StudentId",
|
||||
column: x => x.StudentId,
|
||||
principalTable: "Students",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_StudentEventRanking_StudentId",
|
||||
table: "StudentEventRanking",
|
||||
column: "StudentId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "StudentEventRanking");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250826181144_StudentProperties")]
|
||||
partial class StudentProperties
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Grade")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NationalID")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OfficerRole")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RegionalID")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StateID")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TsaYear")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.Property<int>("EventDefinitionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StudentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EventDefinitionId", "StudentId");
|
||||
|
||||
b.HasIndex("StudentId");
|
||||
|
||||
b.ToTable("StudentEventRanking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TeamNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventDefinitionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Student", "Student")
|
||||
.WithMany("EventRankings")
|
||||
.HasForeignKey("StudentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EventDefinition");
|
||||
|
||||
b.Navigation("Student");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.Navigation("Captain");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Navigation("EventRankings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class StudentProperties : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Grade",
|
||||
table: "Students",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "NationalID",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OfficerRole",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RegionalID",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "StateID",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "TsaYear",
|
||||
table: "Students",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Grade",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "NationalID",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OfficerRole",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RegionalID",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "StateID",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TsaYear",
|
||||
table: "Students");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250827183503_StudentCleanup")]
|
||||
partial class StudentCleanup
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Grade")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NationalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OfficerRole")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RegionalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StateId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TsaYear")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.Property<int>("EventDefinitionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StudentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EventDefinitionId", "StudentId");
|
||||
|
||||
b.HasIndex("StudentId");
|
||||
|
||||
b.ToTable("StudentEventRanking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TeamId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventDefinitionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Student", "Student")
|
||||
.WithMany("EventRankings")
|
||||
.HasForeignKey("StudentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EventDefinition");
|
||||
|
||||
b.Navigation("Student");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.Navigation("Captain");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Navigation("EventRankings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class StudentCleanup : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "TeamNumber",
|
||||
table: "Teams",
|
||||
newName: "TeamId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "StateID",
|
||||
table: "Students",
|
||||
newName: "StateId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "RegionalID",
|
||||
table: "Students",
|
||||
newName: "RegionalId");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "NationalID",
|
||||
table: "Students",
|
||||
newName: "NationalId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "TeamId",
|
||||
table: "Teams",
|
||||
newName: "TeamNumber");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "StateId",
|
||||
table: "Students",
|
||||
newName: "StateID");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "RegionalId",
|
||||
table: "Students",
|
||||
newName: "RegionalID");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "NationalId",
|
||||
table: "Students",
|
||||
newName: "NationalID");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250827200441_StudentNameSplit")]
|
||||
partial class StudentNameSplit
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Grade")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NationalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OfficerRole")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RegionalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StateId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TsaYear")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.Property<int>("EventDefinitionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StudentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EventDefinitionId", "StudentId");
|
||||
|
||||
b.HasIndex("StudentId");
|
||||
|
||||
b.ToTable("StudentEventRanking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("TeamId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventDefinitionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Student", "Student")
|
||||
.WithMany("EventRankings")
|
||||
.HasForeignKey("StudentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EventDefinition");
|
||||
|
||||
b.Navigation("Student");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.Navigation("Captain");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Navigation("EventRankings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class StudentNameSplit : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Name",
|
||||
table: "Students",
|
||||
newName: "LastName");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "FirstName",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "FirstName",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "LastName",
|
||||
table: "Students",
|
||||
newName: "Name");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250903121202_StudentAddEmailPhone")]
|
||||
partial class StudentAddEmailPhone
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Grade")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NationalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("OfficerRole")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RegionalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StateId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TsaYear")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.Property<int>("EventDefinitionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StudentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EventDefinitionId", "StudentId");
|
||||
|
||||
b.HasIndex("StudentId");
|
||||
|
||||
b.ToTable("StudentEventRanking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TeamId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.HasIndex("EventId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventDefinitionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Student", "Student")
|
||||
.WithMany("EventRankings")
|
||||
.HasForeignKey("StudentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EventDefinition");
|
||||
|
||||
b.Navigation("Student");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.HasOne("Core.Entities.EventDefinition", "Event")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Captain");
|
||||
|
||||
b.Navigation("Event");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Navigation("EventRankings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class StudentAddEmailPhone : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EventId",
|
||||
table: "Teams",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Name",
|
||||
table: "Teams",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "OfficerRole",
|
||||
table: "Students",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "TEXT",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Email",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "PhoneNumber",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Teams_EventId",
|
||||
table: "Teams",
|
||||
column: "EventId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Teams_Events_EventId",
|
||||
table: "Teams",
|
||||
column: "EventId",
|
||||
principalTable: "Events",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Teams_Events_EventId",
|
||||
table: "Teams");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Teams_EventId",
|
||||
table: "Teams");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EventId",
|
||||
table: "Teams");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Name",
|
||||
table: "Teams");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Email",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PhoneNumber",
|
||||
table: "Students");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "OfficerRole",
|
||||
table: "Students",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
// <auto-generated />
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
partial class AppDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.8");
|
||||
|
||||
modelBuilder.Entity("Core.Entities.EventDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Documentation")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Eligibility")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventFormat")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("LevelOfEffort")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamCountState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MaxTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MinTeamSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RegionalEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("RegionalPresubmit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SemifinalistActivity")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("StatePreliminaryRound")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePresubmission")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StatePretesting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Theme")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("Events");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Grade")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NationalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("OfficerRole")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RegionalId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StateId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TsaYear")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.Property<int>("EventDefinitionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StudentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("EventDefinitionId", "StudentId");
|
||||
|
||||
b.HasIndex("StudentId");
|
||||
|
||||
b.ToTable("StudentEventRanking");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("CaptainId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EventId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TeamId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CaptainId");
|
||||
|
||||
b.HasIndex("EventId");
|
||||
|
||||
b.ToTable("Teams");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.Property<int>("StudentsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TeamsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StudentsId", "TeamsId");
|
||||
|
||||
b.HasIndex("TeamsId");
|
||||
|
||||
b.ToTable("StudentTeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.StudentEventRanking", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.EventDefinition", "EventDefinition")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventDefinitionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Student", "Student")
|
||||
.WithMany("EventRankings")
|
||||
.HasForeignKey("StudentId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("EventDefinition");
|
||||
|
||||
b.Navigation("Student");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Team", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", "Captain")
|
||||
.WithMany()
|
||||
.HasForeignKey("CaptainId");
|
||||
|
||||
b.HasOne("Core.Entities.EventDefinition", "Event")
|
||||
.WithMany()
|
||||
.HasForeignKey("EventId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Captain");
|
||||
|
||||
b.Navigation("Event");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("StudentTeam", b =>
|
||||
{
|
||||
b.HasOne("Core.Entities.Student", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("StudentsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Core.Entities.Team", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TeamsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Core.Entities.Student", b =>
|
||||
{
|
||||
b.Navigation("EventRankings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Data.Services
|
||||
{
|
||||
public class TsaEventServices
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -6,7 +6,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{E9FBB9FD-6F5E-497E-AF41-A66E14421F00}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web-Original", "Web-Original\Web-Original.csproj", "{855D693D-16FF-4C4A-AD3C-74A6B21ADB72}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "WebApp\WebApp.csproj", "{039E1539-EDA8-4F4E-ACC0-B8292827A3A9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data", "Data\Data.csproj", "{B5401DC8-8008-414A-ACE9-FAEF2B1B8113}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{338B8571-2953-4EA3-A680-F000F1431DFF} = {338B8571-2953-4EA3-A680-F000F1431DFF}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -22,10 +27,14 @@ Global
|
||||
{E9FBB9FD-6F5E-497E-AF41-A66E14421F00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E9FBB9FD-6F5E-497E-AF41-A66E14421F00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E9FBB9FD-6F5E-497E-AF41-A66E14421F00}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{855D693D-16FF-4C4A-AD3C-74A6B21ADB72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{855D693D-16FF-4C4A-AD3C-74A6B21ADB72}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{855D693D-16FF-4C4A-AD3C-74A6B21ADB72}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{855D693D-16FF-4C4A-AD3C-74A6B21ADB72}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{039E1539-EDA8-4F4E-ACC0-B8292827A3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{039E1539-EDA8-4F4E-ACC0-B8292827A3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{039E1539-EDA8-4F4E-ACC0-B8292827A3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{039E1539-EDA8-4F4E-ACC0-B8292827A3A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B5401DC8-8008-414A-ACE9-FAEF2B1B8113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B5401DC8-8008-414A-ACE9-FAEF2B1B8113}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B5401DC8-8008-414A-ACE9-FAEF2B1B8113}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B5401DC8-8008-414A-ACE9-FAEF2B1B8113}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -7,16 +7,16 @@ public class DataProcessingTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void GetEventStudentPicksTest()
|
||||
public void GetEventStudentRankingsTest()
|
||||
{
|
||||
var events = TestEntityHandler.GetCompetitiveEvents();
|
||||
var events = TestEntityHandler.GetEvents();
|
||||
var students = TestEntityHandler.GetStudents(events);
|
||||
var rankings = TestEntityHandler.GetStudentEventRankings(students, events);
|
||||
|
||||
var eventStudentPicksArray = DataProcessing.GetEventStudentPicks(events, students);
|
||||
foreach (var eventPicks in eventStudentPicksArray)
|
||||
foreach (var ranking in rankings)
|
||||
{
|
||||
Console.WriteLine(eventPicks.Event.Name);
|
||||
Console.WriteLine(string.Join(", ", eventPicks.StudentPicks.Select(s => $"{s.Item2}: {s.Item1.Name}")));
|
||||
Console.WriteLine(ranking.EventDefinition.Name);
|
||||
Console.WriteLine($"{ranking.Student.FirstName}: {ranking.Rank}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using Core.Calculation;
|
||||
using Core.Entities;
|
||||
using Core.Parsers;
|
||||
using Tests.Parsers;
|
||||
|
||||
namespace Tests.Calculation;
|
||||
[TestFixture]
|
||||
public class EventAssignerTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void SolutionTest()
|
||||
{
|
||||
var events = TestEntityHandler.GetCompetitiveEvents();
|
||||
var students = TestEntityHandler.GetStudents(events);
|
||||
|
||||
var eventAssignment = new EventAssigner(events, students, new AssignmentParameters());
|
||||
var teams = eventAssignment.Solve();
|
||||
|
||||
var teamWriter = new TeamWriter(teams, @"c:\temp\teams.csv");
|
||||
teamWriter.Write();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Core.Calculation;
|
||||
using Core.Entities;
|
||||
using Core.Parsers;
|
||||
using Tests.Parsers;
|
||||
using EventAssignment = Core.Calculation.EventAssignment;
|
||||
|
||||
namespace Tests.Calculation;
|
||||
[TestFixture]
|
||||
public class EventAssignmentTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void SolutionTest()
|
||||
{
|
||||
var events = TestEntityHandler.GetEvents();
|
||||
var students = TestEntityHandler.GetStudents(events);
|
||||
|
||||
var eventAssignment = new EventAssignment(events, students, new AssignmentParameters());
|
||||
var solution = eventAssignment.Solve().Result;
|
||||
|
||||
var teamWriter = new TeamWriter(solution.Teams, @"c:\temp\teams.csv");
|
||||
teamWriter.Write();
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,14 @@ public class TeamSchedulerTest
|
||||
[Test]
|
||||
public void Prototype_Test()
|
||||
{
|
||||
var teamSchedulerTest = new Core.Calculation.TeamScheduler_Prototype();
|
||||
var teamSchedulerTest = new TeamScheduler_Prototype();
|
||||
teamSchedulerTest.Solve();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SolutionTest()
|
||||
{
|
||||
var events = TestEntityHandler.GetCompetitiveEvents();
|
||||
var events = TestEntityHandler.GetEvents();
|
||||
var students = TestEntityHandler.GetStudents(events);
|
||||
|
||||
var allTeams = TestEntityHandler.GetTeams(events, students);
|
||||
@@ -28,12 +28,12 @@ public class TeamSchedulerTest
|
||||
from t in teams
|
||||
where t.Event == e
|
||||
//&& t.Students.Count > 1
|
||||
&& (e.Format == EventFormat.Team && e.RegionalEvent)
|
||||
&& (e.EventFormat == EventFormat.Team && e.RegionalEvent)
|
||||
select t).ToArray();
|
||||
teams =
|
||||
teams.Where(t => !t.Event.Name.Contains("Tech Bowl")).ToArray();
|
||||
|
||||
//var eventAssignment = new EventAssigner(events, students);
|
||||
//var eventAssignment = new EventAssignment(events, students);
|
||||
//var teams = eventAssignment.Solve();
|
||||
|
||||
IList<Team>[] timeSlots;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace Tests.Parsers;
|
||||
|
||||
public class AssignmentAssumption_Tests
|
||||
{
|
||||
[Test]
|
||||
public void ParseTest()
|
||||
{
|
||||
var competitiveEvents = TestEntityHandler.GetCompetitiveEvents();
|
||||
var students = TestEntityHandler.GetStudents(competitiveEvents);
|
||||
var eventAssumptions = TestEntityHandler.GetEventAssumptions(competitiveEvents, students);
|
||||
|
||||
foreach (var ea in eventAssumptions)
|
||||
{
|
||||
if (ea.Student == null)
|
||||
continue;
|
||||
Console.WriteLine($"{ea.Assumption,10} {ea.Event.ShortName, -20} {ea.Student.FirstName}");
|
||||
}
|
||||
|
||||
Assert.Pass();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Tests.Parsers;
|
||||
|
||||
public class AssignmentRequirement_Tests
|
||||
{
|
||||
[Test]
|
||||
public void ParseTest()
|
||||
{
|
||||
var competitiveEvents = TestEntityHandler.GetEvents();
|
||||
var students = TestEntityHandler.GetStudents(competitiveEvents);
|
||||
var eventRequirements = TestEntityHandler.GetEventRequirements(competitiveEvents, students);
|
||||
|
||||
foreach (var ea in eventRequirements)
|
||||
{
|
||||
Console.WriteLine($"{ea.Requirement,10} {ea.EventDefinition.ShortName, -20} {ea.Student.FirstName}");
|
||||
}
|
||||
|
||||
Assert.Pass();
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ public class EventDefinitionParser_Tests
|
||||
[Test]
|
||||
public void ParseTest()
|
||||
{
|
||||
var events = TestEntityHandler.GetCompetitiveEvents();
|
||||
var events = TestEntityHandler.GetEvents();
|
||||
|
||||
Console.WriteLine($"Event, Type, Int/Pres, Team Size, Team Count, Regional");
|
||||
foreach (var @event in events)
|
||||
{
|
||||
Console.WriteLine($"{@event.ShortName}, {@event.Format}, {@event.Eligibility}, {@event.Description}, {@event.InterviewOrPresentation}, {@event.TeamSize}, {@event.MaxTeamCountState}, {@event.InterviewOrPresentation}, {@event.RegionalEvent}");
|
||||
Console.WriteLine($"{@event.ShortName}, {@event.EventFormat}, {@event.Eligibility}, {@event.Description}, {@event.InterviewOrPresentation}, {@event.TeamSize}, {@event.MaxTeamCountState}, {@event.InterviewOrPresentation}, {@event.RegionalEvent}");
|
||||
}
|
||||
|
||||
foreach (var @event in events)
|
||||
|
||||
@@ -9,7 +9,7 @@ public class EventOccurrenceParser_Tests
|
||||
[Test]
|
||||
public void ParseTest()
|
||||
{
|
||||
var events = TestEntityHandler.GetCompetitiveEvents();
|
||||
var events = TestEntityHandler.GetEvents();
|
||||
var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceFileInfo(), events);
|
||||
var dictionary = parser.Parse();
|
||||
Console.WriteLine($"Occurrence, Month, Date, Time, Location");
|
||||
@@ -19,7 +19,7 @@ public class EventOccurrenceParser_Tests
|
||||
|
||||
if (!dictionary.ContainsKey(@event))
|
||||
{
|
||||
Console.WriteLine("!!! event not found " + @event.Name);
|
||||
Console.WriteLine("!!! eventDefinition not found " + @event.Name);
|
||||
continue;
|
||||
}
|
||||
var eventOccurrences = dictionary[@event];
|
||||
@@ -30,13 +30,13 @@ public class EventOccurrenceParser_Tests
|
||||
}
|
||||
|
||||
Console.WriteLine("General Schedule");
|
||||
foreach (var eo in dictionary[CompetitiveEvent.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
|
||||
foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime))
|
||||
{
|
||||
Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}");
|
||||
}
|
||||
|
||||
if (dictionary.ContainsKey(CompetitiveEvent.VotingDelegates))
|
||||
foreach (var eo in dictionary[CompetitiveEvent.VotingDelegates])
|
||||
if (dictionary.ContainsKey(EventDefinition.VotingDelegates))
|
||||
foreach (var eo in dictionary[EventDefinition.VotingDelegates])
|
||||
{
|
||||
Console.WriteLine($"{eo.Name} {eo.StartTime}, {eo.Location}");
|
||||
}
|
||||
|
||||
@@ -7,14 +7,12 @@ public class StudentParser_Tests
|
||||
[Test]
|
||||
public void ParseTest()
|
||||
{
|
||||
var students = TestEntityHandler.GetStudents(TestEntityHandler.GetCompetitiveEvents());
|
||||
var students = TestEntityHandler.GetStudents(TestEntityHandler.GetEvents());
|
||||
|
||||
foreach (var student in students)
|
||||
{
|
||||
|
||||
Console.WriteLine($"{student.Name}, {student.Grade}, {student.StateID}, {student.RegionalID}, {student.TsaYear}, {student.Officer}");
|
||||
Console.WriteLine("\t{0}", string.Join(", ", student.RankedEventPicks.Select(e => e.Name)));
|
||||
|
||||
Console.WriteLine($"{student.Name}, {student.Grade}, {student.StateId}, {student.RegionalId}, {student.TsaYear}, {student.OfficerRole}");
|
||||
}
|
||||
|
||||
Console.WriteLine( string.Join(", ", students.OrderBy(s => s.FirstName).Select(s => s.FirstName)));
|
||||
|
||||
@@ -5,7 +5,7 @@ public class TeamParser_Tests
|
||||
[Test]
|
||||
public void ParseTest()
|
||||
{
|
||||
var competitiveEvents = TestEntityHandler.GetCompetitiveEvents();
|
||||
var competitiveEvents = TestEntityHandler.GetEvents();
|
||||
var teams = TestEntityHandler.GetTeams(competitiveEvents, TestEntityHandler.GetStudents(competitiveEvents));
|
||||
|
||||
foreach (var team in teams)
|
||||
@@ -23,17 +23,17 @@ public class TeamParser_Tests
|
||||
[Test]
|
||||
public void RegionalPresubmissions()
|
||||
{
|
||||
var competitiveEvents = TestEntityHandler.GetCompetitiveEvents();
|
||||
var competitiveEvents = TestEntityHandler.GetEvents();
|
||||
var students = TestEntityHandler.GetStudents(competitiveEvents);
|
||||
var teams = TestEntityHandler.GetTeams(competitiveEvents, students);
|
||||
|
||||
foreach (var team in teams.Where(t => t.Event.RegionalEvent))
|
||||
{
|
||||
Console.WriteLine($"{team.Name} {team.Event.RegionalPresubmit} {team.RegionalTimeSlot}");
|
||||
Console.WriteLine($"{team.Name} {team.Event.RegionalPresubmit} team.RegionalTimeSlot");
|
||||
var join = string.Join(", ", team.Students.OrderByDescending(s => team.Captain == s).ThenByDescending(s => s.Grade + s.TsaYear).Select(s => $"{s.FirstNameLastName}{(team.Captain == s ? " *" +
|
||||
"(Cpt.)" : "")}"));
|
||||
Console.WriteLine($"\t{join}");
|
||||
Console.WriteLine(team.RegionalTimeSlotObj);
|
||||
//Console.WriteLine(team.RegionalTimeSlotObj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections;
|
||||
using Core.Entities;
|
||||
using Core.Parsers;
|
||||
using Core.Utility;
|
||||
@@ -8,37 +9,37 @@ public static class TestEntityHandler
|
||||
{
|
||||
private const string ContentDirectory = @"Parsers\TestInput\";
|
||||
|
||||
public static CompetitiveEvent[] GetCompetitiveEvents()
|
||||
public static EventDefinition[] GetEvents()
|
||||
{
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - Event Definitions.csv");
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2025-26 RMS TSA - Event Definitions.csv");
|
||||
var eventRankingsParser = new EventDefinitionParser(fileInfo);
|
||||
return eventRankingsParser.Parse();
|
||||
}
|
||||
|
||||
public static FileInfo GetEventOccurrenceFileInfo()
|
||||
{
|
||||
return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Event Times.txt");
|
||||
return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Events Times.txt");
|
||||
}
|
||||
|
||||
public static Student[] GetStudents(IList<CompetitiveEvent> competitiveEvents)
|
||||
public static Student[] GetStudents(IList<EventDefinition> events)
|
||||
{
|
||||
//var studentEventRankingsCsv = "Student Event Rankings.csv";
|
||||
var studentEventRankingsCsv = "2024-25 RMS TSA student & event - Nationals Student Event Rankings.csv";
|
||||
var studentEventRankingsCsv = "2025-26 RMS TSA student & eventDefinition - Nationals Student Event Rankings.csv";
|
||||
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
||||
var eventRankingsParser = new StudentParser(fileInfo);
|
||||
return eventRankingsParser.Parse(competitiveEvents);
|
||||
return eventRankingsParser.Parse();
|
||||
}
|
||||
|
||||
|
||||
public static Team[] GetTeams(IList<CompetitiveEvent> competitiveEvents, IList<Student> students)
|
||||
public static Team[] GetTeams(IList<EventDefinition> events, IList<Student> students)
|
||||
{
|
||||
//var studentEventRankingsCsv = "Student Event Rankings.csv";
|
||||
var studentEventRankingsCsv = "2024-25 RMS TSA student & event - Nationals Teams.csv";
|
||||
var studentEventRankingsCsv = "2025-26 RMS TSA Teams.csv";
|
||||
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
||||
var eventRankingsParser = new TeamParser(fileInfo);
|
||||
var teams = eventRankingsParser.Parse(competitiveEvents, students);
|
||||
var teams = eventRankingsParser.Parse(events, students);
|
||||
|
||||
foreach (var student in students)
|
||||
{
|
||||
@@ -47,11 +48,18 @@ public static class TestEntityHandler
|
||||
return teams;
|
||||
}
|
||||
|
||||
public static AssignmentAssumption[] GetEventAssumptions(IList<CompetitiveEvent> competitiveEvents, IList<Student> students)
|
||||
public static AssignmentRequirement[] GetEventRequirements(IList<EventDefinition> events, IList<Student> students)
|
||||
{
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - assumptions.csv");
|
||||
var assumptionParser = new AssignmentAssumptionParser(fileInfo);
|
||||
return assumptionParser.Parse(competitiveEvents, students);
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - assumptions.csv");
|
||||
var assumptionParser = new AssignmentRequirementParser(fileInfo);
|
||||
return assumptionParser.Parse(events, students);
|
||||
}
|
||||
|
||||
public static StudentEventRanking[] GetStudentEventRankings(Student[] students, EventDefinition[] events)
|
||||
{
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA - Student Event Rankings.csv");
|
||||
|
||||
var rankingParser = new StudentEventRankingParser(fileInfo);
|
||||
return rankingParser.Parse(students, events);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
Event,Short Name,Level of Effort,Format,Semifinalist Activity,Team Size,Eligibility,Theme,Description,Documentation,State Count,State Presubmission,State Pretesting,State Preliminary Round,State Submit Entry,State Time Semifinalist Sign-up,State Time Semifinalist Presentations/Interviews,State Time Pick-up,Regional Count,Regional Presubmission,Regional Notes
|
||||
Biotechnology,Biotech,2,Team,Interview (3 Team Members),2 to 6,five (5) teams per state,Biotechnology that supports sustainable cosmetics packaging to reduce waste to landfills,"To address the annual theme, participants select a contemporary biotechnology issue and demonstrate understanding of the topic through their documented research and an original display. Semifinalists participate in an interview.",Portfolio,2,,,,,,,,,,
|
||||
Career Prep,Career Prep,1,Individual,Interview,1,one (1) individual per chapter,"Theme: Select a career from one (1) of the following: Bioenergy Technicians, Data Architect, Machine Learning Engineer, Nurse Practitioner","Based on the annual theme, participants conduct research on a technology-related career, prepare a letter of introduction to a potential employer, and develop a job-specific resume. Semifinalists participate in a mock job interview.","Cover Letter, Resume",2,TRUE,,,,,,,3,TRUE,12:50pm-1:00pm
|
||||
Challenging Technology Issues,Chlg. Tech Issues,1,Team,Debate Presentation,2,three (3) teams of two (2) individuals per state,"Topics:, Genetic Testing and Counseling, Animal Testing for Scientific Research, The Use of Drones for Surveillance, Privatization of Space Travel, Nanotechnology in Consumer Goods","Following the onsite random selection of a technology topic from a group of pre-conference posted topics, participants work to prepare for and deliver a debate-style presentation, in which they explain opposing views of the selected topic.",Note Cards,2,,,TRUE,,,,,3,,"Competition, Video due by Noon"
|
||||
Chapter Team,Chapter Team,,Team,Ceremony,6,one (1) team of six (6) individuals per chapter,,"Participants take a parliamentary procedure test to qualify for the semifinal round of competition. Semifinalists conduct an opening ceremony, items of business, parliamentary actions, and a closing ceremony.",,2,,TRUE,,,,,,,,Practice
|
||||
Children's Stories,Children's Stories,3,Team,Interview (2 Team Members),3 to 6,three (3) teams per state; individual entries are permitted,An interactive or pop-up book that focuses on making friends in-person,"Participants create an illustrated children’s story based on the annual theme. The entry product is a physical storybook of artistic, instructional, and social value. Semifinalists read their story aloud and participate in an interview.","Portfolio, Book",2,,,,,,,,3,TRUE,RMS Interview
|
||||
Coding,Coding,2,Team,Challenge,2,one (1) team of two (2) individuals per chapter,"To prepare for MS Coding competition, teams should have knowledge of concepts (software development, computer science, and coding topics) that will be on the Coding written test. They also should be familiar and comfortable with using the Scratch programming language. , , Scratch is a free visual programming language available from the MIT Media Lab (https://scratch.mit.edu/download). An offline version of the Scratch tool should be downloaded and available on each team’s laptop. , , Teams that advance to the semifinalist level, based on written test performance, will perform a challenge using the Scratch programming language. Semifinalist teams will receive the challenge on site and will have two hours to complete it. (PLEASE NOTE: Semifinalist teams MUST have a version of this program available for offline use, as there will be no Internet access available during the semifinalist level of the competition.) , , Examples of the types of challenges students may be asked to complete can be found at this link: https://scratch.mit.edu/starter-projects","To qualify for the semifinal round of competition, participants take a test that concentrates on computer science and coding. Semifinalists demonstrate their programming knowledge by developing a solution to an onsite coding challenge.",,2,,TRUE,,,,,,,,
|
||||
Community Service Video,Comm. Service Vid,2,Team,Presentation and Interview,1 to 6,one (1) team per chapter; individual entries are permitted,,Participants create a video that depicts the local TSA chapter’s involvement in a community service project. Semifinalists deliver a presentation on the project and participate in an interview.,"Copyright Checklist, Release Forms, Video Link",2,TRUE,,,,,,,3,TRUE,RMS Interview
|
||||
CAD foundations,CAD,1,Individual,Challenge,1,two (2) individuals per state,,Participants demonstrate their understanding of CAD fundamentals by creating a two-dimensional (2D) graphic representation of an engineering part or object and answering questions from evaluators about their entry.,Laptop w/CAD Software,2,,,,,,,,,,
|
||||
Construction Challenge,Construction Chlg.,3,Team,Presentation and Interview (2-4 Team Members),3 to 6,one (1) team of at least two (2) individuals per chapter,,"Participants submit a scale model, display, and documentation portfolio for a design that fulfills a community need related to construction. Semifinalists deliver a presentation about their entry and participate in an interview.","Prototype, Display, Portfolio",2,,,,,,,,,,
|
||||
Cybersecurity,Cybersecurity,2,Individual,Presentation,1,three (3) individuals per chapter,Problem Statement: Byte Inc.'s Internet of things and Bluetooth are acting strangely on devices in the company’s network. Address the potential cause and propose a solution.,"Participants take a test that assesses knowledge of cybersecurity vocabulary and the skills needed to execute common cybersecurity tasks. Using digital presentation software, semifinalists deliver a presentation that addresses the annual theme/problem.",Presentation,2,,TRUE,,,,,,,,Practice
|
||||
Data Science & Analytics,Data Sci.,2,Team,Challenge and Presentation,2 to 3,three (3) teams of two to three (2-3) individuals per state,"Determine the potential ""movie success"" of a fictitious feature film based on different public metrics, such as, but not limited to box office revenue, date of release, movie genre (selected by the team), movie production budget, and more.","Participants conduct research on the annual topic, collect data, use analytics to assess the data and make predictions, and document their work in a portfolio and a display. To address a challenge presented onsite at the conference, semifinalists review specific data sets, provide insights, make predictions, and present their findings for evaluation.",Display,2,,,,,,,,,,
|
||||
Digital Photography,Digital Photography,2,Individual,Onsite Photography and Presentation,1,three (3) individuals per state,Students will take four photographs that fit the theme “Through the Eye of an Animal.”,Participants produce and submit a digital photographic portfolio that relates to the annual theme. Semifinalists participate in an onsite photographic challenge and a presentation/interview.,"Portfolio, Camera, Laptop",2,TRUE,,,,,,,3,TRUE,
|
||||
Dragster,Dragster,2,Individual,Race and Interview,1,two (2) individuals per chapter,Address weights and lengths only; there are no special design challenges.,"Participants design, draw, and construct a CO2-powered dragster that adheres to the annual specifications, design and documentation requirements, and theme. Semifinalists participate in an interview and compete in a double-elimination race.","Dragster, Technical Drawing, Parts and Materials",2,,,,,,,,,,
|
||||
Electrical Applications,Electrical App.,1,Team,Circuit Build and Interview,2,one (1) team of two (2) individuals per chapter,,"Participants take a test on basic electrical and electronic theory. In response to an onsite challenge, semifinalists assemble a specified circuit from a schematic diagram, make required electrical measurements, and explain their solution in an interview.",,2,,TRUE,,,,,,,,
|
||||
Essays on Technology ,Essays on Tech ,1,Individual,Written Essay,1,three (3) individuals per state,"Topic: Reducing our impact on the planet. Subtopics: Reducing microplastics in our oceans, Reducing forever chemicals in soil, Reducing energy consumption for cooling in residential and commercial buildings as temperatures rise","Participants conduct research on specific subtopics from a broad technology area posted as part of the annual theme. Using a previously prepared note card as an approved resource, participants draft an outline of the subtopic randomly selected onsite at the conference. Semifinalists write an essay on that subtopic.",Note Cards,2,,,,,,,,,,
|
||||
Flight,Flight,2,Individual,Constuct and Fly Glider,1,two (2) individuals per chapter,,Participants submit a documentation portfolio and fabricate a glider designed to stay in flight for the greatest elapsed time. Semifinalists use their technical drawing skills to construct a glider that is flown onsite.,"Glider, Portfolio, Eyewear",2,,,,,,,,,,
|
||||
Forensic Technology,Forensic Tech,1,Team,Demonstration,2,one (1) team of two (2) individuals per chapter,"Be familiar with, and be able to demonstrate, the following forensic concepts: Tool mark identification, Hair and fiber analysis , Forensic biometrics",Participants take a test of basic forensic science theory to qualify for the semifinal round of competition. Semifinalists participate in an onsite forensic skills demonstration.,,2,,TRUE,,,,,,3,,Practice Test
|
||||
Inventions & Innovations,I&I,3,Team,Presentation and Interview (2 Team Members),3 to 6,one (1) team of three (3) to six (6) individuals per chapter,Create a product that enhances the daily productivity of a middle school student. ,"To address the annual theme, participants research a need - and brainstorm a solution - for an invention or innovation of a device, system, or process. Participants document their work in an interactive display and the creation of a model/prototype. Semifinalists deliver a presentation about their work and participate in an interview.","Display, Prototype",2,,,,,,,,,,
|
||||
Solar Sprint,Solar Sprint,2,Team,Race and Interview,2 to 4,one (1) team of two to four (2-4) individuals per chapter,,"Participants apply STEM concepts, creativity, teamwork, and problem-solving skills to design, construct, and race a solar-powered model car. Documentation of the process is required. Learn more about JSS, then register via an Army Educational Outreach Program (AEOP) portal to begin the JSS journey.","Car, Portfolio, Display",2,TRUE,,,,,,,,,
|
||||
Leadership Strategies,Leadership Str.,1,Team,Presentation,3,three (3) teams of three (3) individuals per state,,Participants prepare for and deliver a presentation about a specific challenge that officers of a TSA chapter might encounter. Semifinalists follow the same competition procedure but must respond to a different chapter challenge.,Note Cards,2,,,TRUE,,,,,3,,10:10am-10:20am
|
||||
Mass Production,Mass Production,3,Team,Demonstration and Interview (2 Team Members),3 to 6,one (1) team of at least three (3) individuals per chapter,Pet supply storage tower ,"Participants manufacture a marketable product that addresses the annual theme. The development of the product prototype is documented in a portfolio that presents participant knowledge and skills related to the mass production process. Through a demonstration of the prototype and an interview, semifinalists support the viability of the prototype.","Portfolio, Photo Timeline",2,,,,,,,,,,
|
||||
Mechanical Engineering,Mechanical Eng.,3,Team,Race and Interview,2 to 3,one (1) team of two to three (2-3) individuals per chapter,"Problem Statement: A vehicle must go forward at least ten (10) feet, then reverse to a full stop at seven (7) feet from the original starting line. The floor surface for the 2025 National TSA Conference will be convention center concrete. The floor surfaces for state conferences may vary.","Participants design, document, and build a mechanical device (mousetrap car) that incorporates the elements of the annual theme/problem – and then race the car. Finalists are determined based on an evaluation of the documentation portfolio, the race exit interview, and the race placement.","Car, Portfolio, Technical Drawing",2,,,,,,,,,,
|
||||
Medical Technology,Medical Tech,2,Team,Presentation and Interview (3 Team Members),3,three (3) teams per state,Medical Drugs and Genetics: Why do some people respond to medicines differently than others?,"Participants conduct research on a contemporary medical technology issue related to the annual theme, document their research, create a display, and build a prototype. Semifinalists deliver a presentation about their entry and participate in an interview.","Display, Prototype",2,,,,,,,,,,
|
||||
Microcontroller Design,Microcontroller,3,Team,Presentation and Interview,1 to 6,one (1) team per chapter; individual entries are permitted,Interactive gift box ,"To address the annual theme/problem, participants design and create a working digital device, document the development process, and demonstrate their product as part of a presentation.","Device, Portfolio",2,,,,,,,,,,
|
||||
Off the Grid,Off the Grid,3,Team,Presentation and Interview (2 Team Members),3 to 6,three (3) teams per state; individual entries are permitted,"Design a home for a family of four (4) in a country (of your choice) in which a boreal forest (taiga) biome is found. The house must be designed for an area that does not have access to a power grid. In addition, the house must include a renewable energy source, one (1) agricultural system, and must solve one (1) problem that is specific to the area.","Based on the annual theme, participants conduct research on a sustainable architectural design for a home in a country not their own. Participants produce a portfolio and create a display and a model. Semifinalists present their design and participate in an interview.","Display, Model, Portfolio",2,,,,,,,,,,
|
||||
Prepared Speech,Prepared Speech,2,Individual,Speech,1,three (3) individuals per state,2025 National Conference Theme: Tune into Technology,Participants deliver a timed speech that relates to the theme of the current national TSA conference. Semifinalists and finalists are determined using the same competition procedure.,"Note Cards permitted, memorization is better",2,,,TRUE,,,,,3,TRUE,
|
||||
Problem Solving,Problem Solving,1,Team,Challenge,2,one (1) team of two (2) individuals per chapter,,"Participants use problem-solving skills to design and build a solution to an onsite challenge. Solutions are evaluated using measures appropriate to the challenge, such as elapsed time, horizontal or vertical distance, and/or strength.",Toolkit,1,,,,,,,,,,Practice Prompt
|
||||
Promotional Marketing,Promo Marketing,2,Individual,Challenge,1,one (1) individual per chapter,"Charitable and service organizations are the backbone of our communities. A group called “Students Helping Grandparents” has contacted your chapter advisor about your chapter hosting an event at a public library, during which chapter members will lead focus group discussions with senior citizens. The topic for the focus groups will be helping senior citizens understand and avoid online cybercrime. The event is Tuesday May 27, 2025, from 4:00 PM – 6:00 PM. Printable: Design a tri-fold brochure. Wearable: Design a hat that can be given to participants. Digital Signage: Create an auto-advancing slide presentation that is no longer than two (2) minutes in length.",Participants create and submit a marketing portfolio and required elements that address the annual theme/problem. Semifinalists complete a layout and design assignment for evaluation.,"Advertisement, Design, Digital Signage",2,TRUE,,,,,,,3,TRUE,
|
||||
STEM Animation,STEM Ani.,3,Team,Presentation and Interview (2 Team Members),3 to 6,three (3) teams per state,Robotics in automobile manufacturing,Participants design and create a STEM animation video and documentation portfolio to address the annual theme/problem. Semifinalists present their animation and explain the elements of their portfolio/entry.,"Portfolio, Video Link",2,TRUE,,,,,,,,,
|
||||
Structural Engineering,Structural Eng.,2,Team,Challenge,2,one (1) team of two (2) individuals per chapter,,Participants apply the principles of structural engineering to design and construct a structure that complies with the annual challenge. An assessment of the required documentation and the destructive testing of the structure (to determine its design efficiency) determine both semifinalists and finalists.,"Technical 3D drawing, cuts part list of materials",2,,,,,,,,,,
|
||||
System Control Technology,System Control Tech,2,Team,Challenge,3,one (1) team of three (3) individuals per state,,"In response to a challenge presented onsite at the conference, participants analyze a problem (typically one in an industrial setting), build and program a computer-controlled mechanical model to solve the problem, explain the program and the features of the mechanical model solution, and provide instructions for evaluators to operate the device.","Construction Kit, Inventor's Log",2,,,,,,,,,,
|
||||
Tech Bowl,Tech Bowl,1,Team,Test,3,one (1) team of three (3) individuals per chapter,,"Participants demonstrate their knowledge of TSA and concepts addressed in technology content standards by completing an objective test. Semifinalists participate in a head-to-head, team competition.",,2,,TRUE,,,,,,2,,12:30 (Arrive 10 min early)
|
||||
Technical Design,Tech Design,2,Team,Challenge,2,one (1) team of two (2) individuals per chapter,,Participants demonstrate their ability to use the technical design process to solve an engineering design problem provided onsite at the conference. Required elements of the entry are presented in a portfolio that includes technical drawings for a minimum of three viable solutions.,Portfolio incl. Technical Drawings,2,,,,,,,,,,
|
||||
Video Game Design,Video Game,3,Team,Presentation and Interview,2 to 6,One (1) team of two (2) to six (6) individuals per chapter,,"Participants design, build, provide documentation for, and launch an E-rated, online game on a subject of their choice. Onsite at the conference, semifinalists deliver a presentation and participate in an interview to demonstrate the knowledge and expertise gained during the development of the game.","Portfolio, Game Link",2,TRUE,,,,,,,3,TRUE,
|
||||
Vlogging,Vlogging,3,Team,Challenge,2 to 6,two (2) teams of two (2) to six (6) members per chapter,Setting and achieving goals; the minimum number of audio pieces is four (4).,"Participants use digital video technology to create original content about a pre-determined technology theme. Semifinalists compete in an onsite challenge to produce additional video(s) based on specified criteria, such as provided props, lines of dialog, and topics.","Portfolio, Video Link",2,TRUE,,,,,,,3,TRUE,RMS Interview (?)
|
||||
Website Design,Website,3,Team,Presentation and Interview,3 to 6,one (1) team of at least three (3) and a maximum of six (6) individuals per chapter,Topic: Website for food preparation recipes. Challenge – Develop an original website with simple recipes for young cooks (MS and HS age). It should have an interactive element that will ask a few questions to direct the user to the desired ingredients and recipe. ,"To address the annual challenge, participants design, build, provide documentation for, and launch a website that incorporates the elements of website design, graphic layout, and proper coding techniques. Semifinalists participate in an interview to demonstrate the knowledge and expertise gained during the development of the website.","Copyright Checklist, Release Forms, Work Log, Website Link",2,TRUE,,,,,,,3,TRUE,RMS Interview
|
||||
|
Binary file not shown.
+4
-1
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
@@ -31,6 +31,9 @@
|
||||
<Content Include="Parsers\TestInput\2024-25 RMS TSA student & event - assumptions.csv">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Parsers\TestInput\2025-26 RMS TSA student & event - Event Definitions.csv">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Parsers\TestInput\2024-25 RMS TSA student & event - Event Definitions.csv">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -14,23 +14,31 @@ namespace Web.Controllers
|
||||
{
|
||||
private const string ContentDirectory = @"C:\Users\james\source\TSA Chapter Organizer\Tests\Parsers\TestInput\";
|
||||
|
||||
private CompetitiveEvent[] GetCompetitiveEvents()
|
||||
private EventDefinition[] GetEvents()
|
||||
{
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - Event Definitions.csv");
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - EventDefinition Definitions.csv");
|
||||
var eventRankingsParser = new EventDefinitionParser(fileInfo);
|
||||
return eventRankingsParser.Parse();
|
||||
}
|
||||
|
||||
private Student[] GetStudents(IList<CompetitiveEvent> events)
|
||||
private Student[] GetStudents(IList<EventDefinition> events)
|
||||
{
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & event - Nationals Student Event Rankings.csv");
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - Nationals Student EventDefinition Rankings.csv");
|
||||
var eventRankingsParser = new StudentParser(fileInfo);
|
||||
return eventRankingsParser.Parse(events);
|
||||
}
|
||||
|
||||
public Team[] GetTeams(IList<CompetitiveEvent> competitiveEvents, IList<Student> students)
|
||||
public static StudentEventRanking[] GetStudentEventRankings(Student[] students, EventDefinition[] events)
|
||||
{
|
||||
var studentEventRankingsCsv = "2024-25 RMS TSA student & event - Nationals Teams.csv";
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA - Student Event Rankings.csv");
|
||||
|
||||
var rankingParser = new StudentEventRankingParser(fileInfo);
|
||||
return rankingParser.Parse(students, events);
|
||||
}
|
||||
|
||||
public Team[] GetTeams(IList<EventDefinition> competitiveEvents, IList<Student> students)
|
||||
{
|
||||
var studentEventRankingsCsv = "2024-25 RMS TSA student & eventDefinition - Nationals Teams.csv";
|
||||
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
|
||||
var eventRankingsParser = new TeamParser(fileInfo);
|
||||
@@ -43,9 +51,9 @@ namespace Web.Controllers
|
||||
return teams;
|
||||
}
|
||||
|
||||
public AssignmentAssumption[] GetAssignmentAssumptions(IList<CompetitiveEvent> competitiveEvents, IList<Student> students)
|
||||
public AssignmentAssumption[] GetAssignmentAssumptions(IList<EventDefinition> competitiveEvents, IList<Student> students)
|
||||
{
|
||||
var assumptionsCsv = "2024-25 RMS TSA student & event - assumptions.csv";
|
||||
var assumptionsCsv = "2024-25 RMS TSA student & eventDefinition - assumptions.csv";
|
||||
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, assumptionsCsv);
|
||||
var assumptionParser = new AssignmentAssumptionParser(fileInfo);
|
||||
@@ -54,17 +62,17 @@ namespace Web.Controllers
|
||||
return assumptions;
|
||||
}
|
||||
|
||||
public IDictionary<CompetitiveEvent, List<EventOccurrence>> GetStateEventOccurrences(IList<CompetitiveEvent> competitiveEvents)
|
||||
public IDictionary<EventDefinition, List<EventOccurrence>> GetStateEventOccurrences(IList<EventDefinition> competitiveEvents)
|
||||
{
|
||||
var eventTimesFilename = "2025 TN TSA State Competition Event Times.txt";
|
||||
var eventTimesFilename = "2025 TN TSA State Competition EventDefinition 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)
|
||||
public IDictionary<EventDefinition, List<EventOccurrence>> GetNationalEventOccurrences(IList<EventDefinition> competitiveEvents)
|
||||
{
|
||||
var eventTimesFilename = "2025 TSA Nationals Competition Event Times.txt";
|
||||
var eventTimesFilename = "2025 TSA Nationals Competition EventDefinition Times.txt";
|
||||
var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
|
||||
var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
|
||||
var nationalEventOccurrences = parser.Parse();
|
||||
@@ -104,13 +112,13 @@ namespace Web.Controllers
|
||||
|
||||
public IActionResult Events()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents().Where(e => e.Name != "Chapter Team").ToArray();
|
||||
var competitiveEvents = GetEvents().Where(e => e.Name != "Chapter Team").ToArray();
|
||||
return View(competitiveEvents);
|
||||
}
|
||||
|
||||
public IActionResult StudentEventHandout()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var teams = GetTeams(competitiveEvents, students);
|
||||
|
||||
@@ -119,10 +127,12 @@ namespace Web.Controllers
|
||||
|
||||
public IActionResult StudentEvents()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var events = GetEvents();
|
||||
var students = GetStudents(events);
|
||||
var rankings = GetStudentEventRankings(students, events);
|
||||
|
||||
var eventStudentPicksArray = DataProcessing.GetEventStudentPicks(competitiveEvents, students);
|
||||
|
||||
var eventStudentPicksArray = StudentEventRanking.GetEventStudentRankings(rankings);
|
||||
var assignmentParameters = new AssignmentParameters
|
||||
{
|
||||
EffortUpperBound = 9,
|
||||
@@ -131,9 +141,9 @@ namespace Web.Controllers
|
||||
TeamSizeLimit = 4
|
||||
};
|
||||
|
||||
var eventAssignment = new EventAssigner(competitiveEvents, students, assignmentParameters);
|
||||
var eventAssignment = new EventAssigner(events, students, assignmentParameters);
|
||||
|
||||
var assignmentAssumptions = GetAssignmentAssumptions(competitiveEvents, students);
|
||||
var assignmentAssumptions = GetAssignmentAssumptions(events, students);
|
||||
|
||||
foreach (var assumption in assignmentAssumptions)
|
||||
{
|
||||
@@ -149,39 +159,39 @@ namespace Web.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
eventAssignment.RemoveEvent(new List<CompetitiveEvent>
|
||||
eventAssignment.RemoveEvent(new List<EventDefinition>
|
||||
{
|
||||
competitiveEvents.First(e => e.Name == "Chapter Team")
|
||||
events.First(e => e.Name == "Chapter Team")
|
||||
});
|
||||
|
||||
eventAssignment.IncludedEvents(new List<CompetitiveEvent>
|
||||
eventAssignment.IncludedEvents(new List<EventDefinition>
|
||||
{
|
||||
//competitiveEvents.First(e => e.Name == "System Control Technology")
|
||||
});
|
||||
|
||||
|
||||
var eventAssignmentsList = eventAssignment.Solve();
|
||||
return View(Tuple.Create(competitiveEvents, students, eventStudentPicksArray, eventAssignmentsList, assignmentParameters));
|
||||
return View(Tuple.Create(events, students, eventStudentPicksArray, eventAssignmentsList, assignmentParameters));
|
||||
}
|
||||
|
||||
public IActionResult Teams()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var teams = GetTeams(competitiveEvents, students);
|
||||
|
||||
//teams = teams.Where(t => t.Event.RegionalEvent).ToArray();
|
||||
//teams = teams.Where(t => t.EventDefinition.RegionalEvent).ToArray();
|
||||
|
||||
return View(Tuple.Create(teams));
|
||||
}
|
||||
|
||||
public IActionResult Regionals()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var teams = GetTeams(competitiveEvents, students);
|
||||
|
||||
teams = teams.Where(t => t.Event.RegionalEvent).ToArray();
|
||||
teams = teams.Where(t => t.EventDefinition.RegionalEvent).ToArray();
|
||||
var enumerable = students.Where(s => !teams.SelectMany(ts => ts.Students).Contains(s)).ToArray();
|
||||
|
||||
return View(Tuple.Create(teams, enumerable));
|
||||
@@ -189,7 +199,7 @@ namespace Web.Controllers
|
||||
|
||||
public IActionResult State()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var teams = GetTeams(competitiveEvents, students);
|
||||
var eventOccurrences = GetStateEventOccurrences(competitiveEvents);
|
||||
@@ -205,7 +215,7 @@ namespace Web.Controllers
|
||||
}
|
||||
public IActionResult Nationals()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var teams = GetTeams(competitiveEvents, students);
|
||||
var eventOccurrences = GetNationalEventOccurrences(competitiveEvents);
|
||||
@@ -222,7 +232,7 @@ namespace Web.Controllers
|
||||
|
||||
public IActionResult TeamGrid()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
var teams = GetTeams(competitiveEvents, students);
|
||||
|
||||
@@ -231,7 +241,7 @@ namespace Web.Controllers
|
||||
|
||||
public IActionResult Students()
|
||||
{
|
||||
var competitiveEvents = GetCompetitiveEvents();
|
||||
var competitiveEvents = GetEvents();
|
||||
var students = GetStudents(competitiveEvents);
|
||||
GetTeams(competitiveEvents, students);
|
||||
|
||||
@@ -276,7 +286,7 @@ namespace Web.Controllers
|
||||
reverse: true,
|
||||
slotPush:0);
|
||||
|
||||
var events = GetCompetitiveEvents();
|
||||
var events = GetEvents();
|
||||
var students = GetStudents(events);
|
||||
|
||||
var allTeams = GetTeams(events, students);
|
||||
@@ -284,23 +294,23 @@ namespace Web.Controllers
|
||||
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 RegionalPredicate(Team t) => t.EventDefinition.RegionalEvent;
|
||||
bool TeamPredicate(Team t) => t.EventDefinition.EventFormat 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 HighEffort(Team t) => t.EventDefinition.LevelOfEffort == 3;
|
||||
bool LowEffort(Team t) => t.EventDefinition.LevelOfEffort == 1;
|
||||
|
||||
bool RegionalTeamSomeEffortPredicate(Team t) => RegionalTeamPredicate(t) && !LowEffort(t);
|
||||
|
||||
bool IndividualPredicate(Team t) => t.Event.Format is EventFormat.Individual;
|
||||
bool IndividualPredicate(Team t) => t.EventDefinition.EventFormat is EventFormat.Individual;
|
||||
bool negativePredicate(Team t) => false;
|
||||
|
||||
var mustIncludeTeams =
|
||||
from e in events
|
||||
from t in allTeams
|
||||
where t.Event == e &&
|
||||
where t.EventDefinition == e &&
|
||||
(scheduleOptions.MustIncludeEvents?.Any(s => s == "RegionalTeam") == true && RegionalTeamSomeEffortPredicate(t)
|
||||
|| scheduleOptions.MustIncludeEvents?.Any( t.Event.Name.Contains) == true
|
||||
|| scheduleOptions.MustIncludeEvents?.Any( t.EventDefinition.Name.Contains) == true
|
||||
|| HighEffort(t))
|
||||
select t;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using Core.Entities
|
||||
@using Core.Utility
|
||||
@model CompetitiveEvent[]
|
||||
@model EventDefinition[]
|
||||
@{
|
||||
ViewData["Title"] = "Events Page";
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<i>Regional Event</i>
|
||||
<i>Regional EventDefinition</i>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -21,14 +21,14 @@
|
||||
<h5>@evt.Name</h5>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@if (evt.Format is EventFormat.Team)
|
||||
@if (evt.EventFormat is EventFormat.Team)
|
||||
{
|
||||
<html><strong>@evt.Format</strong><br/>Size: <strong>@evt.TeamSize</strong></html>
|
||||
<html><strong>@evt.EventFormat</strong><br/>Size: <strong>@evt.TeamSize</strong></html>
|
||||
}
|
||||
else
|
||||
{
|
||||
<html>
|
||||
<strong>@evt.Format</strong>
|
||||
<strong>@evt.EventFormat</strong>
|
||||
</html>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
@using System.Text.RegularExpressions
|
||||
@using Core.Entities
|
||||
@model Tuple<Team[], Student[], IDictionary<CompetitiveEvent, List<EventOccurrence>>>
|
||||
@model Tuple<Team[], Student[], IDictionary<EventDefinition, List<EventOccurrence>>>
|
||||
@{
|
||||
ViewData["Title"] = "Teams";
|
||||
var eventOccurrences = Model.Item3;
|
||||
}
|
||||
<div class="container nobrk pt-5">
|
||||
<h2>Nationals Events</h2>
|
||||
<h2>Nationals EventDefinitions</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -20,7 +20,7 @@
|
||||
@foreach (var team in Model.Item1)
|
||||
{
|
||||
var students = team.Students;
|
||||
@if (true @* team.Event.Format == EventFormat.Team *@)
|
||||
@if (true @* team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Team *@)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@@ -46,32 +46,32 @@
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@* @if (team.Event.StatePresubmission)
|
||||
@* @if (team.EventDefinition.StatePresubmission)
|
||||
{
|
||||
<text>Pre-submission due Friday, March 14</text>
|
||||
}
|
||||
@if (team.Event.StatePretesting)
|
||||
@if (team.EventDefinition.StatePretesting)
|
||||
{
|
||||
<text>Pre-testing Wednesday, April 2nd</text>
|
||||
}
|
||||
@if (team.Event.StatePreliminaryRound)
|
||||
@if (team.EventDefinition.StatePreliminaryRound)
|
||||
{
|
||||
<text>Preliminary and Semifinalist Rounds</text>
|
||||
} *@
|
||||
</td>
|
||||
<td>
|
||||
@team.Event.Documentation
|
||||
@team.EventDefinition.Documentation
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
}
|
||||
@* else if (team.Event.Format == EventFormat.Individual)
|
||||
@* else if (team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Individual)
|
||||
{
|
||||
foreach (var student in students)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@team.Event.Name - @student.FirstNameLastName (@student.RegionalID)
|
||||
@team.EventDefinition.Name - @student.FirstNameLastName (@student.RegionalID)
|
||||
</td>
|
||||
<td>@team.RegionalTimeSlot</td>
|
||||
<td></td>
|
||||
@@ -92,14 +92,14 @@
|
||||
<h3>@student.FirstNameLastName - @student.NationalID</h3>
|
||||
<h4>TSA 2025 Nationals Schedule</h4>
|
||||
<div class="row">
|
||||
<div class="col col-2">Events</div>
|
||||
<div class="col col-2">EventDefinitions</div>
|
||||
<div class="col">
|
||||
@foreach (var ev in student.Teams)
|
||||
{
|
||||
<div class="row">
|
||||
|
||||
<div class="col col-2">
|
||||
@if (ev.Event.Format is EventFormat.Team)
|
||||
@if (ev.EventDefinition.EventFormat is EventFormat.Team)
|
||||
{
|
||||
@ev.TeamNumber
|
||||
}
|
||||
@@ -117,7 +117,7 @@
|
||||
</text>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@ev.Event.SemifinalistActivity
|
||||
@ev.EventDefinition.SemifinalistActivity
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -127,7 +127,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Time</td>
|
||||
<td>Event</td>
|
||||
<td>EventDefinition</td>
|
||||
<td></td>
|
||||
<td>Location</td>
|
||||
</tr>
|
||||
@@ -136,9 +136,9 @@
|
||||
@foreach (var date in
|
||||
eventOccurrences
|
||||
.Where(eo =>
|
||||
student.Teams.Select(t => t.Event).Any(a => a == eo.Key)
|
||||
|| eo.Key == CompetitiveEvent.GeneralSchedule
|
||||
|| (eo.Key == CompetitiveEvent.VotingDelegates && student.VotingDelegate))
|
||||
student.Teams.Select(t => t.EventDefinition).Any(a => a == eo.Key)
|
||||
|| eo.Key == EventDefinition.GeneralSchedule
|
||||
|| (eo.Key == EventDefinition.VotingDelegates && student.VotingDelegate))
|
||||
.SelectMany(eo => eo.Value.Select(v => Tuple.Create(v, eo.Key)))
|
||||
.GroupBy(de => de.Item1.StartTime.Date)
|
||||
.OrderBy(d => d.Key)
|
||||
@@ -151,8 +151,8 @@
|
||||
// filter out occurrences where non-captain
|
||||
.Where(de =>
|
||||
!de.Item1.SignupSubmitPickup
|
||||
|| de.Item2.Format is EventFormat.Individual
|
||||
|| student.Teams.Any(t => t.Captain == student && t.Event == de.Item2)
|
||||
|| de.Item2.EventFormat is EventFormat.Individual
|
||||
|| student.Teams.Any(t => t.Captain == student && t.EventDefinition == de.Item2)
|
||||
)
|
||||
.OrderBy(de => de.Item1.StartTime);
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<td class="@hlClass">@eventOccurrence.Item2.Name</td>
|
||||
<td class="@hlClass">
|
||||
@eventOccurrence.Item1.Name
|
||||
@if (eventOccurrence.Item1.Name.Contains("Pick") && eventOccurrence.Item2.Format is EventFormat.Team)
|
||||
@if (eventOccurrence.Item1.Name.Contains("Pick") && eventOccurrence.Item2.EventFormat is EventFormat.Team)
|
||||
{
|
||||
<br/>
|
||||
<text>or coordinate with a teammate</text>
|
||||
@@ -217,17 +217,17 @@
|
||||
<tr><td colspan="4"><strong>@eventsForDate.Key.ToString("MMMM d") </strong> </td></tr>
|
||||
@foreach (var occurrence in eventsForDate.OrderBy(o => o.Item1.StartTime))
|
||||
{
|
||||
var teams = Model.Item1.Where(t => t.Event == occurrence.Item2);
|
||||
if (occurrence.Item2 != CompetitiveEvent.GeneralSchedule && occurrence.Item2 != CompetitiveEvent.VotingDelegates && !teams.Any())
|
||||
var teams = Model.Item1.Where(t => t.EventDefinition == occurrence.Item2);
|
||||
if (occurrence.Item2 != EventDefinition.GeneralSchedule && occurrence.Item2 != EventDefinition.VotingDelegates && !teams.Any())
|
||||
continue;
|
||||
<tr>
|
||||
<td style="white-space:nowrap;">@occurrence.Item1.Time</td>
|
||||
<td>
|
||||
@if (occurrence.Item2 == CompetitiveEvent.GeneralSchedule)
|
||||
@if (occurrence.Item2 == EventDefinition.GeneralSchedule)
|
||||
{
|
||||
<text>Everyone</text>
|
||||
}
|
||||
else if (occurrence.Item2 == CompetitiveEvent.VotingDelegates)
|
||||
else if (occurrence.Item2 == EventDefinition.VotingDelegates)
|
||||
{
|
||||
<text>Voting Delegates - @string.Join(", ", Model.Item2.Where(stu => stu.VotingDelegate).Select(stu => stu.FirstName))</text>
|
||||
}
|
||||
@@ -294,7 +294,7 @@
|
||||
@foreach (var t in assignments)
|
||||
{
|
||||
<td>
|
||||
@if (t.Event.Format != EventFormat.Individual)
|
||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
||||
{
|
||||
@t.Name
|
||||
@if (t.Captain == student)
|
||||
@@ -304,7 +304,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@t.Event.Name
|
||||
@t.EventDefinition.Name
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@@ -337,7 +337,7 @@
|
||||
@foreach (var t in assignments)
|
||||
{
|
||||
<p>
|
||||
@if (t.Event.Format != EventFormat.Individual)
|
||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
||||
{
|
||||
@t.Name
|
||||
@if (t.Captain == student)
|
||||
@@ -347,7 +347,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@t.Event.Name
|
||||
@t.EventDefinition.Name
|
||||
}
|
||||
</p>
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<td>Team Members</td>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var team in Model.Item1.OrderBy(t => t.RegionalTimeSlotObj))
|
||||
@foreach (var team in Model.Item1/*.OrderBy(t => t.RegionalTimeSlotObj)*/)
|
||||
{
|
||||
var students = team.Students;
|
||||
@if (team.Event.Format == EventFormat.Team)
|
||||
@if (team.EventDefinition.EventFormat == EventFormat.Team)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@@ -28,7 +28,7 @@
|
||||
@team.RegionalTimeSlot
|
||||
</td>*@
|
||||
<td>
|
||||
@team.Event.RegionalNotes
|
||||
@team.EventDefinition.Notes
|
||||
</td>
|
||||
<td>
|
||||
@{
|
||||
@@ -50,15 +50,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
else if (team.Event.Format == EventFormat.Individual)
|
||||
else if (team.EventDefinition.EventFormat == EventFormat.Individual)
|
||||
{
|
||||
foreach (var student in students)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@team.Event.Name #@student.RegionalID
|
||||
@team.EventDefinition.Name #@student.RegionalID
|
||||
</td>
|
||||
<td>@team.Event.RegionalNotes</td>
|
||||
<td>@team.EventDefinition.Notes</td>
|
||||
<td>@student.FirstNameLastName</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -86,7 +86,7 @@
|
||||
<td></td>
|
||||
</tr>
|
||||
var teams = Model.Item1.Where(t => t.Students.Contains(student));
|
||||
foreach (var team in teams.OrderBy(t => t.RegionalTimeSlotObj))
|
||||
foreach (var team in teams/*.OrderBy(t => t.RegionalTimeSlotObj)*/)
|
||||
{
|
||||
<tr>
|
||||
<td></td>
|
||||
@@ -97,13 +97,13 @@
|
||||
<text>(Cpt.)</text>
|
||||
}
|
||||
</td>
|
||||
<td>@team.Event.RegionalNotes @team.RegionalTimeSlot</td>
|
||||
<td>@team.EventDefinition.Notes @*@team.RegionalTimeSlot*@</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
}
|
||||
<p>No regional events: @string.Join(", ", @Model.Item2.Select(s=> s.FirstName))</p>
|
||||
<p>No regional eventDefinitions: @string.Join(", ", @Model.Item2.Select(s=> s.FirstName))</p>
|
||||
|
||||
@functions
|
||||
{
|
||||
@@ -114,7 +114,7 @@
|
||||
|
||||
private void GetTeamClass(Team team)
|
||||
{
|
||||
if (team.Event.RegionalEvent)
|
||||
if (team.EventDefinition.RegionalEvent)
|
||||
{
|
||||
@Html.Raw("regional")
|
||||
;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
@foreach (var timeslot in schedule)
|
||||
{
|
||||
overlaps = Team.GetStudentTeamOverlaps(timeslot).ToList();
|
||||
var partialTeams = timeslot.Where(t => t is PartialTeam && t.Event.Format is not EventFormat.Individual);
|
||||
var partialTeams = timeslot.Where(t => t is PartialTeam && t.EventDefinition.EventFormat is not EventFormat.Individual);
|
||||
var fullTeams = timeslot.Where(t => !partialTeams.Contains(t));
|
||||
|
||||
<h3>Time Slot @(slot + 1)</h3>
|
||||
@@ -33,7 +33,7 @@
|
||||
<th class="col-5"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var team in fullTeams.Where(t => t.Event.Format is EventFormat.Team).OrderBy(t => t.Name))
|
||||
@foreach (var team in fullTeams.Where(t => t.EventDefinition.EventFormat is EventFormat.Team).OrderBy(t => t.Name))
|
||||
{
|
||||
@await Html.PartialAsync("ScheduleTeamPartial", Tuple.Create(team, overlaps, Model.Item3.AbsentStudents))
|
||||
}
|
||||
@@ -58,16 +58,16 @@
|
||||
}
|
||||
|
||||
<h4>Individual</h4>
|
||||
<p><i>Use time for individual event or to <strong>work on a team event</strong> </i></p>
|
||||
<p><i>Use time for individual eventDefinition or to <strong>work on a team eventDefinition</strong> </i></p>
|
||||
<table class="table schedule">
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
|
||||
@foreach (var team in fullTeams.Where(t => t.Event.Format is EventFormat.Individual).OrderBy(t => t.Name))
|
||||
@foreach (var team in fullTeams.Where(t => t.EventDefinition.EventFormat is EventFormat.Individual).OrderBy(t => t.Name))
|
||||
{
|
||||
<td>
|
||||
<strong>@team.Captain?.FirstName</strong> (@team.Event.Name@if(team.Event.RegionalEvent){ @* <text> (<i>Regional</i>)</text> *@})
|
||||
<strong>@team.Captain?.FirstName</strong> (@team.EventDefinition.Name@if(team.EventDefinition.RegionalEvent){ @* <text> (<i>Regional</i>)</text> *@})
|
||||
</td>
|
||||
@* @await Html.PartialAsync("ScheduleTeamPartial", Tuple.Create(team, overlaps)) *@
|
||||
}
|
||||
@@ -86,14 +86,14 @@
|
||||
slot++;
|
||||
}
|
||||
|
||||
<h3>Missed team events for today</h3>
|
||||
<h3>Missed team eventDefinitions for today</h3>
|
||||
<table class="table schedule">
|
||||
@foreach (var student in allStudents.OrderBy(s => s.FirstName))
|
||||
{
|
||||
var studentMeetings = student.Teams.Where(t => allMeetingTeams.Any(mt => mt.Name == t.Name && mt.Students.Contains(student)));
|
||||
var studentMissed = student.Teams.Where(t => studentMeetings.All(mt => mt.Name != t.Name)).ToList();
|
||||
var studentMissedTeams = studentMissed.Where(t => t.Event.Format is EventFormat.Team);
|
||||
var studentMissedIndividual = studentMissed.Where(t => t.Event.Format is EventFormat.Individual);
|
||||
var studentMissedTeams = studentMissed.Where(t => t.EventDefinition.EventFormat is EventFormat.Team);
|
||||
var studentMissedIndividual = studentMissed.Where(t => t.EventDefinition.EventFormat is EventFormat.Individual);
|
||||
if (studentMissedTeams.Any())
|
||||
{
|
||||
<tr>
|
||||
@@ -121,7 +121,7 @@
|
||||
{
|
||||
private void GetTeamClass(Team team)
|
||||
{
|
||||
if (team.Event.RegionalEvent)
|
||||
if (team.EventDefinition.RegionalEvent)
|
||||
{
|
||||
@Html.Raw("regional")
|
||||
;
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
<tr>
|
||||
<td class="@{ GetTeamClass(team); } col-6" >
|
||||
<strong>@team</strong>
|
||||
@if (!string.IsNullOrEmpty(team.Event.EventAttributes()))
|
||||
@if (!string.IsNullOrEmpty(team.EventDefinition.EventAttributes()))
|
||||
{
|
||||
<i>(@team.Event.EventAttributes())</i>
|
||||
<i>(@team.EventDefinition.EventAttributes())</i>
|
||||
}
|
||||
<small><i>@team.Event.SemifinalistActivity</i></small>
|
||||
@* @if (team.Event.StatePresubmission)
|
||||
<small><i>@team.EventDefinition.SemifinalistActivity</i></small>
|
||||
@* @if (team.EventDefinition.StatePresubmission)
|
||||
{
|
||||
<small>(pre-submission)</small>
|
||||
}
|
||||
|
||||
@if (team.Event.StatePretesting)
|
||||
@if (team.EventDefinition.StatePretesting)
|
||||
{
|
||||
<small>(pre-testing)</small>
|
||||
} *@
|
||||
@@ -58,7 +58,7 @@
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@team.Event.Documentation
|
||||
@team.EventDefinition.Documentation
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
{
|
||||
private void GetTeamClass(Team team)
|
||||
{
|
||||
// if (team.Event.RegionalEvent)
|
||||
// if (team.EventDefinition.RegionalEvent)
|
||||
// {
|
||||
// @Html.Raw("regional")
|
||||
// ;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
@using System.Text.RegularExpressions
|
||||
@using Core.Entities
|
||||
@using Core.Utility
|
||||
@model Tuple<Team[], Student[], IDictionary<CompetitiveEvent, List<EventOccurrence>>>
|
||||
@model Tuple<Team[], Student[], IDictionary<EventDefinition, List<EventOccurrence>>>
|
||||
@{
|
||||
ViewData["Title"] = "Teams";
|
||||
var eventOccurrences = Model.Item3;
|
||||
}
|
||||
<div class="container nobrk pt-5">
|
||||
<h2>State Events</h2>
|
||||
<h2>State EventDefinitions</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -21,7 +21,7 @@
|
||||
@foreach (var team in Model.Item1)
|
||||
{
|
||||
var students = team.Students;
|
||||
@if (true @* team.Event.Format == EventFormat.Team *@)
|
||||
@if (true @* team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Team *@)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@@ -47,32 +47,32 @@
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (team.Event.StatePresubmission)
|
||||
@if (team.EventDefinition.StatePresubmission)
|
||||
{
|
||||
<text>Pre-submission due Friday, March 14</text>
|
||||
}
|
||||
@if (team.Event.StatePretesting)
|
||||
@if (team.EventDefinition.StatePretesting)
|
||||
{
|
||||
<text>Pre-testing Wednesday, April 2nd</text>
|
||||
}
|
||||
@if (team.Event.StatePreliminaryRound)
|
||||
@if (team.EventDefinition.StatePreliminaryRound)
|
||||
{
|
||||
<text>Preliminary and Semifinalist Rounds</text>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@team.Event.Documentation
|
||||
@team.EventDefinition.Documentation
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
}
|
||||
@* else if (team.Event.Format == EventFormat.Individual)
|
||||
@* else if (team.EventDefinition.EventDefinitionFormat == EventDefinitionEventDefinitionFormat.Individual)
|
||||
{
|
||||
foreach (var student in students)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@team.Event.Name - @student.FirstNameLastName (@student.RegionalID)
|
||||
@team.EventDefinition.Name - @student.FirstNameLastName (@student.RegionalID)
|
||||
</td>
|
||||
<td>@team.RegionalTimeSlot</td>
|
||||
<td></td>
|
||||
@@ -93,14 +93,14 @@
|
||||
<h3>@student.FirstNameLastName - @student.StateID</h3>
|
||||
<h4>TSA 2025 TN State Schedule</h4>
|
||||
<div class="row">
|
||||
<div class="col col-2">Events</div>
|
||||
<div class="col col-2">EventDefinitions</div>
|
||||
<div class="col">
|
||||
@foreach (var ev in student.Teams)
|
||||
{
|
||||
<div class="row">
|
||||
|
||||
<div class="col col-2">
|
||||
@if (ev.Event.Format is EventFormat.Team)
|
||||
@if (ev.EventDefinition.EventFormat is EventFormat.Team)
|
||||
{
|
||||
@ev.TeamNumber
|
||||
}
|
||||
@@ -118,7 +118,7 @@
|
||||
</text>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@ev.Event.SemifinalistActivity
|
||||
@ev.EventDefinition.SemifinalistActivity
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -128,7 +128,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Time</td>
|
||||
<td>Event</td>
|
||||
<td>EventDefinition</td>
|
||||
<td></td>
|
||||
<td>Location</td>
|
||||
</tr>
|
||||
@@ -137,9 +137,9 @@
|
||||
@foreach (var date in
|
||||
eventOccurrences
|
||||
.Where(eo =>
|
||||
student.Teams.Select(t => t.Event).Any(a => a == eo.Key)
|
||||
|| eo.Key == CompetitiveEvent.GeneralSchedule
|
||||
|| (eo.Key == CompetitiveEvent.VotingDelegates && student.VotingDelegate))
|
||||
student.Teams.Select(t => t.EventDefinition).Any(a => a == eo.Key)
|
||||
|| eo.Key == EventDefinition.GeneralSchedule
|
||||
|| (eo.Key == EventDefinition.VotingDelegates && student.VotingDelegate))
|
||||
.SelectMany(eo => eo.Value.Select(v => Tuple.Create(v, eo.Key)))
|
||||
.GroupBy(de => de.Item1.Date + ", " + de.Item1.StartTime.DayOfWeek)
|
||||
.OrderBy(d => d.Key)
|
||||
@@ -152,8 +152,8 @@
|
||||
// filter out occurrences where non-captain
|
||||
.Where(de =>
|
||||
!de.Item1.SignupSubmitPickup
|
||||
|| de.Item2.Format is EventFormat.Individual
|
||||
|| student.Teams.Any(t => t.Captain == student && t.Event == de.Item2)
|
||||
|| de.Item2.EventFormat is EventFormat.Individual
|
||||
|| student.Teams.Any(t => t.Captain == student && t.EventDefinition == de.Item2)
|
||||
)
|
||||
.OrderBy(de => de.Item1.StartTime);
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
<td>@eventOccurrence.Item1.Time</td>
|
||||
<td>@eventOccurrence.Item2.Name</td>
|
||||
<td>@eventOccurrence.Item1.Name
|
||||
@if (eventOccurrence.Item1.Name.Contains("Pick") && eventOccurrence.Item2.Format is EventFormat.Team)
|
||||
@if (eventOccurrence.Item1.Name.Contains("Pick") && eventOccurrence.Item2.EventFormat is EventFormat.Team)
|
||||
{
|
||||
<br/>
|
||||
<text>or coordinate with a teammate</text>
|
||||
@@ -210,17 +210,17 @@
|
||||
<tr><td colspan="4"><strong>@eventsForDate.Key.ToString("MMMM d") </strong> </td></tr>
|
||||
@foreach (var occurrence in eventsForDate.OrderBy(o => o.Item1.StartTime))
|
||||
{
|
||||
var teams = Model.Item1.Where(t => t.Event == occurrence.Item2);
|
||||
if (occurrence.Item2 != CompetitiveEvent.GeneralSchedule && occurrence.Item2 != CompetitiveEvent.VotingDelegates && !teams.Any())
|
||||
var teams = Model.Item1.Where(t => t.EventDefinition == occurrence.Item2);
|
||||
if (occurrence.Item2 != EventDefinition.GeneralSchedule && occurrence.Item2 != EventDefinition.VotingDelegates && !teams.Any())
|
||||
continue;
|
||||
<tr>
|
||||
<td>@occurrence.Item1.Time</td>
|
||||
<td>
|
||||
@if (occurrence.Item2 == CompetitiveEvent.GeneralSchedule)
|
||||
@if (occurrence.Item2 == EventDefinition.GeneralSchedule)
|
||||
{
|
||||
<text>Everyone</text>
|
||||
}
|
||||
else if (occurrence.Item2 == CompetitiveEvent.VotingDelegates)
|
||||
else if (occurrence.Item2 == EventDefinition.VotingDelegates)
|
||||
{
|
||||
<text>Voting Delegates - @string.Join(", ", Model.Item2.Where(stu => stu.VotingDelegate).Select(stu => stu.FirstName))</text>
|
||||
}
|
||||
@@ -287,7 +287,7 @@
|
||||
@foreach (var t in assignments)
|
||||
{
|
||||
<td>
|
||||
@if (t.Event.Format != EventFormat.Individual)
|
||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
||||
{
|
||||
@t.Name
|
||||
@if (t.Captain == student)
|
||||
@@ -297,14 +297,14 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@t.Event.Name
|
||||
@t.EventDefinition.Name
|
||||
}
|
||||
@{
|
||||
if (t.Event.Format == EventFormat.Individual)
|
||||
if (t.EventDefinition.EventFormat == EventFormat.Individual)
|
||||
{
|
||||
<sup class="activity">(ind)</sup>
|
||||
}
|
||||
if (t.Event.OnSiteActivity)
|
||||
if (t.EventDefinition.OnSiteActivity)
|
||||
{
|
||||
<sup class="activity">(act)</sup>
|
||||
}
|
||||
@@ -340,7 +340,7 @@
|
||||
@foreach (var t in assignments)
|
||||
{
|
||||
<p>
|
||||
@if (t.Event.Format != EventFormat.Individual)
|
||||
@if (t.EventDefinition.EventFormat != EventFormat.Individual)
|
||||
{
|
||||
@t.Name
|
||||
@if (t.Captain == student)
|
||||
@@ -350,7 +350,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@t.Event.Name
|
||||
@t.EventDefinition.Name
|
||||
}
|
||||
</p>
|
||||
}
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
{
|
||||
<div class="container nobrk pt-5">
|
||||
<p><i>@DateTime.Today.ToShortDateString()</i></p>
|
||||
<h2><i>TSA teams and events:</i> @s.FirstNameLastName </h2>
|
||||
<h2><i>TSA teams and eventDefinitions:</i> @s.FirstNameLastName </h2>
|
||||
|
||||
@foreach (var team in
|
||||
s.Teams.OrderByDescending(t => t.Event.Format == EventFormat.Team)
|
||||
.ThenByDescending(t => t.Event.LevelOfEffort)
|
||||
s.Teams.OrderByDescending(t => t.EventDefinition.EventFormat == EventFormat.Team)
|
||||
.ThenByDescending(t => t.EventDefinition.LevelOfEffort)
|
||||
.ThenByDescending(t => t.Name))
|
||||
{
|
||||
var evt = team.Event;
|
||||
var evt = team.EventDefinition;
|
||||
<div>
|
||||
@if (evt.RegionalEvent)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<i>Regional Event</i>
|
||||
<i>Regional EventDefinition</i>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
<h5>@evt.Name @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)</h5>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@if (evt.Format is EventFormat.Team)
|
||||
@if (evt.EventFormat is EventFormat.Team)
|
||||
{
|
||||
<html>
|
||||
<strong>Teammates</strong><br/>
|
||||
@@ -40,7 +40,7 @@
|
||||
else
|
||||
{
|
||||
<html>
|
||||
<strong>@evt.Format</strong>
|
||||
<strong>@evt.EventFormat</strong>
|
||||
</html>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
@using Core.Entities
|
||||
@using Core.Utility
|
||||
@model Tuple<CompetitiveEvent[], Student[], EventStudentPicks[], Team[], AssignmentParameters>
|
||||
@model Tuple<EventDefinition[], Student[], EventStudentRankings[], Team[], AssignmentParameters>
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
var maxStudentPicks = Model.Item3.MaxBy(picks => picks.StudentPicks.Count).StudentPicks.Count;
|
||||
var maxStudentPicks = Model.Item3.MaxBy(picks => picks.StudentRankings.Count).StudentRankings.Count;
|
||||
var parameters = Model.Item5;
|
||||
var unassignedEvents = Model.Item1.Where(e => Model.Item4.All(t => t.Event != e));
|
||||
var unassignedEvents = Model.Item1.Where(e => Model.Item4.All(t => t.EventDefinition != e));
|
||||
|
||||
}
|
||||
|
||||
@@ -27,16 +27,17 @@
|
||||
{
|
||||
var assignments
|
||||
= Model.Item4.Where(ea => ea.Students.Contains(student))
|
||||
.Select(ea => ea.Event)
|
||||
.Select(ea => ea.EventDefinition)
|
||||
.Distinct()
|
||||
.OrderBy(e =>
|
||||
@* .OrderBy(e =>
|
||||
{
|
||||
|
||||
var r = student.RankedEventPicks.IndexOf(e);
|
||||
r = r >= 0 ? r : 10;
|
||||
//r = r * (4 - e.LevelOfEffort.Value);
|
||||
return r;
|
||||
});
|
||||
}) *@
|
||||
;
|
||||
|
||||
<tr>
|
||||
<td><strong>@student.FirstName</strong></td>
|
||||
@@ -44,11 +45,12 @@
|
||||
|
||||
@foreach (var evt in assignments)
|
||||
{
|
||||
var h = student.RankedEventPicks.IndexOf(evt) + 1;
|
||||
<td class="@{ GetOrderClass(h); }">
|
||||
@* var h = student.RankedEventPicks.IndexOf(evt) + 1; *@
|
||||
@* @{ class="GetOrderClass(h);" } *@
|
||||
<td class="">
|
||||
@evt.ShortName @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
||||
@{
|
||||
if (evt.Format == EventFormat.Individual)
|
||||
if (evt.EventFormat == EventFormat.Individual)
|
||||
{
|
||||
<sup class="activity">(ind)</sup>
|
||||
}
|
||||
@@ -74,9 +76,9 @@
|
||||
<thead>
|
||||
<tr><td>Teams</td></tr>
|
||||
</thead>
|
||||
@foreach (var evt in Model.Item1.OrderByDescending(e => e.Format is EventFormat.Team))
|
||||
@foreach (var evt in Model.Item1.OrderByDescending(e => e.EventFormat is EventFormat.Team))
|
||||
{
|
||||
var assignments = Model.Item4.FirstOrDefault(i => i.Event == evt);
|
||||
var assignments = Model.Item4.FirstOrDefault(i => i.EventDefinition == evt);
|
||||
@if (assignments == null)
|
||||
{
|
||||
continue;
|
||||
@@ -86,7 +88,7 @@
|
||||
<td class="table-primary"><strong>@evt.Name</strong></td>
|
||||
<td colspan="1">
|
||||
@Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
||||
@if (evt.Format is EventFormat.Individual)
|
||||
@if (evt.EventFormat is EventFormat.Individual)
|
||||
{
|
||||
<text>(ind)</text>
|
||||
}
|
||||
@@ -120,7 +122,7 @@
|
||||
@{
|
||||
var unassigned = string.Join(", ", unassignedEvents.Select(e => e.Name));
|
||||
}
|
||||
<td colspan="8">Unassigned Events: @unassigned</td>
|
||||
<td colspan="8">Unassigned EventDefinitions: @unassigned</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -151,7 +153,7 @@
|
||||
continue;
|
||||
<td class="@{ GetOrderClass(h); }">
|
||||
@evt.ShortName @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
||||
@if (evt.Format == EventFormat.Individual)
|
||||
@if (evt.EventFormat == EventFormat.Individual)
|
||||
{
|
||||
<sup class="activity">(ind)</sup>
|
||||
}
|
||||
@@ -171,11 +173,11 @@
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><td>Event</td><td>Level of Effort</td><td>Individual</td><td>Regional</td><td>On-site Activity</td><td>Team Size</td><td>Max Team Count</td></tr>
|
||||
<tr><td>EventDefinition</td><td>Level of Effort</td><td>Individual</td><td>Regional</td><td>On-site Activity</td><td>Team Size</td><td>Max Team Count</td></tr>
|
||||
</thead>
|
||||
@foreach (var evt in Model.Item1)
|
||||
{
|
||||
var esp = Model.Item3.FirstOrDefault(i => i.Event == evt);
|
||||
var esp = Model.Item3.FirstOrDefault(i => i.EventDefinition == evt);
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
@@ -183,7 +185,7 @@
|
||||
</td>
|
||||
|
||||
<td>@Html.Partial("EffortStarsPartial", evt.LevelOfEffort)</td>
|
||||
<td>@if (evt.Format is EventFormat.Individual) { <text>ind</text> }</td>
|
||||
<td>@if (evt.EventFormat is EventFormat.Individual) { <text>ind</text> }</td>
|
||||
<td>@if (evt.RegionalEvent) { <text>reg</text> }</td>
|
||||
<td>@if (evt.OnSiteActivity) { <text>act</text> }</td>
|
||||
<td style="nowrap">@evt.TeamSize</td>
|
||||
@@ -191,7 +193,7 @@
|
||||
|
||||
@for (var i = 0; i < maxStudentPicks; i++)
|
||||
{
|
||||
var d = esp?.StudentPicks.Skip(i).FirstOrDefault();
|
||||
var d = esp?.StudentRankings.Skip(i).FirstOrDefault();
|
||||
<td class="@{GetOrderClass(d?.Item2 ?? int.MaxValue);}">@d?.Item1.FirstName</td>
|
||||
}
|
||||
</tr>
|
||||
|
||||
@@ -16,15 +16,16 @@
|
||||
{
|
||||
var assignments
|
||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
||||
.Select(ea => ea.Event)
|
||||
.Select(ea => ea.EventDefinition)
|
||||
.Distinct()
|
||||
.OrderBy(e =>
|
||||
@* .OrderBy(e =>
|
||||
{
|
||||
var r = student.RankedEventPicks.IndexOf(e);
|
||||
r = r >= 0 ? r : 10;
|
||||
//r = r * (4 - e.LevelOfEffort.Value);
|
||||
return r;
|
||||
}).ToList();
|
||||
}) *@
|
||||
.ToList();
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
@@ -34,11 +35,12 @@
|
||||
|
||||
@foreach (var evt in assignments)
|
||||
{
|
||||
var h = student.RankedEventPicks.IndexOf(evt) + 1;
|
||||
<td class="@{ GetOrderClass(h); }">
|
||||
@* var h = student.RankedEventPicks.IndexOf(evt) + 1; *@
|
||||
@* @{ GetOrderClass(h); } *@
|
||||
<td class="">
|
||||
@evt.ShortName @Html.Partial("EffortStarsPartial", evt.LevelOfEffort)
|
||||
@{
|
||||
if (evt.Format == EventFormat.Individual)
|
||||
if (evt.EventFormat == EventFormat.Individual)
|
||||
{
|
||||
<sup class="activity">(ind)</sup>
|
||||
}
|
||||
@@ -63,7 +65,7 @@
|
||||
<th>Team</th>
|
||||
<th>Teammates</th>
|
||||
</tr>
|
||||
@foreach (var student in Model.Item1.OrderBy(s => s.Name))
|
||||
@foreach (var student in Model.Item1.RankBy(s => s.Name))
|
||||
{
|
||||
var teams = student.Teams;
|
||||
|
||||
@@ -87,11 +89,11 @@
|
||||
{
|
||||
<span>(Cpt .)</span>
|
||||
}
|
||||
if (!team.Event.InterviewOrPresentation)
|
||||
if (!team.EventDefinition.InterviewOrPresentation)
|
||||
{
|
||||
ind.Add("a");
|
||||
}
|
||||
if (team.Event.Format is EventFormat.Individual)
|
||||
if (team.EventDefinition.EventDefinitionFormat is EventDefinitionEventDefinitionFormat.Individual)
|
||||
{
|
||||
ind.Add("i");
|
||||
}
|
||||
@@ -102,16 +104,16 @@
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@string.Join(", ", team.Students.Where(s => s != student).OrderByDescending(s => s.TsaYear + s.Grade).Select(s => s.FirstName))
|
||||
@string.Join(", ", team.Students.Where(s => s != student).RankByDescending(s => s.TsaYear + s.Grade).Select(s => s.FirstName))
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
<p>
|
||||
(a) denotes an event that has activity other than interview or presentation at state
|
||||
(a) denotes an eventDefinition that has activity other than interview or presentation at state
|
||||
<br />
|
||||
(i) denotes an individual event
|
||||
(i) denotes an individual eventDefinition
|
||||
</p> *@
|
||||
|
||||
|
||||
@@ -125,15 +127,16 @@
|
||||
{
|
||||
var assignments
|
||||
= student.Teams.Where(ea => ea.Students.Contains(student))
|
||||
.Select(ea => ea.Event)
|
||||
.Select(ea => ea.EventDefinition)
|
||||
.Distinct()
|
||||
.OrderBy(e =>
|
||||
@* .OrderBy(e =>
|
||||
{
|
||||
var r = student.RankedEventPicks.IndexOf(e);
|
||||
r = r >= 0 ? r : 10;
|
||||
//r = r * (4 - e.LevelOfEffort.Value);
|
||||
return r;
|
||||
}).ToList();
|
||||
}) *@
|
||||
.ToList();
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
@@ -142,11 +145,10 @@
|
||||
|
||||
@foreach (var evt in assignments)
|
||||
{
|
||||
var h = student.RankedEventPicks.IndexOf(evt) + 1;
|
||||
<td>
|
||||
@evt.Name
|
||||
@{
|
||||
if (evt.Format == EventFormat.Individual)
|
||||
if (evt.EventFormat == EventFormat.Individual)
|
||||
{
|
||||
<sup class="activity">(individual)</sup>
|
||||
}
|
||||
@@ -170,7 +172,7 @@
|
||||
|
||||
private void GetTeamClass(Team team)
|
||||
{
|
||||
// if (team.Event.RegionalEvent)
|
||||
// if (team.EventDefinition.RegionalEvent)
|
||||
// {
|
||||
// @Html.Raw("regional");
|
||||
// }
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<tr> <th></th>
|
||||
@foreach (var team in Model.Item1)
|
||||
{
|
||||
if (team.Name == team.Event.Name)
|
||||
if (team.Name == team.EventDefinition.Name)
|
||||
{
|
||||
<td style="writing-mode: vertical-rl;text-orientation:sideways">
|
||||
@team.Event.ShortName
|
||||
@team.EventDefinition.ShortName
|
||||
</td>
|
||||
}
|
||||
else
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
}
|
||||
|
||||
<table>
|
||||
@foreach (var team in Model.Item1.Where(t => t.Event.Documentation.Contains("Port")))
|
||||
@foreach (var team in Model.Item1.Where(t => t.EventDefinition.Documentation.Contains("Port")))
|
||||
{
|
||||
<tr>
|
||||
<td>@team.Name @if (team.Event.Format is EventFormat.Individual)
|
||||
<td>@team.Name @if (team.EventDefinition.EventFormat is EventFormat.Individual)
|
||||
{
|
||||
<text>(ind)</text>
|
||||
}
|
||||
</td><td>@team.Event.Documentation</td>
|
||||
</td><td>@team.EventDefinition.Documentation</td>
|
||||
@foreach(var student in @team.Students.OrderByDescending(s => s.TsaYear + s.Grade))
|
||||
{
|
||||
<td>@student.FirstName</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
@foreach (var student in Model.Item1.SelectMany(t => t.Students).Distinct().Where(s => s.Teams.Any(t => t.Event.Name.Contains("Port"))))
|
||||
@foreach (var student in Model.Item1.SelectMany(t => t.Students).Distinct().Where(s => s.Teams.Any(t => t.EventDefinition.Name.Contains("Port"))))
|
||||
{
|
||||
<tr>
|
||||
<td>@student.FirstName</td>
|
||||
@@ -34,9 +34,9 @@
|
||||
@{
|
||||
var ind = false;
|
||||
}
|
||||
@foreach (var team in Model.Item1.OrderByDescending(t => t.Event.Format is EventFormat.Team))
|
||||
@foreach (var team in Model.Item1.OrderByDescending(t => t.EventDefinition.EventFormat is EventFormat.Team))
|
||||
{
|
||||
@if (!ind && team.Event.Format is EventFormat.Individual)
|
||||
@if (!ind && team.EventDefinition.EventFormat is EventFormat.Individual)
|
||||
{
|
||||
<tr>
|
||||
<td><hr/></td><td>Individual</td><td><hr/></td>
|
||||
@@ -46,17 +46,17 @@
|
||||
<tr class="table-primary">
|
||||
<td class="table-primary"><strong>@team.Name</strong></td>
|
||||
<td colspan="1">
|
||||
@* @Html.Partial("EffortStarsPartial", team.Event.LevelOfEffort) *@
|
||||
@* @if (team.Event.RegionalEvent)
|
||||
@* @Html.Partial("EffortStarsPartial", team.EventDefinition.LevelOfEffort) *@
|
||||
@* @if (team.EventDefinition.RegionalEventDefinition)
|
||||
{
|
||||
<text>(reg)</text>
|
||||
}
|
||||
|
||||
@if (team.Event.OnSiteActivity)
|
||||
@if (team.EventDefinition.OnSiteActivity)
|
||||
{
|
||||
<text>(act)</text>
|
||||
} *@
|
||||
@team.Event.TeamSize
|
||||
@team.EventDefinition.TeamSize
|
||||
</td>
|
||||
@*<td style="nowrap">@evt.MaxTeamCountState</td>*@
|
||||
@foreach (var student in team.Students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
||||
@@ -71,9 +71,9 @@
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Event</td>
|
||||
<td>EventDefinition</td>
|
||||
<td>Pre-submission</td>
|
||||
<td>RegionalNotes</td>
|
||||
<td>Notes</td>
|
||||
<td>Team Members</td>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -87,19 +87,19 @@
|
||||
|
||||
</tr>
|
||||
|
||||
@foreach (var student in students.OrderByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
||||
@foreach (var student in students.RankByDescending(s => (s.Grade + s.TsaYear) * (team.Captain == s ? 2 : 1)).ThenBy(s => s.FirstNameLastName))
|
||||
{
|
||||
<td>@student.FirstNameLastName @if (team.Captain == student) { <text>(Cpt.)</text>}</td>
|
||||
}
|
||||
|
||||
<tr>
|
||||
<td class="event-desc" colspan="4">Team Size: @team.Event.TeamSize, Max Teams: @team.Event.MaxTeamCountState
|
||||
<td class="eventDefinition-desc" colspan="4">Team Size: @team.EventDefinition.TeamSize, Max Teams: @team.EventDefinition.MaxTeamCountState
|
||||
@{
|
||||
if (!team.Event.InterviewOrPresentation)
|
||||
if (!team.EventDefinition.InterviewOrPresentation)
|
||||
{
|
||||
<span class="activity"> (a)</span>
|
||||
}
|
||||
if (team.Event.Format is EventFormat.Individual)
|
||||
if (team.EventDefinition.EventDefinitionFormat is EventDefinitionEventDefinitionFormat.Individual)
|
||||
{
|
||||
<span class="activity"> (i)</span>
|
||||
}
|
||||
@@ -109,9 +109,9 @@
|
||||
}
|
||||
</table>
|
||||
<p>
|
||||
(a) denotes an event that has activity other than interview or presentation at state
|
||||
(a) denotes an eventDefinition that has activity other than interview or presentation at state
|
||||
<br />
|
||||
(i) denotes an individual event
|
||||
(i) denotes an individual eventDefinition
|
||||
</p>
|
||||
*@
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
|
||||
private void GetTeamClass(Team team)
|
||||
{
|
||||
// if (team.Event.RegionalEvent)
|
||||
// if (team.EventDefinition.RegionalEvent)
|
||||
// {
|
||||
// @Html.Raw("regional");
|
||||
// }
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Events">Events</a>
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Events">EventDefinitions</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="StudentEvents">Student Event Picks</a>
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="StudentEvents">Student EventDefinition Picks</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="StudentEventHandout">Student Event Handout</a>
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="StudentEventHandout">Student EventDefinition Handout</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Teams">Teams</a>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="bootstrap" Version="5.3.2" />
|
||||
<PackageReference Include="RandomNameGenerator" Version="1.0.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebApp
|
||||
{
|
||||
public class ChapterSettings
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public required string ShortName { get; set; }
|
||||
public required string RegionalId { get; set; }
|
||||
public required string StateId { get; set; }
|
||||
public required string NationalId { get; set; }
|
||||
public required string CompetitionYear { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
|
||||
<link href="@Assets["_content/MudBlazor/MudBlazor.min.css"]" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="WebApp.styles.css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes @rendermode="InteractiveServer" />
|
||||
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<script src="@Assets["_content/MudBlazor/MudBlazor.min.js"]"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<MudThemeProvider />
|
||||
<MudPopoverProvider />
|
||||
|
||||
<div class="page">
|
||||
|
||||
<div class="sidebar">
|
||||
<NavMenu/>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
@* <div class="top-row px-4">
|
||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||
</div> *@
|
||||
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
@@ -0,0 +1,96 @@
|
||||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-row.auth ::deep a:first-child {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.top-row, article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">WebApp</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="events">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Events
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="students">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Students
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="teams">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Teams
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="import">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Import
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
.navbar-toggler {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
width: 3.5rem;
|
||||
height: 2.5rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.navbar-toggler:checked {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
height: 3.5rem;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.bi {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link {
|
||||
color: #d7d7d7;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-toggler:checked ~ .nav-scrollable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
@page "/events/create"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Create</PageTitle>
|
||||
|
||||
<h1>Create</h1>
|
||||
|
||||
<h2>EventDefinition</h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<EditForm method="post" Model="EventDefinition" OnValidSubmit="AddEventDefinition" FormName="create" 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>
|
||||
|
||||
<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()
|
||||
{
|
||||
context.Events.Add(EventDefinition);
|
||||
await context.SaveChangesAsync();
|
||||
NavigationManager.NavigateTo("/events");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
@page "/events/delete"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Delete</PageTitle>
|
||||
|
||||
<h1>Delete</h1>
|
||||
|
||||
<p>Are you sure you want to delete this?</p>
|
||||
<div>
|
||||
<h2>EventDefinition</h2>
|
||||
<hr />
|
||||
@if (eventdefinition is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else {
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Name</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Name</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Short Name</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.ShortName</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Event Format</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.EventFormat</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Min Team Size</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.MinTeamSize</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Max Team Size</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.MaxTeamSize</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">SemifinalistActivity</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.SemifinalistActivity</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Notes</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Notes</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">MaxTeamCountState</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.MaxTeamCountState</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">RegionalEvent</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.RegionalEvent</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">RegionalPresubmit</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.RegionalPresubmit</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">StatePresubmission</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.StatePresubmission</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">StatePretesting</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.StatePretesting</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">StatePreliminaryRound</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.StatePreliminaryRound</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Documentation</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Documentation</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Eligibility</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Eligibility</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Theme</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Theme</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Description</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Description</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">LevelOfEffort</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.LevelOfEffort</dd>
|
||||
</dl>
|
||||
<EditForm method="post" Model="eventdefinition" OnValidSubmit="DeleteEventDefinition" FormName="delete" Enhance>
|
||||
<button type="submit" class="btn btn-danger" disabled="@(eventdefinition is null)">Delete</button> |
|
||||
<a href="/events">Back to List</a>
|
||||
</EditForm>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private EventDefinition? eventdefinition;
|
||||
|
||||
[SupplyParameterFromQuery]
|
||||
private int Id { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
eventdefinition = await context.Events.FirstOrDefaultAsync(m => m.Id == Id);
|
||||
|
||||
if (eventdefinition is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteEventDefinition()
|
||||
{
|
||||
context.Events.Remove(eventdefinition!);
|
||||
await context.SaveChangesAsync();
|
||||
NavigationManager.NavigateTo("/events");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
@page "/events/details"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Details</PageTitle>
|
||||
|
||||
<h1>Details</h1>
|
||||
|
||||
<div>
|
||||
<h2>EventDefinition</h2>
|
||||
<hr />
|
||||
@if (eventdefinition is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else {
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Name</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Name</dd>
|
||||
<dt class="col-sm-2">ShortName</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.ShortName</dd>
|
||||
<dt class="col-sm-2">EventFormat</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.EventFormat</dd>
|
||||
<dt class="col-sm-2">MinTeamSize</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.MinTeamSize</dd>
|
||||
<dt class="col-sm-2">MaxTeamSize</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.MaxTeamSize</dd>
|
||||
<dt class="col-sm-2">SemifinalistActivity</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.SemifinalistActivity</dd>
|
||||
<dt class="col-sm-2">Notes</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Notes</dd>
|
||||
<dt class="col-sm-2">MaxTeamCountState</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.MaxTeamCountState</dd>
|
||||
<dt class="col-sm-2">RegionalEvent</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.RegionalEvent</dd>
|
||||
<dt class="col-sm-2">RegionalPresubmit</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.RegionalPresubmit</dd>
|
||||
<dt class="col-sm-2">StatePresubmission</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.StatePresubmission</dd>
|
||||
<dt class="col-sm-2">StatePretesting</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.StatePretesting</dd>
|
||||
<dt class="col-sm-2">StatePreliminaryRound</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.StatePreliminaryRound</dd>
|
||||
<dt class="col-sm-2">Documentation</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Documentation</dd>
|
||||
<dt class="col-sm-2">Eligibility</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Eligibility</dd>
|
||||
<dt class="col-sm-2">Theme</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Theme</dd>
|
||||
<dt class="col-sm-2">Description</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.Description</dd>
|
||||
<dt class="col-sm-2">LevelOfEffort</dt>
|
||||
<dd class="col-sm-10">@eventdefinition.LevelOfEffort</dd>
|
||||
</dl>
|
||||
<div>
|
||||
<a href="@($"/eventdefinitions/edit?id={eventdefinition.Id}")">Edit</a> |
|
||||
<a href="@($"/events")">Back to List</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private EventDefinition? eventdefinition;
|
||||
|
||||
[SupplyParameterFromQuery]
|
||||
private int Id { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
eventdefinition = await context.Events.FirstOrDefaultAsync(m => m.Id == Id);
|
||||
|
||||
if (eventdefinition is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
@page "/events/edit"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Edit</PageTitle>
|
||||
|
||||
<h1>Edit</h1>
|
||||
|
||||
<h2>EventDefinition</h2>
|
||||
<hr />
|
||||
@if (EventDefinition is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<EditForm method="post" Model="EventDefinition" OnValidSubmit="UpdateEventDefinition" FormName="edit" Enhance>
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary role="alert"/>
|
||||
<input type="hidden" name="EventDefinition.Id" value="@EventDefinition.Id" />
|
||||
<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">Save</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div>
|
||||
<a href="/events">Back to List</a>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromQuery]
|
||||
private int Id { get; set; }
|
||||
|
||||
[SupplyParameterFromForm]
|
||||
private EventDefinition? EventDefinition { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
EventDefinition ??= await context.Events.FirstOrDefaultAsync(m => m.Id == Id);
|
||||
|
||||
if (EventDefinition is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
}
|
||||
|
||||
// To protect from overposting attacks, enable the specific properties you want to bind to.
|
||||
// For more information, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.
|
||||
private async Task UpdateEventDefinition()
|
||||
{
|
||||
context.Attach(EventDefinition!).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!EventDefinitionExists(EventDefinition!.Id))
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo("/events");
|
||||
}
|
||||
|
||||
private bool EventDefinitionExists(int id)
|
||||
{
|
||||
return context.Events.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@page "/events/descriptions"
|
||||
@inject IConfiguration Configuration
|
||||
@inject AppDbContext Context
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>TSA Events @Configuration["ChapterSettings:CompetitionYear"]</PageTitle>
|
||||
|
||||
<h1>TSA Events @Configuration["ChapterSettings:CompetitionYear"]</h1>
|
||||
|
||||
@if (_events == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
@foreach (var evt in _events)
|
||||
{
|
||||
<div class="container nobrk">
|
||||
@if (evt.RegionalEvent)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<i>Regional Event</i>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div div class="row">
|
||||
<div class="col-4">
|
||||
<h5>@evt.Name</h5>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
@if (evt.EventFormat is EventFormat.Team)
|
||||
{
|
||||
<html><strong>@evt.EventFormat</strong><br/>Size: <strong>@evt.TeamSize</strong></html>
|
||||
}
|
||||
else
|
||||
{
|
||||
<html>
|
||||
<strong>@evt.EventFormat</strong>
|
||||
</html>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
Eligibility: @evt.Eligibility
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<strong> Effort</strong>: @evt.LevelOfEffort
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<strong>Activity</strong>: @evt.SemifinalistActivity
|
||||
</div>
|
||||
</div>
|
||||
<div div class="row mt-3">
|
||||
<div class="col">@evt.Description</div></div>
|
||||
@if (!string.IsNullOrEmpty(evt.Theme))
|
||||
{
|
||||
<div div class="row mt-2">
|
||||
<div class="col-3 text-center"><i>Theme for 2025-26:</i></div>
|
||||
<div class="col" style="white-space:pre-wrap;">@evt.Theme</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(evt.Documentation))
|
||||
{
|
||||
<div div class="row mt-2">
|
||||
<div class="col-3 text-center"><i>Materials:</i></div>
|
||||
<div class="col">@evt.Documentation</div>
|
||||
</div>
|
||||
}
|
||||
<hr/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@code {
|
||||
private EventDefinition[]? _events = null;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_events = await Context.Events.OrderBy(e => e.Name).Where(e => e.Name != "Chapter Team").ToArrayAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
@page "/events"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@inject AppDbContext Context
|
||||
|
||||
|
||||
<PageTitle>Events - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Events</MudText>
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Create" Href="events/create">Create New</MudButton>
|
||||
|
||||
|
||||
<MudDataGrid T="EventDefinition" ServerData="ServerReload" @ref="_dataGrid" Filterable="true" RowsPerPage="50">
|
||||
<Columns>
|
||||
<PropertyColumn Property="@(e => e.Name)" Title="Event Name" Sortable="true" />
|
||||
<PropertyColumn Property="@(e => e.EventFormat)" Title="Event Format" />
|
||||
<PropertyColumn Property="@(e => e.LevelOfEffort)" Title="Level of Effort" />
|
||||
<PropertyColumn Property="@(e => e.SemifinalistActivity)" Title="On-site Activity" />
|
||||
<PropertyColumn Property="@(e => e.RegionalEvent)" Title="Regional Event" />
|
||||
<TemplateColumn Title="Team Size" CellStyle="white-space:nowrap">
|
||||
<CellTemplate>
|
||||
[@context.Item.MinTeamSize - @context.Item.MaxTeamSize]
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn Title="Teams State #">
|
||||
<CellTemplate>
|
||||
@context.Item.MaxTeamCountState
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
<MudStack Row>
|
||||
<MudButtonGroup Size="Size.Small">
|
||||
<MudTooltip Text="Details">
|
||||
<MudIconButton Href="@($"/events/details?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Description">Details</MudIconButton>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Edit">
|
||||
<MudIconButton Href="@($"/events/edit?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Edit">Edit</MudIconButton>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Delete">
|
||||
<MudIconButton Href="@($"/events/delete?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Delete" Color="@Color.Warning">Delete</MudIconButton>
|
||||
</MudTooltip>
|
||||
</MudButtonGroup>
|
||||
</MudStack>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
<PagerContent>
|
||||
<MudDataGridPager T="EventDefinition"></MudDataGridPager>
|
||||
</PagerContent>
|
||||
</MudDataGrid>
|
||||
|
||||
@*
|
||||
<QuickGrid Class="table" Items="context.Events">
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.Name" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.EventFormat" />
|
||||
@* <PropertyColumn Property="eventdefinition => eventdefinition.MinTeamSize" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.MaxTeamSize" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.SemifinalistActivity" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.Notes" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.MaxTeamCountState" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.RegionalEvent" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.RegionalPresubmit" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.StatePresubmission" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.StatePretesting" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.StatePreliminaryRound" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.Documentation" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.Eligibility" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.Theme" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.Description" />
|
||||
<PropertyColumn Property="eventdefinition => eventdefinition.LevelOfEffort" />
|
||||
*@
|
||||
|
||||
@code {
|
||||
MudDataGrid<EventDefinition> _dataGrid = null!;
|
||||
|
||||
private async Task<GridData<EventDefinition>> ServerReload(GridState<EventDefinition> state)
|
||||
{
|
||||
|
||||
var query = Context.Events.Where(state.FilterDefinitions).OrderBy(state.SortDefinitions);
|
||||
|
||||
var totalItems = await query.CountAsync();
|
||||
var pagedData = await query.Skip(state.Page * state.PageSize).Take(state.PageSize).ToArrayAsync();
|
||||
|
||||
return new GridData<EventDefinition>
|
||||
{
|
||||
TotalItems = totalItems,
|
||||
Items = pagedData
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@page "/"
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h2">TSA Chapter Organizer</MudText>
|
||||
<MudText Typo="Typo.h3">@Configuration["ChapterSettings:Name"]</MudText>
|
||||
|
||||
<MudLink Href="events">
|
||||
<MudCard>
|
||||
<MudCardHeader>Events</MudCardHeader>
|
||||
</MudCard>
|
||||
</MudLink>
|
||||
@@ -0,0 +1,113 @@
|
||||
@page "/import"
|
||||
@using Core.Parsers
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@inject AppDbContext Context
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Import Data</PageTitle>
|
||||
|
||||
<h1>Import Data</h1>
|
||||
|
||||
<h3>Events</h3>
|
||||
<InputFile OnChange="UploadEvents"></InputFile>
|
||||
<text>@_events?.Length Events</text>
|
||||
<button class="btn btn-primary" @onclick="SaveEvents">Save to Database</button>
|
||||
<br/>
|
||||
|
||||
<h3>Students</h3>
|
||||
<InputFile OnChange="UploadStudents"></InputFile>
|
||||
<text>@_students?.Length Students</text>
|
||||
<button class="btn btn-primary" @onclick="SaveStudents">Save to Database</button>
|
||||
|
||||
@code {
|
||||
private EventDefinition[]? _events;
|
||||
private Student[]? _students;
|
||||
|
||||
async Task UploadEvents(InputFileChangeEventArgs arg)
|
||||
{
|
||||
await GetStreamReaderFromInputFile(arg, reader =>
|
||||
{
|
||||
var eventDefinitionParser = new EventDefinitionParser(reader);
|
||||
_events = eventDefinitionParser.Parse();
|
||||
});
|
||||
}
|
||||
|
||||
async Task SaveEvents()
|
||||
{
|
||||
if (_events == null)
|
||||
return;
|
||||
|
||||
foreach (var evt in _events)
|
||||
{
|
||||
// check if it already exists
|
||||
var exists
|
||||
= await Context.Events
|
||||
.FirstOrDefaultAsync(e => e.Name == evt.Name);
|
||||
if (exists != null)
|
||||
continue;
|
||||
await Context.Events.AddAsync(evt);
|
||||
}
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
async Task UploadStudents(InputFileChangeEventArgs arg)
|
||||
{
|
||||
await GetStreamReaderFromInputFile(arg, reader =>
|
||||
{
|
||||
var studentParser = new StudentParser(reader);
|
||||
_students = studentParser.Parse();
|
||||
});
|
||||
}
|
||||
|
||||
async Task SaveStudents()
|
||||
{
|
||||
if (_students == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var student in _students)
|
||||
{
|
||||
// check if it already exists
|
||||
var exists
|
||||
= await Context.Students
|
||||
.FirstOrDefaultAsync(e
|
||||
=> e.FirstName == student.FirstName
|
||||
&& e.LastName == student.LastName);
|
||||
if (exists != null)
|
||||
continue;
|
||||
await Context.Students.AddAsync(student);
|
||||
}
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static async Task GetStreamReaderFromInputFile(InputFileChangeEventArgs arg, Action<StreamReader> f)
|
||||
{
|
||||
StreamReader? streamReader = null;
|
||||
try
|
||||
{
|
||||
var browserFile = arg.File;
|
||||
|
||||
await using var fs = browserFile.OpenReadStream();
|
||||
await using var ms = new MemoryStream();
|
||||
|
||||
await fs.CopyToAsync(ms);
|
||||
ms.Seek(0,0);
|
||||
streamReader = new StreamReader(ms);
|
||||
f(streamReader);
|
||||
}
|
||||
catch
|
||||
{
|
||||
streamReader?.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
@page "/students/create"
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Create Student - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Create</MudText>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromForm]
|
||||
private Student Student { get; set; } = new() { TsaYear = 1 };
|
||||
|
||||
private async Task AddStudent()
|
||||
{
|
||||
|
||||
Context.Students.Add(Student);
|
||||
await Context.SaveChangesAsync();
|
||||
NavigationManager.NavigateTo("/students");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
@page "/students/delete"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Delete Student - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<h1>Delete</h1>
|
||||
|
||||
<p>Are you sure you want to delete this?</p>
|
||||
<div>
|
||||
<h2>Student</h2>
|
||||
<hr />
|
||||
@if (student is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else {
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">FirstName</dt>
|
||||
<dd class="col-sm-10">@student.FirstName</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">LastName</dt>
|
||||
<dd class="col-sm-10">@student.LastName</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">Grade</dt>
|
||||
<dd class="col-sm-10">@student.Grade</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">StateId</dt>
|
||||
<dd class="col-sm-10">@student.StateId</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">RegionalId</dt>
|
||||
<dd class="col-sm-10">@student.RegionalId</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">NationalId</dt>
|
||||
<dd class="col-sm-10">@student.NationalId</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">TsaYear</dt>
|
||||
<dd class="col-sm-10">@student.TsaYear</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">OfficerRole</dt>
|
||||
<dd class="col-sm-10">@student.OfficerRole</dd>
|
||||
</dl>
|
||||
<EditForm method="post" Model="student" OnValidSubmit="DeleteStudent" FormName="delete" Enhance>
|
||||
<button type="submit" class="btn btn-danger" disabled="@(student is null)">Delete</button> |
|
||||
<a href="/students">Back to List</a>
|
||||
</EditForm>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private Student? student;
|
||||
|
||||
[SupplyParameterFromQuery]
|
||||
private int Id { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
student = await context.Students.FirstOrDefaultAsync(m => m.Id == Id);
|
||||
|
||||
if (student is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteStudent()
|
||||
{
|
||||
context.Students.Remove(student!);
|
||||
await context.SaveChangesAsync();
|
||||
NavigationManager.NavigateTo("/students");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
@page "/students/details"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Core.Entities
|
||||
@using Data
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Student Details - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<h1>Details</h1>
|
||||
|
||||
<div>
|
||||
<h2>Student</h2>
|
||||
<hr />
|
||||
@if (student is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else {
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">FirstName</dt>
|
||||
<dd class="col-sm-10">@student.FirstName</dd>
|
||||
<dt class="col-sm-2">LastName</dt>
|
||||
<dd class="col-sm-10">@student.LastName</dd>
|
||||
<dt class="col-sm-2">Grade</dt>
|
||||
<dd class="col-sm-10">@student.Grade</dd>
|
||||
<dt class="col-sm-2">Email</dt>
|
||||
<dd class="col-sm-10">@student.Email</dd>
|
||||
<dt class="col-sm-2">PhoneNumber</dt>
|
||||
<dd class="col-sm-10">@student.PhoneNumber</dd>
|
||||
<dt class="col-sm-2">TsaYear</dt>
|
||||
<dd class="col-sm-10">@student.TsaYear</dd>
|
||||
<dt class="col-sm-2">StateId</dt>
|
||||
<dd class="col-sm-10">@student.StateId</dd>
|
||||
<dt class="col-sm-2">RegionalId</dt>
|
||||
<dd class="col-sm-10">@student.RegionalId</dd>
|
||||
<dt class="col-sm-2">NationalId</dt>
|
||||
<dd class="col-sm-10">@student.NationalId</dd>
|
||||
<dt class="col-sm-2">OfficerRole</dt>
|
||||
<dd class="col-sm-10">@student.OfficerRole</dd>
|
||||
</dl>
|
||||
<div>
|
||||
<a href="@($"/students/edit?id={student.Id}")">Edit</a> |
|
||||
<a href="@($"/students")">Back to List</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private Student? student;
|
||||
|
||||
[SupplyParameterFromQuery]
|
||||
private int Id { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
student = await context.Students.FirstOrDefaultAsync(m => m.Id == Id);
|
||||
|
||||
if (student is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
@page "/students/edit"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Edit Student - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Edit</MudText>
|
||||
<MudText Typo="Typo.h4">Student@(@Student == null ? "" : $" ({Student.Name})")</MudText>
|
||||
|
||||
|
||||
|
||||
@if (Student is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
/* https://www.mudblazor.com/components/form */
|
||||
/* https://medium.com/@husainalbar/applying-mudblazor-for-crud-operations-in-our-blazor-project-a343037a52ef */
|
||||
<EditForm method="post" Model="Student" OnValidSubmit="UpdateStudent" FormName="edit" Enhance>
|
||||
<DataAnnotationsValidator/>
|
||||
<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>
|
||||
<MudTextField T="string" Label="Regional Id" @bind-Value="Student.RegionalId" For="@(() => Student.RegionalId)"></MudTextField>
|
||||
<MudTextField T="string" Label="State Id" @bind-Value="Student.StateId" For="@(() => Student.StateId)"></MudTextField>
|
||||
<MudTextField T="string" Label="National Id" @bind-Value="Student.NationalId" For="@(() => Student.NationalId)"></MudTextField>
|
||||
<MudSelect T="OfficerRole?" @bind-Value="@Student.OfficerRole" Label="Officer Role">
|
||||
|
||||
<MudSelectItem T="OfficerRole?" Value="@null">-not an officer-</MudSelectItem>
|
||||
@foreach (var officerRole in Enum.GetValues(typeof(OfficerRole)).Cast<OfficerRole>())
|
||||
{
|
||||
<MudSelectItem T="OfficerRole?" Value="@(officerRole)"></MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="students">Back</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Save" OnClick="UpdateStudent">Save</MudButton>
|
||||
</EditForm>
|
||||
}
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromQuery]
|
||||
private int Id { get; set; }
|
||||
|
||||
[SupplyParameterFromForm]
|
||||
private Student? Student { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Student ??= await Context.Students.FirstOrDefaultAsync(m => m.Id == Id);
|
||||
|
||||
if (Student is null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
}
|
||||
|
||||
// To protect from overposting attacks, enable the specific properties you want to bind to.
|
||||
// For more information, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.
|
||||
private async Task UpdateStudent()
|
||||
{
|
||||
if (Student.OfficerRole == 0)
|
||||
Student.OfficerRole = null;
|
||||
|
||||
Context.Attach(Student!).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!StudentExists(Student!.Id))
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo("/students");
|
||||
}
|
||||
|
||||
private bool StudentExists(int id)
|
||||
{
|
||||
return Context.Students.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@page "/students/event-ranking"
|
||||
@inject AppDbContext Context
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Student Event Ranks - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
@if (_students == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="_students" Hover="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info">
|
||||
<HeaderContent>
|
||||
|
||||
<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></MudTh>
|
||||
<MudTh>Warnings</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
|
||||
<MudTd>@context.FirstName</MudTd>
|
||||
<MudTh>@context.Grade</MudTh>
|
||||
<MudTh>@context.TsaYear</MudTh>
|
||||
|
||||
@for (var i = 1; i <= 6; i++)
|
||||
{
|
||||
var st = context.EventRankings.FirstOrDefault(e => e.Rank == i);
|
||||
<MudTd Class="@($"event-rank-{i})")">
|
||||
@if (st != null)
|
||||
{
|
||||
<span>@st.EventDefinition.ShortName
|
||||
@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))
|
||||
{
|
||||
<MudTooltip Text="No On-Site Activity">
|
||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.LocalActivity"></MudIcon>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (!context.RankedEvents.Any(re => re.RegionalEvent))
|
||||
{
|
||||
<MudTooltip Text="No Regional Event">
|
||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.PinDrop"></MudIcon>
|
||||
</MudTooltip>
|
||||
}
|
||||
|
||||
@if (context.RankedEvents.All(re => re.EventFormat != EventFormat.Individual))
|
||||
{
|
||||
<MudTooltip Text="No Individual Event">
|
||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.Person"></MudIcon>
|
||||
</MudTooltip>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
<MudTable Items="_eventStudentRankings" Hover="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info">
|
||||
<HeaderContent>
|
||||
<MudTh>Event</MudTh>
|
||||
<MudTh>Level of Effort</MudTh>
|
||||
<MudTh>Individual</MudTh>
|
||||
<MudTh>Regional</MudTh>
|
||||
<MudTh>On-site Activity</MudTh>
|
||||
<MudTh>Team Size</MudTh>
|
||||
@for (var i = 0; i < _maxEventStudentRankings; i++)
|
||||
{
|
||||
var i1 = i + 1;
|
||||
<MudTh>@i1</MudTh>
|
||||
}
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Event.Name</MudTd>
|
||||
<MudTd>@context.Event.LevelOfEffort</MudTd>
|
||||
<MudTd>@if (context.Event.EventFormat == EventFormat.Individual) { <span>ⓘ</span> }</MudTd >
|
||||
<MudTd>@if (context.Event.RegionalEvent) { <span>ⓡ</span> }</MudTd >
|
||||
<MudTd>@if (context.Event.OnSiteActivity) { <span>ⓐ</span> }</MudTd >
|
||||
<MudTd>[@context.Event.MinTeamSize-@context.Event.MaxTeamSize]</MudTd >
|
||||
@for (var j = 0; j < _maxEventStudentRankings; j++)
|
||||
{
|
||||
var student = j < context.StudentRanking.Length ? context.StudentRanking[j] : null;
|
||||
var eventClass = student != null ? $"event-rank-{student.Item2}" : "";
|
||||
<MudTd Class="@eventClass">
|
||||
@if (student != null)
|
||||
{
|
||||
@student.Item1.FirstName
|
||||
}
|
||||
</MudTd>
|
||||
}
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
}
|
||||
@code {
|
||||
private Student[]? _students;
|
||||
|
||||
private class EventStudentRankings {
|
||||
public EventDefinition Event {get; set; }
|
||||
public Tuple<Student,int> [] StudentRanking { get; set; }
|
||||
}
|
||||
|
||||
private EventStudentRankings[] _eventStudentRankings;
|
||||
private int _maxEventStudentRankings;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_students =
|
||||
await Context.Students
|
||||
.Include(e => e.EventRankings)
|
||||
.ThenInclude(e => e.EventDefinition)
|
||||
.OrderBy(e => e.FirstName).ToArrayAsync();
|
||||
|
||||
_eventStudentRankings =
|
||||
_students.SelectMany(s =>
|
||||
s.EventRankings,
|
||||
(student, ranking) => new { e = ranking.EventDefinition, a = Tuple.Create(student, ranking.Rank) }
|
||||
)
|
||||
.GroupBy(e => e.e)
|
||||
.Select(e =>
|
||||
new EventStudentRankings
|
||||
{
|
||||
Event = e.Key,
|
||||
StudentRanking = e.Select(er => er.a).OrderBy(ser => ser.Item2).ThenByDescending(ser => ser.Item1.Grade + ser.Item1.TsaYear).ToArray()
|
||||
})
|
||||
.OrderBy(e => e.Event.Name)
|
||||
.ToArray();
|
||||
|
||||
var events = await Context.Events.ToArrayAsync();
|
||||
var remainingEvents =
|
||||
events
|
||||
.Where(e => _eventStudentRankings.All(est => est.Event.Id != e.Id))
|
||||
.Select(e => new EventStudentRankings { Event = e, StudentRanking = Array.Empty<Tuple<Student, int>>() })
|
||||
.OrderBy(e => e.Event.Name)
|
||||
.ToArray();
|
||||
|
||||
_eventStudentRankings = _eventStudentRankings.Concat(remainingEvents).ToArray();
|
||||
|
||||
_maxEventStudentRankings = _eventStudentRankings.Max(esr => esr.StudentRanking.Length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using BlazorSortableList
|
||||
@using WebApp.Models
|
||||
@page "/students/event-ranking-edit/{StudentId:int}"
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Student Event Ranks - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Student Event Ranks</MudText>
|
||||
|
||||
<div>
|
||||
@if (_student == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.h4">@_student.Name</MudText>
|
||||
<MudText Color="Color.Warning">Warning: drag and drop is currently a bit squirrely - double check!</MudText>
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="students/event-ranking">Back</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Save" OnClick="Save">Save</MudButton>
|
||||
|
||||
/* https://github.com/AlexNek/BlazorSortableList */
|
||||
<MudGrid>
|
||||
<MudItem xs="6" md="4" xl="3" Class="ranked-event-column">
|
||||
<SortableList
|
||||
Group="GroupId" Id="ListId1" Context="item"
|
||||
Items="_rankedEvents" OnRemove="RankedEventsRemove" OnUpdate="Update">
|
||||
<SortableItemTemplate>
|
||||
<MudCard Outlined="true">
|
||||
<MudCardContent>@item.Name</MudCardContent>
|
||||
</MudCard>
|
||||
</SortableItemTemplate>
|
||||
</SortableList>
|
||||
</MudItem>
|
||||
<MudItem xs="6" md="4" xl="3">
|
||||
<SortableList
|
||||
Group="GroupId" Id="ListId2" Context="item"
|
||||
Items="_availableEvents" OnRemove="AvailableEventsRemove" Sort="false">
|
||||
<SortableItemTemplate>
|
||||
<MudCard Outlined="true">
|
||||
<MudCardContent>@item.Name</MudCardContent>
|
||||
</MudCard>
|
||||
</SortableItemTemplate>
|
||||
|
||||
</SortableList>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
private const string ListId1 = "SharedListId1";
|
||||
private const string ListId2 = "SharedListId2";
|
||||
private const string GroupId = "CommonGroup";
|
||||
|
||||
[Parameter] public int? StudentId { get; set; }
|
||||
|
||||
private Student? _student;
|
||||
private List<EventDefinition>? _events;
|
||||
|
||||
public List<EventDefinition> _rankedEvents = [];
|
||||
public List<EventDefinition> _availableEvents = [];
|
||||
|
||||
SharedSortableListGroup _group;
|
||||
|
||||
private void RankedEventsRemove((int oldIndex, int newIndex) indices)
|
||||
{
|
||||
// get the item at the old index in list 1
|
||||
var item = _rankedEvents[indices.oldIndex];
|
||||
|
||||
// add it to the new index in list 2
|
||||
_availableEvents.Insert(indices.newIndex, item);
|
||||
|
||||
// remove the item from the old index in list 1
|
||||
_rankedEvents.Remove(_rankedEvents[indices.oldIndex]);
|
||||
}
|
||||
|
||||
private void AvailableEventsRemove((int oldIndex, int newIndex) indices)
|
||||
{
|
||||
// get the item at the old index in list 2
|
||||
var item = _availableEvents[indices.oldIndex];
|
||||
|
||||
// add it to the new index in list 1
|
||||
_rankedEvents.Insert(indices.newIndex, item);
|
||||
|
||||
// remove the item from the old index in list 2
|
||||
_availableEvents.Remove(_availableEvents[indices.oldIndex]);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_student =
|
||||
await Context.Students
|
||||
.Include(e => e.EventRankings)
|
||||
.Where(e => e.Id == StudentId).FirstAsync();
|
||||
_events =
|
||||
await Context.Events
|
||||
.OrderBy(e => e.Name)
|
||||
.ToListAsync();
|
||||
|
||||
_rankedEvents = _student.EventRankings.OrderBy(e => e.Rank).Select(e => e.EventDefinition).ToList();
|
||||
_availableEvents = _events.Where(e => !_rankedEvents.Contains(e)).ToList();
|
||||
|
||||
_group = new SharedSortableListGroup(StateHasChanged);
|
||||
_group.AddModel(ListId1, new SortableListModel<EventDefinition>(_rankedEvents) { Group = GroupId });
|
||||
_group.AddModel(ListId2, new SortableListModel<EventDefinition>(_availableEvents) { Group = GroupId });
|
||||
}
|
||||
|
||||
private void Update((int oldIndex, int newIndex) indices)
|
||||
{
|
||||
var (oldIndex, newIndex) = indices;
|
||||
|
||||
var items = _rankedEvents;
|
||||
var itemToMove = items[oldIndex];
|
||||
items.RemoveAt(oldIndex);
|
||||
|
||||
if (newIndex < items.Count)
|
||||
{
|
||||
items.Insert(newIndex, itemToMove);
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(itemToMove);
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
||||
async Task Save()
|
||||
{
|
||||
if (_student == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_student.EventRankings.Clear();
|
||||
for (var index = 0; index < _rankedEvents.Count; index++)
|
||||
{
|
||||
var evt = _rankedEvents[index];
|
||||
_student.EventRankings.Add(new StudentEventRanking
|
||||
{
|
||||
EventDefinition = evt,
|
||||
Student = _student,
|
||||
Rank = index + 1
|
||||
});
|
||||
}
|
||||
|
||||
Context.Students.Update(_student);
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
NavigationManager.NavigateTo("/students/event-ranking");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
@page "/students"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Models
|
||||
@inject AppDbContext Context
|
||||
|
||||
<PageTitle>Students - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Students</MudText>
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Create" Href="students/create">Create New</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.AddChart" Href="students/event-ranking">Event Rankings</MudButton>
|
||||
|
||||
<MudDataGrid T="Student" ServerData="ServerReload" @ref="_dataGrid" Filterable="true" RowsPerPage="25">
|
||||
<Columns>
|
||||
@* <PropertyColumn Property="@(e => e.Name)" Title="First Name" SortBy="e => e.FirstName" /> *@
|
||||
<TemplateColumn Title="Name" SortBy="e => e.FirstName" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.Name
|
||||
@if (context.Item.OfficerRole != null)
|
||||
{
|
||||
<MudChip T="string" Icon="@(AppIcons.OfficerRoleIcon(context.Item.OfficerRole.Value))">@context.Item.OfficerRole</MudChip>
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn Title="Grade (TSA Year)" SortBy="e => e.Grade" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.Grade (@context.Item.TsaYear)
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
<MudStack Row>
|
||||
<MudButtonGroup Size="Size.Small">
|
||||
<MudTooltip Text="Details">
|
||||
<MudIconButton Href="@($"/students/details?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Description">Details</MudIconButton>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Edit">
|
||||
<MudIconButton Href="@($"/students/edit?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Edit">Edit</MudIconButton>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Delete">
|
||||
<MudIconButton Href="@($"/students/delete?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Delete" Color="@Color.Warning">Delete</MudIconButton>
|
||||
</MudTooltip>
|
||||
</MudButtonGroup>
|
||||
</MudStack>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
<PagerContent>
|
||||
<MudDataGridPager T="Student"></MudDataGridPager>
|
||||
</PagerContent>
|
||||
</MudDataGrid>
|
||||
|
||||
@code {
|
||||
MudDataGrid<Student> _dataGrid = null!;
|
||||
|
||||
private async Task<GridData<Student>> ServerReload(GridState<Student> state)
|
||||
{
|
||||
|
||||
var query = Context.Students.Where(state.FilterDefinitions).OrderBy(state.SortDefinitions);
|
||||
|
||||
var totalItems = await query.CountAsync();
|
||||
var pagedData = await query.Skip(state.Page * state.PageSize).Take(state.PageSize).ToArrayAsync();
|
||||
|
||||
return new GridData<Student>
|
||||
{
|
||||
TotalItems = totalItems,
|
||||
Items = pagedData
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
@page "/teams/assignment"
|
||||
@using Core.Calculation
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Models
|
||||
@using EventAssignment = Core.Calculation.EventAssignment
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Event Assignment - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Assignment</MudText>
|
||||
|
||||
|
||||
<MudText>Optimized team assignments based on the student event rankings</MudText>
|
||||
|
||||
<MudGrid>
|
||||
|
||||
<MudItem xs="12" lg="8">
|
||||
<MudText Typo="Typo.h4">Students</MudText>
|
||||
|
||||
<MudTable T="StudentEventStatistics" ServerData="ReloadStatistics" @ref="_statisticData" >
|
||||
<ColGroup>
|
||||
<col style="width: 150px;" />
|
||||
<col style="width: 50px;" />
|
||||
<col style="width: 50px;" />
|
||||
<col style="width: 60px;" />
|
||||
</ColGroup>
|
||||
<HeaderContent>
|
||||
<MudTh>Student</MudTh>
|
||||
<MudTh><MudTooltip Text="How many events they're assigned'">Event Count</MudTooltip></MudTh>
|
||||
<MudTh><MudTooltip Text="Level of Effort Total">LOE Sum</MudTooltip></MudTh>
|
||||
<MudTh><MudTooltip Text="Assignment Warnings">Warnings</MudTooltip></MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd><b>@context.Student.FirstName</b></MudTd>
|
||||
<MudTd>@context.EventCount</MudTd>
|
||||
<MudTd>@context.TotalLevelOfEffort</MudTd>
|
||||
<MudTd>@if (!context.HasOnSiteActivity)
|
||||
{
|
||||
<MudTooltip Text="No On-Site Activity">
|
||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.LocalActivity"></MudIcon>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (!context.HasRegionalEvent)
|
||||
{
|
||||
<MudTooltip Text="No Regional Event">
|
||||
<MudIcon Color="Color.Warning" Icon="@Icons.Material.Filled.PinDrop"></MudIcon>
|
||||
</MudTooltip>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
<ChildRowContent>
|
||||
<MudTr><td colspan="4">
|
||||
@{
|
||||
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.RankedEvent(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};";
|
||||
}
|
||||
|
||||
<MudPaper Class="d-inline-flex align-center pa-2 mx-3 my-1 border-solid" Style="@(style)">
|
||||
@e.ShortName
|
||||
@{
|
||||
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)
|
||||
{
|
||||
<MudTooltip Title="@($"Add requirement for {context.Student.FirstName} in {e.ShortName}")">
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.ThumbUpAlt" Class="ml-3" Size="Size.Small" Color="Color.Default"
|
||||
OnClick="() => RequireEvent(e, context.Student, Requirement.Include)"></MudIconButton>
|
||||
</MudTooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTooltip Title="@($"Remove requirement for {context.Student.FirstName} in {e.ShortName}")">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbUpAlt" Class="ml-3" Size="Size.Small" Color="Color.Dark"
|
||||
OnClick="() => RemoveRequireEvent(e, context.Student, Requirement.Include)"></MudIconButton>
|
||||
</MudTooltip>
|
||||
}
|
||||
|
||||
@if (isExcluded)
|
||||
{
|
||||
<MudTooltip Title="@($"Add restriction against {context.Student.FirstName} in {e.ShortName}")">
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.ThumbDownAlt" Size="Size.Small" Color="Color.Default"
|
||||
OnClick="() => RequireEvent(e, context.Student, Requirement.Exclude)"></MudIconButton>
|
||||
</MudTooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTooltip Title="@($"Remove restriction against {context.Student.FirstName} in {e.ShortName}")">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbDownAlt" Size="Size.Small" Color="Color.Dark"
|
||||
OnClick="() => RemoveRequireEvent(e, context.Student, Requirement.Exclude)"></MudIconButton>
|
||||
</MudTooltip>
|
||||
}
|
||||
</MudPaper>
|
||||
}
|
||||
<MudDivider Style="border-width:3px" />
|
||||
</td></MudTr>
|
||||
</ChildRowContent>
|
||||
</MudTable>
|
||||
</MudItem>
|
||||
<MudItem xs="12" lg="4">
|
||||
<MudText Typo="Typo.h4">Teams</MudText>
|
||||
<MudTable T="Team" ServerData="SolveAssignments" @ref="_teamData">
|
||||
<ColGroup>
|
||||
<col style="width: 200px;" />
|
||||
<col style="width: 40px; white-space:nowrap" />
|
||||
</ColGroup>
|
||||
<HeaderContent>
|
||||
<MudTh>Team</MudTh>
|
||||
<MudTh><MudTooltip Text="Number of Student Rankings, Number of Teams [Eligibility Lower Bound-Upper Bound]"><MudText Style="white-space:nowrap;">R, # [LB-UB]</MudText> </MudTooltip></MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
@{
|
||||
var thresholds = _eventAssignmentThresholds.First(e => e.Event == context.Event);
|
||||
}
|
||||
<MudTd><b>@context.Event.Name</b></MudTd>
|
||||
<MudTd Style="white-space:nowrap">@thresholds.StudentRankingCount, @thresholds.TeamCount × [@thresholds.LowerBound-@thresholds.UpperBound]</MudTd>
|
||||
|
||||
</RowTemplate>
|
||||
<ChildRowContent>
|
||||
<MudTr>
|
||||
<td colspan="2">
|
||||
@foreach (var student in
|
||||
context.Students
|
||||
.OrderBy(e =>
|
||||
e.EventRankings
|
||||
.Find(er => er.EventDefinition == context.Event)?.Rank ?? 10)
|
||||
.ThenBy(s => s.Grade + s.TsaYear))
|
||||
{
|
||||
var eventRank =
|
||||
student.EventRankings
|
||||
.Find(e => e.EventDefinition == context.Event)?.Rank;
|
||||
var color = AppIcons.RankedEvent(eventRank ?? 0);
|
||||
|
||||
<MudPaper Class="d-inline-flex pa-2 mx-3 my-1" Style="@($"background:{color};")">
|
||||
@student.FirstName
|
||||
</MudPaper>
|
||||
}
|
||||
<MudDivider Style="border-width:3px" />
|
||||
</td>
|
||||
</MudTr>
|
||||
</ChildRowContent>
|
||||
<NoRecordsContent>
|
||||
<MudText Color="Color.Warning">Solution status: @_solutionStatus</MudText>
|
||||
</NoRecordsContent>
|
||||
<LoadingContent>
|
||||
<MudText>Loading...</MudText>
|
||||
</LoadingContent>
|
||||
</MudTable>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
<MudPaper Class="pa-4 mt-5">
|
||||
<MudGrid>
|
||||
<MudItem Style="width:160px;">
|
||||
<MudNumericField @bind-Value="_parameters.TeamSizeLimit"
|
||||
Label="Team Size Limit" Min="3" Max="8"></MudNumericField>
|
||||
</MudItem>
|
||||
<MudItem>
|
||||
<MudTooltip Text="Require at least one On-Site Event">
|
||||
<MudSwitch @bind-Value="_parameters.RequireOnSite" 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">
|
||||
<HeaderContent>
|
||||
<MudTh></MudTh>
|
||||
<MudTh>Student</MudTh>
|
||||
<MudTh>Event</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate Context="item">
|
||||
<MudTd Class="align-center">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.RemoveCircle"
|
||||
OnClick="() => RemoveRequireEvent(item)"></MudIconButton>
|
||||
</MudTd>
|
||||
<MudTd Class="align-center">@item.Student.FirstName</MudTd>
|
||||
<MudTd Class="align-center">
|
||||
@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>
|
||||
</MudGrid>
|
||||
<MudButton OnClick="Solve" Variant="Variant.Filled" Disabled="@_isSolving">Solve</MudButton>
|
||||
</MudPaper>
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Edit" Href="students/event-ranking">Edit Student Event Rankings</MudButton>
|
||||
|
||||
@code {
|
||||
public bool TestSwitch { get; set; } = false;
|
||||
|
||||
private readonly AssignmentParameters _parameters = new () {LimitTeamsToOne = false};
|
||||
|
||||
private List<EventDefinition>? _events;
|
||||
private List<Student>? _students;
|
||||
private List<EventAssignmentThresholds> _eventAssignmentThresholds = [];
|
||||
|
||||
MudTable<Team> _teamData;
|
||||
MudTable<StudentEventStatistics> _statisticData;
|
||||
private List<StudentEventStatistics> _statistics = [];
|
||||
MudTable<AssignmentRequirement> _assignmentRequirementData;
|
||||
private List<AssignmentRequirement> _assignmentRequirements = [];
|
||||
|
||||
private string _solutionStatus = string.Empty;
|
||||
|
||||
private bool _isSolving = 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();
|
||||
}
|
||||
|
||||
private async Task AddTeam()
|
||||
{
|
||||
//Context.Teams.Add(Team);
|
||||
await Context.SaveChangesAsync();
|
||||
//NavigationManager.NavigateTo("/teams");
|
||||
}
|
||||
|
||||
private void Solve()
|
||||
{
|
||||
_teamData.ReloadServerData();
|
||||
}
|
||||
|
||||
private async Task<TableData<Team>> SolveAssignments(TableState arg1, CancellationToken arg2)
|
||||
{
|
||||
_isSolving = true;
|
||||
var eventAssignment = new EventAssignment(_events, _students, _parameters);
|
||||
foreach (var requirement in _assignmentRequirements)
|
||||
{
|
||||
eventAssignment.AddAssignmentRequirement(requirement);
|
||||
}
|
||||
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
|
||||
return new TableData<Team> { Items = solution.Teams };
|
||||
}
|
||||
|
||||
private async Task<TableData<StudentEventStatistics>> ReloadStatistics(TableState arg1, CancellationToken arg2)
|
||||
{
|
||||
return new TableData<StudentEventStatistics> {Items = _statistics};
|
||||
}
|
||||
|
||||
private async Task<TableData<AssignmentRequirement>> ReloadAssignmentRequirements(TableState arg1, CancellationToken arg2)
|
||||
{
|
||||
return new TableData<AssignmentRequirement> { Items = _assignmentRequirements };
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
@page "/teams/create"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Create Team - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Create</MudText>
|
||||
<MudText Typo="Typo.h4">Team</MudText>
|
||||
<MudDivider />
|
||||
|
||||
<EditForm method="post" Model="Team" OnValidSubmit="AddTeam" FormName="create" Enhance>
|
||||
<DataAnnotationsValidator />
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="7">
|
||||
<MudPaper Class="pa-4">
|
||||
<MudSelect T="EventDefinition" @bind-Value="@Team.Event" Label="Event">
|
||||
|
||||
@foreach (var evt in _events)
|
||||
{
|
||||
<MudSelectItem T="EventDefinition" Value="@(evt)"></MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<MudTextField T="string" Label="Name" @bind-Value="Team.Name" For="@(() => Team.Name)"></MudTextField>
|
||||
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="students">Back</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Add" OnClick="AddTeam">Add</MudButton>
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromForm]
|
||||
private Team Team { get; set; } = new();
|
||||
|
||||
private List<EventDefinition>? _events;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_events =
|
||||
await Context.Events
|
||||
.OrderBy(e => e.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private async Task AddTeam()
|
||||
{
|
||||
Context.Teams.Add(Team);
|
||||
await Context.SaveChangesAsync();
|
||||
NavigationManager.NavigateTo("/teams");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
@page "/teams"
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Models
|
||||
@inject AppDbContext Context
|
||||
|
||||
<PageTitle>Teams</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Teams</MudText>
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Create" Href="teams/create">Create New</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Assignment" Href="teams/assignment">Assignment</MudButton>
|
||||
|
||||
<MudDataGrid T="Team" ServerData="ServerReload" @ref="_dataGrid" Filterable="true" RowsPerPage="25">
|
||||
<Columns>
|
||||
<PropertyColumn Property="@(e => e.Name)" Title="Name" />
|
||||
<PropertyColumn Property="@(e => e.Event.Name)" Title="Event" />
|
||||
@* <TemplateColumn Title="Name" SortBy="e => e.FirstName" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.Name
|
||||
@if (context.Item.OfficerRole != null)
|
||||
{
|
||||
<MudChip T="string" Icon="@(AppIcons.OfficerRoleIcon(context.Item.OfficerRole.Value))">@context.Item.OfficerRole</MudChip>
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn> *@
|
||||
@* <TemplateColumn Title="Grade (TSA Year)" SortBy="e => e.Grade" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.Grade (@context.Item.TsaYear)
|
||||
</CellTemplate>
|
||||
</TemplateColumn> *@
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
<MudStack Row>
|
||||
<MudButtonGroup Size="Size.Small">
|
||||
<MudTooltip Text="Details">
|
||||
<MudIconButton Href="@($"/teams/details?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Description">Details</MudIconButton>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Edit">
|
||||
<MudIconButton Href="@($"/teams/edit?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Edit">Edit</MudIconButton>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Delete">
|
||||
<MudIconButton Href="@($"/teams/delete?id={context.Item.Id}")" Icon="@Icons.Material.Filled.Delete" Color="@Color.Warning">Delete</MudIconButton>
|
||||
</MudTooltip>
|
||||
</MudButtonGroup>
|
||||
</MudStack>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
<PagerContent>
|
||||
<MudDataGridPager T="Student"></MudDataGridPager>
|
||||
</PagerContent>
|
||||
</MudDataGrid>
|
||||
|
||||
@code {
|
||||
MudDataGrid<Team> _dataGrid = null!;
|
||||
|
||||
private async Task<GridData<Team>> ServerReload(GridState<Team> state)
|
||||
{
|
||||
var query
|
||||
= Context.Teams
|
||||
.Include(e => e.Event)
|
||||
.Include(e => e.Students)
|
||||
.Where(state.FilterDefinitions)
|
||||
.OrderBy(state.SortDefinitions);
|
||||
|
||||
var totalItems = await query.CountAsync();
|
||||
var pagedData = await query.Skip(state.Page * state.PageSize).Take(state.PageSize).ToArrayAsync();
|
||||
|
||||
return new GridData<Team>
|
||||
{
|
||||
TotalItems = totalItems,
|
||||
Items = pagedData
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user