100 lines
3.8 KiB
C#
100 lines
3.8 KiB
C#
using LehrerApp.Core.Interfaces;
|
||
using LehrerApp.Core.Models;
|
||
|
||
namespace LehrerApp.Core.Services;
|
||
|
||
/// <summary>Gemeinsame Terminlogik für Verschieben und Trennen von Stunden.</summary>
|
||
public sealed class LessonSchedulingService(ILessonRepository lessons)
|
||
{
|
||
public void Move(Lesson lesson, DateOnly newDate, int? newPeriod, bool shiftFollowing)
|
||
{
|
||
EnsureTargetIsFree(lesson, newDate, newPeriod);
|
||
var oldDate = lesson.Date;
|
||
var delta = newDate.DayNumber - oldDate.DayNumber;
|
||
|
||
if (shiftFollowing && delta != 0)
|
||
{
|
||
foreach (var other in lessons.GetByUnit(lesson.UnitId))
|
||
{
|
||
if (other.Id == lesson.Id || other.Status == LessonStatus.Conducted || other.Date <= oldDate)
|
||
continue;
|
||
other.Date = other.Date.AddDays(delta);
|
||
lessons.Save(other);
|
||
}
|
||
}
|
||
|
||
lesson.Date = newDate;
|
||
if (newPeriod.HasValue) lesson.LessonNumber = newPeriod;
|
||
lessons.Save(lesson);
|
||
}
|
||
|
||
public Lesson SplitAndMoveSecondPart(Lesson source, int splitAfterMinutes, DateOnly newDate,
|
||
int newPeriod, TimeOnly? newStartTime)
|
||
{
|
||
if (splitAfterMinutes <= 0 || source.Phases.Sum(p => p.DurationMinutes) <= splitAfterMinutes)
|
||
throw new InvalidOperationException("Der Verlauf reicht nicht über die erste Stunde hinaus.");
|
||
EnsureTargetIsFree(source, newDate, newPeriod);
|
||
|
||
var first = new List<LessonPhaseStep>();
|
||
var second = new List<LessonPhaseStep>();
|
||
var elapsed = 0;
|
||
foreach (var phase in source.Phases)
|
||
{
|
||
var remainingInFirst = splitAfterMinutes - elapsed;
|
||
if (remainingInFirst <= 0)
|
||
second.Add(Clone(phase, phase.DurationMinutes));
|
||
else if (phase.DurationMinutes <= remainingInFirst)
|
||
first.Add(Clone(phase, phase.DurationMinutes));
|
||
else
|
||
{
|
||
first.Add(Clone(phase, remainingInFirst));
|
||
second.Add(Clone(phase, phase.DurationMinutes - remainingInFirst));
|
||
}
|
||
elapsed += phase.DurationMinutes;
|
||
}
|
||
|
||
source.Phases = first;
|
||
var continuation = new Lesson
|
||
{
|
||
UnitId = source.UnitId,
|
||
GroupId = source.GroupId,
|
||
Date = newDate,
|
||
LessonNumber = newPeriod,
|
||
StartTime = newStartTime,
|
||
Topic = string.IsNullOrWhiteSpace(source.Topic) ? "Fortsetzung" : $"{source.Topic} – Fortsetzung",
|
||
Phases = second,
|
||
Homework = source.Homework,
|
||
HomeworkChecked = source.HomeworkChecked,
|
||
HomeworkCheckDismissed = source.HomeworkCheckDismissed,
|
||
Reflection = source.Reflection,
|
||
Status = source.Status == LessonStatus.Conducted ? LessonStatus.Draft : source.Status,
|
||
};
|
||
source.Homework = null;
|
||
source.HomeworkChecked = false;
|
||
source.HomeworkCheckDismissed = false;
|
||
source.Reflection = null;
|
||
lessons.Save(source);
|
||
lessons.Save(continuation);
|
||
return continuation;
|
||
}
|
||
|
||
private void EnsureTargetIsFree(Lesson source, DateOnly date, int? period)
|
||
{
|
||
if (period is null) return;
|
||
var occupied = lessons.GetByGroupAndDate(source.GroupId, date)
|
||
.Any(l => l.Id != source.Id && l.LessonNumber == period);
|
||
if (occupied)
|
||
throw new InvalidOperationException($"Für die Lerngruppe existiert am {date:dd.MM.yyyy} in der {period}. Stunde bereits eine Planung.");
|
||
}
|
||
|
||
private static LessonPhaseStep Clone(LessonPhaseStep source, int duration) => new()
|
||
{
|
||
Name = source.Name,
|
||
DurationMinutes = duration,
|
||
Activity = source.Activity,
|
||
Material = source.Material,
|
||
Shorthand = source.Shorthand,
|
||
AlternativePathId = source.AlternativePathId,
|
||
};
|
||
}
|