← back to La Permits School
live_board.py
78 lines
#!/usr/bin/env python3
"""
live_board.py — ranked LIVE board from master_live.csv (the loop's analytical product). $0.
Turns the accumulating live permit+contractor dataset into the "who is building in LA
right now" board: top contractors by ACTIVE pipeline value, newest big permits, and a
council-district cut. Writes LIVE-BOARD.md (committed each cycle so the repo shows a
current, dated snapshot).
"""
import csv, datetime, os
OWNER = {"owner-builder", "owner builder"}
def money(v):
try:
return float(v)
except (TypeError, ValueError):
return 0.0
def main():
if not os.path.exists("master_live.csv"):
print("no master_live.csv yet"); return
rows = list(csv.DictReader(open("master_live.csv", newline="", encoding="utf-8")))
if not rows:
print("master empty"); return
firms = {}
for r in rows:
name = (r.get("contractor_name") or "").strip()
if not name or name.lower() in OWNER:
continue
f = firms.setdefault(name, {"n": 0, "val": 0.0, "lic": r.get("contractor_license", "")})
f["n"] += 1
f["val"] += money(r.get("valuation"))
# active build value by council district (the "where is LA building now" cut)
cd = {}
for r in rows:
k = (r.get("council_district") or "?").strip() or "?"
c = cd.setdefault(k, {"n": 0, "val": 0.0})
c["n"] += 1
c["val"] += money(r.get("valuation"))
top_cd = sorted(cd.items(), key=lambda kv: kv[1]["val"], reverse=True)[:10]
top = sorted(firms.items(), key=lambda kv: kv[1]["val"], reverse=True)[:15]
newest = sorted(rows, key=lambda r: (r.get("issue_date") or ""), reverse=True)[:12]
total = sum(money(r.get("valuation")) for r in rows)
out = []
out.append("# LA LIVE Construction Board")
out.append(f"\n_Auto-generated from live LADBS data. Master: **{len(rows)} active permits**, "
f"**${total:,.0f}** total declared value._\n")
out.append("## Top contractors by active pipeline value")
out.append("\n| # | Contractor | Active $ | Permits | CSLB Lic |")
out.append("|---|---|---|---|---|")
for i, (name, f) in enumerate(top, 1):
out.append(f"| {i} | {name} | ${int(f['val']):,} | {f['n']} | {f['lic'] or '—'} |")
out.append("\n## Active build value by City Council district")
out.append("\n| District | Active $ | Permits |")
out.append("|---|---|---|")
for k, c in top_cd:
out.append(f"| CD {k} | ${int(c['val']):,} | {c['n']} |")
out.append("\n## Newest major permits")
out.append("\n| Issued | Value | Contractor | Address |")
out.append("|---|---|---|---|")
for r in newest:
out.append(f"| {(r.get('issue_date') or '')[:10]} | ${int(money(r.get('valuation'))):,} | "
f"{(r.get('contractor_name') or '—')[:30]} | {(r.get('address') or '')[:34]} |")
open("LIVE-BOARD.md", "w").write("\n".join(out) + "\n")
print(f"LIVE-BOARD.md written: {len(rows)} permits, {len(firms)} contractors (cost: $0)")
if __name__ == "__main__":
main()