This commit is contained in:
2026-03-29 23:47:31 +02:00
commit 216d5d2280
75 changed files with 5702 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LehrerApp.Core\LehrerApp.Core.csproj" />
</ItemGroup>
<ItemGroup>
<!-- LiteDB 5.x ist die aktuelle stabile Version für .NET 9 -->
<PackageReference Include="LiteDB" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,117 @@
using LiteDB;
using LehrerApp.Core.Models;
namespace LehrerApp.Data;
/// <summary>
/// Zentrale Datenbankverbindung. Einmalig als Singleton registrieren.
/// Eine Instanz = eine .db Datei = ein Nutzer.
/// </summary>
public class LiteDbContext : IDisposable
{
private readonly LiteDatabase _db;
public LiteDbContext(string databasePath)
{
// WAL-Modus für bessere Performance bei Concurrent Reads
var connectionString = new ConnectionString(databasePath)
{
Connection = ConnectionType.Shared,
};
_db = new LiteDatabase(connectionString);
EnsureIndexes();
}
// ── Collections ──────────────────────────────────────────────────────────
public ILiteCollection<Student> Students =>
_db.GetCollection<Student>("students");
public ILiteCollection<LearningGroup> Groups =>
_db.GetCollection<LearningGroup>("groups");
public ILiteCollection<Enrollment> Enrollments =>
_db.GetCollection<Enrollment>("enrollments");
public ILiteCollection<Exam> Exams =>
_db.GetCollection<Exam>("exams");
public ILiteCollection<ExamResult> ExamResults =>
_db.GetCollection<ExamResult>("exam_results");
public ILiteCollection<Grade> Grades =>
_db.GetCollection<Grade>("grades");
public ILiteCollection<Unit> Units =>
_db.GetCollection<Unit>("units");
public ILiteCollection<Lesson> Lessons =>
_db.GetCollection<Lesson>("lessons");
public ILiteCollection<Documentation> Documentation =>
_db.GetCollection<Documentation>("documentation");
public ILiteCollection<WorkTask> Tasks =>
_db.GetCollection<WorkTask>("tasks");
public ILiteCollection<TimeEntry> TimeEntries =>
_db.GetCollection<TimeEntry>("time_entries");
// ── Transaktionen ─────────────────────────────────────────────────────────
public T Transaction<T>(Func<T> action) => _db.BeginTrans() ? action() : action();
public void Checkpoint() => _db.Checkpoint();
// ── Backup ───────────────────────────────────────────────────────────────
public void BackupTo(string targetPath)
{
_db.Checkpoint();
File.Copy(_db.UserVersion.ToString(), targetPath, overwrite: true);
}
// ── Indizes ──────────────────────────────────────────────────────────────
private void EnsureIndexes()
{
Students.EnsureIndex(x => x.LastName);
Students.EnsureIndex(x => x.IsActive);
Groups.EnsureIndex(x => x.SchoolYear);
Groups.EnsureIndex(x => x.Type);
Enrollments.EnsureIndex(x => x.StudentId);
Enrollments.EnsureIndex(x => x.GroupId);
Enrollments.EnsureIndex(x => x.SchoolYear);
Exams.EnsureIndex(x => x.GroupId);
Exams.EnsureIndex(x => x.Date);
Exams.EnsureIndex(x => x.Status);
ExamResults.EnsureIndex(x => x.ExamId);
ExamResults.EnsureIndex(x => x.StudentId);
Grades.EnsureIndex(x => x.StudentId);
Grades.EnsureIndex(x => x.GroupId);
Units.EnsureIndex(x => x.GroupId);
Units.EnsureIndex(x => x.Status);
Lessons.EnsureIndex(x => x.UnitId);
Lessons.EnsureIndex(x => x.GroupId);
Lessons.EnsureIndex(x => x.Date);
Documentation.EnsureIndex(x => x.StudentId);
Documentation.EnsureIndex(x => x.Type);
Tasks.EnsureIndex(x => x.Status);
Tasks.EnsureIndex(x => x.DueDate);
TimeEntries.EnsureIndex(x => x.Date);
TimeEntries.EnsureIndex(x => x.TaskId);
}
public void Dispose() => _db.Dispose();
}

View File

@@ -0,0 +1,95 @@
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
namespace LehrerApp.Data.Repositories;
public class GroupRepository(LiteDbContext db) : IGroupRepository
{
public LearningGroup? GetById(Guid id) =>
db.Groups.FindById(id);
public List<LearningGroup> GetAll() =>
db.Groups.FindAll()
.OrderBy(g => g.SchoolYear)
.ThenBy(g => g.Name)
.ToList();
public List<LearningGroup> GetBySchoolYear(string schoolYear) =>
db.Groups.Find(g => g.SchoolYear == schoolYear)
.OrderBy(g => g.Name)
.ToList();
public void Save(LearningGroup group)
{
group.UpdatedAt = DateTime.UtcNow;
db.Groups.Upsert(group);
}
public void Delete(Guid id) =>
db.Groups.Delete(id);
}
public class EnrollmentRepository(LiteDbContext db) : IEnrollmentRepository
{
public List<Enrollment> GetByStudent(Guid studentId) =>
db.Enrollments.Find(e => e.StudentId == studentId).ToList();
public List<Enrollment> GetByGroup(Guid groupId) =>
db.Enrollments.Find(e => e.GroupId == groupId).ToList();
public List<Enrollment> GetByGroupAndYear(Guid groupId, string schoolYear) =>
db.Enrollments
.Find(e => e.GroupId == groupId && e.SchoolYear == schoolYear)
.ToList();
public void Save(Enrollment enrollment) =>
db.Enrollments.Upsert(enrollment);
public void Delete(Guid id) =>
db.Enrollments.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 exam)
{
exam.UpdatedAt = DateTime.UtcNow;
db.Exams.Upsert(exam);
}
public void Delete(Guid id) =>
db.Exams.Delete(id);
}
public class ExamResultRepository(LiteDbContext db) : IExamResultRepository
{
public List<ExamResult> GetByExam(Guid examId) =>
db.ExamResults.Find(r => r.ExamId == examId).ToList();
public List<ExamResult> GetByStudent(Guid studentId) =>
db.ExamResults.Find(r => r.StudentId == studentId).ToList();
public ExamResult? GetByExamAndStudent(Guid examId, Guid studentId) =>
db.ExamResults.FindOne(r => r.ExamId == examId && r.StudentId == studentId);
public void Save(ExamResult result)
{
result.UpdatedAt = DateTime.UtcNow;
db.ExamResults.Upsert(result);
}
public void SaveMany(List<ExamResult> results)
{
var now = DateTime.UtcNow;
foreach (var r in results) r.UpdatedAt = now;
db.ExamResults.Upsert(results);
}
}

