"""Row → API shapes, exactly as documented in docs/API.md. Never invent values: missing inputs stay `null` / omitted.""" from __future__ import annotations import json from typing import Any from urllib.parse import urlparse from companyatlas.taxonomy import Metric, confidence_label INT_METRICS = {Metric.OPEN_JOBS.value} PCT_METRICS = {Metric.HIRING_MOMENTUM_7D.value, Metric.HIRING_MOMENTUM_30D.value, Metric.HIRING_MOMENTUM_90D.value} def _json(value: Any) -> Any: if isinstance(value, str): try: return json.loads(value) except ValueError: return value return value def _dict(value: Any) -> dict[str, Any]: v = _json(value) return v if isinstance(v, dict) else {} def _list(value: Any) -> list[Any]: v = _json(value) return list(v) if isinstance(v, list | tuple) else [] def _float(value: Any, nd: int = 1) -> float | None: if value is None: return None try: return round(float(value), nd) except (TypeError, ValueError): return None def _int(value: Any) -> int | None: if value is None: return None try: return int(value) except (TypeError, ValueError): return None def metric_value(metric: str, value: Any) -> float | int | None: if value is None: return None if metric in INT_METRICS: return _int(value) return _float(value, 1) def metrics_map(raw: Any) -> dict[str, float | int]: out: dict[str, float | int] = {} for k, v in _dict(raw).items(): mv = metric_value(k, v) if mv is not None: out[k] = mv return out # ------------------------------------------------------------------------------------------------ companies def company_ref(row: dict[str, Any], prefix: str = "company_") -> dict[str, Any]: return {"id": row.get("company_id") or row.get("id"), "slug": row.get(f"{prefix}slug"), "display_name": row.get(f"{prefix}display_name"), "canonical_domain": row.get(f"{prefix}domain") or row.get(f"{prefix}canonical_domain"), "country": row.get(f"{prefix}country"), "logo_url": row.get(f"{prefix}logo_url")} def company_ref_from_company(c: dict[str, Any]) -> dict[str, Any]: return {"id": c["id"], "slug": c["slug"], "display_name": c["display_name"], "canonical_domain": c["canonical_domain"], "country": c.get("country"), "logo_url": c.get("logo_url")} def company_card(row: dict[str, Any]) -> dict[str, Any]: stats = _dict(row.get("stats")) def count(key: str, fallback: Any) -> int: v = stats.get(key) if isinstance(v, int | float) and not isinstance(v, bool): return int(v) return int(fallback or 0) card: dict[str, Any] = { "id": row["id"], "slug": row["slug"], "display_name": row["display_name"], "legal_name": row.get("legal_name"), "canonical_domain": row["canonical_domain"], "website": row["website"], "description": row.get("description"), "industries": list(row.get("industries") or []), "industry_primary": row.get("industry_primary"), "country": row.get("country"), "hq_city": row.get("hq_city"), "hq_region": row.get("hq_region"), "public_company": bool(row.get("public_company")), "ticker": row.get("ticker"), "exchange": row.get("exchange"), "founded_year": row.get("founded_year"), "employees_band": row.get("employees_band"), "logo_url": row.get("logo_url"), "status": row.get("status"), "onboarding_status": row.get("onboarding_status"), "importance": _float(row.get("importance"), 3) or 0.0, "tier": int(row.get("tier") or 4), "metrics": metrics_map(row.get("metrics")), "counts": {"sensors": count("sensors", row.get("sensors")), "observations": count("observations", row.get("observations")), "changes": count("changes", row.get("changes")), "events": count("events", row.get("events")), "jobs_open": count("jobs_open", row.get("jobs_open"))}, "last_event_at": row.get("last_event_at"), "last_observed_at": row.get("last_observed_at"), } if "sparkline" in row: card["sparkline"] = [round(float(x), 1) for x in (row.get("sparkline") or [])] profile = _dict(row.get("profile")) if profile: card["profile"] = profile # docs/API.md "Profile & facts" — present once the company has been enriched return card def relationship(row: dict[str, Any]) -> dict[str, Any]: """`company_relationships` row joined with the target company (`slug`, `display_name` aliases).""" return {"kind": row["kind"], "company": {"slug": row["slug"], "display_name": row["display_name"]} if row.get("slug") else None, "to_name": row.get("to_name") or row.get("display_name"), "valid_from": row.get("valid_from"), "valid_to": row.get("valid_to"), "confidence": _float(row.get("confidence"), 3), "source_url": row.get("source_url"), "provenance": _dict(row.get("provenance")), "first_seen_at": row.get("first_seen_at"), "last_seen_at": row.get("last_seen_at")} # ------------------------------------------------------------------------------------------------ events def event(row: dict[str, Any], *, with_company: bool = True) -> dict[str, Any]: conf = float(row.get("confidence") or 0) out: dict[str, Any] = { "id": row["id"], "event_type": row["event_type"], "event_subtype": row["event_subtype"], "importance": _float(row.get("importance"), 3), "confidence": round(conf, 3), "confidence_label": row.get("confidence_label") or confidence_label(conf), "title": row["title"], "summary": row.get("summary"), "old_value": row.get("old_value"), "new_value": row.get("new_value"), "payload": _dict(row.get("payload")), "entities": _dict(row.get("entities")), "tags": list(row.get("tags") or []), "detected_at": row["detected_at"], "effective_at": row.get("effective_at"), "published_at": row.get("published_at"), "source_url": row.get("source_url"), "surface": row.get("surface"), "sensor_id": row.get("sensor_id"), "change_id": row.get("change_id"), "cluster_id": row.get("cluster_id"), "origin": row.get("origin") or "deterministic", "model_name": row.get("model_name"), "prompt_version": row.get("prompt_version"), "status": row.get("status") or "active", } if with_company: out["company"] = company_ref(row) if row.get("status") == "retracted": out["retracted_reason"] = row.get("retracted_reason") return out def event_source(row: dict[str, Any]) -> dict[str, Any]: return {"source_url": row["source_url"], "surface": row.get("surface"), "detected_at": row["detected_at"], "kind": row.get("kind") or "primary", "sensor_id": row.get("sensor_id"), "snapshot_id": row.get("snapshot_id")} # ------------------------------------------------------------------------------------------------ provenance def sensor(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "company_id": row["company_id"], "surface": row["surface"], "connector_id": row["connector_id"], "url": row["url"], "canonical_url": row["canonical_url"], "domain": row["domain"], "status": row["status"], "tier": (row.get("tier") or "D").strip(), "quality_score": _float(row.get("quality_score"), 1), "discovery_confidence": _float(row.get("discovery_confidence"), 3), "discovery_method": row.get("discovery_method"), "current_interval_s": int(row.get("current_interval_s") or 0), "next_run_at": row.get("next_run_at"), "last_run_at": row.get("last_run_at"), "last_success_at": row.get("last_success_at"), "last_change_at": row.get("last_change_at"), "last_status": row.get("last_status"), "last_failure_class": row.get("last_failure_class"), "consecutive_failures": int(row.get("consecutive_failures") or 0), "observation_count": int(row.get("observation_count") or 0), "snapshot_count": int(row.get("snapshot_count") or 0), "change_count": int(row.get("change_count") or 0), "meaningful_change_count": int(row.get("meaningful_change_count") or 0), "event_count": int(row.get("event_count") or 0), "created_at": row.get("created_at")} def sensor_admin(row: dict[str, Any]) -> dict[str, Any]: out = sensor(row) out.update({"base_interval_s": row.get("base_interval_s"), "last_error": row.get("last_error"), "priority": _float(row.get("priority"), 3), "claimed_by": row.get("claimed_by"), "claimed_at": row.get("claimed_at"), "retired_at": row.get("retired_at"), "consecutive_unchanged": row.get("consecutive_unchanged"), "config": _dict(row.get("config")), "updated_at": row.get("updated_at")}) return out def snapshot(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "sensor_id": row["sensor_id"], "company_id": row.get("company_id"), "version_no": int(row.get("version_no") or 1), "fetched_at": row["fetched_at"], "title": row.get("title"), "language": row.get("language"), "text_length": row.get("text_length"), "block_count": row.get("block_count"), "extracted_summary": _dict(row.get("extracted_summary")), "content_hash": row["content_hash"], "previous_snapshot_id": row.get("previous_snapshot_id"), "observation_id": row.get("observation_id"), "collection_method": row.get("collection_method"), "connector_version": row.get("connector_version")} def change(row: dict[str, Any], *, with_diff: bool = False) -> dict[str, Any]: out: dict[str, Any] = {"id": row["id"], "sensor_id": row["sensor_id"], "surface": row["surface"], "company_id": row["company_id"], "detected_at": row["detected_at"], "significance": _float(row.get("significance"), 3), "kind": row["kind"], "blocks_added": int(row.get("blocks_added") or 0), "blocks_removed": int(row.get("blocks_removed") or 0), "blocks_modified": int(row.get("blocks_modified") or 0), "blocks_moved": int(row.get("blocks_moved") or 0), "text_delta_ratio": _float(row.get("text_delta_ratio"), 4), "similarity": _float(row.get("similarity"), 4), "snapshot_before": row.get("snapshot_before"), "snapshot_after": row["snapshot_after"], "status": row.get("status"), "diff_version": row.get("diff_version")} if with_diff: out["diff"] = _dict(row.get("diff")) out["structured_delta"] = _dict(row.get("structured_delta")) return out # ------------------------------------------------------------------------------------------------ entities def job(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "title": row["title"], "department": row.get("department"), "location_text": row.get("location_text"), "city": row.get("city"), "country": row.get("country"), "remote": row.get("remote"), "employment_type": row.get("employment_type"), "seniority": row.get("seniority"), "url": row.get("url"), "posted_at": row.get("posted_at"), "first_seen_at": row["first_seen_at"], "last_seen_at": row["last_seen_at"], "removed_at": row.get("removed_at"), "status": row.get("status") or "open", "is_ai": bool(row.get("is_ai"))} def person(row: dict[str, Any]) -> dict[str, Any]: source_url = row.get("source_url") host = (urlparse(source_url).hostname or "").lower() if source_url else "" return {"id": row["id"], "name": row["name"], "title": row.get("title"), "role_category": row.get("role_category"), "is_executive": bool(row.get("is_executive")), "first_seen_at": row["first_seen_at"], "last_seen_at": row["last_seen_at"], "removed_at": row.get("removed_at"), "status": row.get("status") or "listed", "source_url": source_url, "source": "wikidata" if host.endswith("wikidata.org") else "page"} def product(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "name": row["name"], "category": row.get("category"), "description": row.get("description"), "url": row.get("url"), "first_seen_at": row["first_seen_at"], "last_seen_at": row["last_seen_at"], "removed_at": row.get("removed_at"), "status": row.get("status") or "listed"} def plan(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "plan_name": row["plan_name"], "price": _float(row.get("price"), 2), "price_text": row.get("price_text"), "currency": row.get("currency"), "billing_period": row.get("billing_period"), "unit": row.get("unit"), "features": [str(x) for x in _list(row.get("features"))], "contact_sales": bool(row.get("contact_sales")), "version_no": int(row.get("version_no") or 1), "valid_from": row["valid_from"], "valid_to": row.get("valid_to"), "status": row.get("status") or "current", "source_url": row.get("source_url")} def location(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "kind": row.get("kind") or "office", "name": row.get("name"), "city": row.get("city"), "region": row.get("region"), "country": row.get("country"), "lat": row.get("lat"), "lon": row.get("lon"), "first_seen_at": row["first_seen_at"], "last_seen_at": row["last_seen_at"], "removed_at": row.get("removed_at"), "status": row.get("status") or "listed", "source_url": row.get("source_url")} def news_item(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "title": row["title"], "url": row["url"], "summary": row.get("summary"), "category": row.get("category"), "published_at": row.get("published_at"), "first_seen_at": row["first_seen_at"], "language": row.get("language")} def metric_point(row: dict[str, Any]) -> dict[str, Any]: return {"day": row["day"], "value": _float(row.get("value"), 2), "confidence": _float(row.get("confidence"), 3)} def signal(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "company_id": row.get("company_id"), "scope": row.get("scope") or "company", "scope_key": row.get("scope_key"), "kind": row["kind"], "strength": _float(row.get("strength"), 3), "confidence": _float(row.get("confidence"), 3), "title": row["title"], "explanation": row.get("explanation"), "evidence": _dict(row.get("evidence")), "window_days": int(row.get("window_days") or 30), "detected_at": row["detected_at"], "expires_at": row.get("expires_at"), "status": row.get("status") or "active"} # ------------------------------------------------------------------------------------------------ owner / admin def alert(row: dict[str, Any]) -> dict[str, Any]: out = {"id": row["id"], "name": row["name"], "company_id": row.get("company_id"), "condition": _dict(row.get("condition")), "channel": row.get("channel") or "web", "target": row.get("target"), "enabled": bool(row.get("enabled", True)), "created_at": row["created_at"], "last_fired_at": row.get("last_fired_at")} if row.get("company_slug"): out["company"] = company_ref(row) return out def alert_delivery(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "alert_id": row["alert_id"], "alert_name": row.get("alert_name"), "event_id": row.get("event_id"), "event_title": row.get("event_title"), "delivered_at": row["delivered_at"], "channel": row["channel"], "status": row["status"], "detail": row.get("detail")} def queue_job(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "kind": row["kind"], "key": row["key"], "payload": _dict(row.get("payload")), "priority": _float(row.get("priority"), 3), "run_at": row["run_at"], "locked_at": row.get("locked_at"), "locked_by": row.get("locked_by"), "attempts": row.get("attempts"), "max_attempts": row.get("max_attempts"), "status": row["status"], "last_error": row.get("last_error"), "created_at": row["created_at"], "finished_at": row.get("finished_at")} def llm_job(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "kind": row["kind"], "ref_id": row["ref_id"], "company_id": row.get("company_id"), "model": row.get("model"), "prompt_version": row.get("prompt_version"), "status": row["status"], "attempts": row.get("attempts"), "request_tokens": row.get("request_tokens"), "response_tokens": row.get("response_tokens"), "latency_ms": row.get("latency_ms"), "result": _json(row.get("result")), "error": row.get("error"), "created_at": row["created_at"], "started_at": row.get("started_at"), "finished_at": row.get("finished_at")} def failure(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "sensor_id": row.get("sensor_id"), "company_id": row.get("company_id"), "at": row["at"], "failure_class": row["failure_class"], "status_code": row.get("status_code"), "message": row.get("message"), "url": row.get("url"), "company_slug": row.get("company_slug"), "surface": row.get("surface")} def review(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "kind": row["kind"], "ref_id": row.get("ref_id"), "company_id": row.get("company_id"), "payload": _dict(row.get("payload")), "status": row["status"], "resolution": row.get("resolution"), "created_at": row["created_at"], "resolved_at": row.get("resolved_at"), "company_slug": row.get("company_slug"), "company_display_name": row.get("company_display_name")} def connector(row: dict[str, Any]) -> dict[str, Any]: return {"id": row["id"], "name": row["name"], "version": row["version"], "category": row["category"], "fetch_mode": row.get("fetch_mode"), "enabled": bool(row.get("enabled")), "default_interval_s": row.get("default_interval_s"), "supports_discovery": bool(row.get("supports_discovery")), "supports_incremental": bool(row.get("supports_incremental")), "stats": _dict(row.get("stats")), "created_at": row.get("created_at"), "updated_at": row.get("updated_at")} __all__ = ["alert", "alert_delivery", "change", "company_card", "company_ref", "company_ref_from_company", "connector", "event", "event_source", "failure", "job", "llm_job", "location", "metric_point", "metric_value", "metrics_map", "news_item", "person", "plan", "product", "queue_job", "relationship", "review", "sensor", "sensor_admin", "signal", "snapshot"]