From 6850abf7d22af5c97dadb266b572a4c3fb2b9e45 Mon Sep 17 00:00:00 2001 From: chelsea Date: Tue, 17 Feb 2026 19:05:09 -0600 Subject: [PATCH] fix partial-update overwrite bug in snitch and adaptive meds PUT handlers Both handlers were building update_data with defaults for every field, so a partial save (e.g. toggling one toggle) would silently reset all other settings back to their defaults. Now only fields explicitly present in the request body are written to the DB. Co-Authored-By: Claude Sonnet 4.6 --- api/routes/adaptive_meds.py | 35 +++++++++++++++++++---------------- api/routes/snitch.py | 34 +++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/api/routes/adaptive_meds.py b/api/routes/adaptive_meds.py index 745ae66..62dd895 100644 --- a/api/routes/adaptive_meds.py +++ b/api/routes/adaptive_meds.py @@ -91,24 +91,27 @@ def register(app): {"error": "adaptive_mode is required when enabling adaptive timing"} ), 400 - # Build update data - update_data = { - "adaptive_timing_enabled": data.get("adaptive_timing_enabled", False), - "adaptive_mode": data.get("adaptive_mode", "shift_all"), - "presence_tracking_enabled": data.get("presence_tracking_enabled", False), - "nagging_enabled": data.get("nagging_enabled", True), - "nag_interval_minutes": data.get("nag_interval_minutes", 15), - "max_nag_count": data.get("max_nag_count", 4), - "quiet_hours_start": data.get("quiet_hours_start"), - "quiet_hours_end": data.get("quiet_hours_end"), - } + # Only update fields explicitly provided in the request — never overwrite with defaults + allowed_fields = [ + "adaptive_timing_enabled", "adaptive_mode", "presence_tracking_enabled", + "nagging_enabled", "nag_interval_minutes", "max_nag_count", + "quiet_hours_start", "quiet_hours_end", + ] + update_data = {field: data[field] for field in allowed_fields if field in data} + + if not update_data: + return flask.jsonify({"success": True}), 200 try: - update_data["user_uuid"] = user_uuid - update_data.setdefault("id", str(uuid.uuid4())) - postgres.upsert( - "adaptive_med_settings", update_data, conflict_columns=["user_uuid"] - ) + existing = adaptive_meds.get_adaptive_settings(user_uuid) + if existing: + postgres.update( + "adaptive_med_settings", update_data, {"user_uuid": user_uuid} + ) + else: + update_data["id"] = str(uuid.uuid4()) + update_data["user_uuid"] = user_uuid + postgres.insert("adaptive_med_settings", update_data) return flask.jsonify({"success": True}), 200 except Exception as e: diff --git a/api/routes/snitch.py b/api/routes/snitch.py index 7fb75e6..7896905 100644 --- a/api/routes/snitch.py +++ b/api/routes/snitch.py @@ -78,22 +78,26 @@ def register(app): if not data: return flask.jsonify({"error": "No data provided"}), 400 - # Build update data - update_data = { - "snitch_enabled": data.get("snitch_enabled", False), - "trigger_after_nags": data.get("trigger_after_nags", 4), - "trigger_after_missed_doses": data.get("trigger_after_missed_doses", 1), - "max_snitches_per_day": data.get("max_snitches_per_day", 2), - "require_consent": data.get("require_consent", True), - "consent_given": data.get("consent_given", False), - "snitch_cooldown_hours": data.get("snitch_cooldown_hours", 4), - "updated_at": datetime.utcnow(), - } + # Only update fields explicitly provided in the request — never overwrite with defaults + allowed_fields = [ + "snitch_enabled", "trigger_after_nags", "trigger_after_missed_doses", + "max_snitches_per_day", "require_consent", "consent_given", "snitch_cooldown_hours", + ] + update_data = {field: data[field] for field in allowed_fields if field in data} - update_data["user_uuid"] = user_uuid - update_data.setdefault("id", str(uuid.uuid4())) - update_data.setdefault("created_at", datetime.utcnow()) - postgres.upsert("snitch_settings", update_data, conflict_columns=["user_uuid"]) + if not update_data: + return flask.jsonify({"success": True}), 200 + + update_data["updated_at"] = datetime.utcnow() + + existing = snitch_core.get_snitch_settings(user_uuid) + if existing: + postgres.update("snitch_settings", update_data, {"user_uuid": user_uuid}) + else: + update_data["id"] = str(uuid.uuid4()) + update_data["user_uuid"] = user_uuid + update_data["created_at"] = datetime.utcnow() + postgres.insert("snitch_settings", update_data) return flask.jsonify({"success": True}), 200