View File

@@ -0,0 +1,140 @@
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
namespace LehrerApp.Data.Repositories;
public class GradeRepository(LiteDbContext db) : IGradeRepository
{
public List<Grade> GetByStudentAndGroup(Guid studentId, Guid groupId) =>
db.Grades
.Find(g => g.StudentId == studentId && g.GroupId == groupId)
.OrderBy(g => g.Date)
.ToList();
public List<Grade> GetByGroup(Guid groupId) =>
db.Grades.Find(g => g.GroupId == groupId)
.OrderBy(g => g.Date)
.ToList();
public void Save(Grade grade) =>
db.Grades.Upsert(grade);
public void Delete(Guid id) =>
db.Grades.Delete(id);
}
public class UnitRepository(LiteDbContext db) : IUnitRepository
{
public Unit? GetById(Guid id) =>
db.Units.FindById(id);
public List<Unit> GetByGroup(Guid groupId) =>
db.Units.Find(u => u.GroupId == groupId)
.OrderBy(u => u.StartDate)
.ToList();
public void Save(Unit unit)
{
unit.UpdatedAt = DateTime.UtcNow;
db.Units.Upsert(unit);
}
public void Delete(Guid id) =>
db.Units.Delete(id);
}
public class LessonRepository(LiteDbContext db) : ILessonRepository
{
public List<Lesson> GetByUnit(Guid unitId) =>
db.Lessons.Find(l => l.UnitId == unitId)
.OrderBy(l => l.Date)
.ToList();
public List<Lesson> GetByGroupAndDate(Guid groupId, DateOnly date) =>
db.Lessons.Find(l => l.GroupId == groupId && l.Date == date).ToList();
public List<Lesson> GetByGroupAndRange(Guid groupId, DateOnly from, DateOnly to) =>
db.Lessons
.Find(l => l.GroupId == groupId && l.Date >= from && l.Date <= to)
.OrderBy(l => l.Date)
.ToList();
public void Save(Lesson lesson)
{
lesson.UpdatedAt = DateTime.UtcNow;
db.Lessons.Upsert(lesson);
}
public void Delete(Guid id) =>
db.Lessons.Delete(id);
}
public class DocumentationRepository(LiteDbContext db) : IDocumentationRepository
{
public List<Documentation> GetByStudent(Guid studentId) =>
db.Documentation.Find(d => d.StudentId == studentId)
.OrderByDescending(d => d.Date)
.ToList();
public List<Documentation> GetByStudentAndType(Guid studentId, DocumentationType type) =>
db.Documentation
.Find(d => d.StudentId == studentId && d.Type == type)
.OrderByDescending(d => d.Date)
.ToList();
public void Save(Documentation doc)
{
doc.UpdatedAt = DateTime.UtcNow;
db.Documentation.Upsert(doc);
}
public void Delete(Guid id) =>
db.Documentation.Delete(id);
}
public class WorkTaskRepository(LiteDbContext db) : IWorkTaskRepository
{
public List<WorkTask> GetByStatus(WorkTaskStatus status) =>
db.Tasks.Find(t => t.Status == status)
.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 task)
{
task.UpdatedAt = DateTime.UtcNow;
db.Tasks.Upsert(task);
}
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)
.ThenBy(e => e.StartTime)
.ToList();
public List<TimeEntry> GetByTask(Guid taskId) =>
db.TimeEntries.Find(e => e.TaskId == taskId).ToList();
public void Save(TimeEntry entry) =>
db.TimeEntries.Upsert(entry);
public void Delete(Guid id) =>
db.TimeEntries.Delete(id);
}

View File

@@ -0,0 +1,38 @@
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().OrderBy(s => s.LastName).ToList()
: db.Students.Find(s => s.IsActive).OrderBy(s => s.LastName).ToList();
public List<Student> GetByGroup(Guid groupId, string schoolYear)
{
// Enrollment als Bindeglied Schüler-IDs der Gruppe ermitteln
var studentIds = db.Enrollments
.Find(e => e.GroupId == groupId && e.SchoolYear == schoolYear)
.Select(e => e.StudentId)
.ToHashSet();
return db.Students
.Find(s => studentIds.Contains(s.Id))
.OrderBy(s => s.LastName)
.ToList();
}
public void Save(Student student)
{
student.UpdatedAt = DateTime.UtcNow;
db.Students.Upsert(student);
}
public void Delete(Guid id) =>
db.Students.Delete(id);
}