"""Country Atlas: `/countries`, `/countries/{code}` — code accepts ISO-2 (`CA`) or a name slug (`canada`). Cached 300 s.""" from __future__ import annotations from typing import Any from fastapi import APIRouter, HTTPException, Request from companyatlas.api import aggregates as agg from companyatlas.api import queries as q from companyatlas.api import serializers as ser from companyatlas.api.common import cached, cached_response from companyatlas.db import connection, fetch_all ORDER = 20 router = APIRouter(prefix="/api/v1", tags=["countries"]) @router.get("/countries", summary="Living index per country") async def countries(request: Request) -> Any: return cached_response(request, {"items": await agg.cached_country_rows()}, 300) @router.get("/countries/{code}", summary="Country detail") async def country_detail(code: str, request: Request) -> Any: async def produce() -> dict[str, Any] | None: async with connection() as conn: ref = await agg.resolve_country(conn, code) if ref is None: return None cc = ref["code"] rows = await agg.cached_country_rows() row = next((r for r in rows if r["code"] == cc), None) or { "code": cc, "slug": None, "name": ref["name"], "region": ref.get("region"), "subregion": ref.get("subregion"), "companies": 0, "events_7d": 0, "events_30d": 0, "hiring_momentum_30d": None, "activity_score": None, "ai_adoption": None, "industry_mix": [], "lat": ref.get("lat"), "lon": ref.get("lon")} where, params = q.company_filters(country=cc, status="ACTIVE") ids, _t = await q.company_page_ids(conn, where, params, sort="activity", limit=24) companies = [ser.company_card(r) for r in await q.fetch_cards_by_ids(conn, ids, sparkline=True)] events = await agg.live_events(conn, 20, country=cc) movers = await agg.ranking_cards(conn, "most_active", "7d", country=cc, limit=10) new_ids, _t2 = await q.company_page_ids(conn, where, params, sort="recent", limit=10) new_entrants = [ser.company_card(r) for r in await q.fetch_cards_by_ids(conn, new_ids)] series = await fetch_all(conn, "select ms.day, avg(ms.value) as value, avg(ms.confidence) as confidence from metric_series ms " "join companies c on c.id = ms.company_id where ms.metric = 'activity_score' and c.country = cast(:c as char(2)) " "and ms.day >= :d group by ms.day order by ms.day", c=cc, d=q.days_ago(90).date()) industries = await agg.industry_rows(conn, country=cc) signals = await fetch_all(conn, "select * from signals where scope = 'country' and scope_key = :c and status = 'active' order by detected_at desc limit 10", c=cc) return {**row, "companies_total": row["companies"], "companies": companies, "events": events, "movers": movers, "new_entrants": new_entrants, "series": [ser.metric_point(r) for r in series], "industries": [i for i in industries if i["companies"] > 0][:40], "signals": [ser.signal(s) for s in signals]} payload = await cached(f"country:{code.strip().lower()[:80]}", 300, produce) if payload is None: raise HTTPException(status_code=404, detail="country not found") return cached_response(request, payload, 300)