Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 60 additions & 5 deletions src/accessiweather/taskbar_icon_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,10 @@ def _extract_weather_variables(
data["uv"] = self._format_numeric(getattr(current, "uv_index", None), "")
data["visibility"] = self._format_visibility(current)
data["high"], data["low"] = self._format_forecast_temperatures(weather_data)
data["precip"] = self._format_precipitation(current)
data["precip_chance"] = self._format_numeric(
getattr(current, "precipitation_probability", None), ""
data["precip"] = self._format_precipitation(current, weather_data=weather_data)
data["precip_chance"] = self._format_precipitation_probability(
current,
weather_data=weather_data,
)
data["alert"] = self._extract_alert_event(weather_data)

Expand Down Expand Up @@ -336,23 +337,77 @@ def _format_visibility(self, current: Any) -> str:
unit_system=self._resolve_display_unit_system(),
)

def _format_precipitation(self, current: Any) -> str:
def _format_precipitation(
self,
current: Any,
*,
weather_data: WeatherData | Any | None = None,
) -> str:
"""Format precipitation using the selected unit preference when source data exists."""
precip_in = getattr(current, "precipitation_in", None)
if precip_in is None:
precip_in = getattr(current, "precipitation_inches", None)
if precip_in is None:
precip_in = getattr(current, "precipitation", None)
precip_mm = getattr(current, "precipitation_mm", None)

if precip_in is None and precip_mm is None:
precip_in, precip_mm = self._extract_forecast_precipitation_amount(weather_data)

precision = 0 if self.round_values else 2
return format_precipitation(
precip_in,
unit=self._resolve_temperature_unit(),
precipitation_mm=getattr(current, "precipitation_mm", None),
precipitation_mm=precip_mm,
precision=precision,
unit_system=self._resolve_display_unit_system(),
)

def _format_precipitation_probability(
self,
current: Any,
*,
weather_data: WeatherData | Any | None = None,
) -> str:
"""Format precipitation probability from current conditions or forecast fallback."""
probability = getattr(current, "precipitation_probability", None)
if probability is None:
probability = self._extract_forecast_precipitation_probability(weather_data)
return self._format_numeric(probability, "")

def _extract_forecast_precipitation_amount(
self, weather_data: WeatherData | Any | None
) -> tuple[float | None, float | None]:
"""Return the first forecast precipitation amount when current conditions omit it."""
for period in self._iter_precipitation_periods(weather_data):
amount = getattr(period, "precipitation_amount", None)
if amount is not None:
return amount, amount * 25.4
return None, None

def _extract_forecast_precipitation_probability(
self, weather_data: WeatherData | Any | None
) -> float | int | None:
"""Return the first forecast precipitation probability when current conditions omit it."""
for period in self._iter_precipitation_periods(weather_data):
probability = getattr(period, "precipitation_probability", None)
if probability is not None:
return probability
return None

def _iter_precipitation_periods(self, weather_data: WeatherData | Any | None) -> list[Any]:
"""Yield hourly periods before daily periods for precipitation placeholder fallback."""
if weather_data is None:
return []

periods: list[Any] = []
hourly_forecast = getattr(weather_data, "hourly_forecast", None)
periods.extend(getattr(hourly_forecast, "periods", None) or [])

forecast = getattr(weather_data, "forecast", None)
periods.extend(getattr(forecast, "periods", None) or [])
return periods

def _format_forecast_temperatures(
self, weather_data: WeatherData | Any | None
) -> tuple[str, str]:
Expand Down
59 changes: 59 additions & 0 deletions tests/test_system_tray.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from __future__ import annotations

from datetime import UTC, datetime
from types import SimpleNamespace
from unittest.mock import MagicMock

Expand Down Expand Up @@ -619,6 +620,64 @@ def test_high_low_stay_na_without_forecast_data(self):

assert result == "N/A / N/A"

def test_precip_placeholders_fall_back_to_hourly_forecast_when_current_missing(self):
"""Precipitation placeholders should use forecast data when current conditions omit it."""
from accessiweather.models import HourlyForecast, HourlyForecastPeriod
from accessiweather.taskbar_icon_updater import TaskbarIconUpdater

weather_data = WeatherData(
location=Location(name="Test City", latitude=40.0, longitude=-74.0),
current=CurrentConditions(temperature_f=72.0, temperature_c=22.2, condition="Rain"),
hourly_forecast=HourlyForecast(
periods=[
HourlyForecastPeriod(
start_time=datetime.now(UTC),
precipitation_amount=0.25,
precipitation_probability=40,
)
]
),
)
updater = TaskbarIconUpdater(
text_enabled=True,
format_string="{precip} | {precip_chance}",
temperature_unit="f",
)

result = updater.format_tooltip(weather_data, "Test City")

assert result == "0.25 in | 40"

def test_precip_placeholders_fall_back_to_daily_forecast_when_hourly_missing(self):
"""Daily forecast precipitation should backfill tray placeholders when needed."""
from accessiweather.models import Forecast, ForecastPeriod
from accessiweather.taskbar_icon_updater import TaskbarIconUpdater

weather_data = WeatherData(
location=Location(name="Test City", latitude=40.0, longitude=-74.0),
current=CurrentConditions(temperature_f=72.0, temperature_c=22.2, condition="Rain"),
forecast=Forecast(
periods=[
ForecastPeriod(
name="Today",
temperature=75,
temperature_unit="F",
precipitation_amount=0.5,
precipitation_probability=60,
)
]
),
)
updater = TaskbarIconUpdater(
text_enabled=True,
format_string="{precip} | {precip_chance}",
temperature_unit="f",
)

result = updater.format_tooltip(weather_data, "Test City")

assert result == "0.50 in | 60"


def _weather_data_with_feels_like(
*,
Expand Down
Loading