Skip to content
Merged
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
70 changes: 43 additions & 27 deletions src/accessiweather/display/presentation/current_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
TrendInsight,
WeatherAlerts,
)
from ...units import DisplayUnitSystem
from ...utils import TemperatureUnit
from ...utils.unit_utils import format_precipitation, format_wind_speed
from ..priority_engine import PriorityEngine, WeatherCategory
from ..weather_presenter import CurrentConditionsPresentation, Metric
from .environmental import AirQualityPresentation
Expand All @@ -40,6 +42,8 @@ def _build_basic_metrics(
show_dewpoint: bool,
show_visibility: bool,
show_uv_index: bool,
*,
unit_system: DisplayUnitSystem | str | None = None,
) -> list[Metric]:
"""Build basic weather metrics (temperature, feels like, humidity, wind, dewpoint, etc.)."""
# Format temperature with inline feels-like when there's a significant difference
Expand All @@ -55,19 +59,29 @@ def _build_basic_metrics(
if current.humidity is not None:
metrics.append(Metric("Humidity", f"{current.humidity:.0f}%"))

wind_value = format_wind(current, unit_pref, precision=precision)
wind_value = format_wind(current, unit_pref, precision=precision, unit_system=unit_system)
if wind_value:
metrics.append(Metric("Wind", wind_value))

dewpoint_value = format_dewpoint(current, unit_pref, precision)
if show_dewpoint and dewpoint_value:
metrics.append(Metric("Dewpoint", dewpoint_value))

pressure_value = format_pressure_value(current, unit_pref, precision=precision)
pressure_value = format_pressure_value(
current,
unit_pref,
precision=precision,
unit_system=unit_system,
)
if pressure_value:
metrics.append(Metric("Pressure", pressure_value))

visibility_value = format_visibility_value(current, unit_pref, precision=precision)
visibility_value = format_visibility_value(
current,
unit_pref,
precision=precision,
unit_system=unit_system,
)
if show_visibility and visibility_value:
metrics.append(Metric("Visibility", visibility_value))

Expand All @@ -79,31 +93,26 @@ def _build_basic_metrics(
metrics.append(Metric("Cloud cover", f"{current.cloud_cover:.0f}%"))

if current.wind_gust_mph is not None:
if unit_pref == TemperatureUnit.CELSIUS:
gust_kph = current.wind_gust_kph or current.wind_gust_mph * 1.60934
metrics.append(Metric("Wind gusts", f"{gust_kph:.0f} kph"))
elif unit_pref == TemperatureUnit.FAHRENHEIT:
metrics.append(Metric("Wind gusts", f"{current.wind_gust_mph:.0f} mph"))
else:
gust_kph = current.wind_gust_kph or current.wind_gust_mph * 1.60934
metrics.append(
Metric("Wind gusts", f"{current.wind_gust_mph:.0f} mph ({gust_kph:.0f} kph)")
)
gust_value = format_wind_speed(
current.wind_gust_mph,
unit=unit_pref,
wind_speed_kph=current.wind_gust_kph,
precision=0,
unit_system=unit_system,
)
if unit_system is None:
gust_value = gust_value.replace("km/h", "kph")
metrics.append(Metric("Wind gusts", gust_value))

if current.precipitation_in is not None and current.precipitation_in > 0:
if unit_pref == TemperatureUnit.CELSIUS:
precip_mm = current.precipitation_mm or current.precipitation_in * 25.4
metrics.append(Metric("Precipitation", f"{precip_mm:.{precision}f} mm"))
elif unit_pref == TemperatureUnit.FAHRENHEIT:
metrics.append(Metric("Precipitation", f"{current.precipitation_in:.{precision}f} in"))
else:
precip_mm = current.precipitation_mm or current.precipitation_in * 25.4
metrics.append(
Metric(
"Precipitation",
f"{current.precipitation_in:.{precision}f} in ({precip_mm:.{precision}f} mm)",
)
)
precip_value = format_precipitation(
current.precipitation_in,
unit=unit_pref,
precipitation_mm=current.precipitation_mm,
precision=precision,
unit_system=unit_system,
)
metrics.append(Metric("Precipitation", precip_value))

return metrics

Expand Down Expand Up @@ -430,6 +439,7 @@ def build_current_conditions(
hourly_forecast: HourlyForecast | None = None,
air_quality: AirQualityPresentation | None = None,
alerts: WeatherAlerts | None = None,
unit_system: DisplayUnitSystem | str | None = None,
) -> CurrentConditionsPresentation:
"""Create a structured presentation for the current weather using helper functions."""
title = f"Current conditions for {location.name}"
Expand Down Expand Up @@ -467,7 +477,13 @@ def build_current_conditions(
metrics: list[Metric] = []
metrics.extend(
_build_basic_metrics(
current, unit_pref, precision, show_dewpoint, show_visibility, show_uv_index
current,
unit_pref,
precision,
show_dewpoint,
show_visibility,
show_uv_index,
unit_system=unit_system,
)
)

Expand Down
21 changes: 19 additions & 2 deletions src/accessiweather/display/presentation/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import UTC, datetime, timedelta

from ...models import CurrentConditions, ForecastPeriod, HourlyForecastPeriod
from ...units import DisplayUnitSystem
from ...utils import (
TemperatureUnit,
calculate_dewpoint,
Expand All @@ -30,7 +31,11 @@ def format_temperature_pair(


def format_wind(
current: CurrentConditions, unit_pref: TemperatureUnit, precision: int = 1
current: CurrentConditions,
unit_pref: TemperatureUnit,
precision: int = 1,
*,
unit_system: DisplayUnitSystem | str | None = None,
) -> str | None:
"""Describe wind direction and speed or return calm when wind is negligible."""
if (
Expand Down Expand Up @@ -59,6 +64,7 @@ def format_wind(
unit_pref,
wind_speed_kph=current.wind_speed_kph,
precision=precision,
unit_system=unit_system,
)
if direction and speed:
return f"{direction} at {speed}"
Expand Down Expand Up @@ -100,6 +106,8 @@ def format_pressure_value(
current: CurrentConditions,
unit_pref: TemperatureUnit,
precision: int = 1,
*,
unit_system: DisplayUnitSystem | str | None = None,
) -> str | None:
"""Format station pressure in the preferred unit, if available."""
if current.pressure_in is None and current.pressure_mb is None:
Expand All @@ -108,13 +116,21 @@ def format_pressure_value(
pressure_mb = current.pressure_mb
if pressure_in is None and pressure_mb is not None:
pressure_in = pressure_mb / 33.8639
return format_pressure(pressure_in, unit_pref, pressure_mb=pressure_mb, precision=precision)
return format_pressure(
pressure_in,
unit_pref,
pressure_mb=pressure_mb,
precision=precision,
unit_system=unit_system,
)


def format_visibility_value(
current: CurrentConditions,
unit_pref: TemperatureUnit,
precision: int = 1,
*,
unit_system: DisplayUnitSystem | str | None = None,
) -> str | None:
"""Format horizontal visibility taking unit preference into account."""
if current.visibility_miles is None and current.visibility_km is None:
Expand All @@ -124,6 +140,7 @@ def format_visibility_value(
unit_pref,
visibility_km=current.visibility_km,
precision=precision,
unit_system=unit_system,
)


Expand Down
26 changes: 16 additions & 10 deletions src/accessiweather/display/weather_presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
WeatherAlerts,
WeatherData,
)
from ..units import resolve_display_unit_system, resolve_temperature_unit_preference
from ..utils import TemperatureUnit, decode_taf_text
from .presentation.environmental import AirQualityPresentation, build_air_quality_panel

Expand Down Expand Up @@ -193,7 +194,7 @@ def __init__(self, settings: AppSettings):

def present(self, weather_data: WeatherData) -> WeatherPresentation:
"""Build a structured presentation for the given weather data."""
unit_pref = self._get_temperature_unit_preference()
unit_pref, unit_system = self._resolve_unit_preferences(weather_data.location)

air_quality_panel = (
build_air_quality_panel(
Expand All @@ -214,6 +215,7 @@ def present(self, weather_data: WeatherData) -> WeatherPresentation:
hourly_forecast=weather_data.hourly_forecast,
air_quality=air_quality_panel,
alerts=weather_data.alerts,
unit_system=unit_system,
)
if weather_data.current
else None
Expand Down Expand Up @@ -274,7 +276,7 @@ def present_current(
) -> CurrentConditionsPresentation | None:
if not current or not current.has_data():
return None
unit_pref = self._get_temperature_unit_preference()
unit_pref, unit_system = self._resolve_unit_preferences(location)
air_quality_panel = (
build_air_quality_panel(location, environmental, settings=self.settings)
if environmental
Expand All @@ -290,6 +292,7 @@ def present_current(
hourly_forecast=hourly_forecast,
air_quality=air_quality_panel,
alerts=alerts,
unit_system=unit_system,
)

def present_forecast(
Expand All @@ -301,7 +304,7 @@ def present_forecast(
) -> ForecastPresentation | None:
if not forecast or not forecast.has_data():
return None
unit_pref = self._get_temperature_unit_preference()
unit_pref, _unit_system = self._resolve_unit_preferences(location)
return self._build_forecast(
forecast, hourly_forecast, location, unit_pref, confidence=confidence
)
Expand Down Expand Up @@ -329,6 +332,7 @@ def _build_current_conditions(
hourly_forecast: HourlyForecast | None = None,
air_quality: AirQualityPresentation | None = None,
alerts: WeatherAlerts | None = None,
unit_system=None,
) -> CurrentConditionsPresentation:
return build_current_conditions(
current,
Expand All @@ -340,6 +344,7 @@ def _build_current_conditions(
hourly_forecast=hourly_forecast,
air_quality=air_quality,
alerts=alerts,
unit_system=unit_system,
)

def _build_forecast(
Expand Down Expand Up @@ -644,13 +649,14 @@ def _build_source_attribution(
aria_label=aria_label,
)

def _get_temperature_unit_preference(self) -> TemperatureUnit:
unit_pref = (self.settings.temperature_unit or "both").lower()
if unit_pref in {"fahrenheit", "f"}:
return TemperatureUnit.FAHRENHEIT
if unit_pref in {"celsius", "c"}:
return TemperatureUnit.CELSIUS
return TemperatureUnit.BOTH
def _resolve_unit_preferences(
self, location: Location
) -> tuple[TemperatureUnit, object | None]:
preference = getattr(self.settings, "temperature_unit", "both")
return (
resolve_temperature_unit_preference(preference, location),
resolve_display_unit_system(preference, location),
)

def _format_timestamp(self, value: datetime) -> str:
mode = getattr(self.settings, "time_display_mode", "local")
Expand Down
4 changes: 2 additions & 2 deletions src/accessiweather/pirate_weather_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ def __init__(
api_key: Pirate Weather API key.
user_agent: HTTP User-Agent header value.
units: Unit system – "us" (°F, mph, in), "si" (°C, m/s, mm),
"ca" (°C, km/h, mm), or "uk2" (°C, mph, mm).
"ca" (°C, km/h, mm), or "uk"/"uk2" (°C, mph, mm).

"""
self.api_key = api_key
self.user_agent = user_agent
self.units = units
self.units = "uk2" if units == "uk" else units
self.timeout = 15.0

def _build_url(self, lat: float, lon: float) -> str:
Expand Down
Loading
Loading