dikasterion/backend/app/routers/registry.py

119 lines
3.7 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, or_
from typing import List, Optional
from app.database import get_db
from app.models import Case
from app.schemas import RegistryEntry, RegistryFilter
router = APIRouter()
@router.get("/", response_model=List[RegistryEntry])
async def list_registry(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
search: Optional[str] = None,
verdict: Optional[str] = None,
db: AsyncSession = Depends(get_db)
):
"""Public registry of all closed cases"""
query = select(Case).where(Case.status == "closed")
if verdict:
query = query.where(Case.verdict == verdict)
if search:
query = query.where(
or_(
Case.title.ilike(f"%{search}%"),
Case.case_number.ilike(f"%{search}%"),
Case.description.ilike(f"%{search}%")
)
)
query = query.order_by(Case.created_at.desc()).offset(skip).limit(limit)
result = await db.execute(query)
cases = result.scalars().all()
return [
RegistryEntry(
case_number=c.case_number,
title=c.title,
verdict=c.verdict,
verdict_reason=c.verdict_reason,
plaintiff_username=c.plaintiff.username,
defendant_username=c.defendant.username,
created_at=c.created_at,
closed_at=c.verdict_deadline
)
for c in cases
]
@router.get("/stats")
async def get_registry_stats(db: AsyncSession = Depends(get_db)):
"""Statistics for homepage"""
# Total cases
result = await db.execute(select(func.count(Case.id)))
total_cases = result.scalar()
# Closed cases with verdicts
result = await db.execute(
select(
Case.verdict,
func.count(Case.id)
).where(Case.status == "closed")
.group_by(Case.verdict)
)
verdict_counts = {v: c for v, c in result.all() if v}
# Average resolution time
result = await db.execute(
select(
func.avg(
func.extract('epoch', Case.verdict_deadline - Case.created_at) / 3600
)
).where(Case.status == "closed")
)
avg_hours = result.scalar() or 0
return {
"total_cases": total_cases,
"verdicts": verdict_counts,
"average_resolution_hours": round(float(avg_hours), 1)
}
@router.get("/{case_number}")
async def get_case_details(case_number: str, db: AsyncSession = Depends(get_db)):
"""Get full details of a specific case (public)"""
result = await db.execute(select(Case).where(Case.case_number == case_number))
case = result.scalar_one_or_none()
if not case:
raise HTTPException(status_code=404, detail="Case not found")
# Only show closed cases publicly
if case.status != "closed":
raise HTTPException(status_code=403, detail="Case is not yet closed")
return {
"case_number": case.case_number,
"title": case.title,
"description": case.description,
"plaintiff": case.plaintiff.username,
"defendant": case.defendant.username,
"verdict": case.verdict,
"verdict_reason": case.verdict_reason,
"created_at": case.created_at,
"closed_at": case.verdict_deadline,
"evidence_urls": case.evidence_urls,
"judge_votes": [
{
"judge": ja.judge.username,
"vote": ja.vote,
"reasoning": ja.reasoning
}
for ja in case.judge_assignments if ja.vote
]
}