diff --git a/LehrerApp.Desktop.Tests/DashboardViewModelTests.cs b/LehrerApp.Desktop.Tests/DashboardViewModelTests.cs index 3cfc311..fbbac5f 100644 --- a/LehrerApp.Desktop.Tests/DashboardViewModelTests.cs +++ b/LehrerApp.Desktop.Tests/DashboardViewModelTests.cs @@ -40,7 +40,8 @@ public sealed class DashboardViewModelTests FakeReportGrades? reportGrades = null, FakeMemberships? memberships = null, FakeWorkTasks? tasks = null, FakeStudents? students = null, FakeDocumentation? documentation = null, DashboardSettingsService? dashboardSettings = null, FakeSchoolHolidays? schoolHolidays = null, - FakeLessons? lessons = null, FakeSubstitutionEntries? substitutions = null) + FakeLessons? lessons = null, FakeSubstitutionEntries? substitutions = null, + FakeSessions? sessions = null) { lessons ??= new FakeLessons(); lessons.Add(lesson); @@ -48,7 +49,7 @@ public sealed class DashboardViewModelTests new FakeGroups([group]), new FakeSubjects([]), lessons, exams ?? new FakeExams([]), results ?? new FakeResults(), grades ?? new FakeGrades(), reportGrades ?? new FakeReportGrades(), memberships ?? new FakeMemberships([]), - tasks ?? new FakeWorkTasks(), new FakeSessions([]), new FakeEntries(), + tasks ?? new FakeWorkTasks(), sessions ?? new FakeSessions([]), new FakeEntries(), students ?? new FakeStudents([]), documentation ?? new FakeDocumentation(), slots ?? new FakeTimetableSlots(), periodSchedule ?? NewPeriodSchedule(), new AttendanceBalanceService(), new SchoolYearService(), dashboardSettings ?? NewDashboardSettings(), @@ -223,6 +224,25 @@ public sealed class DashboardViewModelTests Assert.Contains(vm.SelectedDayEvents, e => e.Kind == CalendarEventKind.Exam && e.Title == "Test"); } + [Fact] + public void Kalender_ZeigtMitarbeitssitzungenAlsEigenenTermintyp() + { + var today = DateOnly.FromDateTime(DateTime.Today); + var group = new LearningGroup { Name = "9c" }; + var sessionDate = today.AddDays(3); + var session = new ParticipationSession { GroupId = group.Id, Date = sessionDate, Comment = "Aufsatz" }; + var vm = BuildVm(group, new Lesson { GroupId = group.Id, Date = today }, + sessions: new FakeSessions([session])); + + var day = vm.CalendarDays.Single(d => d.Date == sessionDate); + Assert.True(day.HasSession); + + vm.SelectCalendarDayCommand.Execute(day); + + Assert.Contains(vm.SelectedDayEvents, + e => e.Kind == CalendarEventKind.ParticipationSession && e.Subtitle == "Aufsatz"); + } + [Fact] public void DashboardKacheln_SichtbarkeitUndReihenfolgeWerdenGespeichert() { diff --git a/LehrerApp.Desktop.Tests/GenerateLessonSeriesDialogViewModelTests.cs b/LehrerApp.Desktop.Tests/GenerateLessonSeriesDialogViewModelTests.cs index 9c71427..fa182d5 100644 --- a/LehrerApp.Desktop.Tests/GenerateLessonSeriesDialogViewModelTests.cs +++ b/LehrerApp.Desktop.Tests/GenerateLessonSeriesDialogViewModelTests.cs @@ -85,7 +85,7 @@ public sealed class GenerateLessonSeriesDialogViewModelTests } [Fact] - public void Save_Doppelstunde_ErzeugtZweiStundenAmSelbenTagMitUnterschiedlicherStundennummer() + public void Save_Doppelstunde_ErzeugtNurEineStundeVerankertAnDerErstenPeriode() { var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); @@ -98,9 +98,50 @@ public sealed class GenerateLessonSeriesDialogViewModelTests vm.SaveCommand.Execute(null); + Assert.Equal(1, vm.Result!.Created); + var created = lessons.GetByGroupAndRange(groupId, from, from).Select(l => l.LessonNumber).ToList(); + Assert.Equal([3], created); + } + + [Fact] + public void Save_Dreifachstunde_ErzeugtNurEineStundeVerankertAnDerErstenPeriode() + { + var groupId = Guid.NewGuid(); + var unitId = Guid.NewGuid(); + var from = new DateOnly(2025, 9, 1); + var slots = new FakeTimetableSlots(); + slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 3 }); + slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 4 }); + slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 5 }); + var lessons = new FakeLessons(); + var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, from); + + vm.SaveCommand.Execute(null); + + Assert.Equal(1, vm.Result!.Created); + var created = lessons.GetByGroupAndRange(groupId, from, from).Select(l => l.LessonNumber).ToList(); + Assert.Equal([3], created); + } + + [Fact] + public void Save_ZweiGetrennteEinzelstundenAmSelbenTagBleibenGetrennt() + { + // Perioden 1 und 3 sind NICHT aufeinanderfolgend (Periode 2 fehlt) — keine Doppelstunde, + // beide bekommen eine eigene Lesson. + var groupId = Guid.NewGuid(); + var unitId = Guid.NewGuid(); + var from = new DateOnly(2025, 9, 1); + var slots = new FakeTimetableSlots(); + slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 1 }); + slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 3 }); + var lessons = new FakeLessons(); + var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, from); + + vm.SaveCommand.Execute(null); + Assert.Equal(2, vm.Result!.Created); var created = lessons.GetByGroupAndRange(groupId, from, from).Select(l => l.LessonNumber).OrderBy(n => n).ToList(); - Assert.Equal([3, 4], created); + Assert.Equal([1, 3], created); } [Fact] diff --git a/LehrerApp.Desktop.Tests/GroupDetailViewModelTests.cs b/LehrerApp.Desktop.Tests/GroupDetailViewModelTests.cs index 3bed1c1..ff28984 100644 --- a/LehrerApp.Desktop.Tests/GroupDetailViewModelTests.cs +++ b/LehrerApp.Desktop.Tests/GroupDetailViewModelTests.cs @@ -22,7 +22,7 @@ public sealed class GroupDetailViewModelTests students, memberships, groups, new FakeCompetencyDomains()), new GradeOverviewTabViewModel(grades, exams, new FakeResults(), students, memberships, new GradingService()), new PlanningTabViewModel(new FakeUnits(), new FakeLessons(), groups, subjects, - new FakeCompetencyDomains(), TestSupport.BuildAiSettingsService()), + new FakeCompetencyDomains(), TestSupport.BuildAiSettingsService(), new FakeSessions([])), new CompetencyOverviewTabViewModel(new FakeUnits(), exams, new FakeResults(), new FakeCompetencyDomains(), students, new CompetencyAnalysisService()), new SeatingPlanTabViewModel(new FakeSeatingPlans(), students, memberships, diff --git a/LehrerApp.Desktop.Tests/ParticipationSessionEditingTests.cs b/LehrerApp.Desktop.Tests/ParticipationSessionEditingTests.cs index 5950e7c..6cd5dbe 100644 --- a/LehrerApp.Desktop.Tests/ParticipationSessionEditingTests.cs +++ b/LehrerApp.Desktop.Tests/ParticipationSessionEditingTests.cs @@ -59,4 +59,49 @@ public sealed class ParticipationSessionEditingTests Assert.Equal("Nachbesprechung Elektrizität", sessions.GetById(session.Id)?.Comment); Assert.Contains("Nachbesprechung Elektrizität", vm.SelectedSessionDisplay); } + + [Fact] + public async Task LoeschenBefehl_FragtBestaetigungAbUndEntferntSessionMitsamtBewertungen() + { + var group = new LearningGroup { Name = "8a", SchoolYear = "2026/27" }; + var session = new ParticipationSession { GroupId = group.Id, Date = new DateOnly(2026, 8, 19) }; + var sessions = new FakeSessions([session]); + var entries = new FakeEntries(); + entries.Add(new ParticipationEntry { SessionId = session.Id, StudentId = Guid.NewGuid() }); + var vm = new ParticipationTabViewModel( + sessions, entries, new FakeAspects(), new FakeStudents([]), + new FakeMemberships([]), new FakeGroups([group]), new FakeCompetencyDomains()); + vm.Initialize(group.Id, group.SchoolYear); + vm.SelectedSession = vm.Sessions.Single(s => s.Id == session.Id); + + int? passedEntryCount = null; + vm.OnConfirmDeleteSession = (_, count) => + { + passedEntryCount = count; + return Task.FromResult(true); + }; + + await vm.DeleteSessionCommand.ExecuteAsync(null); + + Assert.Equal(1, passedEntryCount); + Assert.Null(sessions.GetById(session.Id)); + } + + [Fact] + public async Task LoeschenBefehl_BrichtBeiAbgelehnterBestaetigungAb() + { + var group = new LearningGroup { Name = "8a", SchoolYear = "2026/27" }; + var session = new ParticipationSession { GroupId = group.Id, Date = new DateOnly(2026, 8, 19) }; + var sessions = new FakeSessions([session]); + var vm = new ParticipationTabViewModel( + sessions, new FakeEntries(), new FakeAspects(), new FakeStudents([]), + new FakeMemberships([]), new FakeGroups([group]), new FakeCompetencyDomains()); + vm.Initialize(group.Id, group.SchoolYear); + vm.SelectedSession = vm.Sessions.Single(s => s.Id == session.Id); + vm.OnConfirmDeleteSession = (_, _) => Task.FromResult(false); + + await vm.DeleteSessionCommand.ExecuteAsync(null); + + Assert.NotNull(sessions.GetById(session.Id)); + } } diff --git a/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs b/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs index 92ac451..89b9e40 100644 --- a/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs +++ b/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs @@ -20,7 +20,7 @@ public class PlanningTabViewModelTests var lessons = new FakeLessons(); var vm = new PlanningTabViewModel(units, lessons, groups, subjects, competencyDomains, - TestSupport.BuildAiSettingsService()); + TestSupport.BuildAiSettingsService(), new FakeSessions([])); vm.Initialize(groupId); return (vm, units, lessons, groupId); @@ -227,4 +227,71 @@ public class PlanningTabViewModelTests Assert.Equal(["L->AB01", "Plenum"], vm.KnownShorthands); } + + [Fact] + public void CreateParticipationSession_LegtSitzungMitDatumUndThemaDerStundeAn() + { + var groupId = Guid.NewGuid(); + var group = new LearningGroup { Id = groupId, Name = "Testgruppe" }; + var groups = new FakeGroups([group]); + var units = new FakeUnits(); + var lessons = new FakeLessons(); + var sessions = new FakeSessions([]); + var vm = new PlanningTabViewModel(units, lessons, groups, new FakeSubjects([]), + new FakeCompetencyDomains(), TestSupport.BuildAiSettingsService(), sessions); + vm.Initialize(groupId); + + var unit = new Unit { GroupId = groupId, Title = "Optik" }; + units.Add(unit); + var lesson = new Lesson + { + UnitId = unit.Id, GroupId = groupId, Date = new DateOnly(2025, 9, 1), Topic = "Brechung", + }; + lessons.Add(lesson); + vm.RefreshPlanning(unit.Id, lesson.Id); + vm.SelectedLesson = vm.Lessons.Single(l => l.Id == lesson.Id); + + string? notified = null; + vm.OnNotify = m => notified = m; + + vm.CreateParticipationSessionCommand.Execute(null); + + var created = Assert.Single(sessions.GetByGroup(groupId)); + Assert.Equal(lesson.Id, created.LessonId); + Assert.Equal(lesson.Date, created.Date); + Assert.Equal("Brechung", created.Comment); + Assert.Equal("Sitzung aus der Stunde erstellt.", notified); + } + + [Fact] + public void CreateParticipationSession_ZweiterAufrufLegtKeineDoppelteSitzungAn() + { + var groupId = Guid.NewGuid(); + var group = new LearningGroup { Id = groupId, Name = "Testgruppe" }; + var groups = new FakeGroups([group]); + var units = new FakeUnits(); + var lessons = new FakeLessons(); + var sessions = new FakeSessions([]); + var vm = new PlanningTabViewModel(units, lessons, groups, new FakeSubjects([]), + new FakeCompetencyDomains(), TestSupport.BuildAiSettingsService(), sessions); + vm.Initialize(groupId); + + var unit = new Unit { GroupId = groupId, Title = "Optik" }; + units.Add(unit); + var lesson = new Lesson + { + UnitId = unit.Id, GroupId = groupId, Date = new DateOnly(2025, 9, 1), Topic = "Brechung", + }; + lessons.Add(lesson); + vm.RefreshPlanning(unit.Id, lesson.Id); + vm.SelectedLesson = vm.Lessons.Single(l => l.Id == lesson.Id); + + vm.CreateParticipationSessionCommand.Execute(null); + string? notified = null; + vm.OnNotify = m => notified = m; + vm.CreateParticipationSessionCommand.Execute(null); + + Assert.Single(sessions.GetByGroup(groupId)); + Assert.Equal("Für diese Stunde existiert bereits eine Sitzung.", notified); + } } diff --git a/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs b/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs index 9f4c393..20c3277 100644 --- a/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs +++ b/LehrerApp.Desktop/ViewModels/DashboardViewModel.cs @@ -554,6 +554,16 @@ public partial class DashboardViewModel : ObservableObject agg.Details.Add(new CalendarEventItem(CalendarEventKind.Exam, exam.Date, exam.Title, g.Name, g.Id)); } + + foreach (var session in _participationSessions.GetByGroup(g.Id) + .Where(s => s.Date >= gridStart && s.Date <= gridEnd)) + { + var agg = Agg(session.Date); + agg.HasSession = true; + if (g.IsOwnClass) agg.IsOwnClassDay = true; + agg.Details.Add(new CalendarEventItem(CalendarEventKind.ParticipationSession, session.Date, + g.Name, session.Comment ?? "", g.Id)); + } } for (var i = 0; i < 42; i++) @@ -561,8 +571,8 @@ public partial class DashboardViewModel : ObservableObject var date = gridStart.AddDays(i); byDay.TryGetValue(date, out var agg); CalendarDays.Add(new CalendarDayCell(date, date.Month == firstOfMonth.Month, date == today, - agg?.HasLesson ?? false, agg?.HasExam ?? false, agg?.IsOwnClassDay ?? false, - agg?.Details ?? [])); + agg?.HasLesson ?? false, agg?.HasExam ?? false, agg?.HasSession ?? false, + agg?.IsOwnClassDay ?? false, agg?.Details ?? [])); } SelectCalendarDay(CalendarDays.FirstOrDefault(d => d.Date == today && d.IsCurrentMonth) ?? CalendarDays.First(d => d.IsCurrentMonth)); @@ -583,7 +593,7 @@ public partial class DashboardViewModel : ObservableObject { if (item is null) return; if (item.Kind == CalendarEventKind.Exam) OnNavigateToExam?.Invoke(item.GroupId); - else OnNavigateToLesson?.Invoke(item.GroupId); + else OnNavigateToLesson?.Invoke(item.GroupId); // auch für ParticipationSession: Tab "Mitarbeit" } [RelayCommand] @@ -633,6 +643,7 @@ public partial class DashboardViewModel : ObservableObject { public bool HasLesson; public bool HasExam; + public bool HasSession; public bool IsOwnClassDay; public List Details { get; } = []; } @@ -716,12 +727,13 @@ public partial class CalendarDayCell : ObservableObject public bool IsToday { get; } public bool HasLesson { get; } public bool HasExam { get; } + public bool HasSession { get; } public bool IsOwnClassDay { get; } public string Tooltip { get; } public IReadOnlyList Events { get; } internal CalendarDayCell(DateOnly date, bool isCurrentMonth, bool isToday, - bool hasLesson, bool hasExam, bool isOwnClassDay, List details) + bool hasLesson, bool hasExam, bool hasSession, bool isOwnClassDay, List details) { Date = date; DayNumber = date.Day; @@ -729,6 +741,7 @@ public partial class CalendarDayCell : ObservableObject IsToday = isToday; HasLesson = hasLesson; HasExam = hasExam; + HasSession = hasSession; IsOwnClassDay = isOwnClassDay; Events = details; Tooltip = details.Count == 0 ? date.ToString("dd.MM.yyyy") @@ -736,7 +749,7 @@ public partial class CalendarDayCell : ObservableObject } } -public enum CalendarEventKind { Lesson, Exam } +public enum CalendarEventKind { Lesson, Exam, ParticipationSession } public sealed class CalendarEventItem(CalendarEventKind kind, DateOnly date, string title, string subtitle, Guid groupId) @@ -746,7 +759,12 @@ public sealed class CalendarEventItem(CalendarEventKind kind, DateOnly date, str public string Title { get; } = title; public string Subtitle { get; } = subtitle; public Guid GroupId { get; } = groupId; - public string KindLabel => Kind == CalendarEventKind.Exam ? "Klausur" : "Unterricht"; + public string KindLabel => Kind switch + { + CalendarEventKind.Exam => "Klausur", + CalendarEventKind.ParticipationSession => "Sitzung", + _ => "Unterricht" + }; } public enum UpcomingDateKind { Exam, SupportPlan, Deadline } diff --git a/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs b/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs index ea6ef03..b42f23a 100644 --- a/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs +++ b/LehrerApp.Desktop/ViewModels/Groups/ParticipationViewModels.cs @@ -54,6 +54,7 @@ public partial class ParticipationTabViewModel : ObservableObject public Func? OnComputeGrade { get; set; } public Func? OnOpenWizard { get; set; } public Func? OnManageAspects { get; set; } + public Func>? OnConfirmDeleteSession { get; set; } public ParticipationTabViewModel( IParticipationSessionRepository sessions, @@ -120,6 +121,7 @@ public partial class ParticipationTabViewModel : ObservableObject { OnPropertyChanged(nameof(SelectedSessionDisplay)); EditSessionCommand.NotifyCanExecuteChanged(); + DeleteSessionCommand.NotifyCanExecuteChanged(); if (value is null) { StudentRows.Clear(); @@ -282,7 +284,11 @@ public partial class ParticipationTabViewModel : ObservableObject private bool CanEditSession() => !IsReadOnly && SelectedSession is not null; - partial void OnIsReadOnlyChanged(bool value) => EditSessionCommand.NotifyCanExecuteChanged(); + partial void OnIsReadOnlyChanged(bool value) + { + EditSessionCommand.NotifyCanExecuteChanged(); + DeleteSessionCommand.NotifyCanExecuteChanged(); + } [RelayCommand(CanExecute = nameof(CanQuickInput))] private async Task QuickInput() @@ -325,14 +331,20 @@ public partial class ParticipationTabViewModel : ObservableObject if (SelectedSession is not null) LoadGrid(SelectedSession.Id); } - [RelayCommand] - private void DeleteSession() + [RelayCommand(CanExecute = nameof(CanDeleteSession))] + private async Task DeleteSession() { if (SelectedSession is null) return; + var entryCount = _entries.GetBySession(SelectedSession.Id).Count; + if (OnConfirmDeleteSession is not null + && !await OnConfirmDeleteSession(SelectedSession, entryCount)) return; + _sessions.Delete(SelectedSession.Id); LoadSessions(); } + private bool CanDeleteSession() => !IsReadOnly && SelectedSession is not null; + public void SaveNote(Guid sessionId, Guid studentId, string note) { var entry = _entries.GetBySessionAndStudent(sessionId, studentId); diff --git a/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs b/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs index 7abcdc4..ebd9652 100644 --- a/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs +++ b/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs @@ -32,6 +32,7 @@ public partial class PlanningTabViewModel : ObservableObject private readonly ISubjectRepository _subjects; private readonly ICompetencyDomainRepository _competencyDomains; private readonly AiSettingsService _aiSettings; + private readonly IParticipationSessionRepository _participationSessions; private Guid _groupId; @@ -75,14 +76,16 @@ public partial class PlanningTabViewModel : ObservableObject public Func? OnShowLesson { get; set; } public Func>? OnGenerateLessonSeries { get; set; } public Func>? OnAiAssist { get; set; } + public Action? OnNotify { get; set; } public PlanningTabViewModel(IUnitRepository units, ILessonRepository lessons, IGroupRepository groups, ISubjectRepository subjects, - ICompetencyDomainRepository competencyDomains, AiSettingsService aiSettings) + ICompetencyDomainRepository competencyDomains, AiSettingsService aiSettings, + IParticipationSessionRepository participationSessions) { _units = units; _lessons = lessons; _groups = groups; _subjects = subjects; _competencyDomains = competencyDomains; - _aiSettings = aiSettings; + _aiSettings = aiSettings; _participationSessions = participationSessions; } public void Initialize(Guid groupId, bool isReadOnly = false) @@ -367,6 +370,32 @@ public partial class PlanningTabViewModel : ObservableObject _lessons.Save(lesson); LoadUnits(); } + + /// Übernimmt Datum + Thema der Stunde in eine neue Mitarbeitssitzung (3.3.1) — verknüpft über + /// das bisher ungenutzte Lesson.LessonId-Feld auf ParticipationSession, damit ein zweiter Klick + /// auf dieselbe Stunde keine doppelte Sitzung anlegt, sondern nur darauf hinweist. + [RelayCommand(CanExecute = nameof(HasSelectedLesson))] + private void CreateParticipationSession() + { + if (SelectedLesson is null) return; + var lesson = SelectedLesson.Model; + var existing = _participationSessions.GetByGroup(lesson.GroupId) + .FirstOrDefault(s => s.LessonId == lesson.Id); + if (existing is not null) + { + OnNotify?.Invoke("Für diese Stunde existiert bereits eine Sitzung."); + return; + } + + _participationSessions.Save(new ParticipationSession + { + GroupId = lesson.GroupId, + Date = lesson.Date, + LessonId = lesson.Id, + Comment = lesson.Topic, + }); + OnNotify?.Invoke("Sitzung aus der Stunde erstellt."); + } } // ── Anzeige-DTOs ────────────────────────────────────────────────────────────── @@ -1122,6 +1151,12 @@ public partial class GenerateLessonSeriesDialogViewModel : ObservableObject /// halten), wird nichts doppelt angelegt. Neue Stunden bekommen bewusst kein Thema — die /// sonst übliche "Thema erforderlich"-Regel des manuellen "+Stunde"-Dialogs gilt hier nicht, /// da diese Platzhalter zum späteren Ausfüllen gedacht sind. + /// + /// Doppelstunden (zwei direkt aufeinanderfolgende Perioden desselben Wochentags/derselben + /// Gruppe im Stundenplan — dieselbe Konvention wie bei + /// und den Ferien-Badges in TimetableViewModel) bekommen bewusst nur EINE Lesson, verankert an + /// der ersten Periode: eine Folgeperiode, deren Vorgängerperiode ebenfalls im Stundenplan steht, + /// wird übersprungen, statt eine zweite Lesson mit gleichem Datum anzulegen. [RelayCommand] private void Save() { @@ -1136,6 +1171,9 @@ public partial class GenerateLessonSeriesDialogViewModel : ObservableObject if (slotsForGroup.Count == 0) { DateError = "Für diese Gruppe ist noch keine Stunde im Stundenplan eingetragen."; return; } + var periodsByWeekday = slotsForGroup.GroupBy(s => s.Weekday) + .ToDictionary(g => g.Key, g => g.Select(s => s.PeriodNumber).ToHashSet()); + var schoolHolidays = _schoolHolidays.GetAll(); var publicHolidayDates = new HashSet(); for (var year = from.Year; year <= to.Year; year++) @@ -1152,6 +1190,10 @@ public partial class GenerateLessonSeriesDialogViewModel : ObservableObject foreach (var slot in slotsForGroup.Where(s => s.Weekday == date.DayOfWeek)) { + // Zweite (und weitere) Periode einer Doppelstunde: gehört zur Lesson der ersten + // Periode, keine eigene Lesson. + if (periodsByWeekday[slot.Weekday].Contains(slot.PeriodNumber - 1)) continue; + if (isFreeDay) { skippedHoliday++; continue; } if (existing.Contains((date, (int?)slot.PeriodNumber))) { skippedExisting++; continue; } diff --git a/LehrerApp.Desktop/Views/Dashboard/DashboardView.axaml b/LehrerApp.Desktop/Views/Dashboard/DashboardView.axaml index 1a7e847..3cbff6c 100644 --- a/LehrerApp.Desktop/Views/Dashboard/DashboardView.axaml +++ b/LehrerApp.Desktop/Views/Dashboard/DashboardView.axaml @@ -199,9 +199,11 @@ - + + + + @@ -218,6 +220,10 @@ + + + + diff --git a/LehrerApp.Desktop/Views/Groups/ParticipationTabView.axaml b/LehrerApp.Desktop/Views/Groups/ParticipationTabView.axaml index 2b41fb5..f57b157 100644 --- a/LehrerApp.Desktop/Views/Groups/ParticipationTabView.axaml +++ b/LehrerApp.Desktop/Views/Groups/ParticipationTabView.axaml @@ -56,6 +56,8 @@