Fix saved layouts silently dropping weather widget settings
Build and push server image / test (push) Successful in 38s
Build and push server image / build-and-push (push) Successful in 2m37s
Build and push server image / deploy (push) Successful in 52s

LAYOUT_CONFIG_FIELDS never had a "weather" entry, so saving a layout
captured an empty config for any weather widget -- applying it back
(including via hold-to-cycle) reset mode/provider/city/units/etc to
defaults instead of restoring what was configured.
This commit is contained in:
2026-07-28 14:44:15 +00:00
parent d974e872ba
commit 37d57a1f88
2 changed files with 57 additions and 0 deletions
+4
View File
@@ -63,6 +63,10 @@ LAYOUT_CONFIG_FIELDS: dict[str, tuple[str, ...]] = {
"text": ("content", "font_size", "font_family", "align", "background_color"), "text": ("content", "font_size", "font_family", "align", "background_color"),
"whiteboard": ("user_id", "url"), "whiteboard": ("user_id", "url"),
"battery": ("mode",), "battery": ("mode",),
"weather": (
"mode", "provider", "units", "city_label", "city_latitude", "city_longitude",
"hourly_interval_hours", "daily_days", "cities",
),
} }
# widget_type -> (FrameCalendar|FrameTaskList model, SavedLayoutSource.kind) # widget_type -> (FrameCalendar|FrameTaskList model, SavedLayoutSource.kind)
+53
View File
@@ -27,6 +27,7 @@ from app.models import (
TaskWidgetConfig, TaskWidgetConfig,
User, User,
Widget, Widget,
WeatherWidgetConfig,
) )
from .conftest import csrf_headers, link_user, login, make_user from .conftest import csrf_headers, link_user, login, make_user
@@ -56,6 +57,16 @@ def _add_tasks_widget(db_session, frame_id=1, x=3, y=0, w=2, h=2, sort_order=2)
return widget return widget
def _add_weather_widget(db_session, frame_id=1, x=0, y=0, w=2, h=2, sort_order=1) -> Widget:
widget = Widget(frame_id=frame_id, widget_type="weather", x=x, y=y, w=w, h=h,
sort_order=sort_order, created_at=time.time())
db_session.add(widget)
db_session.flush()
db_session.add(WeatherWidgetConfig(widget_id=widget.id))
db_session.commit()
return widget
def _setup_alice(client) -> None: def _setup_alice(client) -> None:
resp = client.post("/setup", data={"username": "alice", "password": "hunter22"}) resp = client.post("/setup", data={"username": "alice", "password": "hunter22"})
assert resp.status_code == 303, resp.text assert resp.status_code == 303, resp.text
@@ -89,6 +100,48 @@ def test_save_captures_placement_and_photo_settings_but_not_queue_state(client,
"queue_target_len": 30} "queue_target_len": 30}
def test_save_and_apply_round_trip_weather_settings(client, db_session):
_setup_alice(client)
weather = _add_weather_widget(db_session)
with db_session.no_autoflush:
wcfg = db_session.get(WeatherWidgetConfig, weather.id)
wcfg.mode = "daily"
wcfg.provider = "nws"
wcfg.units = "celsius"
wcfg.city_label = "Boston, MA"
wcfg.city_latitude = 42.36
wcfg.city_longitude = -71.06
wcfg.hourly_interval_hours = 6
wcfg.daily_days = 7
wcfg.checked_at = 12345.0
wcfg.cached = {"stale": "runtime state, not a setting"}
db_session.commit()
db_session.expunge(wcfg)
save_resp = client.post("/api/frames/1/layouts", json={"name": "Weather Layout"}, headers=csrf_headers(client))
assert save_resp.status_code == 200, save_resp.text
layout = db_session.query(SavedLayout).filter_by(user_id=1, name="Weather Layout").one()
snap = db_session.query(SavedLayoutWidget).filter_by(saved_layout_id=layout.id, widget_type="weather").one()
assert snap.config == {
"mode": "daily", "provider": "nws", "units": "celsius", "city_label": "Boston, MA",
"city_latitude": 42.36, "city_longitude": -71.06, "hourly_interval_hours": 6, "daily_days": 7,
"cities": None,
}
client.delete("/api/frames/1/widgets", headers=csrf_headers(client))
apply_resp = client.post(f"/api/frames/1/layouts/{layout.id}/apply", headers=csrf_headers(client))
assert apply_resp.status_code == 200, apply_resp.text
new_widget = db_session.query(Widget).filter_by(frame_id=1, widget_type="weather").one()
new_cfg = db_session.get(WeatherWidgetConfig, new_widget.id)
assert (new_cfg.mode, new_cfg.provider, new_cfg.units) == ("daily", "nws", "celsius")
assert (new_cfg.city_label, new_cfg.city_latitude, new_cfg.city_longitude) == ("Boston, MA", 42.36, -71.06)
assert (new_cfg.hourly_interval_hours, new_cfg.daily_days) == (6, 7)
# Runtime fetch-cache state is never captured/restored by a saved layout.
assert new_cfg.checked_at == 0.0
assert new_cfg.cached is None
def test_save_captures_calendar_sources_and_button_actions(client, db_session): def test_save_captures_calendar_sources_and_button_actions(client, db_session):
_setup_alice(client) _setup_alice(client)
db_session.query(Widget).filter_by(frame_id=1, widget_type="photos").delete() db_session.query(Widget).filter_by(frame_id=1, widget_type="photos").delete()