Add class-teacher slots on SchoolClass with owner-only assign/clear.

EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-21 09:37:37 +03:00
co-authored by Cursor
parent 99eca80d6d
commit 4096d5306e
22 changed files with 992 additions and 22 deletions
+82
View File
@@ -0,0 +1,82 @@
namespace HSchool.People;
public enum ClassTeacherError
{
None,
UnknownSchool,
UnknownClass,
UnknownPerson,
NotTeacher,
}
public sealed record ClassTeacherOutcome(ClassTeacherError Error, Roster Roster);
/// <summary>
/// Assigns or clears the class-teacher slot on a <see cref="SchoolClass"/>. Not a
/// <see cref="PositionDef"/> — any hired teacher may cover several classes.
/// </summary>
public static class ClassTeachers
{
public static ClassTeacherOutcome UnknownSchool() =>
new(ClassTeacherError.UnknownSchool, new Roster([], [], []));
public static ClassTeacherOutcome Assign(Roster roster, string classId, string personId)
{
ArgumentNullException.ThrowIfNull(roster);
ArgumentNullException.ThrowIfNull(classId);
ArgumentNullException.ThrowIfNull(personId);
var index = IndexOf(roster.Classes, classId);
if (index < 0)
{
return new ClassTeacherOutcome(ClassTeacherError.UnknownClass, roster);
}
var person = roster.People.FirstOrDefault(candidate => candidate.Id.Equals(personId, StringComparison.Ordinal));
if (person is null)
{
return new ClassTeacherOutcome(ClassTeacherError.UnknownPerson, roster);
}
if (!person.IsStaff || !string.Equals(person.Position, Staffing.TeacherPosition, StringComparison.Ordinal))
{
return new ClassTeacherOutcome(ClassTeacherError.NotTeacher, roster);
}
return Replace(roster, index, personId);
}
public static ClassTeacherOutcome Clear(Roster roster, string classId)
{
ArgumentNullException.ThrowIfNull(roster);
ArgumentNullException.ThrowIfNull(classId);
var index = IndexOf(roster.Classes, classId);
if (index < 0)
{
return new ClassTeacherOutcome(ClassTeacherError.UnknownClass, roster);
}
return Replace(roster, index, null);
}
private static ClassTeacherOutcome Replace(Roster roster, int index, string? teacherId)
{
var classes = roster.Classes.ToArray();
classes[index] = classes[index] with { ClassTeacherId = teacherId };
return new ClassTeacherOutcome(ClassTeacherError.None, roster with { Classes = classes });
}
private static int IndexOf(IReadOnlyList<SchoolClass> classes, string classId)
{
for (var i = 0; i < classes.Count; i++)
{
if (classes[i].Id.Equals(classId, StringComparison.Ordinal))
{
return i;
}
}
return -1;
}
}
+3 -1
View File
@@ -142,4 +142,6 @@ public sealed record SchoolClass(
string Letter,
string RoomId,
int Capacity,
IReadOnlyList<string> PupilIds);
IReadOnlyList<string> PupilIds,
/// <summary>Hired teacher who is the class teacher, or null when the slot is empty.</summary>
string? ClassTeacherId = null);