Add footnotes support to event occurrence parsing

This commit introduces a new property for capturing footnotes in both the EventOccurrenceParseResult and EventOccurrenceParserResult classes. The EventOccurrenceParser has been updated to handle footnotes, which are identified by lines starting with "*" or as parenthetical notes. The logic for processing these footnotes has been integrated into the parsing flow, ensuring that they are correctly associated with their respective event definitions. Additionally, the EventOccurrenceParserService has been modified to copy footnotes from the parser result, enhancing the overall event parsing functionality.
This commit is contained in:
2026-01-09 10:30:53 -05:00
parent bcd0acb480
commit cf2c0d8068
3 changed files with 49 additions and 17 deletions
@@ -52,6 +52,12 @@ public class EventOccurrenceParseResult
/// </summary> /// </summary>
public int SkippedHSEventCount { get; set; } public int SkippedHSEventCount { get; set; }
/// <summary>
/// Footnotes captured for each event definition. Footnotes are lines that start with "*" or are parenthetical notes.
/// Multiple footnotes are concatenated into a single string. These provide additional context or information about the event occurrences.
/// </summary>
public IDictionary<EventDefinition, string> Footnotes { get; set; } = new Dictionary<EventDefinition, string>();
/// <summary> /// <summary>
/// Total number of event occurrences successfully parsed. /// Total number of event occurrences successfully parsed.
/// </summary> /// </summary>
+37 -17
View File
@@ -18,6 +18,11 @@ public class EventOccurrenceParserResult
public List<string> SkippedMSSectionHeaders { get; set; } = new(); public List<string> SkippedMSSectionHeaders { get; set; } = new();
public int SkippedMSEventCount { get; set; } public int SkippedMSEventCount { get; set; }
public int SkippedHSEventCount { get; set; } public int SkippedHSEventCount { get; set; }
/// <summary>
/// Footnotes captured for each event definition. Footnotes are lines that start with "*" or are parenthetical notes.
/// Multiple footnotes are concatenated into a single string.
/// </summary>
public IDictionary<EventDefinition, string> Footnotes { get; set; } = new Dictionary<EventDefinition, string>();
} }
public class EventOccurrenceParser public class EventOccurrenceParser
@@ -38,8 +43,9 @@ public class EventOccurrenceParser
var result = new EventOccurrenceParserResult(); var result = new EventOccurrenceParserResult();
var occurrences = result.Occurrences; var occurrences = result.Occurrences;
var issues = result.Issues; var issues = result.Issues;
var footnotes = result.Footnotes;
EventDefinition? currentEventDefinition = null; EventDefinition? currentEventDefinition = null;
bool inContinuationMode = false; bool inFootnoteMode = false;
SchoolLevel? currentSectionLevel = null; SchoolLevel? currentSectionLevel = null;
var lines = File.ReadLines(_txtFile.FullName); var lines = File.ReadLines(_txtFile.FullName);
@@ -52,16 +58,16 @@ public class EventOccurrenceParser
// Skip empty lines // Skip empty lines
if (EventOccurrenceParsers.LineClassifier.IsEmptyLine(normalizedLine)) if (EventOccurrenceParsers.LineClassifier.IsEmptyLine(normalizedLine))
{ {
// Empty lines break continuation mode // Empty lines break footnote mode
inContinuationMode = false; inFootnoteMode = false;
continue; continue;
} }
// Skip comment lines (starting with "#") - use grammar parser // Skip comment lines (starting with "#") - use grammar parser
if (EventOccurrenceParsers.LineClassifier.IsCommentLine(normalizedLine)) if (EventOccurrenceParsers.LineClassifier.IsCommentLine(normalizedLine))
{ {
// Comment lines break continuation mode // Comment lines break footnote mode
inContinuationMode = false; inFootnoteMode = false;
continue; continue;
} }
@@ -76,8 +82,8 @@ public class EventOccurrenceParser
{ {
var (eventNamePart, schoolLevel) = sectionHeader.Value; var (eventNamePart, schoolLevel) = sectionHeader.Value;
// Section headers break continuation mode // Section headers break footnote mode
inContinuationMode = false; inFootnoteMode = false;
// Convert string school level to enum // Convert string school level to enum
var sectionSchoolLevel = SetCurrentSectionLevel(schoolLevel); var sectionSchoolLevel = SetCurrentSectionLevel(schoolLevel);
@@ -115,8 +121,8 @@ public class EventOccurrenceParser
// Check for General Schedule/Session using grammar parser // Check for General Schedule/Session using grammar parser
if (EventOccurrenceParsers.SectionHeaderMatcher.IsGeneralSchedule(normalizedLine)) if (EventOccurrenceParsers.SectionHeaderMatcher.IsGeneralSchedule(normalizedLine))
{ {
// General schedule breaks continuation mode // General schedule breaks footnote mode
inContinuationMode = false; inFootnoteMode = false;
currentSectionLevel = null; // Reset section level currentSectionLevel = null; // Reset section level
currentEventDefinition = EventDefinition.GeneralSchedule; currentEventDefinition = EventDefinition.GeneralSchedule;
continue; continue;
@@ -125,8 +131,8 @@ public class EventOccurrenceParser
// Also check for simple "MS" or "HS" in line (backward compatibility) // Also check for simple "MS" or "HS" in line (backward compatibility)
if (EventOccurrenceParsers.SectionHeaderMatcher.HasSchoolLevel(normalizedLine)) if (EventOccurrenceParsers.SectionHeaderMatcher.HasSchoolLevel(normalizedLine))
{ {
// Section headers break continuation mode // Section headers break footnote mode
inContinuationMode = false; inFootnoteMode = false;
// Extract school level from line // Extract school level from line
SchoolLevel? sectionSchoolLevel = null; SchoolLevel? sectionSchoolLevel = null;
@@ -165,15 +171,29 @@ public class EventOccurrenceParser
continue; continue;
} }
// Check if line starts with "*" to enter continuation mode // Check if line starts with "*" to enter footnote mode
if (normalizedLine.TrimStart().StartsWith("*", StringComparison.Ordinal)) if (normalizedLine.TrimStart().StartsWith("*", StringComparison.Ordinal))
{ {
inContinuationMode = true; inFootnoteMode = true;
} }
// Skip continuation lines (in continuation mode OR line starts with "*" or is parenthetical) // Capture footnote lines (in footnote mode OR line starts with "*" or is parenthetical)
if (inContinuationMode || EventOccurrenceParsers.LineClassifier.IsContinuationLine(normalizedLine)) if (inFootnoteMode || EventOccurrenceParsers.LineClassifier.IsContinuationLine(normalizedLine))
{ {
// Capture footnote for current event definition if we have one
if (currentEventDefinition != null)
{
if (!footnotes.ContainsKey(currentEventDefinition))
{
footnotes[currentEventDefinition] = string.Empty;
}
// Append footnote, adding a space if there's already content
if (!string.IsNullOrEmpty(footnotes[currentEventDefinition]))
{
footnotes[currentEventDefinition] += " ";
}
footnotes[currentEventDefinition] += normalizedLine;
}
continue; continue;
} }
@@ -192,8 +212,8 @@ public class EventOccurrenceParser
continue; continue;
} }
// Occurrence lines break continuation mode // Occurrence lines break footnote mode
inContinuationMode = false; inFootnoteMode = false;
// Skip occurrences under sections that don't match the school level setting // Skip occurrences under sections that don't match the school level setting
if (ShouldSkipOccurrence(currentSectionLevel, result)) if (ShouldSkipOccurrence(currentSectionLevel, result))
@@ -115,6 +115,12 @@ public class EventOccurrenceParserService : IEventOccurrenceParserService
result.SkippedMSEventCount = parserResult.SkippedMSEventCount; result.SkippedMSEventCount = parserResult.SkippedMSEventCount;
result.SkippedHSEventCount = parserResult.SkippedHSEventCount; result.SkippedHSEventCount = parserResult.SkippedHSEventCount;
// Copy footnotes from parser result
foreach (var kvp in parserResult.Footnotes)
{
result.Footnotes[kvp.Key] = kvp.Value;
}
// Add informational messages about skipped events // Add informational messages about skipped events
if (parserResult.SkippedMSEventCount > 0) if (parserResult.SkippedMSEventCount > 0)
{ {