Wetterdienst: Verhersage verlängert, Anzeige im Stundenplan.
This commit is contained in:
@@ -65,6 +65,9 @@ public partial class TimetableViewModel : ObservableObject
|
||||
private readonly ISubstitutionEntryRepository _substitutions;
|
||||
private readonly IUntisSlotMappingRepository _untisMappings;
|
||||
private readonly WebUntisSettingsService _untisSettings;
|
||||
private readonly SchoolWeatherService? _schoolWeather;
|
||||
private readonly SemaphoreSlim _weatherGate = new(1, 1);
|
||||
private WeatherSnapshot? _weatherSnapshot;
|
||||
|
||||
public ObservableCollection<TimetableCellItem> Cells { get; } = [];
|
||||
public ObservableCollection<WeekCellItem> WeekItems { get; } = [];
|
||||
@@ -73,6 +76,7 @@ public partial class TimetableViewModel : ObservableObject
|
||||
public ObservableCollection<TodaySupervisionItem> TodaySupervisions { get; } = [];
|
||||
public ObservableCollection<TodaySpecialAssignmentItem> TodaySpecialAssignments { get; } = [];
|
||||
public ObservableCollection<UpcomingExamItem> UpcomingExams { get; } = [];
|
||||
public ObservableCollection<WeekWeatherWarningItem> WeekWeatherWarnings { get; } = [];
|
||||
|
||||
/// Cells/WeekItems zeilenweise gruppiert (GridColumns Zellen je Zeile), zusätzlich zur
|
||||
/// flachen Liste - Grundlage für die zeilenweise Höhensteuerung in der View (Aufsicht-Zeilen
|
||||
@@ -108,13 +112,15 @@ public partial class TimetableViewModel : ObservableObject
|
||||
ISchoolHolidayRepository schoolHolidays, SchoolCalendarSettingsService calendarSettings,
|
||||
PublicHolidayService publicHolidays, SchoolYearService schoolYear,
|
||||
ISupervisionDutyRepository supervisionDuties, ISubstitutionEntryRepository substitutions,
|
||||
IUntisSlotMappingRepository untisMappings, WebUntisSettingsService untisSettings)
|
||||
IUntisSlotMappingRepository untisMappings, WebUntisSettingsService untisSettings,
|
||||
SchoolWeatherService? schoolWeather = null)
|
||||
{
|
||||
_slots = slots; _groups = groups; _subjects = subjects; _lessons = lessons; _exams = exams;
|
||||
_schoolHolidays = schoolHolidays; _calendarSettings = calendarSettings;
|
||||
_publicHolidays = publicHolidays; _schoolYear = schoolYear;
|
||||
_supervisionDuties = supervisionDuties; _substitutions = substitutions;
|
||||
_untisMappings = untisMappings; _untisSettings = untisSettings;
|
||||
_schoolWeather = schoolWeather;
|
||||
Load();
|
||||
}
|
||||
|
||||
@@ -152,12 +158,58 @@ public partial class TimetableViewModel : ObservableObject
|
||||
|
||||
BuildGrid(holidayBadges, duties);
|
||||
BuildWeekOverview(today, publicHolidayDates, duties);
|
||||
ApplyWeatherToWeekHeaders(today);
|
||||
_ = LoadWeatherAsync(today);
|
||||
BuildToday(today);
|
||||
BuildHoursWarnings();
|
||||
BuildUpcomingExams(today);
|
||||
LoadUntisMismatch();
|
||||
}
|
||||
|
||||
private async Task LoadWeatherAsync(DateOnly today)
|
||||
{
|
||||
if (_schoolWeather?.IsAvailable != true) return;
|
||||
if (_weatherSnapshot is not null &&
|
||||
DateTime.UtcNow - _weatherSnapshot.RetrievedAt.ToUniversalTime() < TimeSpan.FromMinutes(5))
|
||||
return;
|
||||
if (!await _weatherGate.WaitAsync(0)) return;
|
||||
try
|
||||
{
|
||||
_weatherSnapshot = await _schoolWeather.GetWeatherAsync();
|
||||
ApplyWeatherToWeekHeaders(today);
|
||||
}
|
||||
catch (SchoolWeatherException) { /* Wetter ist eine optionale Planungshilfe. */ }
|
||||
finally { _weatherGate.Release(); }
|
||||
}
|
||||
|
||||
private void ApplyWeatherToWeekHeaders(DateOnly today)
|
||||
{
|
||||
WeekWeatherWarnings.Clear();
|
||||
var datedHeaders = WeekItems.Where(x => x.IsHeader && x.Date.HasValue).ToList();
|
||||
foreach (var header in datedHeaders)
|
||||
{
|
||||
var date = header.Date!.Value;
|
||||
var warnings = _weatherSnapshot?.Warnings
|
||||
.Where(x => date >= today && SchoolDayWeatherSummary.WarningTouchesDate(x, date))
|
||||
.ToList() ?? [];
|
||||
var summary = date >= today && _weatherSnapshot is not null
|
||||
? SchoolDayWeatherSummary.From(_weatherSnapshot, date)
|
||||
: null;
|
||||
header.SetWeather(summary, warnings);
|
||||
}
|
||||
|
||||
if (_weatherSnapshot is null || datedHeaders.Count == 0) return;
|
||||
var from = datedHeaders.Min(x => x.Date!.Value);
|
||||
var until = datedHeaders.Max(x => x.Date!.Value);
|
||||
foreach (var warning in _weatherSnapshot.Warnings
|
||||
.Where(x => Enumerable.Range(0, until.DayNumber - from.DayNumber + 1)
|
||||
.Select(from.AddDays)
|
||||
.Any(date => date >= today && SchoolDayWeatherSummary.WarningTouchesDate(x, date)))
|
||||
.DistinctBy(x => x.Identifier)
|
||||
.OrderBy(x => x.Onset))
|
||||
WeekWeatherWarnings.Add(new WeekWeatherWarningItem(warning));
|
||||
}
|
||||
|
||||
private void LoadUntisMismatch()
|
||||
{
|
||||
if (!_untisSettings.Enabled || !_untisSettings.IsConfigured) { HasUntisMismatch = false; return; }
|
||||
@@ -689,7 +741,7 @@ public class TimetableRowItem(IReadOnlyList<TimetableCellItem> cells)
|
||||
}
|
||||
|
||||
/// <summary>Zelle im schreibgeschützten Wochenraster der "Heute"-Ansicht.</summary>
|
||||
public class WeekCellItem
|
||||
public partial class WeekCellItem : ObservableObject
|
||||
{
|
||||
public bool IsHeader { get; private init; }
|
||||
public bool IsPeriodLabel { get; private init; }
|
||||
@@ -705,6 +757,7 @@ public class WeekCellItem
|
||||
public DayOfWeek? Weekday { get; private init; }
|
||||
public int PeriodNumber { get; private init; }
|
||||
public bool IsToday { get; private init; }
|
||||
public DateOnly? Date { get; private init; }
|
||||
public Guid GroupId { get; private init; }
|
||||
public string SubjectLabel { get; private init; } = "";
|
||||
public string GroupName { get; private init; } = "";
|
||||
@@ -726,6 +779,20 @@ public class WeekCellItem
|
||||
/// Grundlage für den Direktsprung in den Verlaufsplan-Viewer (4.5.2).
|
||||
public Lesson? Lesson { get; private init; }
|
||||
public string OpenButtonLabel => Lesson is not null ? "Verlaufsplan ansehen" : "Zur Lerngruppe";
|
||||
[ObservableProperty] private string _weatherSymbol = "";
|
||||
[ObservableProperty] private string _weatherTooltip = "";
|
||||
[ObservableProperty] private bool _hasWeatherWarning;
|
||||
public bool HasWeather => WeatherSymbol.Length > 0;
|
||||
|
||||
partial void OnWeatherSymbolChanged(string value) => OnPropertyChanged(nameof(HasWeather));
|
||||
|
||||
public void SetWeather(SchoolDayWeatherSummary? summary, IReadOnlyList<WeatherWarning> warnings)
|
||||
{
|
||||
WeatherSymbol = summary?.Symbol ?? "";
|
||||
WeatherTooltip = summary?.Tooltip ?? string.Join("\n", warnings.Select(x =>
|
||||
$"⚠ {x.Headline} ({SchoolDayWeatherSummary.WarningPeriod(x)})"));
|
||||
HasWeatherWarning = warnings.Count > 0;
|
||||
}
|
||||
|
||||
public static WeekCellItem Corner() => new() { IsHeader = true };
|
||||
|
||||
@@ -733,6 +800,7 @@ public class WeekCellItem
|
||||
{
|
||||
IsHeader = true,
|
||||
IsToday = isToday,
|
||||
Date = date,
|
||||
Text = (day switch
|
||||
{
|
||||
DayOfWeek.Monday => "Mo", DayOfWeek.Tuesday => "Di", DayOfWeek.Wednesday => "Mi",
|
||||
|
||||
Reference in New Issue
Block a user