179 lines
5.7 KiB
Python
179 lines
5.7 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, func, and_
|
|
from datetime import datetime, timedelta
|
|
from typing import List
|
|
import random
|
|
|
|
from app.database import get_db
|
|
from app.models import User, Case, JudgeAssignment
|
|
from app.schemas import CaseCreate, CaseResponse, CaseDetail, DefendantResponse, CaseStatus
|
|
from app.routers.auth import get_current_active_user
|
|
from app.utils.notifications import notify_user
|
|
|
|
router = APIRouter()
|
|
|
|
async def generate_case_number(db: AsyncSession) -> str:
|
|
"""Generate case number in format DIK-YYYY-NNNN"""
|
|
year = datetime.now().year
|
|
result = await db.execute(
|
|
select(Case).where(Case.case_number.like(f"DIK-{year}-%")).order_by(Case.id.desc())
|
|
)
|
|
last_case = result.scalar_one_or_none()
|
|
|
|
if last_case:
|
|
last_num = int(last_case.case_number.split("-")[-1])
|
|
new_num = last_num + 1
|
|
else:
|
|
new_num = 1
|
|
|
|
return f"DIK-{year}-{new_num:04d}"
|
|
|
|
async def assign_judges(db: AsyncSession, case_id: int, exclude_user_ids: List[int]):
|
|
"""Assign 3 random judges from pool, excluding plaintiff and defendant"""
|
|
result = await db.execute(
|
|
select(User).where(
|
|
and_(
|
|
User.is_judge == True,
|
|
User.is_active == True,
|
|
User.id.notin_(exclude_user_ids)
|
|
)
|
|
)
|
|
)
|
|
available_judges = result.scalars().all()
|
|
|
|
if len(available_judges) < 3:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Not enough judges available in pool"
|
|
)
|
|
|
|
selected_judges = random.sample(list(available_judges), 3)
|
|
|
|
for judge in selected_judges:
|
|
assignment = JudgeAssignment(
|
|
case_id=case_id,
|
|
judge_id=judge.id,
|
|
status="pending"
|
|
)
|
|
db.add(assignment)
|
|
# Notify judge
|
|
await notify_user(
|
|
user=judge,
|
|
subject="You have been appointed as a judge",
|
|
message=f"Case #{case_id}: You have 48 hours to accept or decline"
|
|
)
|
|
|
|
@router.post("/", response_model=CaseResponse)
|
|
async def create_case(
|
|
case: CaseCreate,
|
|
background_tasks: BackgroundTasks,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
# Find defendant
|
|
result = await db.execute(select(User).where(User.username == case.defendant_username))
|
|
defendant = result.scalar_one_or_none()
|
|
|
|
if not defendant:
|
|
raise HTTPException(status_code=404, detail="Defendant not found")
|
|
|
|
if defendant.id == current_user.id:
|
|
raise HTTPException(status_code=400, detail="Cannot sue yourself")
|
|
|
|
# Generate case number
|
|
case_number = await generate_case_number(db)
|
|
|
|
# Create case
|
|
db_case = Case(
|
|
case_number=case_number,
|
|
plaintiff_id=current_user.id,
|
|
defendant_id=defendant.id,
|
|
title=case.title,
|
|
description=case.description,
|
|
evidence_urls=case.evidence_urls,
|
|
status="pending",
|
|
)
|
|
db.add(db_case)
|
|
await db.commit()
|
|
await db.refresh(db_case)
|
|
|
|
# Assign judges
|
|
await assign_judges(db, db_case.id, [current_user.id, defendant.id])
|
|
await db.commit()
|
|
|
|
# Notify defendant
|
|
background_tasks.add_task(
|
|
notify_user,
|
|
user=defendant,
|
|
subject=f"You have been sued: {case.title}",
|
|
message=f"Case #{case_number}: {current_user.username} has filed a case against you. Please accept or decline jurisdiction."
|
|
)
|
|
|
|
return db_case
|
|
|
|
@router.get("/", response_model=List[CaseResponse])
|
|
async def list_my_cases(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user),
|
|
skip: int = 0,
|
|
limit: int = 20
|
|
):
|
|
"""List cases where user is plaintiff, defendant, or judge"""
|
|
result = await db.execute(
|
|
select(Case).where(
|
|
(Case.plaintiff_id == current_user.id) |
|
|
(Case.defendant_id == current_user.id) |
|
|
(Case.judge_assignments.any(JudgeAssignment.judge_id == current_user.id))
|
|
).offset(skip).limit(limit).order_by(Case.created_at.desc())
|
|
)
|
|
return result.scalars().all()
|
|
|
|
@router.get("/{case_id}", response_model=CaseDetail)
|
|
async def get_case(
|
|
case_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
result = await db.execute(select(Case).where(Case.id == case_id))
|
|
case = result.scalar_one_or_none()
|
|
|
|
if not case:
|
|
raise HTTPException(status_code=404, detail="Case not found")
|
|
|
|
return case
|
|
|
|
@router.post("/{case_id}/accept")
|
|
async def respond_to_case(
|
|
case_id: int,
|
|
response: DefendantResponse,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
result = await db.execute(select(Case).where(Case.id == case_id))
|
|
case = result.scalar_one_or_none()
|
|
|
|
if not case:
|
|
raise HTTPException(status_code=404, detail="Case not found")
|
|
|
|
if case.defendant_id != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Only defendant can respond")
|
|
|
|
if case.status != "pending":
|
|
raise HTTPException(status_code=400, detail="Case is no longer pending")
|
|
|
|
if response.accept_jurisdiction:
|
|
case.status = "hearing"
|
|
case.hearing_deadline = datetime.now() + timedelta(hours=72)
|
|
case.defendant_response = response.response_text
|
|
else:
|
|
case.status = "declined"
|
|
|
|
await db.commit()
|
|
|
|
return {
|
|
"status": "accepted" if response.accept_jurisdiction else "declined",
|
|
"case_id": case_id,
|
|
"next_deadline": case.hearing_deadline if response.accept_jurisdiction else None
|
|
}
|