205 lines
6.6 KiB
Python
205 lines
6.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, and_, func
|
|
from sqlalchemy.orm import selectinload
|
|
from datetime import datetime, timedelta
|
|
|
|
from app.database import get_db
|
|
from app.models import User, Case, JudgeAssignment
|
|
from app.schemas import JudgeVote, JudgeAcceptance
|
|
from app.routers.auth import get_current_active_user
|
|
from app.utils.notifications import notify_user
|
|
|
|
router = APIRouter()
|
|
|
|
@router.get("/pending")
|
|
async def get_pending_assignments(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
"""Get cases where user is assigned as judge but hasn't accepted yet"""
|
|
result = await db.execute(
|
|
select(JudgeAssignment, Case).join(Case).where(
|
|
and_(
|
|
JudgeAssignment.judge_id == current_user.id,
|
|
JudgeAssignment.status == "pending"
|
|
)
|
|
)
|
|
)
|
|
assignments = result.all()
|
|
return [
|
|
{
|
|
"assignment_id": ja.id,
|
|
"case_id": c.id,
|
|
"case_number": c.case_number,
|
|
"title": c.title,
|
|
"assigned_at": ja.assigned_at
|
|
}
|
|
for ja, c in assignments
|
|
]
|
|
|
|
@router.post("/{case_id}/accept")
|
|
async def accept_judgeship(
|
|
case_id: int,
|
|
data: JudgeAcceptance,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
result = await db.execute(
|
|
select(JudgeAssignment).where(
|
|
and_(
|
|
JudgeAssignment.case_id == case_id,
|
|
JudgeAssignment.judge_id == current_user.id
|
|
)
|
|
)
|
|
)
|
|
assignment = result.scalar_one_or_none()
|
|
|
|
if not assignment:
|
|
raise HTTPException(status_code=404, detail="Assignment not found")
|
|
|
|
if assignment.status != "pending":
|
|
raise HTTPException(status_code=400, detail="Already accepted or declined")
|
|
|
|
if data.accept:
|
|
assignment.status = "accepted"
|
|
await db.commit()
|
|
return {"status": "accepted", "case_id": case_id}
|
|
else:
|
|
assignment.status = "declined"
|
|
await db.commit()
|
|
|
|
# Find replacement judge
|
|
result = await db.execute(select(Case).where(Case.id == case_id))
|
|
case = result.scalar_one()
|
|
|
|
result = await db.execute(
|
|
select(User).where(
|
|
and_(
|
|
User.is_judge == True,
|
|
User.is_active == True,
|
|
User.id.notin_([
|
|
case.plaintiff_id,
|
|
case.defendant_id,
|
|
current_user.id
|
|
])
|
|
)
|
|
)
|
|
)
|
|
available = result.scalars().all()
|
|
|
|
if available:
|
|
import random
|
|
new_judge = random.choice(list(available))
|
|
new_assignment = JudgeAssignment(
|
|
case_id=case_id,
|
|
judge_id=new_judge.id,
|
|
status="pending"
|
|
)
|
|
db.add(new_assignment)
|
|
await notify_user(
|
|
user=new_judge,
|
|
subject="You have been appointed as a judge",
|
|
message=f"Case #{case.case_number}: Replacement needed"
|
|
)
|
|
|
|
await db.commit()
|
|
return {"status": "declined", "case_id": case_id, "replacement_found": bool(available)}
|
|
|
|
@router.post("/{case_id}/vote")
|
|
async def submit_vote(
|
|
case_id: int,
|
|
vote: JudgeVote,
|
|
background_tasks: BackgroundTasks,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_active_user)
|
|
):
|
|
result = await db.execute(
|
|
select(JudgeAssignment).where(
|
|
and_(
|
|
JudgeAssignment.case_id == case_id,
|
|
JudgeAssignment.judge_id == current_user.id
|
|
)
|
|
)
|
|
)
|
|
assignment = result.scalar_one_or_none()
|
|
|
|
if not assignment:
|
|
raise HTTPException(status_code=404, detail="You are not assigned to this case")
|
|
|
|
if assignment.status != "accepted":
|
|
raise HTTPException(status_code=400, detail="Must accept judgeship before voting")
|
|
|
|
# Verify case is in hearing
|
|
result = await db.execute(select(Case).where(Case.id == case_id))
|
|
case = result.scalar_one()
|
|
|
|
if case.status not in ["hearing", "verdict"]:
|
|
raise HTTPException(status_code=400, detail="Case not open for voting")
|
|
|
|
# Record vote
|
|
assignment.vote = vote.vote
|
|
assignment.reasoning = vote.reasoning
|
|
assignment.voted_at = datetime.utcnow()
|
|
assignment.status = "voted"
|
|
|
|
# Check if verdict should be rendered
|
|
result = await db.execute(
|
|
select(JudgeAssignment).where(
|
|
and_(
|
|
JudgeAssignment.case_id == case_id,
|
|
JudgeAssignment.status == "voted"
|
|
)
|
|
)
|
|
)
|
|
voted_assignments = result.scalars().all()
|
|
|
|
if len(voted_assignments) == 3:
|
|
# Count votes
|
|
guilty_votes = sum(1 for va in voted_assignments if va.vote == "guilty")
|
|
innocent_votes = sum(1 for va in voted_assignments if va.vote == "innocent")
|
|
|
|
if guilty_votes >= 2:
|
|
case.verdict = "guilty"
|
|
elif innocent_votes >= 2:
|
|
case.verdict = "innocent"
|
|
else:
|
|
case.verdict = "dismissed" # All abstained or tied
|
|
|
|
case.status = "closed"
|
|
case.verdict_deadline = datetime.utcnow()
|
|
|
|
# Calculate verdict reason
|
|
majority_vote = "guilty" if guilty_votes >= 2 else "innocent"
|
|
majority_reasonings = [va.reasoning for va in voted_assignments if va.vote == majority_vote]
|
|
case.verdict_reason = " | ".join(majority_reasonings)
|
|
|
|
# Update reputations
|
|
if case.verdict == "guilty":
|
|
case.defendant.reputation_score = max(0, case.defendant.reputation_score - 10)
|
|
elif case.verdict == "innocent":
|
|
case.plaintiff.reputation_score = max(0, case.plaintiff.reputation_score - 5)
|
|
|
|
# Notify parties
|
|
background_tasks.add_task(
|
|
notify_user,
|
|
user=case.plaintiff,
|
|
subject=f"Verdict reached: {case.case_number}",
|
|
message=f"The verdict is: {case.verdict.upper()}"
|
|
)
|
|
background_tasks.add_task(
|
|
notify_user,
|
|
user=case.defendant,
|
|
subject=f"Verdict reached: {case.case_number}",
|
|
message=f"The verdict is: {case.verdict.upper()}"
|
|
)
|
|
|
|
await db.commit()
|
|
|
|
return {
|
|
"vote_recorded": True,
|
|
"vote": vote.vote,
|
|
"case_status": case.status,
|
|
"verdict": case.verdict
|
|
}
|