feat: merge event rankings and attribute badges into the page printer
Interview notes can print each student's ranked events and a shared attribute legend from the same catalog as the ranking index, without leftover table markup or collapsed answer space. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,8 @@ namespace Core.Printing;
|
||||
/// Unknown tokens are left unchanged. Known empty values become blank.
|
||||
/// <c>{{PageBreak}}</c> becomes a print page break after HTML conversion.
|
||||
/// <c>{{AnswerSpace}}</c> becomes ruled write-in space after HTML conversion.
|
||||
/// <c>{{RankedEvents}}</c> and <c>{{RankedStudents}}</c> become ranking-index badges.
|
||||
/// <c>{{Legend}}</c> becomes the shared attribute-mark legend.
|
||||
/// </summary>
|
||||
public static class NoteTemplateMerger
|
||||
{
|
||||
@@ -16,14 +18,29 @@ public static class NoteTemplateMerger
|
||||
|
||||
public const string AnswerSpaceToken = "AnswerSpace";
|
||||
public const string AnswerSpaceSentinel = "<!--tsa-answer-space-->";
|
||||
public const string AnswerSpaceHtml = "<div class=\"print-answer-space\"></div>";
|
||||
public const string AnswerSpaceHtml = "<div class=\"print-answer-space\"> </div>";
|
||||
|
||||
public const string LegendToken = "Legend";
|
||||
public const string LegendSentinel = "<!--tsa-legend-->";
|
||||
|
||||
public const string RankedEventsToken = "RankedEvents";
|
||||
public const string RankedStudentsToken = "RankedStudents";
|
||||
|
||||
public static readonly string[] HtmlFragmentTokens =
|
||||
[
|
||||
RankedEventsToken,
|
||||
RankedStudentsToken
|
||||
];
|
||||
|
||||
public static string HtmlFragmentSentinel(string name) => $"<!--tsa-html:{name}-->";
|
||||
|
||||
private readonly record struct LayoutToken(string Name, string Sentinel, string Html);
|
||||
|
||||
private static readonly LayoutToken[] LayoutTokens =
|
||||
[
|
||||
new(PageBreakToken, PageBreakSentinel, PageBreakHtml),
|
||||
new(AnswerSpaceToken, AnswerSpaceSentinel, AnswerSpaceHtml)
|
||||
new(AnswerSpaceToken, AnswerSpaceSentinel, AnswerSpaceHtml),
|
||||
new(LegendToken, LegendSentinel, PrintRankBadgeHtml.Legend())
|
||||
];
|
||||
|
||||
private static readonly Regex TokenRegex = new(@"\{\{([^}]+)\}\}", RegexOptions.Compiled);
|
||||
@@ -47,6 +64,12 @@ public static class NoteTemplateMerger
|
||||
return layout.Sentinel;
|
||||
}
|
||||
|
||||
foreach (var htmlName in HtmlFragmentTokens)
|
||||
{
|
||||
if (key.Equals(htmlName, StringComparison.OrdinalIgnoreCase))
|
||||
return HtmlFragmentSentinel(htmlName);
|
||||
}
|
||||
|
||||
return tokens.TryGetValue(key, out var value)
|
||||
? value ?? string.Empty
|
||||
: match.Value;
|
||||
@@ -56,7 +79,9 @@ public static class NoteTemplateMerger
|
||||
/// <summary>
|
||||
/// Turns layout sentinels into HTML after markdown has been rendered.
|
||||
/// </summary>
|
||||
public static string ApplyLayout(string? html)
|
||||
public static string ApplyLayout(
|
||||
string? html,
|
||||
IReadOnlyDictionary<string, string>? htmlFragments = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(html))
|
||||
return string.Empty;
|
||||
@@ -68,6 +93,18 @@ public static class NoteTemplateMerger
|
||||
.Replace(layout.Sentinel, layout.Html, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
foreach (var name in HtmlFragmentTokens)
|
||||
{
|
||||
var sentinel = HtmlFragmentSentinel(name);
|
||||
var fragment = htmlFragments is not null
|
||||
&& htmlFragments.TryGetValue(name, out var value)
|
||||
? value ?? string.Empty
|
||||
: string.Empty;
|
||||
html = html
|
||||
.Replace($"<p>{sentinel}</p>", fragment, StringComparison.Ordinal)
|
||||
.Replace(sentinel, fragment, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user