diff --git a/Core/Entities/EventDefinition.cs b/Core/Entities/EventDefinition.cs index befea1e..7ea2e53 100644 --- a/Core/Entities/EventDefinition.cs +++ b/Core/Entities/EventDefinition.cs @@ -85,6 +85,9 @@ public class EventDefinition } public static readonly EventDefinition GeneralSchedule = new(){Name = "General Schedule"}; - public static readonly EventDefinition VotingDelegates = new(){Name = "Voting Delegates"}; + public static readonly EventDefinition MeetTheCandidates = new(){Name = "Meet the Candidates"}; + public static readonly EventDefinition ChapterOfficerMeeting = new(){Name = "Chapter Officer Meeting"}; + public static readonly EventDefinition VotingDelegateMeeting = new(){Name = "Voting Delegate Meeting"}; + public static readonly EventDefinition SocialGathering = new(){Name = "Social Gathering"}; } \ No newline at end of file diff --git a/Core/Models/EventOccurrenceParseResult.cs b/Core/Models/EventOccurrenceParseResult.cs index c84d376..f8661e2 100644 --- a/Core/Models/EventOccurrenceParseResult.cs +++ b/Core/Models/EventOccurrenceParseResult.cs @@ -10,7 +10,8 @@ public class EventOccurrenceParseResult { /// /// Dictionary of parsed event occurrences, keyed by EventDefinition. - /// For special events (GeneralSchedule/VotingDelegates), the EventDefinition key will be the static instance. + /// For special events (GeneralSchedule, MeetTheCandidates, ChapterOfficerMeeting, VotingDelegateMeeting, SocialGathering), + /// the EventDefinition key will be the static instance. /// public IDictionary> Occurrences { get; set; } = new Dictionary>(); diff --git a/Core/Parsers/EventOccurrenceParser.cs b/Core/Parsers/EventOccurrenceParser.cs index 9d2ea60..111eaba 100644 --- a/Core/Parsers/EventOccurrenceParser.cs +++ b/Core/Parsers/EventOccurrenceParser.cs @@ -52,33 +52,31 @@ public class EventOccurrenceParser currentEventDefinition = evt; continue; } - if (line == "General Schedule") + if (line == "General Schedule" || line == "General Session") { currentEventDefinition = EventDefinition.GeneralSchedule; continue; } - if (line == "Voting Delegates") - { - currentEventDefinition = EventDefinition.VotingDelegates; - continue; - } - + // "Voting Delegates" section header is no longer used - occurrences are categorized by name pattern + // Continue without setting currentEventDefinition for this section continue; } - if (currentEventDefinition == null) - continue; - var occurrenceName = match.Groups["Name"].Captures[0].Value; var month = match.Groups["Month"].Captures[0].Value; var dayOfMonth = match.Groups["DayOfMonth"].Captures[0].Value; var timeAndLocation = match.Groups["TimeAndLocation"].Captures[0].Value; - occurrenceName = Regex.Replace(occurrenceName, @"(?Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\s?$", "").Trim(); + // Determine event definition based on occurrence name pattern or current section + EventDefinition? eventDefinition = DetermineEventDefinition(occurrenceName, currentEventDefinition); + + // Skip if we can't determine the event definition + if (eventDefinition == null) + continue; timeAndLocation = SanitizeInput(timeAndLocation); var timeAndLocationMatch = _timeLocationRegex.Match(timeAndLocation); @@ -103,14 +101,41 @@ public class EventOccurrenceParser Location = location }; - if (!occurrences.ContainsKey(currentEventDefinition)) - occurrences.Add(currentEventDefinition, []); - occurrences[currentEventDefinition].Add(eventOccurrence); + if (!occurrences.ContainsKey(eventDefinition)) + occurrences.Add(eventDefinition, []); + occurrences[eventDefinition].Add(eventOccurrence); } return occurrences; } + /// + /// Determines the EventDefinition for an occurrence based on its name pattern or current section context. + /// + private EventDefinition? DetermineEventDefinition(string occurrenceName, EventDefinition? currentEventDefinition) + { + // Check for special event name patterns first (regardless of current section) + if (occurrenceName.Contains("Meet the Candidates Session", StringComparison.OrdinalIgnoreCase)) + return EventDefinition.MeetTheCandidates; + + if (occurrenceName.Contains("Chapter Officer Meeting", StringComparison.OrdinalIgnoreCase)) + return EventDefinition.ChapterOfficerMeeting; + + if (occurrenceName.Contains("Voting Delegate Meeting", StringComparison.OrdinalIgnoreCase)) + return EventDefinition.VotingDelegateMeeting; + + // If we're in a General Schedule/Session section and no pattern matched, use GeneralSchedule + if (currentEventDefinition == EventDefinition.GeneralSchedule) + return EventDefinition.GeneralSchedule; + + // If we have a current event definition from section header (e.g., regular events), use it + if (currentEventDefinition != null) + return currentEventDefinition; + + // Cannot determine event definition + return null; + } + private string SanitizeInput(string input) { diff --git a/Core/Services/EventOccurrenceParserService.cs b/Core/Services/EventOccurrenceParserService.cs index 8952e56..06fc631 100644 --- a/Core/Services/EventOccurrenceParserService.cs +++ b/Core/Services/EventOccurrenceParserService.cs @@ -41,17 +41,26 @@ public class EventOccurrenceParserService : IEventOccurrenceParserService var eventDefinition = kvp.Key; var occurrences = kvp.Value; - // Check if this is a special event type (GeneralSchedule or VotingDelegates) + // Check if this is a special event type (not stored in database) if (eventDefinition == EventDefinition.GeneralSchedule || - eventDefinition == EventDefinition.VotingDelegates) + eventDefinition == EventDefinition.MeetTheCandidates || + eventDefinition == EventDefinition.ChapterOfficerMeeting || + eventDefinition == EventDefinition.VotingDelegateMeeting || + eventDefinition == EventDefinition.SocialGathering) { // For special events, set EventDefinitionId to null and set SpecialEventType foreach (var occurrence in occurrences) { occurrence.EventDefinitionId = null; - occurrence.SpecialEventType = eventDefinition == EventDefinition.GeneralSchedule - ? "GeneralSchedule" - : "VotingDelegates"; + occurrence.SpecialEventType = eventDefinition switch + { + var ed when ed == EventDefinition.GeneralSchedule => "GeneralSchedule", + var ed when ed == EventDefinition.MeetTheCandidates => "MeetTheCandidates", + var ed when ed == EventDefinition.ChapterOfficerMeeting => "ChapterOfficerMeeting", + var ed when ed == EventDefinition.VotingDelegateMeeting => "VotingDelegateMeeting", + var ed when ed == EventDefinition.SocialGathering => "SocialGathering", + _ => throw new InvalidOperationException($"Unknown special event type: {eventDefinition.Name}") + }; } // Add to result with the special EventDefinition as key diff --git a/Tests/Parsers/EventOccurrenceParser_Tests.cs b/Tests/Parsers/EventOccurrenceParser_Tests.cs index 75ff65f..b741167 100644 --- a/Tests/Parsers/EventOccurrenceParser_Tests.cs +++ b/Tests/Parsers/EventOccurrenceParser_Tests.cs @@ -7,10 +7,10 @@ public class EventOccurrenceParser_Tests { [Test] - public void ParseTest() + public void ParseNationalsTest() { var events = TestEntityHandler.GetEvents(); - var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceFileInfo(), events); + var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceNationalsFileInfo(), events); var dictionary = parser.Parse(); Console.WriteLine($"Occurrence, Month, Date, Time, Location"); foreach (var @event in events) @@ -30,17 +30,104 @@ public class EventOccurrenceParser_Tests } Console.WriteLine("General Schedule"); - foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime)) + if (dictionary.ContainsKey(EventDefinition.GeneralSchedule)) { - Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + 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(EventDefinition.VotingDelegates)) - foreach (var eo in dictionary[EventDefinition.VotingDelegates]) + Console.WriteLine("Meet the Candidates"); + if (dictionary.ContainsKey(EventDefinition.MeetTheCandidates)) { - Console.WriteLine($"{eo.Name} {eo.StartTime}, {eo.Location}"); + foreach (var eo in dictionary[EventDefinition.MeetTheCandidates]) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Console.WriteLine("Chapter Officer Meeting"); + if (dictionary.ContainsKey(EventDefinition.ChapterOfficerMeeting)) + { + foreach (var eo in dictionary[EventDefinition.ChapterOfficerMeeting]) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Console.WriteLine("Voting Delegate Meeting"); + if (dictionary.ContainsKey(EventDefinition.VotingDelegateMeeting)) + { + foreach (var eo in dictionary[EventDefinition.VotingDelegateMeeting]) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } } Assert.Pass(); } + + + [Test] + public void ParseStatesTest() + { + var events = TestEntityHandler.GetEvents(); + var parser = new EventOccurrenceParser(TestEntityHandler.GetEventOccurrenceStateFileInfo(), events); + var dictionary = parser.Parse(); + Console.WriteLine($"Occurrence, Month, Date, Time, Location"); + foreach (var @event in events) + { + Console.WriteLine($"{@event.Name}"); + + if (!dictionary.ContainsKey(@event)) + { + Console.WriteLine("!!! eventDefinition not found " + @event.Name); + continue; + } + var eventOccurrences = dictionary[@event]; + foreach (var eo in eventOccurrences) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Console.WriteLine("General Schedule"); + if (dictionary.ContainsKey(EventDefinition.GeneralSchedule)) + { + foreach (var eo in dictionary[EventDefinition.GeneralSchedule].OrderBy(occurrence => occurrence.StartTime)) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Console.WriteLine("Meet the Candidates"); + if (dictionary.ContainsKey(EventDefinition.MeetTheCandidates)) + { + foreach (var eo in dictionary[EventDefinition.MeetTheCandidates]) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Console.WriteLine("Chapter Officer Meeting"); + if (dictionary.ContainsKey(EventDefinition.ChapterOfficerMeeting)) + { + foreach (var eo in dictionary[EventDefinition.ChapterOfficerMeeting]) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Console.WriteLine("Voting Delegate Meeting"); + if (dictionary.ContainsKey(EventDefinition.VotingDelegateMeeting)) + { + foreach (var eo in dictionary[EventDefinition.VotingDelegateMeeting]) + { + Console.WriteLine($"\t{eo.StartTime.DayOfWeek} {eo.Time}, {eo.Name}, {eo.Location}"); + } + } + + Assert.Pass(); + } } \ No newline at end of file diff --git a/Tests/Parsers/TestEntityHandler.cs b/Tests/Parsers/TestEntityHandler.cs index 7e94159..d96ffa4 100644 --- a/Tests/Parsers/TestEntityHandler.cs +++ b/Tests/Parsers/TestEntityHandler.cs @@ -16,12 +16,18 @@ public static class TestEntityHandler return eventRankingsParser.Parse(); } - public static FileInfo GetEventOccurrenceFileInfo() + public static FileInfo GetEventOccurrenceNationalsFileInfo() { return FileUtility.GetContentFile(ContentDirectory, "2025 TSA Nationals Competition Event Times.txt"); } - public static Student[] GetStudents(IList events) + + public static FileInfo GetEventOccurrenceStateFileInfo() + { + return FileUtility.GetContentFile(ContentDirectory, "2025 TN TSA State Competition Event Times.txt"); + } + + public static Student[] GetStudents(IList events) { //var studentEventRankingsCsv = "Student Event Rankings.csv"; var studentEventRankingsCsv = "2024 Student Event Rankings.csv"; diff --git a/Web-Original/Views/Home/Nationals.cshtml b/Web-Original/Views/Home/Nationals.cshtml index 0120c8c..a8af6f2 100644 --- a/Web-Original/Views/Home/Nationals.cshtml +++ b/Web-Original/Views/Home/Nationals.cshtml @@ -138,7 +138,9 @@ .Where(eo => student.Teams.Select(t => t.EventDefinition).Any(a => a == eo.Key) || eo.Key == EventDefinition.GeneralSchedule - || (eo.Key == EventDefinition.VotingDelegates && student.VotingDelegate)) + || eo.Key == EventDefinition.MeetTheCandidates + || (eo.Key == EventDefinition.ChapterOfficerMeeting && student.OfficerRole != null) + || (eo.Key == EventDefinition.VotingDelegateMeeting && student.VotingDelegate)) .SelectMany(eo => eo.Value.Select(v => Tuple.Create(v, eo.Key))) .GroupBy(de => de.Item1.StartTime.Date) .OrderBy(d => d.Key) @@ -218,16 +220,24 @@ @foreach (var occurrence in eventsForDate.OrderBy(o => o.Item1.StartTime)) { var teams = Model.Item1.Where(t => t.EventDefinition == occurrence.Item2); - if (occurrence.Item2 != EventDefinition.GeneralSchedule && occurrence.Item2 != EventDefinition.VotingDelegates && !teams.Any()) + if (occurrence.Item2 != EventDefinition.GeneralSchedule + && occurrence.Item2 != EventDefinition.MeetTheCandidates + && occurrence.Item2 != EventDefinition.ChapterOfficerMeeting + && occurrence.Item2 != EventDefinition.VotingDelegateMeeting + && !teams.Any()) continue; @occurrence.Item1.Time - @if (occurrence.Item2 == EventDefinition.GeneralSchedule) + @if (occurrence.Item2 == EventDefinition.GeneralSchedule || occurrence.Item2 == EventDefinition.MeetTheCandidates) { Everyone } - else if (occurrence.Item2 == EventDefinition.VotingDelegates) + else if (occurrence.Item2 == EventDefinition.ChapterOfficerMeeting) + { + Chapter Officers - @string.Join(", ", Model.Item2.Where(stu => stu.OfficerRole != null).Select(stu => stu.FirstName)) + } + else if (occurrence.Item2 == EventDefinition.VotingDelegateMeeting) { Voting Delegates - @string.Join(", ", Model.Item2.Where(stu => stu.VotingDelegate).Select(stu => stu.FirstName)) } diff --git a/Web-Original/Views/Home/State.cshtml b/Web-Original/Views/Home/State.cshtml index 378779b..02ea628 100644 --- a/Web-Original/Views/Home/State.cshtml +++ b/Web-Original/Views/Home/State.cshtml @@ -139,7 +139,9 @@ .Where(eo => student.Teams.Select(t => t.EventDefinition).Any(a => a == eo.Key) || eo.Key == EventDefinition.GeneralSchedule - || (eo.Key == EventDefinition.VotingDelegates && student.VotingDelegate)) + || eo.Key == EventDefinition.MeetTheCandidates + || (eo.Key == EventDefinition.ChapterOfficerMeeting && student.OfficerRole != null) + || (eo.Key == EventDefinition.VotingDelegateMeeting && 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) @@ -211,16 +213,24 @@ @foreach (var occurrence in eventsForDate.OrderBy(o => o.Item1.StartTime)) { var teams = Model.Item1.Where(t => t.EventDefinition == occurrence.Item2); - if (occurrence.Item2 != EventDefinition.GeneralSchedule && occurrence.Item2 != EventDefinition.VotingDelegates && !teams.Any()) + if (occurrence.Item2 != EventDefinition.GeneralSchedule + && occurrence.Item2 != EventDefinition.MeetTheCandidates + && occurrence.Item2 != EventDefinition.ChapterOfficerMeeting + && occurrence.Item2 != EventDefinition.VotingDelegateMeeting + && !teams.Any()) continue; @occurrence.Item1.Time - @if (occurrence.Item2 == EventDefinition.GeneralSchedule) + @if (occurrence.Item2 == EventDefinition.GeneralSchedule || occurrence.Item2 == EventDefinition.MeetTheCandidates) { Everyone } - else if (occurrence.Item2 == EventDefinition.VotingDelegates) + else if (occurrence.Item2 == EventDefinition.ChapterOfficerMeeting) + { + Chapter Officers - @string.Join(", ", Model.Item2.Where(stu => stu.OfficerRole != null).Select(stu => stu.FirstName)) + } + else if (occurrence.Item2 == EventDefinition.VotingDelegateMeeting) { Voting Delegates - @string.Join(", ", Model.Item2.Where(stu => stu.VotingDelegate).Select(stu => stu.FirstName)) } diff --git a/WebApp/Components/Features/Calendar/Import.razor b/WebApp/Components/Features/Calendar/Import.razor index 42668a6..bc3aed3 100644 --- a/WebApp/Components/Features/Calendar/Import.razor +++ b/WebApp/Components/Features/Calendar/Import.razor @@ -239,8 +239,14 @@ { if (eventDefinition == EventDefinition.GeneralSchedule) return "General Schedule"; - if (eventDefinition == EventDefinition.VotingDelegates) - return "Voting Delegates"; + if (eventDefinition == EventDefinition.MeetTheCandidates) + return "Meet the Candidates"; + if (eventDefinition == EventDefinition.ChapterOfficerMeeting) + return "Chapter Officer Meeting"; + if (eventDefinition == EventDefinition.VotingDelegateMeeting) + return "Voting Delegate Meeting"; + if (eventDefinition == EventDefinition.SocialGathering) + return "Social Gathering"; return eventDefinition.Name; } } diff --git a/WebApp/Components/Features/Calendar/Index.razor b/WebApp/Components/Features/Calendar/Index.razor index 29dbb51..ae44787 100644 --- a/WebApp/Components/Features/Calendar/Index.razor +++ b/WebApp/Components/Features/Calendar/Index.razor @@ -8,7 +8,7 @@ - Import + Import