Add sync module: push seed data to main app DB (dev/prod)

- New sync.py: reads seed import's 39-col schema, maps to main app's
  customers/locations/assets schema with foreign keys
- New POST /api/sync endpoint (target=dev|prod, dry_run=true/false)
- New GET /api/sync/targets endpoint
- Category mapping: Bev→Beverage, Snack→Snack, Food→Food, etc.
- Automatically creates customers and locations from company/place
- Idempotent: updates existing assets by machine_id
- Dry-run mode: preview changes without writing
This commit is contained in:
2026-05-25 00:04:43 -04:00
parent 0205866e7a
commit bd3bfea37c
2 changed files with 362 additions and 0 deletions
+38
View File
@@ -37,6 +37,7 @@ sys.path.insert(0, str(PROJECT_ROOT))
from parser import parse_excel
from db_writer import write_seed, write_update, ALL_COLUMNS, PRESERVE_FIELDS, AUTO_FIELDS
from backup import backup as do_backup, compare as compare_backup
from sync import push as sync_push, TARGETS as SYNC_TARGETS
# ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -550,6 +551,43 @@ async def api_db_wipe(db_path: str = Query(DEFAULT_DB_PATH)):
raise HTTPException(500, f"Wipe error: {e}")
# ─── Sync / Push to Main App ──────────────────────────────────────────────────
@app.post("/api/sync")
async def api_sync(
target: str = Query("dev"),
dry_run: bool = Query(False),
):
"""Push seed import data to the main app database."""
if target not in SYNC_TARGETS:
raise HTTPException(400, f"Unknown target: {target}. Options: {list(SYNC_TARGETS.keys())}")
target_db = SYNC_TARGETS[target]["path"]
source_db = DEFAULT_DB_PATH
report = sync_push(source_db, target_db, dry_run=dry_run)
if "error" in report:
raise HTTPException(500, report["error"])
return report
@app.get("/api/sync/targets")
async def api_sync_targets():
"""Return available sync targets."""
return {
"targets": {
name: {
"path": info["path"],
"exists": os.path.exists(info["path"]),
}
for name, info in SYNC_TARGETS.items()
},
"source": {
"path": DEFAULT_DB_PATH,
"exists": os.path.exists(DEFAULT_DB_PATH),
},
}
# ─── Static files ─────────────────────────────────────────────────────────────
STATIC_DIR = Path(__file__).parent / "static"