SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
2.7 KB · 61 lines python
Raw Blame History
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