"""`catlas` profile enrichment commands: `enrich-companies`, `profile` (services/enrichment.py).""" from __future__ import annotations import json from typing import Annotated import typer from rich.table import Table def register(app: typer.Typer) -> None: @app.command("enrich-companies") def enrich_companies( limit: Annotated[int | None, typer.Option("--limit", help="companies per run (default: CA_ENRICH_BATCH)")] = None, company: Annotated[list[str] | None, typer.Option("--company", help="slug, id or QID (repeatable); forces re-enrichment")] = None, source: Annotated[str, typer.Option("--source", help="wikidata|wikipedia|homepage|llm|all (comma-separated allowed)")] = "all", concurrency: Annotated[int | None, typer.Option("--concurrency")] = None, no_llm: Annotated[bool, typer.Option("--no-llm", help="never call the LLM even when configured")] = False, ) -> None: """Enrich company profiles (Wikidata → Wikipedia → homepage → optional LLM) with provenance; never-enriched companies first.""" from companyatlas.cli import console, out, run_async from companyatlas.services.enrichment import SOURCES, enrich_pending wanted = tuple(SOURCES) if source.strip().lower() in ("all", "") else tuple(s.strip().lower() for s in source.split(",") if s.strip()) unknown = [s for s in wanted if s not in SOURCES] if unknown: console.print(f"[red]unknown source(s): {', '.join(unknown)} — choose from {', '.join(SOURCES)} or all[/]") raise typer.Exit(1) stats = run_async(enrich_pending(limit=limit, concurrency=concurrency, sources=wanted, llm=not no_llm, company_keys=company)) table = Table(title="enrich-companies", show_header=False) for k, v in stats.items(): table.add_row(k, json.dumps(v) if isinstance(v, dict) else str(v)) out.print(table) @app.command("profile") def profile_cmd(key: Annotated[str, typer.Argument(help="company slug, id or Wikidata QID")], as_json: Annotated[bool, typer.Option("--json", help="raw profile JSON")] = False) -> None: """Print a company's stored profile (source_meta.profile) with the key facts and their sources.""" from companyatlas.cli import console, out, run_async from companyatlas.db import connection, fetch_all from companyatlas.services.enrichment import load_company, person_source, profile_facts async def go() -> tuple[dict | None, list[dict], list[dict]]: async with connection() as conn: c = await load_company(conn, key) if c is None: return None, [], [] people = await fetch_all(conn, "select name, title, status, source_url from people where company_id = :c order by is_executive desc, name limit 40", c=c["id"]) rels = await fetch_all(conn, "select r.kind, coalesce(o.display_name, r.to_name) as name, r.valid_from, r.valid_to, r.provenance from company_relationships r " "left join companies o on o.id = r.to_company_id where r.from_company_id = :c order by r.kind, name limit 60", c=c["id"]) return c, people, rels company, people, rels = run_async(go()) if company is None: console.print(f"[red]company not found: {key}[/]") raise typer.Exit(1) meta = company.get("source_meta") or {} if isinstance(meta, str): meta = json.loads(meta) profile = meta.get("profile") if not profile: console.print(f"[yellow]{company['slug']} has not been enriched yet — run `catlas enrich-companies --company {company['slug']}`[/]") raise typer.Exit(2) if as_json: out.print_json(json.dumps(profile, default=str, ensure_ascii=False)) return out.print(f"[bold]{company['display_name']}[/] · {company['canonical_domain']} · enriched {profile.get('enriched_at')} · sources: " f"{', '.join(meta.get('enrichment', {}).get('sources') or [])}") if profile.get("description"): out.print(f"\n{profile['description']}\n[dim]— {profile.get('description_source')} · {profile.get('description_attribution') or ''} " f"{profile.get('description_url') or ''}[/]\n") t = Table(title="facts") for col in ("fact", "value", "source", "url"): t.add_column(col) for f in profile_facts(profile): t.add_row(f["label"], str(f["value"]), f.get("source") or "", (f.get("url") or "")[:80]) out.print(t) if profile.get("industries") or profile.get("products"): out.print(f"industries: {', '.join(profile.get('industries') or [])} · labels: {', '.join(profile.get('industry_labels') or [])}") out.print(f"products: {', '.join(profile.get('products') or [])}") if profile.get("socials"): out.print("socials: " + " ".join(f"{k}={v}" for k, v in profile["socials"].items())) if people: p = Table(title="people") for col in ("name", "title", "status", "source"): p.add_column(col) for r in people: p.add_row(r["name"], r.get("title") or "", r["status"], person_source(r.get("source_url"))) out.print(p) if rels: r_ = Table(title="relationships") for col in ("kind", "company", "valid_from", "valid_to", "property"): r_.add_column(col) for r in rels: prov = r.get("provenance") or {} if isinstance(prov, str): prov = json.loads(prov) r_.add_row(r["kind"], r.get("name") or "", str(r.get("valid_from") or ""), str(r.get("valid_to") or ""), prov.get("property") or "") out.print(r_)