mattrguerra/meridian-skillsmainPrivate on GitHub, published here in full

merge_writeback.py

Language Python4.1 KB89 lines

skills/client-enrich/scripts/merge_writeback.py

  1. #!/usr/bin/env python3
  2. """
  3. merge_writeback.py: unified write-back combining Apollo person/company data and
  4. web-search signals for client-enrich. Calls enrich_writeback.py so the vault
  5. contract holds (writes only under ## Research + frontmatter; never Human Notes).
  6. Per stub:
  7. - Person = Apollo person lines if matched, else from existing frontmatter.
  8. - Company = Apollo company lines if matched, else minimal (name + LinkedIn + TBD firmographics).
  9. - Signals = Apollo company_signals (funding/growth) + web-search signals; [TBD] if none.
  10. - Sources = web sources + Apollo sources + provenance lines.
  11. - Frontmatter = filled from Apollo (empty fields only; enrich_writeback enforces that).
  12. Usage:
  13. python3 merge_writeback.py --batch no_li_40.json --web web_signals_40.json \
  14. --apollo apollo_results.json --stubdir "/path/03-Clients/Check-In" \
  15. --script "/path/enrich_writeback.py" [--date YYYY-MM-DD]
  16. """
  17. import argparse, json, os, subprocess, sys, datetime, tempfile
  18. def main():
  19. ap = argparse.ArgumentParser()
  20. ap.add_argument("--batch", required=True)
  21. ap.add_argument("--web", required=True)
  22. ap.add_argument("--apollo", required=True)
  23. ap.add_argument("--stubdir", required=True)
  24. ap.add_argument("--script", required=True)
  25. ap.add_argument("--date", default=datetime.date.today().isoformat())
  26. args = ap.parse_args()
  27. batch = {r["file"]: r for r in json.load(open(args.batch, encoding="utf-8"))}
  28. web = json.load(open(args.web, encoding="utf-8"))
  29. apollo = json.load(open(args.apollo, encoding="utf-8")) if os.path.exists(args.apollo) else {}
  30. rows = []
  31. for fname, r in batch.items():
  32. ap_e = apollo.get(fname, {})
  33. matched = bool(ap_e.get("matched"))
  34. web_e = web.get(fname, {})
  35. if matched and ap_e.get("person"):
  36. person = ap_e["person"]
  37. else:
  38. person = []
  39. if r.get("title"): person.append(f"- Title: {r['title']}")
  40. if r.get("seniority"): person.append(f"- Seniority: {r['seniority']}")
  41. loc = ", ".join([x for x in [r.get("city"), r.get("state")] if x])
  42. if loc: person.append(f"- Location: {loc}")
  43. person = person or ["[TBD]"]
  44. if matched and ap_e.get("company"):
  45. company = ap_e["company"]
  46. else:
  47. company = []
  48. if r.get("company"): company.append(f"- {r['company']}")
  49. if r.get("company_li"): company.append(f"- LinkedIn: {r['company_li']}")
  50. company.append("- [TBD: firmographics not available (Apollo miss or no corporate domain)]")
  51. signals = list(ap_e.get("company_signals") or []) + list(web_e.get("signals") or [])
  52. if not signals:
  53. signals = ["[TBD: no recent signals found via Apollo or web search]"]
  54. sources = list(web_e.get("sources") or []) + list(ap_e.get("sources") or [])
  55. if web_e.get("signals"):
  56. sources.append(f"- Web search, {args.date}")
  57. if not matched:
  58. sources.append("- Stub frontmatter (prior enrichment)")
  59. payload = {"frontmatter": ap_e.get("frontmatter", {}) if matched else {},
  60. "person": person, "company": company,
  61. "signals": signals, "sources": sources}
  62. with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as tf:
  63. json.dump(payload, tf); pj = tf.name
  64. res = subprocess.run([sys.executable, args.script, "--stub",
  65. os.path.join(args.stubdir, fname), "--payload", pj,
  66. "--date", args.date], capture_output=True, text=True)
  67. os.unlink(pj)
  68. rows.append((fname, matched, len([s for s in signals if not s.startswith("[TBD")]),
  69. res.returncode == 0, res.stderr.strip()[:120]))
  70. okc = sum(1 for _,_,_,k,_ in rows if k)
  71. mc = sum(1 for _,m,_,_,_ in rows if m)
  72. sc = sum(1 for _,_,n,_,_ in rows if n > 0)
  73. print(f"WROTE {okc}/{len(rows)} OK | Apollo-matched: {mc} | with >=1 signal: {sc}")
  74. for f, m, n, k, err in rows:
  75. print(f" {f[:30]:32} | {'APOLLO' if m else 'frontmtr':8} | {n} sig | {'OK' if k else 'FAIL '+err}")
  76. if __name__ == "__main__":
  77. main()