fix: Handle numeric weather values and hide N/A fields in weather view

The weather view was crashing with CaseClauseError when receiving numeric
values (like 83.0) from the database instead of strings. Also improved
the UI to only show weather fields that have valid data.

Changes:
- Added support for numeric values in format_weather_value function
- Added has_weather_field? helper to check for valid weather data
- Updated template to conditionally display weather fields
- Fields with nil, "N/A", or empty values are now hidden
- Wind field shows direction and speed independently if available

This prevents crashes and provides a cleaner UI by only showing available
weather data instead of displaying N/A values.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Graham McIntire 2025-08-03 15:33:09 -05:00
parent fd60467da8
commit e6d8894bf6
No known key found for this signature in database
2 changed files with 101 additions and 54 deletions

View file

@ -195,6 +195,20 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
rain_since_midnight: {&WeatherUnits.format_rain/2, " "}
}
@doc """
Checks if a weather field has a valid value (not nil, "N/A", or empty).
"""
def has_weather_field?(packet, field) do
value = PacketUtils.get_weather_field(packet, field)
case value do
nil -> false
"N/A" -> false
"" -> false
_ -> true
end
end
@doc """
Formats weather values with appropriate units based on locale.
"""
@ -202,8 +216,21 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
value = PacketUtils.get_weather_field(packet, key)
case value do
nil ->
nil
"N/A" ->
"N/A"
nil
value when is_number(value) ->
case @weather_formatters[key] do
{formatter, separator} ->
{converted_value, unit} = formatter.(value, locale)
"#{converted_value}#{separator}#{unit}"
nil ->
"#{value}"
end
value when is_binary(value) ->
case @weather_formatters[key] do
@ -214,12 +241,15 @@ defmodule AprsmeWeb.WeatherLive.CallsignView do
"#{converted_value}#{separator}#{unit}"
:error ->
value
nil
end
nil ->
"#{value}"
end
_ ->
nil
end
end
end

View file

@ -86,58 +86,75 @@
<% end %>
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Temperature")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :temperature, @locale)}
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Humidity")}</dt>
<dd class="mt-1 text-sm font-semibold">
{PacketUtils.get_weather_field(@weather_packet, :humidity)}%
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Wind")}</dt>
<dd class="mt-1 text-sm font-semibold">
{PacketUtils.get_weather_field(@weather_packet, :wind_direction)}° @ {format_weather_value(
@weather_packet,
:wind_speed,
@locale
)}
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Gusts")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :wind_gust, @locale)}
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Pressure")}</dt>
<dd class="mt-1 text-sm font-semibold">
{PacketUtils.get_weather_field(@weather_packet, :pressure)} hPa
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Rain (1h)")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :rain_1h, @locale)}
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Rain (24h)")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :rain_24h, @locale)}
</dd>
</div>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Rain (since midnight)")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :rain_since_midnight, @locale)}
</dd>
</div>
<%= if has_weather_field?(@weather_packet, :temperature) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Temperature")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :temperature, @locale)}
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :humidity) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Humidity")}</dt>
<dd class="mt-1 text-sm font-semibold">
{PacketUtils.get_weather_field(@weather_packet, :humidity)}%
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :wind_direction) || has_weather_field?(@weather_packet, :wind_speed) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Wind")}</dt>
<dd class="mt-1 text-sm font-semibold">
<%= if has_weather_field?(@weather_packet, :wind_direction) do %>
{PacketUtils.get_weather_field(@weather_packet, :wind_direction)}°
<% end %>
<%= if has_weather_field?(@weather_packet, :wind_speed) do %>
@ {format_weather_value(@weather_packet, :wind_speed, @locale)}
<% end %>
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :wind_gust) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Gusts")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :wind_gust, @locale)}
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :pressure) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Pressure")}</dt>
<dd class="mt-1 text-sm font-semibold">
{PacketUtils.get_weather_field(@weather_packet, :pressure)} hPa
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :rain_1h) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Rain (1h)")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :rain_1h, @locale)}
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :rain_24h) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Rain (24h)")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :rain_24h, @locale)}
</dd>
</div>
<% end %>
<%= if has_weather_field?(@weather_packet, :rain_since_midnight) do %>
<div>
<dt class="text-xs font-medium opacity-70">{gettext("Rain (since midnight)")}</dt>
<dd class="mt-1 text-sm font-semibold">
{format_weather_value(@weather_packet, :rain_since_midnight, @locale)}
</dd>
</div>
<% end %>
<div class="col-span-2">
<dt class="text-xs font-medium opacity-70">{gettext("Raw Packet")}</dt>
<dd class="mt-1 text-xs font-mono break-all">