#!/usr/bin/env python3 """Re-import fallback leads (appointment_requests.jsonl) into Odoo. When Odoo is unreachable during a call, the lead is saved to the JSONL fallback so it's never lost — but nothing pushed it into Odoo afterwards, so staff would never see it. This script drains the file: each record is written to Odoo; successes are removed, failures are kept for the next run. server.py also warns at startup when the file has pending leads. Usage (loads ../.env itself): python scripts/reimport_leads.py # import pending leads python scripts/reimport_leads.py --dry-run # show what would be imported """ import argparse import json import os import sys from pathlib import Path PROJ = Path(__file__).resolve().parent.parent sys.path.insert(0, str(PROJ)) def load_env(path): """Minimal .env loader (KEY=VALUE lines; doesn't override existing env).""" if not path.exists(): return for line in path.read_text().splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue k, v = line.split("=", 1) v = v.split(" #")[0].strip().strip("'\"") os.environ.setdefault(k.strip(), v) def main(): ap = argparse.ArgumentParser() ap.add_argument("--dry-run", action="store_true") args = ap.parse_args() load_env(PROJ / ".env") jsonl = Path(os.environ.get("REQUESTS_LOG", PROJ / "appointment_requests.jsonl")) if not jsonl.exists() or jsonl.stat().st_size == 0: print("No pending fallback leads — nothing to do.") return 0 from odoo_client import create_appointment_request # after env is loaded kept, imported = [], 0 lines = [ln for ln in jsonl.read_text().splitlines() if ln.strip()] print(f"{len(lines)} pending lead(s) in {jsonl}") for ln in lines: try: rec = json.loads(ln) except json.JSONDecodeError: print(f" SKIP (unparseable, kept): {ln[:80]}") kept.append(ln) continue kind = rec.get("kind", "appointment") label = f"{kind}: {rec.get('patient_name')} / {rec.get('reason')} ({rec.get('ts')})" if args.dry_run: print(f" would import {label}") kept.append(ln) continue try: if kind == "callback": reason = rec.get("reason") or "callback request" else: reason = f"[{rec.get('location') or 'location TBD'}] {rec.get('reason') or ''}".strip() model, rec_id = create_appointment_request( patient_name=rec.get("patient_name"), callback_number=rec.get("callback_number"), reason=f"{reason} (re-imported from fallback)", preferred_time=rec.get("preferred_time"), insurance=rec.get("insurance"), call_sid=rec.get("call_sid"), kind=kind, ) imported += 1 print(f" OK -> Odoo {model} id={rec_id}: {label}") except Exception as e: print(f" FAILED (kept for next run): {label} — {e}") kept.append(ln) if not args.dry_run: if kept: jsonl.write_text("\n".join(kept) + "\n") else: jsonl.unlink() print(f"Done: {imported} imported, {len(kept)} kept.") return 0 if not kept or args.dry_run else 1 if __name__ == "__main__": sys.exit(main())