Jahresplanimport und Ablgeich von Untis
This commit is contained in:
@@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Core.Services;
|
||||
using LehrerApp.Desktop.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -34,6 +35,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
private readonly PublicHolidayService _publicHolidays;
|
||||
private readonly SchoolCalendarSettingsService _calendarSettings;
|
||||
private readonly ISubstitutionEntryRepository _substitutions;
|
||||
private readonly IAnnualPlanEventRepository? _annualPlanEvents;
|
||||
|
||||
private const int OpenExcuseMaxAgeDays = 21;
|
||||
private const int SupportPlanDueWithinDays = 14;
|
||||
@@ -108,7 +110,8 @@ public partial class DashboardViewModel : ObservableObject
|
||||
PeriodScheduleService periodSchedule, AttendanceBalanceService attendanceBalance, SchoolYearService sy,
|
||||
DashboardSettingsService dashboardSettings, ISchoolHolidayRepository schoolHolidays,
|
||||
PublicHolidayService publicHolidays, SchoolCalendarSettingsService calendarSettings,
|
||||
ISubstitutionEntryRepository substitutions)
|
||||
ISubstitutionEntryRepository substitutions, IAnnualPlanEventRepository? annualPlanEvents = null,
|
||||
AnnualPlanSyncService? annualPlanSync = null)
|
||||
{
|
||||
_groups = groups; _subjects = subjects; _lessons = lessons; _exams = exams; _tasks = tasks;
|
||||
_examResults = examResults; _grades = grades; _reportGrades = reportGrades; _memberships = memberships;
|
||||
@@ -118,6 +121,12 @@ public partial class DashboardViewModel : ObservableObject
|
||||
_attendanceBalance = attendanceBalance; _sy = sy; _dashboardSettings = dashboardSettings;
|
||||
_schoolHolidays = schoolHolidays; _publicHolidays = publicHolidays; _calendarSettings = calendarSettings;
|
||||
_substitutions = substitutions;
|
||||
_annualPlanEvents = annualPlanEvents;
|
||||
if (annualPlanSync is not null)
|
||||
{
|
||||
annualPlanSync.DataChanged += () => Avalonia.Threading.Dispatcher.UIThread.Post(LoadCalendar);
|
||||
_ = annualPlanSync.PollAsync();
|
||||
}
|
||||
LoadDashboardCards();
|
||||
Load();
|
||||
}
|
||||
@@ -580,6 +589,27 @@ public partial class DashboardViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
// Der Jahresplan wird lediglich in dieselbe Anzeigeprojektion eingemischt. Er nimmt an
|
||||
// keiner Stundenplan-/Vertretungslogik teil; mehrtägige Termine erscheinen an jedem
|
||||
// betroffenen Kalendertag.
|
||||
if (_annualPlanEvents is not null)
|
||||
{
|
||||
foreach (var annualEvent in _annualPlanEvents.GetByRange(gridStart, gridEnd)
|
||||
.Where(e => !string.Equals(e.Status, "CANCELLED", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var visibleStart = annualEvent.StartDate < gridStart ? gridStart : annualEvent.StartDate;
|
||||
var visibleEnd = annualEvent.EndDate > gridEnd ? gridEnd : annualEvent.EndDate;
|
||||
for (var date = visibleStart; date <= visibleEnd; date = date.AddDays(1))
|
||||
{
|
||||
var agg = Agg(date);
|
||||
agg.HasAnnualPlanEvent = true;
|
||||
agg.Details.Add(new CalendarEventItem(CalendarEventKind.AnnualPlan, date,
|
||||
annualEvent.Title, FormatAnnualPlanSubtitle(annualEvent), null,
|
||||
annualEvent.Description));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Meine Klasse"-Ring: bisher nur gesetzt, wenn für den Tag schon eine Lesson/Exam/Sitzung
|
||||
// existiert — ein Tag, an dem laut Stundenplan (4.3) eine eigene Klasse ansteht, für den
|
||||
// aber noch keine Lesson angelegt wurde (z.B. "morgen"), zeigte den Ring fälschlich nicht.
|
||||
@@ -615,7 +645,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
byDay.TryGetValue(date, out var agg);
|
||||
CalendarDays.Add(new CalendarDayCell(date, date.Month == firstOfMonth.Month, date == today,
|
||||
agg?.HasLesson ?? false, agg?.HasExam ?? false, agg?.HasSession ?? false,
|
||||
agg?.IsOwnClassDay ?? false, agg?.Details ?? []));
|
||||
agg?.HasAnnualPlanEvent ?? false, agg?.IsOwnClassDay ?? false, agg?.Details ?? []));
|
||||
}
|
||||
SelectCalendarDay(CalendarDays.FirstOrDefault(d => d.Date == today && d.IsCurrentMonth)
|
||||
?? CalendarDays.First(d => d.IsCurrentMonth));
|
||||
@@ -635,8 +665,9 @@ public partial class DashboardViewModel : ObservableObject
|
||||
private void OpenCalendarEvent(CalendarEventItem? item)
|
||||
{
|
||||
if (item is null) return;
|
||||
if (item.Kind == CalendarEventKind.Exam) OnNavigateToExam?.Invoke(item.GroupId);
|
||||
else OnNavigateToLesson?.Invoke(item.GroupId); // auch für ParticipationSession: Tab "Mitarbeit"
|
||||
if (item.GroupId is not { } groupId) return; // Jahresplantermine sind reine Information.
|
||||
if (item.Kind == CalendarEventKind.Exam) OnNavigateToExam?.Invoke(groupId);
|
||||
else OnNavigateToLesson?.Invoke(groupId); // auch für ParticipationSession: Tab "Mitarbeit"
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -699,9 +730,35 @@ public partial class DashboardViewModel : ObservableObject
|
||||
public bool HasLesson;
|
||||
public bool HasExam;
|
||||
public bool HasSession;
|
||||
public bool HasAnnualPlanEvent;
|
||||
public bool IsOwnClassDay;
|
||||
public List<CalendarEventItem> Details { get; } = [];
|
||||
}
|
||||
|
||||
private static string FormatAnnualPlanSubtitle(AnnualPlanEvent entry)
|
||||
{
|
||||
string time;
|
||||
if (entry.IsAllDay)
|
||||
{
|
||||
time = entry.StartDate == entry.EndDate
|
||||
? "Ganztägig"
|
||||
: $"{entry.StartDate:dd.MM.}–{entry.EndDate:dd.MM.yyyy} · ganztägig";
|
||||
}
|
||||
else if (entry.StartDate == entry.EndDate)
|
||||
{
|
||||
time = entry.EndTime is { } end
|
||||
? $"{entry.StartTime:HH\\:mm}–{end:HH\\:mm}"
|
||||
: $"{entry.StartTime:HH\\:mm}";
|
||||
}
|
||||
else
|
||||
{
|
||||
time = $"{entry.StartDate:dd.MM.} {entry.StartTime:HH\\:mm}–" +
|
||||
$"{entry.EndDate:dd.MM.} {entry.EndTime:HH\\:mm}";
|
||||
}
|
||||
|
||||
return string.Join(" · ", new[] { time, entry.CalendarGroup, entry.Location }
|
||||
.Where(part => !string.IsNullOrWhiteSpace(part)));
|
||||
}
|
||||
}
|
||||
|
||||
public class LessonItem
|
||||
@@ -783,12 +840,14 @@ public partial class CalendarDayCell : ObservableObject
|
||||
public bool HasLesson { get; }
|
||||
public bool HasExam { get; }
|
||||
public bool HasSession { get; }
|
||||
public bool HasAnnualPlanEvent { get; }
|
||||
public bool IsOwnClassDay { get; }
|
||||
public string Tooltip { get; }
|
||||
public IReadOnlyList<CalendarEventItem> Events { get; }
|
||||
|
||||
internal CalendarDayCell(DateOnly date, bool isCurrentMonth, bool isToday,
|
||||
bool hasLesson, bool hasExam, bool hasSession, bool isOwnClassDay, List<CalendarEventItem> details)
|
||||
bool hasLesson, bool hasExam, bool hasSession, bool hasAnnualPlanEvent,
|
||||
bool isOwnClassDay, List<CalendarEventItem> details)
|
||||
{
|
||||
Date = date;
|
||||
DayNumber = date.Day;
|
||||
@@ -797,6 +856,7 @@ public partial class CalendarDayCell : ObservableObject
|
||||
HasLesson = hasLesson;
|
||||
HasExam = hasExam;
|
||||
HasSession = hasSession;
|
||||
HasAnnualPlanEvent = hasAnnualPlanEvent;
|
||||
IsOwnClassDay = isOwnClassDay;
|
||||
Events = details;
|
||||
Tooltip = details.Count == 0 ? date.ToString("dd.MM.yyyy")
|
||||
@@ -804,20 +864,22 @@ public partial class CalendarDayCell : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
public enum CalendarEventKind { Lesson, Exam, ParticipationSession }
|
||||
public enum CalendarEventKind { Lesson, Exam, ParticipationSession, AnnualPlan }
|
||||
|
||||
public sealed class CalendarEventItem(CalendarEventKind kind, DateOnly date, string title,
|
||||
string subtitle, Guid groupId)
|
||||
string subtitle, Guid? groupId, string description = "")
|
||||
{
|
||||
public CalendarEventKind Kind { get; } = kind;
|
||||
public DateOnly Date { get; } = date;
|
||||
public string Title { get; } = title;
|
||||
public string Subtitle { get; } = subtitle;
|
||||
public Guid GroupId { get; } = groupId;
|
||||
public Guid? GroupId { get; } = groupId;
|
||||
public string Description { get; } = description;
|
||||
public string KindLabel => Kind switch
|
||||
{
|
||||
CalendarEventKind.Exam => "Klausur",
|
||||
CalendarEventKind.ParticipationSession => "Sitzung",
|
||||
CalendarEventKind.AnnualPlan => "Jahresplan",
|
||||
_ => "Unterricht"
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user