From 186a9d03f3b7d09bb0f340ec046c9ba87d7ce973 Mon Sep 17 00:00:00 2001 From: Shawn Date: Tue, 2 Jun 2026 22:50:53 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20seed=20import=20update=20mode=20fills=20?= =?UTF-8?q?gaps=20only=20=E2=80=94=20MSFS=20is=20primary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes behavior: ALL fields now use ONLY_IF_EMPTY policy for existing assets. Previously, AUTO fields (name, make, model, company, place, building_name, floor, etc.) always overwrote existing data, clobbering MSFS-synced and manually-cleaned records. - db_writer.py: update() treats every field as 'skip if existing value non-null' — same as old PRESERVE behavior but universal - sync.py: UPDATE path now fetches current values and only sets fields that are currently NULL/empty - New assets (not found by machine_id) still insert with full seed data - sync.py docstring and stats output updated to reflect new policy Closes #13 — seed import no longer overwrites MSFS data --- db_writer.py | 36 ++++++++++++++++-------------------- sync.py | 20 +++++++++++++++----- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/db_writer.py b/db_writer.py index 88fbace..a84ea0e 100644 --- a/db_writer.py +++ b/db_writer.py @@ -350,10 +350,12 @@ class DatabaseWriter: """ Update mode: Upsert by machine_id. - PRESERVE fields (lat, lng, photo_path, install_date, deployed, pulled_date): + MSFS is primary data source — seed import fills gaps only. + + ALL fields (except machine_id PK) are treated as ONLY_IF_EMPTY: Skip update if existing value is non-null. - AUTO fields: Always overwrite. - machine_id: Never changes (used as match key). + + New assets (not found by machine_id) are inserted with full data. Returns dict with counts. """ @@ -408,27 +410,21 @@ class DatabaseWriter: stats["errors"] += 1 print(f" ⚠️ Error inserting machine {mid}: {e}") else: - # UPDATE existing record with preserve/auto rules + # UPDATE existing record — MSFS is primary, seed fills gaps existing_dict = dict(existing) updates = {} - preserved_count = 0 + skipped_count = 0 for col in update_cols: + if col == "machine_id": + continue # PK — never update new_val = row.get(col) old_val = existing_dict.get(col) - - if col in PRESERVE_FIELDS: - # PRESERVE: skip if existing value is non-null - if old_val is not None and old_val != "": - preserved_count += 1 - continue # Don't update - # If old is NULL, we can set it - updates[col] = new_val - elif col == "machine_id": - continue # PK — never update - else: - # AUTO: always overwrite - updates[col] = new_val + # Skip if existing value is non-null/empty (MSFS or user data wins) + if old_val is not None and old_val != "": + skipped_count += 1 + continue + updates[col] = new_val if updates: # Build dynamic UPDATE @@ -448,7 +444,7 @@ class DatabaseWriter: set_values, ) stats["updated"] += 1 - stats["preserved"] += preserved_count + stats["preserved"] += skipped_count except Exception as e: stats["errors"] += 1 print(f" ⚠️ Error updating machine {mid}: {e}") @@ -497,7 +493,7 @@ def write_update(machines, db_path): final_count = writer.get_machine_count() print(f" ✅ Inserted: {stats['inserted']}") print(f" 🔄 Updated: {stats['updated']}") - print(f" 🛡️ Preserved fields: {stats['preserved']}") + print(f" 🛡️ Existing data preserved: {stats['preserved']} fields") print(f" ⏭️ Skipped (no changes): {stats['skipped']}") print(f" 📊 Total in DB: {final_count}") if stats["errors"]: diff --git a/sync.py b/sync.py index 2deb781..f626d7c 100644 --- a/sync.py +++ b/sync.py @@ -138,7 +138,7 @@ def push(source_db: str, target_db: str, dry_run: bool = False) -> dict: location_id = 0 existing = tgt.execute( - "SELECT id FROM assets WHERE machine_id = ?", (mid,) + "SELECT * FROM assets WHERE machine_id = ?", (mid,) ).fetchone() # Category mapping @@ -214,12 +214,22 @@ def push(source_db: str, target_db: str, dry_run: bool = False) -> dict: } if existing: + # MSFS is primary — seed fills gaps only + # Build filtered update dict: only set fields that are currently empty + existing_dict = dict(existing) + filtered_data = {} + for col, val in asset_data.items(): + current = existing_dict.get(col) + if current is None or str(current).strip() == "": + filtered_data[col] = val + report["assets_updated"] += 1 if len(report["first_10"]) < 10: - report["first_10"].append(f"UPDATE {mid}: {name} ({cat})") - if not dry_run: - sets = ", ".join(f"{col} = ?" for col in asset_data) - vals = list(asset_data.values()) + [mid] + skipped_fields = len(asset_data) - len(filtered_data) + report["first_10"].append(f"UPDATE {mid}: {name} ({cat}) — {len(filtered_data)} fields, {skipped_fields} skipped (MSFS/cleanup data preserved)") + if not dry_run and filtered_data: + sets = ", ".join(f"{col} = ?" for col in filtered_data) + vals = list(filtered_data.values()) + [mid] tgt.execute( f"UPDATE assets SET {sets} WHERE machine_id = ?", vals,