""" Public share router — no authentication required. Serves shareable read-only views for briefs and collections. """ from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.database import get_db from app.models.bill import Bill, BillDocument from app.models.brief import BillBrief from app.models.collection import Collection, CollectionBill from app.schemas.schemas import ( BillSchema, BriefSchema, BriefShareResponse, CollectionDetailSchema, ) router = APIRouter() # ── Brief share ─────────────────────────────────────────────────────────────── @router.get("/brief/{token}", response_model=BriefShareResponse) async def get_shared_brief( token: str, db: AsyncSession = Depends(get_db), ): result = await db.execute( select(BillBrief) .options( selectinload(BillBrief.bill).selectinload(Bill.sponsor), selectinload(BillBrief.bill).selectinload(Bill.briefs), selectinload(BillBrief.bill).selectinload(Bill.trend_scores), ) .where(BillBrief.share_token == token) ) brief = result.scalar_one_or_none() if not brief: raise HTTPException(status_code=404, detail="Brief not found") bill = brief.bill bill_schema = BillSchema.model_validate(bill) if bill.briefs: bill_schema.latest_brief = bill.briefs[0] if bill.trend_scores: bill_schema.latest_trend = bill.trend_scores[0] doc_result = await db.execute( select(BillDocument.bill_id).where(BillDocument.bill_id == bill.bill_id).limit(1) ) bill_schema.has_document = doc_result.scalar_one_or_none() is not None return BriefShareResponse( brief=BriefSchema.model_validate(brief), bill=bill_schema, ) # ── Collection share ────────────────────────────────────────────────────────── @router.get("/collection/{token}", response_model=CollectionDetailSchema) async def get_shared_collection( token: str, db: AsyncSession = Depends(get_db), ): result = await db.execute( select(Collection) .options( selectinload(Collection.collection_bills).selectinload(CollectionBill.bill).selectinload(Bill.briefs), selectinload(Collection.collection_bills).selectinload(CollectionBill.bill).selectinload(Bill.trend_scores), selectinload(Collection.collection_bills).selectinload(CollectionBill.bill).selectinload(Bill.sponsor), ) .where(Collection.share_token == token) ) collection = result.scalar_one_or_none() if not collection: raise HTTPException(status_code=404, detail="Collection not found") cb_list = collection.collection_bills bills = [cb.bill for cb in cb_list] bill_ids = [b.bill_id for b in bills] if bill_ids: doc_result = await db.execute( select(BillDocument.bill_id).where(BillDocument.bill_id.in_(bill_ids)).distinct() ) bills_with_docs = {row[0] for row in doc_result} else: bills_with_docs = set() bill_schemas = [] for bill in bills: bs = BillSchema.model_validate(bill) if bill.briefs: bs.latest_brief = bill.briefs[0] if bill.trend_scores: bs.latest_trend = bill.trend_scores[0] bs.has_document = bill.bill_id in bills_with_docs bill_schemas.append(bs) return CollectionDetailSchema( id=collection.id, name=collection.name, slug=collection.slug, is_public=collection.is_public, share_token=collection.share_token, bill_count=len(cb_list), created_at=collection.created_at, bills=bill_schemas, )