diff --git a/Core/Models/EventOccurrenceParseResult.cs b/Core/Models/EventOccurrenceParseResult.cs
index 6eb5c1d..f7f4edd 100644
--- a/Core/Models/EventOccurrenceParseResult.cs
+++ b/Core/Models/EventOccurrenceParseResult.cs
@@ -52,6 +52,12 @@ public class EventOccurrenceParseResult
///
public int SkippedHSEventCount { get; set; }
+ ///
+ /// 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.
+ ///
+ public IDictionary Footnotes { get; set; } = new Dictionary();
+
///
/// Total number of event occurrences successfully parsed.
///
diff --git a/Core/Parsers/EventOccurrenceParser.cs b/Core/Parsers/EventOccurrenceParser.cs
index e10db52..e256f99 100644
--- a/Core/Parsers/EventOccurrenceParser.cs
+++ b/Core/Parsers/EventOccurrenceParser.cs
@@ -18,6 +18,11 @@ public class EventOccurrenceParserResult
public List SkippedMSSectionHeaders { get; set; } = new();
public int SkippedMSEventCount { get; set; }
public int SkippedHSEventCount { get; set; }
+ ///
+ /// Footnotes captured for each event definition. Footnotes are lines that start with "*" or are parenthetical notes.
+ /// Multiple footnotes are concatenated into a single string.
+ ///
+ public IDictionary Footnotes { get; set; } = new Dictionary();
}
public class EventOccurrenceParser
@@ -38,8 +43,9 @@ public class EventOccurrenceParser
var result = new EventOccurrenceParserResult();
var occurrences = result.Occurrences;
var issues = result.Issues;
+ var footnotes = result.Footnotes;
EventDefinition? currentEventDefinition = null;
- bool inContinuationMode = false;
+ bool inFootnoteMode = false;
SchoolLevel? currentSectionLevel = null;
var lines = File.ReadLines(_txtFile.FullName);
@@ -52,16 +58,16 @@ public class EventOccurrenceParser
// Skip empty lines
if (EventOccurrenceParsers.LineClassifier.IsEmptyLine(normalizedLine))
{
- // Empty lines break continuation mode
- inContinuationMode = false;
+ // Empty lines break footnote mode
+ inFootnoteMode = false;
continue;
}
// Skip comment lines (starting with "#") - use grammar parser
if (EventOccurrenceParsers.LineClassifier.IsCommentLine(normalizedLine))
{
- // Comment lines break continuation mode
- inContinuationMode = false;
+ // Comment lines break footnote mode
+ inFootnoteMode = false;
continue;
}
@@ -76,8 +82,8 @@ public class EventOccurrenceParser
{
var (eventNamePart, schoolLevel) = sectionHeader.Value;
- // Section headers break continuation mode
- inContinuationMode = false;
+ // Section headers break footnote mode
+ inFootnoteMode = false;
// Convert string school level to enum
var sectionSchoolLevel = SetCurrentSectionLevel(schoolLevel);
@@ -115,8 +121,8 @@ public class EventOccurrenceParser
// Check for General Schedule/Session using grammar parser
if (EventOccurrenceParsers.SectionHeaderMatcher.IsGeneralSchedule(normalizedLine))
{
- // General schedule breaks continuation mode
- inContinuationMode = false;
+ // General schedule breaks footnote mode
+ inFootnoteMode = false;
currentSectionLevel = null; // Reset section level
currentEventDefinition = EventDefinition.GeneralSchedule;
continue;
@@ -125,8 +131,8 @@ public class EventOccurrenceParser
// Also check for simple "MS" or "HS" in line (backward compatibility)
if (EventOccurrenceParsers.SectionHeaderMatcher.HasSchoolLevel(normalizedLine))
{
- // Section headers break continuation mode
- inContinuationMode = false;
+ // Section headers break footnote mode
+ inFootnoteMode = false;
// Extract school level from line
SchoolLevel? sectionSchoolLevel = null;
@@ -165,15 +171,29 @@ public class EventOccurrenceParser
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))
{
- inContinuationMode = true;
+ inFootnoteMode = true;
}
- // Skip continuation lines (in continuation mode OR line starts with "*" or is parenthetical)
- if (inContinuationMode || EventOccurrenceParsers.LineClassifier.IsContinuationLine(normalizedLine))
+ // Capture footnote lines (in footnote mode OR line starts with "*" or is parenthetical)
+ 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;
}
@@ -192,8 +212,8 @@ public class EventOccurrenceParser
continue;
}
- // Occurrence lines break continuation mode
- inContinuationMode = false;
+ // Occurrence lines break footnote mode
+ inFootnoteMode = false;
// Skip occurrences under sections that don't match the school level setting
if (ShouldSkipOccurrence(currentSectionLevel, result))
diff --git a/Core/Services/EventOccurrenceParserService.cs b/Core/Services/EventOccurrenceParserService.cs
index 12d0532..ced5090 100644
--- a/Core/Services/EventOccurrenceParserService.cs
+++ b/Core/Services/EventOccurrenceParserService.cs
@@ -114,6 +114,12 @@ public class EventOccurrenceParserService : IEventOccurrenceParserService
result.SkippedMSSectionHeaders.AddRange(parserResult.SkippedMSSectionHeaders);
result.SkippedMSEventCount = parserResult.SkippedMSEventCount;
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
if (parserResult.SkippedMSEventCount > 0)