Files
LehrerApp/LehrerApp.Data/Repositories/AllRepositories.cs
T
adminandClaude Sonnet 5 de6ea001e7 Unterrichtsplanung: Einheiten, Verlaufsplan-Editor, Kürzel-Katalog (Kapitel 4.1/4.2)
Neuer Tab "Planung" in GroupDetailView ersetzt den Platzhalter: Einheiten anlegen/bearbeiten/
als Vorlage in andere Gruppe kopieren, Stunden je Einheit mit Verschieben (inkl. Nachrücken
der Folgestunden). Stundeneditor als tabellarischer Verlaufsplan (Phase/Dauer/Tätigkeit/
Material/Kurzsymbol je Zeile, Uhrzeit aus optionalem Stundenbeginn abgeleitet) statt eines
einzelnen Phase-Felds mit Methoden-/Materialien-Chips — Kurzsymbol als Freitext mit
Vorschlägen aus neuem Kürzel-Katalog (Einstellungen) plus bisher verwendeten Werten.
Schema-Migrationen v1-v3 überführen bestehende Daten verlustfrei. 4.2.5 bewusst offen
gelassen (hängt an Stundenplan, Kapitel 4.3).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 00:50:57 +02:00

388 lines
18 KiB
C#

using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
namespace LehrerApp.Data.Repositories;
public class StudentRepository(LiteDbContext db) : IStudentRepository
{
public Student? GetById(Guid id) => db.Students.FindById(id);
public List<Student> GetAll(bool includeInactive = false) =>
(includeInactive ? db.Students.FindAll() : db.Students.Find(s => s.IsActive))
.OrderBy(s => s.LastName).ToList();
public List<Student> GetByGroup(Guid groupId)
{
var ids = db.Memberships
.Find(e => e.GroupId == groupId)
.Select(e => e.StudentId).ToHashSet();
return db.Students.Find(s => ids.Contains(s.Id)).OrderBy(s => s.LastName).ToList();
}
public void Save(Student s) { s.UpdatedAt = DateTime.UtcNow; db.Students.Upsert(s); }
public void Delete(Guid id) => db.Students.Delete(id);
}
public class GroupRepository(LiteDbContext db) : IGroupRepository
{
public LearningGroup? GetById(Guid id) => db.Groups.FindById(id);
public List<LearningGroup> GetAll(bool includeInactive = false) =>
(includeInactive ? db.Groups.FindAll() : db.Groups.Find(g => g.IsActive))
.OrderBy(g => g.SchoolYear).ThenBy(g => g.Name).ToList();
public List<LearningGroup> GetBySchoolYear(string schoolYear, bool includeInactive = false) =>
(includeInactive
? db.Groups.Find(g => g.SchoolYear == schoolYear)
: db.Groups.Find(g => g.SchoolYear == schoolYear && g.IsActive))
.OrderBy(g => g.Name).ToList();
public void Save(LearningGroup g)
{
if (g.SubjectId is Guid subjectId && db.Subjects.FindById(subjectId) is null)
throw new InvalidOperationException("Das gewählte Fach existiert nicht mehr.");
g.UpdatedAt = DateTime.UtcNow;
db.Groups.Upsert(g);
}
public void Delete(Guid id)
{
db.ExecuteInTransaction(() =>
{
foreach (var membership in db.Memberships.Find(e => e.GroupId == id).ToList())
db.Memberships.Delete(membership.Id);
foreach (var exam in db.Exams.Find(e => e.GroupId == id).ToList())
{
foreach (var result in db.ExamResults.Find(r => r.ExamId == exam.Id).ToList())
db.ExamResults.Delete(result.Id);
db.Exams.Delete(exam.Id);
}
foreach (var grade in db.Grades.Find(g => g.GroupId == id).ToList())
db.Grades.Delete(grade.Id);
foreach (var reportGrade in db.ReportGrades.Find(g => g.GroupId == id).ToList())
db.ReportGrades.Delete(reportGrade.Id);
foreach (var scheme in db.GradingSchemes.Find(s => s.GroupId == id).ToList())
db.GradingSchemes.Delete(scheme.Id);
foreach (var unit in db.Units.Find(u => u.GroupId == id).ToList())
{
foreach (var lesson in db.Lessons.Find(l => l.UnitId == unit.Id).ToList())
db.Lessons.Delete(lesson.Id);
db.Units.Delete(unit.Id);
}
foreach (var lesson in db.Lessons.Find(l => l.GroupId == id).ToList())
db.Lessons.Delete(lesson.Id);
foreach (var session in db.ParticipationSessions.Find(s => s.GroupId == id).ToList())
{
foreach (var entry in db.ParticipationEntries.Find(e => e.SessionId == session.Id).ToList())
db.ParticipationEntries.Delete(entry.Id);
db.ParticipationSessions.Delete(session.Id);
}
foreach (var aspect in db.ParticipationAspects.Find(a => a.GroupId == id).ToList())
db.ParticipationAspects.Delete(aspect.Id);
foreach (var section in db.ParticipationSections.Find(s => s.GroupId == id).ToList())
db.ParticipationSections.Delete(section.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())
{
documentation.GroupId = null;
documentation.UpdatedAt = DateTime.UtcNow;
db.Documentation.Update(documentation);
}
foreach (var task in db.Tasks.Find(t => t.GroupId == id).ToList())
{
task.GroupId = null;
task.UpdatedAt = DateTime.UtcNow;
db.Tasks.Update(task);
}
foreach (var timeEntry in db.TimeEntries.Find(t => t.GroupId == id).ToList())
{
timeEntry.GroupId = null;
db.TimeEntries.Update(timeEntry);
}
db.Groups.Delete(id);
});
}
}
public class GroupMembershipRepository(LiteDbContext db) : IGroupMembershipRepository
{
public List<GroupMembership> GetByStudent(Guid id) =>
db.Memberships.Find(e => e.StudentId == id).ToList();
public List<GroupMembership> GetByGroup(Guid id) =>
db.Memberships.Find(e => e.GroupId == id).ToList();
public GroupMembership? GetByStudentAndGroup(Guid studentId, Guid groupId) =>
db.Memberships.FindOne(e => e.StudentId == studentId && e.GroupId == groupId);
public void Save(GroupMembership membership)
{
var existing = GetByStudentAndGroup(membership.StudentId, membership.GroupId);
if (existing is not null && existing.Id != membership.Id)
throw new InvalidOperationException("Der Schüler ist dieser Lerngruppe bereits zugeordnet.");
db.Memberships.Upsert(membership);
}
public void Delete(Guid id) => db.Memberships.Delete(id);
}
public class ExamRepository(LiteDbContext db) : IExamRepository
{
public Exam? GetById(Guid id) => db.Exams.FindById(id);
public List<Exam> GetByGroup(Guid groupId) =>
db.Exams.Find(e => e.GroupId == groupId).OrderByDescending(e => e.Date).ToList();
public void Save(Exam e) { e.UpdatedAt = DateTime.UtcNow; db.Exams.Upsert(e); }
public void Delete(Guid id)
{
foreach (var result in db.ExamResults.Find(r => r.ExamId == id).ToList())
db.ExamResults.Delete(result.Id);
db.Exams.Delete(id);
}
}
public class ExamResultRepository(LiteDbContext db) : IExamResultRepository
{
public List<ExamResult> GetByExam(Guid id) =>
db.ExamResults.Find(r => r.ExamId == id).ToList();
public List<ExamResult> GetByStudent(Guid id) =>
db.ExamResults.Find(r => r.StudentId == id).ToList();
public ExamResult? GetByExamAndStudent(Guid examId, Guid studentId) =>
db.ExamResults.FindOne(r => r.ExamId == examId && r.StudentId == studentId);
public void Save(ExamResult r) { r.UpdatedAt = DateTime.UtcNow; db.ExamResults.Upsert(r); }
public void SaveMany(List<ExamResult> results)
{
var now = DateTime.UtcNow;
foreach (var r in results) r.UpdatedAt = now;
db.ExamResults.Upsert(results);
}
}
public class GradingKeyTemplateRepository(LiteDbContext db) : IGradingKeyTemplateRepository
{
public List<GradingKeyTemplate> GetAll() =>
db.GradingKeyTemplates.FindAll().OrderBy(t => t.Name).ToList();
public List<GradingKeyTemplate> GetByGradingSystem(GradingSystem system) =>
db.GradingKeyTemplates.Find(t => t.GradingSystem == system).OrderBy(t => t.Name).ToList();
public GradingKeyTemplate? GetById(Guid id) => db.GradingKeyTemplates.FindById(id);
public void Save(GradingKeyTemplate t) { t.UpdatedAt = DateTime.UtcNow; db.GradingKeyTemplates.Upsert(t); }
public void Delete(Guid id) => db.GradingKeyTemplates.Delete(id);
}
public class GradeRepository(LiteDbContext db) : IGradeRepository
{
public List<Grade> GetByStudentAndGroup(Guid sid, Guid gid) =>
db.Grades.Find(g => g.StudentId == sid && g.GroupId == gid).OrderBy(g => g.Date).ToList();
public List<Grade> GetByGroup(Guid id) =>
db.Grades.Find(g => g.GroupId == id).OrderBy(g => g.Date).ToList();
public void Save(Grade g) => db.Grades.Upsert(g);
public void Delete(Guid id) => db.Grades.Delete(id);
}
public class GradingSchemeRepository(LiteDbContext db) : IGradingSchemeRepository
{
public GradingScheme? GetByGroup(Guid groupId) => db.GradingSchemes.FindOne(s => s.GroupId == groupId);
public GradingScheme? GetDefaultForType(GroupType type) =>
db.GradingSchemes.FindOne(s => s.GroupId == null && s.GroupType == type);
public void Save(GradingScheme s) { s.UpdatedAt = DateTime.UtcNow; db.GradingSchemes.Upsert(s); }
public void Delete(Guid id) => db.GradingSchemes.Delete(id);
}
public class ReportGradeRepository(LiteDbContext db) : IReportGradeRepository
{
public List<ReportGrade> GetByGroup(Guid groupId) => db.ReportGrades.Find(r => r.GroupId == groupId).ToList();
public ReportGrade? GetByStudentGroupPeriod(Guid studentId, Guid groupId, string period) =>
db.ReportGrades.FindOne(r => r.StudentId == studentId && r.GroupId == groupId && r.Period == period);
public void Save(ReportGrade r) { r.UpdatedAt = DateTime.UtcNow; db.ReportGrades.Upsert(r); }
public void Delete(Guid id) => db.ReportGrades.Delete(id);
}
public class UnitRepository(LiteDbContext db) : IUnitRepository
{
public Unit? GetById(Guid id) => db.Units.FindById(id);
public List<Unit> GetByGroup(Guid id) =>
db.Units.Find(u => u.GroupId == id).OrderBy(u => u.StartDate).ToList();
public void Save(Unit u) { u.UpdatedAt = DateTime.UtcNow; db.Units.Upsert(u); }
public void Delete(Guid id) => db.Units.Delete(id);
}
public class LessonRepository(LiteDbContext db) : ILessonRepository
{
public List<Lesson> GetByUnit(Guid id) =>
db.Lessons.Find(l => l.UnitId == id).OrderBy(l => l.Date).ThenBy(l => l.LessonNumber).ToList();
public List<Lesson> GetByGroupAndDate(Guid gid, DateOnly date) =>
db.Lessons.Find(l => l.GroupId == gid && l.Date == date).ToList();
public List<Lesson> GetByGroupAndRange(Guid gid, DateOnly from, DateOnly to) =>
db.Lessons.Find(l => l.GroupId == gid && l.Date >= from && l.Date <= to)
.OrderBy(l => l.Date).ToList();
public void Save(Lesson l) { l.UpdatedAt = DateTime.UtcNow; db.Lessons.Upsert(l); }
public void Delete(Guid id) => db.Lessons.Delete(id);
}
public class DocumentationRepository(LiteDbContext db) : IDocumentationRepository
{
public List<Documentation> GetByStudent(Guid id) =>
db.Documentation.Find(d => d.StudentId == id && !d.IsDeleted).OrderByDescending(d => d.Date).ToList();
public List<Documentation> GetByStudentAndType(Guid sid, DocumentationType type) =>
db.Documentation.Find(d => d.StudentId == sid && d.Type == type && !d.IsDeleted)
.OrderByDescending(d => d.Date).ToList();
public List<Documentation> GetAll() =>
db.Documentation.Find(d => !d.IsDeleted).OrderByDescending(d => d.Date).ToList();
public void Save(Documentation d) { d.UpdatedAt = DateTime.UtcNow; db.Documentation.Upsert(d); }
public void Delete(Guid id)
{
var doc = db.Documentation.FindById(id);
if (doc is null) return;
doc.IsDeleted = true;
doc.DeletedAt = DateTime.UtcNow;
db.Documentation.Update(doc);
}
public void HardDelete(Guid id)
{
var doc = db.Documentation.FindById(id);
if (doc is not null)
foreach (var attachment in doc.Attachments) db.Attachments.Delete(attachment.StorageId);
db.Documentation.Delete(id);
}
}
public class WorkTaskRepository(LiteDbContext db) : IWorkTaskRepository
{
public List<WorkTask> GetByStatus(WorkTaskStatus s) =>
db.Tasks.Find(t => t.Status == s).OrderBy(t => t.DueDate).ToList();
public List<WorkTask> GetAll() =>
db.Tasks.FindAll().OrderBy(t => t.Status).ThenBy(t => t.DueDate).ToList();
public void Save(WorkTask t) { t.UpdatedAt = DateTime.UtcNow; db.Tasks.Upsert(t); }
public void Delete(Guid id) => db.Tasks.Delete(id);
}
public class TimeEntryRepository(LiteDbContext db) : ITimeEntryRepository
{
public List<TimeEntry> GetByDate(DateOnly date) =>
db.TimeEntries.Find(e => e.Date == date).OrderBy(e => e.StartTime).ToList();
public List<TimeEntry> GetByDateRange(DateOnly from, DateOnly to) =>
db.TimeEntries.Find(e => e.Date >= from && e.Date <= to).OrderBy(e => e.Date).ToList();
public List<TimeEntry> GetByTask(Guid id) =>
db.TimeEntries.Find(e => e.TaskId == id).ToList();
public void Save(TimeEntry e) => db.TimeEntries.Upsert(e);
public void Delete(Guid id) => db.TimeEntries.Delete(id);
}
public class ParticipationSessionRepository(LiteDbContext db) : IParticipationSessionRepository
{
public List<ParticipationSession> GetByGroup(Guid groupId) =>
db.ParticipationSessions.Find(s => s.GroupId == groupId).OrderByDescending(s => s.Date).ToList();
public ParticipationSession? GetById(Guid id) => db.ParticipationSessions.FindById(id);
public void Save(ParticipationSession s) { s.UpdatedAt = DateTime.UtcNow; db.ParticipationSessions.Upsert(s); }
public void Delete(Guid id)
{
db.ParticipationSessions.Delete(id);
foreach (var e in db.ParticipationEntries.Find(e => e.SessionId == id).ToList())
db.ParticipationEntries.Delete(e.Id);
}
}
public class ParticipationRepository(LiteDbContext db) : IParticipationRepository
{
public List<ParticipationEntry> GetBySession(Guid sessionId) =>
db.ParticipationEntries.Find(e => e.SessionId == sessionId).ToList();
public List<ParticipationEntry> GetByStudent(Guid studentId) =>
db.ParticipationEntries.Find(e => e.StudentId == studentId).ToList();
public ParticipationEntry? GetBySessionAndStudent(Guid sessionId, Guid studentId) =>
db.ParticipationEntries.FindOne(e => e.SessionId == sessionId && e.StudentId == studentId);
public void Save(ParticipationEntry e) { e.UpdatedAt = DateTime.UtcNow; db.ParticipationEntries.Upsert(e); }
public void SaveMany(List<ParticipationEntry> entries)
{
var now = DateTime.UtcNow;
foreach (var e in entries) e.UpdatedAt = now;
db.ParticipationEntries.Upsert(entries);
}
public void DeleteBySession(Guid sessionId)
{
foreach (var e in db.ParticipationEntries.Find(e => e.SessionId == sessionId).ToList())
db.ParticipationEntries.Delete(e.Id);
}
}
public class ParticipationAspectRepository(LiteDbContext db) : IParticipationAspectRepository
{
public List<ParticipationAspect> GetDefaults() =>
db.ParticipationAspects.Find(a => a.GroupId == null && a.IsActive).OrderBy(a => a.SortOrder).ToList();
public List<ParticipationAspect> GetByGroup(Guid groupId) =>
db.ParticipationAspects.Find(a => a.GroupId == groupId && a.IsActive).OrderBy(a => a.SortOrder).ToList();
public void Save(ParticipationAspect a) { a.UpdatedAt = DateTime.UtcNow; db.ParticipationAspects.Upsert(a); }
public void Delete(Guid id) => db.ParticipationAspects.Delete(id);
}
public class ParticipationSectionRepository(LiteDbContext db) : IParticipationSectionRepository
{
public List<ParticipationSection> GetByGroup(Guid groupId) =>
db.ParticipationSections.Find(s => s.GroupId == groupId).OrderBy(s => s.StartDate).ToList();
public void Save(ParticipationSection s) => db.ParticipationSections.Upsert(s);
public void Delete(Guid id) => db.ParticipationSections.Delete(id);
}
public class SubjectRepository(LiteDbContext db) : ISubjectRepository
{
public List<Subject> GetAll() => db.Subjects.FindAll().OrderBy(s => s.Name).ToList();
public Subject? GetById(Guid id) => db.Subjects.FindById(id);
public Subject? GetByName(string name) => db.Subjects.FindAll().FirstOrDefault(s =>
string.Equals(s.Name.Trim(), name.Trim(), StringComparison.OrdinalIgnoreCase));
public void Save(Subject s)
{
s.Name = s.Name.Trim();
s.ShortName = s.ShortName.Trim();
if (s.Name.Length == 0) throw new ArgumentException("Der Fachname darf nicht leer sein.");
var duplicate = GetByName(s.Name);
if (duplicate is not null && duplicate.Id != s.Id)
throw new InvalidOperationException("Ein Fach mit diesem Namen existiert bereits.");
s.UpdatedAt = DateTime.UtcNow;
db.Subjects.Upsert(s);
}
public void Delete(Guid id)
{
if (db.Groups.Exists(g => g.SubjectId == id) || db.CompetencyDomains.Exists(d => d.SubjectId == id))
throw new InvalidOperationException("Das Fach wird noch von einer Lerngruppe oder einem Kompetenzkatalog verwendet.");
db.Subjects.Delete(id);
}
}
public class ShorthandCodeRepository(LiteDbContext db) : IShorthandCodeRepository
{
public List<ShorthandCode> GetAll() => db.ShorthandCodes.FindAll().OrderBy(c => c.Code).ToList();
public void Save(ShorthandCode c)
{
c.Code = c.Code.Trim();
c.Label = c.Label.Trim();
if (c.Code.Length == 0) throw new ArgumentException("Das Kürzel darf nicht leer sein.");
var duplicate = db.ShorthandCodes.FindAll()
.FirstOrDefault(x => string.Equals(x.Code, c.Code, StringComparison.OrdinalIgnoreCase));
if (duplicate is not null && duplicate.Id != c.Id)
throw new InvalidOperationException("Ein Kürzel mit diesem Code existiert bereits.");
c.UpdatedAt = DateTime.UtcNow;
db.ShorthandCodes.Upsert(c);
}
public void Delete(Guid id) => db.ShorthandCodes.Delete(id);
}
public class CompetencyDomainRepository(LiteDbContext db) : ICompetencyDomainRepository
{
public List<CompetencyDomain> GetBySubjectAndGrade(Guid subjectId, int gradeLevel) =>
db.CompetencyDomains
.Find(d => d.SubjectId == subjectId && d.GradeLevel == gradeLevel)
.OrderBy(d => d.SortOrder)
.ToList();
public CompetencyDomain? GetById(Guid id) => db.CompetencyDomains.FindById(id);
public void Save(CompetencyDomain d) { d.UpdatedAt = DateTime.UtcNow; db.CompetencyDomains.Upsert(d); }
public void Delete(Guid id) => db.CompetencyDomains.Delete(id);
public void DeleteBySubjectAndGrade(Guid subjectId, int gradeLevel)
{
foreach (var d in db.CompetencyDomains
.Find(d => d.SubjectId == subjectId && d.GradeLevel == gradeLevel)
.ToList())
db.CompetencyDomains.Delete(d.Id);
}
}