119 lines
3.7 KiB
Python
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
|
|
]
|
|
}
|