Wetterdienst
This commit is contained in:
@@ -36,6 +36,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
private readonly SchoolCalendarSettingsService _calendarSettings;
|
||||
private readonly ISubstitutionEntryRepository _substitutions;
|
||||
private readonly IAnnualPlanEventRepository? _annualPlanEvents;
|
||||
private readonly SchoolWeatherService? _schoolWeather;
|
||||
|
||||
private const int OpenExcuseMaxAgeDays = 21;
|
||||
private const int SupportPlanDueWithinDays = 14;
|
||||
@@ -56,6 +57,11 @@ public partial class DashboardViewModel : ObservableObject
|
||||
[ObservableProperty] private DateOnly _calendarMonth = FirstOfMonth(DateTime.Today);
|
||||
[ObservableProperty] private string _selectedDayLabel = "";
|
||||
[ObservableProperty] private bool _isDashboardSettingsOpen;
|
||||
[ObservableProperty] private bool _isWeatherPanelVisible;
|
||||
[ObservableProperty] private string _weatherSummary = "";
|
||||
[ObservableProperty] private string _weatherDetails = "";
|
||||
[ObservableProperty] private string _weatherStatus = "";
|
||||
[ObservableProperty] private bool _hasWeatherWarnings;
|
||||
|
||||
public string CalendarMonthLabel => CalendarMonth.ToString("MMMM yyyy", De);
|
||||
|
||||
@@ -72,6 +78,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
public ObservableCollection<DashboardAlertItem> Alerts { get; } = [];
|
||||
public ObservableCollection<CalendarEventItem> SelectedDayEvents { get; } = [];
|
||||
public ObservableCollection<DashboardCardOption> DashboardCards { get; } = [];
|
||||
public ObservableCollection<DashboardWeatherWarningItem> WeatherWarnings { get; } = [];
|
||||
public static string[] CalendarWeekdayHeaders { get; } = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
|
||||
// Navigation-Callback – wird von App.axaml.cs verdrahtet
|
||||
@@ -111,7 +118,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
DashboardSettingsService dashboardSettings, ISchoolHolidayRepository schoolHolidays,
|
||||
PublicHolidayService publicHolidays, SchoolCalendarSettingsService calendarSettings,
|
||||
ISubstitutionEntryRepository substitutions, IAnnualPlanEventRepository? annualPlanEvents = null,
|
||||
AnnualPlanSyncService? annualPlanSync = null)
|
||||
AnnualPlanSyncService? annualPlanSync = null, SchoolWeatherService? schoolWeather = null)
|
||||
{
|
||||
_groups = groups; _subjects = subjects; _lessons = lessons; _exams = exams; _tasks = tasks;
|
||||
_examResults = examResults; _grades = grades; _reportGrades = reportGrades; _memberships = memberships;
|
||||
@@ -122,6 +129,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
_schoolHolidays = schoolHolidays; _publicHolidays = publicHolidays; _calendarSettings = calendarSettings;
|
||||
_substitutions = substitutions;
|
||||
_annualPlanEvents = annualPlanEvents;
|
||||
_schoolWeather = schoolWeather;
|
||||
if (annualPlanSync is not null)
|
||||
{
|
||||
annualPlanSync.DataChanged += () => Avalonia.Threading.Dispatcher.UIThread.Post(LoadCalendar);
|
||||
@@ -142,6 +150,7 @@ public partial class DashboardViewModel : ObservableObject
|
||||
CurrentDate = now.ToString("dddd, d. MMMM yyyy", De);
|
||||
CurrentSchoolYear = _sy.CurrentSchoolYear();
|
||||
Greeting = now.Hour < 12 ? "Guten Morgen" : now.Hour < 18 ? "Guten Tag" : "Guten Abend";
|
||||
_ = LoadWeatherAsync();
|
||||
|
||||
TodaysLessons.Clear();
|
||||
var groups = _groups.GetBySchoolYear(_sy.CurrentSchoolYear()).ToDictionary(g => g.Id);
|
||||
@@ -197,6 +206,57 @@ public partial class DashboardViewModel : ObservableObject
|
||||
LoadAlerts(groups, today);
|
||||
}
|
||||
|
||||
private async Task LoadWeatherAsync()
|
||||
{
|
||||
if (_schoolWeather?.IsAvailable != true) return;
|
||||
try
|
||||
{
|
||||
var snapshot = await _schoolWeather.GetWeatherAsync();
|
||||
if (snapshot is null) return;
|
||||
var current = snapshot.Forecast
|
||||
.Where(x => x.ValidAt.ToUniversalTime() >= DateTime.UtcNow.AddHours(-1))
|
||||
.MinBy(x => Math.Abs((x.ValidAt.ToUniversalTime() - DateTime.UtcNow).TotalMinutes));
|
||||
WeatherWarnings.Clear();
|
||||
foreach (var warning in snapshot.Warnings)
|
||||
WeatherWarnings.Add(new DashboardWeatherWarningItem(warning));
|
||||
HasWeatherWarnings = WeatherWarnings.Count > 0;
|
||||
|
||||
if (current is not null)
|
||||
{
|
||||
var temperature = current.TemperatureC is { } t ? $"{t:0.#} °C" : "Temperatur unbekannt";
|
||||
WeatherSummary = $"{temperature} · {WeatherDescription(current.WeatherCode)}";
|
||||
var details = new List<string>();
|
||||
if (current.WindSpeedKmh is { } wind) details.Add($"Wind {wind:0.#} km/h");
|
||||
if (current.WindGustKmh is { } gust) details.Add($"Böen {gust:0.#} km/h");
|
||||
if (current.PrecipitationMm is > 0) details.Add($"Niederschlag {current.PrecipitationMm:0.#} mm");
|
||||
details.Add($"DWD-Station {snapshot.StationName} ({snapshot.StationDistanceKm:0.#} km)");
|
||||
WeatherDetails = string.Join(" · ", details);
|
||||
}
|
||||
WeatherStatus = snapshot.IsStale
|
||||
? $"Letzter verfügbarer Stand vom {snapshot.RetrievedAt.ToLocalTime():dd.MM., HH:mm} Uhr"
|
||||
: "";
|
||||
IsWeatherPanelVisible = current is not null || HasWeatherWarnings;
|
||||
}
|
||||
catch (SchoolWeatherException ex)
|
||||
{
|
||||
WeatherStatus = ex.Message;
|
||||
IsWeatherPanelVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static string WeatherDescription(int? code) => code switch
|
||||
{
|
||||
0 => "klar",
|
||||
>= 1 and <= 3 => "bewölkt",
|
||||
45 or 48 => "Nebel",
|
||||
>= 51 and <= 67 => "Regen",
|
||||
>= 71 and <= 77 => "Schnee",
|
||||
>= 80 and <= 82 => "Regenschauer",
|
||||
85 or 86 => "Schneeschauer",
|
||||
>= 95 and <= 99 => "Gewitter",
|
||||
_ => "Wettervorhersage",
|
||||
};
|
||||
|
||||
// ── Fehlzeiten-Warnung (5.2.3) ────────────────────────────────────────────
|
||||
|
||||
private void LoadAttendanceWarnings(DateOnly today)
|
||||
@@ -761,6 +821,32 @@ public partial class DashboardViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DashboardWeatherWarningItem
|
||||
{
|
||||
public string Headline { get; }
|
||||
public string Description { get; }
|
||||
public string Instruction { get; }
|
||||
public string PeriodDisplay { get; }
|
||||
public string SeverityColor { get; }
|
||||
|
||||
public DashboardWeatherWarningItem(WeatherWarning warning)
|
||||
{
|
||||
Headline = string.IsNullOrWhiteSpace(warning.Headline) ? warning.Event : warning.Headline;
|
||||
Description = warning.Description;
|
||||
Instruction = warning.Instruction;
|
||||
PeriodDisplay = (warning.Onset, warning.Expires) switch
|
||||
{
|
||||
({ } onset, { } expires) => $"{onset.ToLocalTime():dd.MM., HH:mm}–{expires.ToLocalTime():HH:mm} Uhr",
|
||||
({ } onset, null) => $"ab {onset.ToLocalTime():dd.MM., HH:mm} Uhr",
|
||||
_ => "",
|
||||
};
|
||||
SeverityColor = warning.Severity switch
|
||||
{
|
||||
"Extreme" => "#7E0023", "Severe" => "#D32F2F", "Moderate" => "#F59E0B", _ => "#FDD835",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class LessonItem
|
||||
{
|
||||
public Guid LessonId { get; set; }
|
||||
|
||||
Reference in New Issue
Block a user