from fastapi import APIRouter, HTTPException, Query, Depends from typing import Optional from pydantic import BaseModel import database as db router = APIRouter(prefix="/admin", tags=["admin"]) import time _admin_cache = {} def verify_admin(email: str = Query(...)): """Dependency to check if user is admin.""" now = time.time() if email in _admin_cache and now - _admin_cache[email] < 3600: return email with db.db_cursor() as cur: cur.execute('SELECT role FROM "user" WHERE email = %s', (email,)) row = cur.fetchone() if not row or row["role"] != "admin": raise HTTPException(status_code=403, detail="Admin privileges required") _admin_cache[email] = now return email class SchoolCreate(BaseModel): school_name: str class ProgramCreate(BaseModel): school_id: int program_name: str credit_weightage: float total_eligible_students: int class OfficerUpdate(BaseModel): name: str phone: Optional[str] = None class StudentCreate(BaseModel): name: str usn: str school_id: int program_id: int batch: str is_active: bool = True # --- Schools --- @router.get("/schools") def get_schools(email: str = Depends(verify_admin)): return db.get_all_schools() @router.post("/schools", status_code=201) def create_school(body: SchoolCreate, email: str = Depends(verify_admin)): return db.create_school(body.school_name) @router.delete("/schools/{school_id}") def delete_school(school_id: int, email: str = Depends(verify_admin)): success = db.delete_school(school_id) if not success: raise HTTPException(status_code=404, detail="School not found") return {"status": "ok"} # --- Programs --- @router.get("/programs") def get_programs(email: str = Depends(verify_admin)): return db.get_all_programs() @router.post("/programs", status_code=201) def create_program(body: ProgramCreate, email: str = Depends(verify_admin)): return db.create_program(body.school_id, body.program_name, body.credit_weightage, body.total_eligible_students) @router.delete("/programs/{program_id}") def delete_program(program_id: int, email: str = Depends(verify_admin)): success = db.delete_program(program_id) if not success: raise HTTPException(status_code=404, detail="Program not found") return {"status": "ok"} # --- Officers --- @router.get("/officers") def get_officers(email: str = Depends(verify_admin)): return db.get_all_officers() @router.get("/officers/{officer_id}/history") def get_officer_history_admin(officer_id: int, year: int = Query(...), email: str = Depends(verify_admin)): import datetime now = datetime.datetime.now() # 1. Fetch history first (1 query) rows = db.get_officer_history(officer_id, year) # 2. Check if current month is missing if year == now.year: has_current = any(r["month"] == now.month for r in rows) if not has_current: from main import recalculate_and_save_snapshot snapshot = recalculate_and_save_snapshot(officer_id, now.month, now.year) rows.append({ "month": now.month, "starting_pool": snapshot["starting_pool"], "target": snapshot["target"], "placed": snapshot["placed"], "prism_credits": snapshot["prism_credits"], "prism_score": snapshot["prism_score"] }) rows.sort(key=lambda x: x["month"]) return [ { "month": r["month"], "starting_pool": r["starting_pool"], "target": r["target"], "placed": r["placed"], "prism_credits": r["prism_credits"], "score": r["prism_score"], } for r in rows ] @router.put("/officers/{officer_id}") def update_officer(officer_id: int, body: OfficerUpdate, email: str = Depends(verify_admin)): res = db.update_officer(officer_id, body.name, body.phone) if not res: raise HTTPException(status_code=404, detail="Officer not found") return res @router.delete("/officers/{officer_id}") def delete_officer(officer_id: int, email: str = Depends(verify_admin)): success = db.delete_officer(officer_id) if not success: raise HTTPException(status_code=404, detail="Officer not found") return {"status": "ok"} # --- Officer-Program Assignments --- @router.post("/officers/{officer_id}/programs/{program_id}", status_code=201) def assign_program(officer_id: int, program_id: int, email: str = Depends(verify_admin)): db.assign_officer_to_program(officer_id, program_id) return {"status": "assigned"} @router.delete("/officers/{officer_id}/programs/{program_id}") def unassign_program(officer_id: int, program_id: int, email: str = Depends(verify_admin)): db.unassign_officer_from_program(officer_id, program_id) return {"status": "unassigned"} @router.get("/officers/{officer_id}/programs") def get_officer_programs(officer_id: int, email: str = Depends(verify_admin)): return db.get_officer_programs(officer_id) # --- Students --- @router.get("/students") def get_students(email: str = Depends(verify_admin)): return db.get_all_students(active_only=False) @router.post("/students", status_code=201) def create_student(body: StudentCreate, email: str = Depends(verify_admin)): return db.create_student(body.name, body.usn, body.school_id, body.program_id, body.batch, body.is_active) @router.delete("/students/{student_id}") def delete_student(student_id: int, email: str = Depends(verify_admin)): success = db.delete_student(student_id) if not success: raise HTTPException(status_code=404, detail="Student not found") return {"status": "ok"}