feat: add seating plans for learning groups
This commit is contained in:
@@ -36,6 +36,7 @@ public class LiteDbContext : IDisposable
|
||||
|
||||
public ILiteCollection<Student> Students => _db.GetCollection<Student>("students");
|
||||
public ILiteCollection<LearningGroup> Groups => _db.GetCollection<LearningGroup>("groups");
|
||||
public ILiteCollection<SeatingPlan> SeatingPlans => _db.GetCollection<SeatingPlan>("seating_plans");
|
||||
public ILiteCollection<GroupMembership> Memberships => _db.GetCollection<GroupMembership>("group_memberships");
|
||||
public ILiteCollection<Exam> Exams => _db.GetCollection<Exam>("exams");
|
||||
public ILiteCollection<ExamResult> ExamResults => _db.GetCollection<ExamResult>("exam_results");
|
||||
@@ -333,6 +334,7 @@ public class LiteDbContext : IDisposable
|
||||
Students.EnsureIndex(x => x.IsActive);
|
||||
Groups.EnsureIndex(x => x.SchoolYear);
|
||||
Groups.EnsureIndex(x => x.IsActive);
|
||||
SeatingPlans.EnsureIndex(x => x.GroupId);
|
||||
Memberships.EnsureIndex(x => x.StudentId);
|
||||
Memberships.EnsureIndex(x => x.GroupId);
|
||||
Memberships.EnsureIndex("ux_student_group",
|
||||
|
||||
@@ -121,6 +121,9 @@ public class GroupRepository(LiteDbContext db) : IGroupRepository
|
||||
foreach (var section in db.ParticipationSections.Find(s => s.GroupId == id).ToList())
|
||||
db.ParticipationSections.Delete(section.Id);
|
||||
|
||||
foreach (var plan in db.SeatingPlans.Find(p => p.GroupId == id).ToList())
|
||||
db.SeatingPlans.Delete(plan.Id);
|
||||
|
||||
// Dokumentation und Arbeitszeit sind historische Nachweise. Sie bleiben erhalten,
|
||||
// werden aber von der nicht mehr existierenden Lerngruppe entkoppelt.
|
||||
foreach (var documentation in db.Documentation.Find(d => d.GroupId == id).ToList())
|
||||
@@ -148,6 +151,58 @@ public class GroupRepository(LiteDbContext db) : IGroupRepository
|
||||
}
|
||||
}
|
||||
|
||||
public class SeatingPlanRepository(LiteDbContext db) : ISeatingPlanRepository
|
||||
{
|
||||
public SeatingPlan? GetById(Guid id) => db.SeatingPlans.FindById(id);
|
||||
|
||||
public List<SeatingPlan> GetByGroup(Guid groupId) =>
|
||||
db.SeatingPlans.Find(p => p.GroupId == groupId)
|
||||
.OrderBy(p => p.Name).ThenBy(p => p.Room).ToList();
|
||||
|
||||
public void Save(SeatingPlan plan)
|
||||
{
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, plan.GroupId);
|
||||
plan.Name = plan.Name.Trim();
|
||||
plan.Room = plan.Room.Trim();
|
||||
if (plan.Name.Length == 0)
|
||||
throw new ArgumentException("Der Name des Sitzplans darf nicht leer sein.");
|
||||
if (plan.Rows is < 1 or > 10 || plan.Columns is < 1 or > 10)
|
||||
throw new ArgumentOutOfRangeException(nameof(plan), "Ein Sitzplan muss zwischen 1 und 10 Reihen und Spalten haben.");
|
||||
if (db.Groups.FindById(plan.GroupId) is null)
|
||||
throw new InvalidOperationException("Die zugehörige Lerngruppe existiert nicht.");
|
||||
|
||||
var duplicateName = db.SeatingPlans.Find(p => p.GroupId == plan.GroupId)
|
||||
.FirstOrDefault(p => p.Id != plan.Id
|
||||
&& string.Equals(p.Name, plan.Name, StringComparison.OrdinalIgnoreCase)
|
||||
&& string.Equals(p.Room, plan.Room, StringComparison.OrdinalIgnoreCase));
|
||||
if (duplicateName is not null)
|
||||
throw new InvalidOperationException("Für diese Lerngruppe existiert bereits ein gleichnamiger Sitzplan in diesem Raum.");
|
||||
|
||||
plan.Assignments = plan.Assignments
|
||||
.Where(a => a.Row >= 0 && a.Row < plan.Rows && a.Column >= 0 && a.Column < plan.Columns)
|
||||
.ToList();
|
||||
if (plan.Assignments.GroupBy(a => (a.Row, a.Column)).Any(g => g.Count() > 1))
|
||||
throw new InvalidOperationException("Ein Sitzplatz darf nur einmal belegt werden.");
|
||||
if (plan.Assignments.GroupBy(a => a.StudentId).Any(g => g.Count() > 1))
|
||||
throw new InvalidOperationException("Ein Schüler darf in einem Sitzplan nur einmal vorkommen.");
|
||||
|
||||
var memberIds = db.Memberships.Find(m => m.GroupId == plan.GroupId)
|
||||
.Select(m => m.StudentId).ToHashSet();
|
||||
if (plan.Assignments.Any(a => !memberIds.Contains(a.StudentId)))
|
||||
throw new InvalidOperationException("Der Sitzplan enthält einen Schüler, der nicht zur Lerngruppe gehört.");
|
||||
|
||||
plan.UpdatedAt = DateTime.UtcNow;
|
||||
db.SeatingPlans.Upsert(plan);
|
||||
}
|
||||
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
if (db.SeatingPlans.FindById(id) is { } plan)
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, plan.GroupId);
|
||||
db.SeatingPlans.Delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public class GroupMembershipRepository(LiteDbContext db) : IGroupMembershipRepository
|
||||
{
|
||||
public List<GroupMembership> GetByStudent(Guid id) =>
|
||||
|
||||
Reference in New Issue
Block a user