Files
chapter-organizer/Core/Printing/StudentRankTokens.cs
T
poprhythmandCursor 3712dba974 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>
2026-08-30 23:03:10 -04:00

58 lines
1.9 KiB
C#

using Core.Entities;
namespace Core.Printing;
/// <summary>
/// Per-student event-rank merge tokens. <c>Rank1</c> is the official event name
/// at rank 1; <c>Rank1.ShortName</c> is the catalog short name. Every rank
/// through <see cref="StudentEventRanking.MaxRank"/> is always a map key so a
/// missing preference prints blank instead of leaving <c>{{Rank5}}</c> visible.
/// </summary>
public static class StudentRankTokens
{
public static string NameToken(int rank) => $"Rank{rank}";
public static string ShortNameToken(int rank) => $"Rank{rank}.ShortName";
public static string AttributesToken(int rank) => $"Rank{rank}.Attributes";
public static IReadOnlyList<string> AllNames { get; } =
[
NoteTemplateMerger.RankedEventsToken,
.. Enumerable.Range(1, StudentEventRanking.MaxRank)
.SelectMany(rank => (string[])
[
NameToken(rank),
ShortNameToken(rank),
AttributesToken(rank)
])
];
public static Dictionary<string, string?> FromRankings(IEnumerable<StudentEventRanking>? rankings)
{
var map = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
for (var rank = 1; rank <= StudentEventRanking.MaxRank; rank++)
{
map[NameToken(rank)] = null;
map[ShortNameToken(rank)] = null;
map[AttributesToken(rank)] = null;
}
if (rankings is null)
return map;
foreach (var ranking in rankings)
{
if (ranking.Rank < 1 || ranking.Rank > StudentEventRanking.MaxRank)
continue;
var evt = ranking.EventDefinition;
map[NameToken(ranking.Rank)] = evt?.Name;
map[ShortNameToken(ranking.Rank)] = evt?.ShortName;
map[AttributesToken(ranking.Rank)] = EventAttributeMarks.For(evt);
}
return map;
}
}