From 92ab6c3ca5bc422491bc0c5af945ab9ebfab932e Mon Sep 17 00:00:00 2001 From: Orinks Date: Tue, 17 Mar 2026 18:29:08 +0000 Subject: [PATCH] feat(display): add round_values setting to show weather as whole numbers Adds a boolean AppSettings.round_values (default False) that strips decimals from all displayed weather values when enabled. Wires through current conditions, forecast, and taskbar icon text. Adds a checkbox in the Settings dialog Display section. Fixes a test assertion using 0.15 (FP banker's rounding) in favour of 0.17. Co-Authored-By: Claude Sonnet 4.6 --- src/accessiweather/app.py | 1 + .../presentation/current_conditions.py | 21 +- .../display/presentation/forecast.py | 14 +- .../display/presentation/formatters.py | 12 +- src/accessiweather/models/config.py | 5 + src/accessiweather/taskbar_icon_updater.py | 19 +- .../ui/dialogs/settings_dialog.py | 9 + tests/test_round_values_setting.py | 357 ++++++++++++++++++ 8 files changed, 414 insertions(+), 24 deletions(-) create mode 100644 tests/test_round_values_setting.py diff --git a/src/accessiweather/app.py b/src/accessiweather/app.py index df7069d28..b72afd8ed 100644 --- a/src/accessiweather/app.py +++ b/src/accessiweather/app.py @@ -820,6 +820,7 @@ def _initialize_taskbar_updater(self) -> None: format_string=getattr(settings, "taskbar_icon_text_format", "{temp} {condition}"), temperature_unit=getattr(settings, "temperature_unit", "both"), verbosity_level=getattr(settings, "verbosity_level", "standard"), + round_values=getattr(settings, "round_values", False), ) logger.debug("Taskbar icon updater initialized") except Exception as e: diff --git a/src/accessiweather/display/presentation/current_conditions.py b/src/accessiweather/display/presentation/current_conditions.py index 5dd7d7e49..aca0067cf 100644 --- a/src/accessiweather/display/presentation/current_conditions.py +++ b/src/accessiweather/display/presentation/current_conditions.py @@ -55,7 +55,7 @@ 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) + wind_value = format_wind(current, unit_pref, precision=precision) if wind_value: metrics.append(Metric("Wind", wind_value)) @@ -63,11 +63,11 @@ def _build_basic_metrics( if show_dewpoint and dewpoint_value: metrics.append(Metric("Dewpoint", dewpoint_value)) - pressure_value = format_pressure_value(current, unit_pref) + pressure_value = format_pressure_value(current, unit_pref, precision=precision) if pressure_value: metrics.append(Metric("Pressure", pressure_value)) - visibility_value = format_visibility_value(current, unit_pref) + visibility_value = format_visibility_value(current, unit_pref, precision=precision) if show_visibility and visibility_value: metrics.append(Metric("Visibility", visibility_value)) @@ -93,13 +93,13 @@ def _build_basic_metrics( 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:.1f} mm")) + metrics.append(Metric("Precipitation", f"{precip_mm:.{precision}f} mm")) elif unit_pref == TemperatureUnit.FAHRENHEIT: - metrics.append(Metric("Precipitation", f"{current.precipitation_in:.2f} in")) + 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:.2f} in ({precip_mm:.1f} mm)") + Metric("Precipitation", f"{current.precipitation_in:.{precision}f} in ({precip_mm:.{precision}f} mm)") ) return metrics @@ -243,13 +243,13 @@ def _build_seasonal_metrics( if current.snow_depth_in is not None and current.snow_depth_in > 0: if unit_pref == TemperatureUnit.CELSIUS: snow_depth_cm = current.snow_depth_cm or current.snow_depth_in * 2.54 - metrics.append(Metric("Snow on ground", f"{snow_depth_cm:.1f} cm")) + metrics.append(Metric("Snow on ground", f"{snow_depth_cm:.{precision}f} cm")) elif unit_pref == TemperatureUnit.FAHRENHEIT: - metrics.append(Metric("Snow on ground", f"{current.snow_depth_in:.1f} in")) + metrics.append(Metric("Snow on ground", f"{current.snow_depth_in:.{precision}f} in")) else: snow_depth_cm = current.snow_depth_cm or current.snow_depth_in * 2.54 metrics.append( - Metric("Snow on ground", f"{current.snow_depth_in:.1f} in ({snow_depth_cm:.1f} cm)") + Metric("Snow on ground", f"{current.snow_depth_in:.{precision}f} in ({snow_depth_cm:.{precision}f} cm)") ) if current.wind_chill_f is not None: @@ -428,7 +428,8 @@ def build_current_conditions( """Create a structured presentation for the current weather using helper functions.""" title = f"Current conditions for {location.name}" description = current.condition or "Unknown" - precision = get_temperature_precision(unit_pref) + round_values = getattr(settings, "round_values", False) if settings else False + precision = 0 if round_values else get_temperature_precision(unit_pref) # Extract settings preferences show_dewpoint = getattr(settings, "show_dewpoint", True) if settings else True diff --git a/src/accessiweather/display/presentation/forecast.py b/src/accessiweather/display/presentation/forecast.py index 941dabd5a..b75d6446b 100644 --- a/src/accessiweather/display/presentation/forecast.py +++ b/src/accessiweather/display/presentation/forecast.py @@ -107,7 +107,8 @@ def build_forecast( ) -> ForecastPresentation: """Create a structured forecast including optional hourly highlights.""" title = f"Forecast for {location.name}" - precision = get_temperature_precision(unit_pref) + round_values = getattr(settings, "round_values", False) if settings else False + precision = 0 if round_values else get_temperature_precision(unit_pref) periods: list[ForecastPeriodPresentation] = [] fallback_lines = [f"Forecast for {location.name}:\n"] @@ -161,7 +162,7 @@ def build_forecast( else None ) snowfall_val = ( - f"{period.snowfall:.1f} in" + f"{period.snowfall:.{precision}f} in" if include_snowfall and period.snowfall is not None and period.snowfall > 0 else None ) @@ -177,7 +178,7 @@ def build_forecast( ) gust_val = period.wind_gust if include_wind_gust and period.wind_gust else None precip_amt = ( - f"{period.precipitation_amount:.2f} in" + f"{period.precipitation_amount:.{precision}f} in" if include_precipitation and period.precipitation_amount is not None and period.precipitation_amount > 0 @@ -261,7 +262,8 @@ def build_hourly_summary( settings: AppSettings | None = None, ) -> list[HourlyPeriodPresentation]: """Generate the next six hours of simplified forecast data.""" - precision = get_temperature_precision(unit_pref) + round_values = getattr(settings, "round_values", False) if settings else False + precision = 0 if round_values else get_temperature_precision(unit_pref) summary: list[HourlyPeriodPresentation] = [] # Extract time display preferences and verbosity from settings @@ -314,7 +316,7 @@ def build_hourly_summary( else None ) snowfall_val = ( - f"{period.snowfall:.1f} in" + f"{period.snowfall:.{precision}f} in" if include_snowfall and period.snowfall is not None and period.snowfall > 0 else None ) @@ -334,7 +336,7 @@ def build_hourly_summary( else None ) precip_amt = ( - f"{period.precipitation_amount:.2f} in" + f"{period.precipitation_amount:.{precision}f} in" if include_precipitation and period.precipitation_amount is not None and period.precipitation_amount > 0 diff --git a/src/accessiweather/display/presentation/formatters.py b/src/accessiweather/display/presentation/formatters.py index 19150b3bf..51398f35e 100644 --- a/src/accessiweather/display/presentation/formatters.py +++ b/src/accessiweather/display/presentation/formatters.py @@ -29,7 +29,9 @@ def format_temperature_pair( return format_temperature(temp_f, unit_pref, temperature_c=temp_c, precision=precision) -def format_wind(current: CurrentConditions, unit_pref: TemperatureUnit) -> str | None: +def format_wind( + current: CurrentConditions, unit_pref: TemperatureUnit, precision: int = 1 +) -> str | None: """Describe wind direction and speed or return calm when wind is negligible.""" if ( current.wind_speed_mph is None @@ -56,7 +58,7 @@ def format_wind(current: CurrentConditions, unit_pref: TemperatureUnit) -> str | current.wind_speed_mph, unit_pref, wind_speed_kph=current.wind_speed_kph, - precision=1, + precision=precision, ) if direction and speed: return f"{direction} at {speed}" @@ -97,6 +99,7 @@ def format_dewpoint( def format_pressure_value( current: CurrentConditions, unit_pref: TemperatureUnit, + precision: int = 1, ) -> str | None: """Format station pressure in the preferred unit, if available.""" if current.pressure_in is None and current.pressure_mb is None: @@ -105,12 +108,13 @@ 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=1) + return format_pressure(pressure_in, unit_pref, pressure_mb=pressure_mb, precision=precision) def format_visibility_value( current: CurrentConditions, unit_pref: TemperatureUnit, + precision: int = 1, ) -> str | None: """Format horizontal visibility taking unit preference into account.""" if current.visibility_miles is None and current.visibility_km is None: @@ -119,7 +123,7 @@ def format_visibility_value( current.visibility_miles, unit_pref, visibility_km=current.visibility_km, - precision=1, + precision=precision, ) diff --git a/src/accessiweather/models/config.py b/src/accessiweather/models/config.py index a193f9f79..9273a2032 100644 --- a/src/accessiweather/models/config.py +++ b/src/accessiweather/models/config.py @@ -55,6 +55,7 @@ # API key settings (loaded lazily via keyring) "visual_crossing_api_key", # Display preferences + "round_values", "show_detailed_forecast", "enable_alerts", "minimize_to_tray", @@ -187,6 +188,8 @@ class AppSettings: # Startup UX guidance flags onboarding_wizard_shown: bool = False portable_missing_api_keys_hint_shown: bool = False + # Display precision + round_values: bool = False @staticmethod def _as_bool(value, default: bool) -> bool: @@ -448,6 +451,7 @@ def to_dict(self) -> dict: "severe_weather_override": self.severe_weather_override, "onboarding_wizard_shown": self.onboarding_wizard_shown, "portable_missing_api_keys_hint_shown": self.portable_missing_api_keys_hint_shown, + "round_values": self.round_values, } @classmethod @@ -542,6 +546,7 @@ def from_dict(cls, data: dict) -> AppSettings: portable_missing_api_keys_hint_shown=cls._as_bool( data.get("portable_missing_api_keys_hint_shown"), False ), + round_values=cls._as_bool(data.get("round_values"), False), ) def to_alert_settings(self): diff --git a/src/accessiweather/taskbar_icon_updater.py b/src/accessiweather/taskbar_icon_updater.py index ac9e7a20d..b63bf4a04 100644 --- a/src/accessiweather/taskbar_icon_updater.py +++ b/src/accessiweather/taskbar_icon_updater.py @@ -53,6 +53,7 @@ def __init__( format_string: str = DEFAULT_TOOLTIP_FORMAT, temperature_unit: str = "both", verbosity_level: str = "standard", + round_values: bool = False, ): """ Initialize the TaskbarIconUpdater. @@ -63,6 +64,7 @@ def __init__( format_string: The format string to use for the tooltip temperature_unit: Temperature unit preference (fahrenheit, celsius, both) verbosity_level: Information verbosity level (minimal, standard, detailed) + round_values: Whether to round numeric values to whole numbers """ self.text_enabled = text_enabled @@ -70,6 +72,7 @@ def __init__( self.format_string = format_string self.temperature_unit = temperature_unit self.verbosity_level = verbosity_level + self.round_values = round_values self.parser = FormatStringParser() self._last_format_error: str | None = None @@ -80,6 +83,7 @@ def update_settings( format_string: str | None = None, temperature_unit: str | None = None, verbosity_level: str | None = None, + round_values: bool | None = None, ) -> None: """ Update taskbar icon settings. @@ -90,6 +94,7 @@ def update_settings( format_string: The format string to use for the tooltip temperature_unit: Temperature unit preference verbosity_level: Information verbosity level (minimal, standard, detailed) + round_values: Whether to round numeric values to whole numbers """ if text_enabled is not None: @@ -102,6 +107,8 @@ def update_settings( self.temperature_unit = temperature_unit if verbosity_level is not None: self.verbosity_level = verbosity_level + if round_values is not None: + self.round_values = round_values def format_tooltip( self, @@ -313,29 +320,32 @@ def _format_wind(self, current: Any) -> str: def _format_wind_speed(self, current: Any) -> str: """Format wind speed using the selected unit preference.""" + precision = 0 if self.round_values else 1 return format_wind_speed( getattr(current, "wind_speed_mph", None), unit=self._normalize_temperature_unit(), wind_speed_kph=getattr(current, "wind_speed_kph", None), - precision=1, + precision=precision, ) def _format_pressure(self, current: Any) -> str: """Format pressure using the selected unit preference.""" + precision = 0 if self.round_values else 2 return format_pressure( getattr(current, "pressure_in", None), unit=self._normalize_temperature_unit(), pressure_mb=getattr(current, "pressure_mb", None), - precision=2, + precision=precision, ) def _format_visibility(self, current: Any) -> str: """Format visibility using the selected unit preference.""" + precision = 0 if self.round_values else 1 return format_visibility( getattr(current, "visibility_miles", None), unit=self._normalize_temperature_unit(), visibility_km=getattr(current, "visibility_km", None), - precision=1, + precision=precision, ) def _format_precipitation(self, current: Any) -> str: @@ -346,11 +356,12 @@ def _format_precipitation(self, current: Any) -> str: if precip_in is None: precip_in = getattr(current, "precipitation", None) + precision = 0 if self.round_values else 2 return format_precipitation( precip_in, unit=self._normalize_temperature_unit(), precipitation_mm=getattr(current, "precipitation_mm", None), - precision=2, + precision=precision, ) def _format_forecast_temperatures( diff --git a/src/accessiweather/ui/dialogs/settings_dialog.py b/src/accessiweather/ui/dialogs/settings_dialog.py index c5f17e4da..9a87e59e6 100644 --- a/src/accessiweather/ui/dialogs/settings_dialog.py +++ b/src/accessiweather/ui/dialogs/settings_dialog.py @@ -190,6 +190,11 @@ def _create_display_tab(self): self._controls["show_pressure_trend"] = wx.CheckBox(panel, label="Show pressure trend") sizer.Add(self._controls["show_pressure_trend"], 0, wx.LEFT, 10) + self._controls["round_values"] = wx.CheckBox( + panel, label="Show values as whole numbers (no decimals)" + ) + sizer.Add(self._controls["round_values"], 0, wx.LEFT | wx.TOP, 10) + self._controls["detailed_forecast"] = wx.CheckBox( panel, label="Show detailed forecast information" ) @@ -1266,6 +1271,9 @@ def _load_settings(self): self._controls["show_pressure_trend"].SetValue( getattr(settings, "show_pressure_trend", True) ) + self._controls["round_values"].SetValue( + getattr(settings, "round_values", False) + ) self._controls["detailed_forecast"].SetValue( getattr(settings, "show_detailed_forecast", True) ) @@ -1520,6 +1528,7 @@ def _save_settings(self) -> bool: "show_visibility": self._controls["show_visibility"].GetValue(), "show_uv_index": self._controls["show_uv_index"].GetValue(), "show_pressure_trend": self._controls["show_pressure_trend"].GetValue(), + "round_values": self._controls["round_values"].GetValue(), "show_detailed_forecast": self._controls["detailed_forecast"].GetValue(), "forecast_duration_days": forecast_duration_values[ self._controls["forecast_duration_days"].GetSelection() diff --git a/tests/test_round_values_setting.py b/tests/test_round_values_setting.py new file mode 100644 index 000000000..61c34b305 --- /dev/null +++ b/tests/test_round_values_setting.py @@ -0,0 +1,357 @@ +"""Tests for the round_values / whole-number display setting.""" + +from __future__ import annotations + +import pytest + +from accessiweather.display.presentation.current_conditions import ( + _build_basic_metrics, + _build_seasonal_metrics, + build_current_conditions, +) +from accessiweather.display.presentation.forecast import build_forecast, build_hourly_summary +from accessiweather.display.presentation.formatters import ( + format_pressure_value, + format_visibility_value, + format_wind, +) +from accessiweather.models import ( + AppSettings, + CurrentConditions, + Forecast, + ForecastPeriod, + HourlyForecast, + HourlyForecastPeriod, + Location, +) +from accessiweather.taskbar_icon_updater import TaskbarIconUpdater +from accessiweather.utils import TemperatureUnit + + +# ── Helpers ────────────────────────────────────────────────────────────────── + + +def _make_current( + temp_f: float = 72.4, + wind_mph: float = 8.7, + pressure_in: float = 29.92, + visibility_miles: float = 9.5, + precip_in: float = 0.15, + snow_in: float = 3.7, + humidity: float = 55.0, +) -> CurrentConditions: + return CurrentConditions( + temperature_f=temp_f, + temperature_c=(temp_f - 32) * 5 / 9, + humidity=humidity, + condition="Partly Cloudy", + wind_speed_mph=wind_mph, + wind_speed_kph=wind_mph * 1.60934, + wind_direction="NW", + pressure_in=pressure_in, + pressure_mb=pressure_in * 33.8639, + visibility_miles=visibility_miles, + visibility_km=visibility_miles * 1.60934, + precipitation_in=precip_in, + precipitation_mm=precip_in * 25.4, + snow_depth_in=snow_in, + snow_depth_cm=snow_in * 2.54, + ) + + +def _make_settings(round_values: bool) -> AppSettings: + return AppSettings(temperature_unit="fahrenheit", round_values=round_values) + + +def _make_location() -> Location: + return Location(name="Test City", latitude=40.0, longitude=-75.0) + + +# ── AppSettings round_values field ─────────────────────────────────────────── + + +class TestAppSettingsRoundValues: + def test_default_is_false(self): + s = AppSettings() + assert s.round_values is False + + def test_round_true_roundtrips(self): + s = AppSettings(round_values=True) + d = s.to_dict() + assert d["round_values"] is True + restored = AppSettings.from_dict(d) + assert restored.round_values is True + + def test_round_false_roundtrips(self): + s = AppSettings(round_values=False) + d = s.to_dict() + assert d["round_values"] is False + restored = AppSettings.from_dict(d) + assert restored.round_values is False + + def test_from_dict_missing_key_defaults_false(self): + s = AppSettings.from_dict({}) + assert s.round_values is False + + def test_from_dict_truthy_string(self): + s = AppSettings.from_dict({"round_values": "true"}) + assert s.round_values is True + + +# ── format_wind precision ───────────────────────────────────────────────────── + + +class TestFormatWindPrecision: + def setup_method(self): + self.current = _make_current(wind_mph=8.7) + + def test_default_has_decimal(self): + result = format_wind(self.current, TemperatureUnit.FAHRENHEIT) + assert "8.7" in result + + def test_precision_zero_rounds(self): + result = format_wind(self.current, TemperatureUnit.FAHRENHEIT, precision=0) + assert "9" in result + assert "8.7" not in result + + +# ── format_pressure_value precision ────────────────────────────────────────── + + +class TestFormatPressurePrecision: + def setup_method(self): + self.current = _make_current(pressure_in=29.92) + + def test_default_has_decimal(self): + result = format_pressure_value(self.current, TemperatureUnit.FAHRENHEIT) + assert "." in result + + def test_precision_zero_no_decimal(self): + result = format_pressure_value(self.current, TemperatureUnit.FAHRENHEIT, precision=0) + assert "." not in result + assert "30" in result + + +# ── format_visibility_value precision ──────────────────────────────────────── + + +class TestFormatVisibilityPrecision: + def setup_method(self): + self.current = _make_current(visibility_miles=9.5) + + def test_default_has_decimal(self): + result = format_visibility_value(self.current, TemperatureUnit.FAHRENHEIT) + assert "9.5" in result + + def test_precision_zero_rounds(self): + result = format_visibility_value(self.current, TemperatureUnit.FAHRENHEIT, precision=0) + assert "10" in result + assert "9.5" not in result + + +# ── build_current_conditions with round_values ─────────────────────────────── + + +class TestBuildCurrentConditionsRoundValues: + def setup_method(self): + self.current = _make_current( + temp_f=72.4, + wind_mph=8.7, + pressure_in=29.92, + visibility_miles=9.5, + precip_in=0.15, + ) + self.location = _make_location() + + def _get_metric(self, presentation, label): + for m in presentation.metrics: + if m.label == label: + return m.value + return None + + def test_round_values_false_keeps_decimals(self): + settings = _make_settings(round_values=False) + pres = build_current_conditions( + self.current, + self.location, + TemperatureUnit.FAHRENHEIT, + settings=settings, + ) + temp = self._get_metric(pres, "Temperature") + assert temp is not None + # With smart_precision in format_temperature, 72.4°F stays as 72.4 + # Wind: 8.7 mph should show decimal + wind = self._get_metric(pres, "Wind") + assert wind is not None + assert "8.7" in wind + + def test_round_values_true_removes_decimals_from_wind(self): + settings = _make_settings(round_values=True) + pres = build_current_conditions( + self.current, + self.location, + TemperatureUnit.FAHRENHEIT, + settings=settings, + ) + wind = self._get_metric(pres, "Wind") + assert wind is not None + assert "8.7" not in wind + assert "9" in wind + + def test_round_values_true_removes_decimals_from_pressure(self): + settings = _make_settings(round_values=True) + pres = build_current_conditions( + self.current, + self.location, + TemperatureUnit.FAHRENHEIT, + settings=settings, + ) + pressure = self._get_metric(pres, "Pressure") + assert pressure is not None + assert "." not in pressure + + def test_round_values_true_removes_decimals_from_visibility(self): + settings = _make_settings(round_values=True) + pres = build_current_conditions( + self.current, + self.location, + TemperatureUnit.FAHRENHEIT, + settings=settings, + ) + visibility = self._get_metric(pres, "Visibility") + assert visibility is not None + assert "9.5" not in visibility + + def test_round_values_true_removes_decimals_from_precipitation(self): + settings = _make_settings(round_values=True) + pres = build_current_conditions( + self.current, + self.location, + TemperatureUnit.FAHRENHEIT, + settings=settings, + ) + precip = self._get_metric(pres, "Precipitation") + assert precip is not None + assert "0.15" not in precip + + +# ── _build_basic_metrics precipitation ─────────────────────────────────────── + + +class TestBuildBasicMetricsPrecipitation: + def test_precision_1_formats_correctly(self): + current = _make_current(precip_in=0.17) + metrics = _build_basic_metrics( + current, TemperatureUnit.FAHRENHEIT, 1, True, True, True + ) + precip = next((m for m in metrics if m.label == "Precipitation"), None) + assert precip is not None + assert "0.2" in precip.value # rounds 0.17 to 0.2 at precision=1 + + def test_precision_0_rounds_to_whole(self): + current = _make_current(precip_in=0.15) + metrics = _build_basic_metrics( + current, TemperatureUnit.FAHRENHEIT, 0, True, True, True + ) + precip = next((m for m in metrics if m.label == "Precipitation"), None) + assert precip is not None + assert "0.15" not in precip.value + assert "." not in precip.value.split(" ")[0] + + +# ── _build_seasonal_metrics snow depth ─────────────────────────────────────── + + +class TestBuildSeasonalMetricsSnowDepth: + def test_default_precision_has_decimal(self): + current = _make_current(snow_in=3.7) + metrics = _build_seasonal_metrics(current, TemperatureUnit.FAHRENHEIT, 1) + snow = next((m for m in metrics if m.label == "Snow on ground"), None) + assert snow is not None + assert "3.7" in snow.value + + def test_precision_0_rounds(self): + current = _make_current(snow_in=3.7) + metrics = _build_seasonal_metrics(current, TemperatureUnit.FAHRENHEIT, 0) + snow = next((m for m in metrics if m.label == "Snow on ground"), None) + assert snow is not None + assert "3.7" not in snow.value + assert "4" in snow.value + + +# ── TaskbarIconUpdater round_values ────────────────────────────────────────── + + +class TestTaskbarIconUpdaterRoundValues: + def _make_current_ns(self, wind_mph=8.7, pressure_in=29.92, visibility_miles=9.5): + from types import SimpleNamespace + + return SimpleNamespace( + temperature_f=72.0, + temperature_c=22.2, + condition="Cloudy", + humidity=55, + wind_speed=wind_mph, + wind_speed_mph=wind_mph, + wind_speed_kph=wind_mph * 1.60934, + wind_direction="NW", + pressure=pressure_in, + pressure_in=pressure_in, + pressure_mb=pressure_in * 33.8639, + feels_like_f=72.0, + feels_like_c=22.2, + uv_index=5, + visibility_miles=visibility_miles, + visibility_km=visibility_miles * 1.60934, + precipitation=0.0, + precipitation_mm=0.0, + precipitation_probability=20, + has_data=lambda: True, + ) + + def test_round_values_false_wind_has_decimal(self): + updater = TaskbarIconUpdater( + text_enabled=True, + temperature_unit="fahrenheit", + round_values=False, + ) + current = self._make_current_ns(wind_mph=8.7) + result = updater._format_wind_speed(current) + assert "8.7" in result + + def test_round_values_true_wind_no_decimal(self): + updater = TaskbarIconUpdater( + text_enabled=True, + temperature_unit="fahrenheit", + round_values=True, + ) + current = self._make_current_ns(wind_mph=8.7) + result = updater._format_wind_speed(current) + assert "8.7" not in result + assert "9" in result + + def test_round_values_true_pressure_no_decimal(self): + updater = TaskbarIconUpdater( + text_enabled=True, + temperature_unit="fahrenheit", + round_values=True, + ) + current = self._make_current_ns(pressure_in=29.92) + result = updater._format_pressure(current) + assert "." not in result + + def test_round_values_true_visibility_no_decimal(self): + updater = TaskbarIconUpdater( + text_enabled=True, + temperature_unit="fahrenheit", + round_values=True, + ) + current = self._make_current_ns(visibility_miles=9.5) + result = updater._format_visibility(current) + assert "9.5" not in result + + def test_update_settings_round_values(self): + updater = TaskbarIconUpdater(text_enabled=True, round_values=False) + assert updater.round_values is False + updater.update_settings(round_values=True) + assert updater.round_values is True