HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1"""/papers listing and /papers/{slug} alias."""2from __future__ import annotations34from typing import Any56from fastapi import APIRouter, Query, Request78from aiatlas.api.common import (9 ENTITY_COLS,10 ENTITY_FROM,11 PAGINATION,12 ApiError,13 Pagination,14 cached,15 entity_summary,16 page,17 parse_date,18)19from aiatlas.api.routers.entities import detail_for_type20from aiatlas.db import connection, fetch_all, fetch_val2122router = APIRouter(prefix="/api/v1/papers", tags=["papers"])23SORTS = {"published": "e.attributes->>'published_at' desc nulls last", "updated": "e.updated_at desc", "name": "e.canonical_name asc",24 "citations": "(case when e.attributes->>'metric.citations' ~ '^[0-9]+$' then (e.attributes->>'metric.citations')::bigint end) desc nulls last"}252627@router.get("")28@cached(300)29async def list_papers(request: Request, p: Pagination = PAGINATION, q: str | None = Query(None, max_length=200), category: str | None = None, org: str | None = None,30 since: str | None = None, until: str | None = None, sort: str = "published") -> dict[str, Any]:31 if sort not in SORTS:32 raise ApiError(400, f"sort must be one of {', '.join(SORTS)}")33 where = ["e.entity_type = 'paper'", "e.merged_into is null"]34 params: dict[str, Any] = {}35 if q:36 where.append("(e.canonical_name ilike :qlike or e.search @@ plainto_tsquery('english', :q))")37 params["q"], params["qlike"] = q, f"%{q}%"38 if category:39 where.append("(e.attributes->>'primary_category' = :cat or e.attributes->'categories' ? :cat)")40 params["cat"] = category41 if org:42 where.append("(eo.slug = :org or eo.id = :org or exists (select 1 from relations r join entities x on x.id = r.object_id where r.subject_id = e.id and r.valid_to is null and (x.slug = :org or x.id = :org)))")43 params["org"] = org44 if since:45 where.append("e.attributes->>'published_at' >= :since")46 params["since"] = parse_date(since, "since").isoformat() # type: ignore[union-attr]47 if until:48 where.append("e.attributes->>'published_at' <= :until")49 params["until"] = parse_date(until, "until").isoformat() # type: ignore[union-attr]50 where_sql = " and ".join(where)51 async with connection() as conn:52 rows = await fetch_all(conn, f"select {ENTITY_COLS} from {ENTITY_FROM} where {where_sql} order by {SORTS[sort]}, e.id limit :lim offset :off", lim=p.limit, off=p.offset, **params)53 total = await fetch_val(conn, f"select count(*) from {ENTITY_FROM} where {where_sql}", **params)54 return page([entity_summary(r) for r in rows], int(total or 0), p)555657@router.get("/{slug}")58@cached(300)59async def get_paper(request: Request, slug: str) -> dict[str, Any]:60 return await detail_for_type(slug, ("paper",))61