Testys's picture
Let's just finish this
3358b33
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlmodel import Session, SQLModel
from typing import List, Optional, Dict
from src.database import get_session
from src.auth import get_current_active_user, get_api_key, require_super_admin
from src.models import (
User, UserCreate, UserRead, UserUpdate, Role,
Student, StudentCreate, StudentReadWithClearance, StudentUpdate, StudentRead,
TagLink, RFIDTagRead, Device, DeviceCreate, DeviceRead, TagScan
)
from src.crud import users as user_crud
from src.crud import students as student_crud
from src.crud import tag_linking as tag_crud
from src.crud import devices as device_crud
# Define the main administrative router
router = APIRouter(
prefix="/admin",
tags=["Administration"],
dependencies=[Depends(get_current_active_user(required_roles=[Role.ADMIN, Role.STAFF]))],
)
# --- All other administrative endpoints remain the same ---
# ... (Stuent Management, User Management, etc.) ...
@router.post("/students/", response_model=StudentReadWithClearance, status_code=status.HTTP_201_CREATED)
def create_student(student: StudentCreate, db: Session = Depends(get_session)):
"""(Admin & Staff) Creates a new student and initializes their clearance status."""
db_student = student_crud.get_student_by_matric_no(db, matric_no=student.matric_no)
if db_student:
raise HTTPException(status_code=400, detail="Matriculation number already registered")
return student_crud.create_student(db=db, student_data=student)
@router.get("/students/", response_model=List[StudentReadWithClearance])
def read_all_students(skip: int = 0, limit: int = 100, db: Session = Depends(get_session)):
"""(Admin & Staff) Retrieves a list of all student records."""
return student_crud.get_all_students(db, skip=skip, limit=limit)
@router.get("/students/lookup", response_model=StudentReadWithClearance)
def lookup_student(
matric_no: Optional[str] = Query(None, description="Matriculation number of the student."),
tag_id: Optional[str] = Query(None, description="RFID tag ID linked to the student."),
db: Session = Depends(get_session)
):
"""(Admin & Staff) Looks up a single student by Matric Number OR Tag ID."""
if not matric_no and not tag_id:
raise HTTPException(status_code=400, detail="A matric_no or tag_id must be provided.")
if matric_no and tag_id:
raise HTTPException(status_code=400, detail="Provide either matric_no or tag_id, not both.")
db_student = None
if matric_no:
db_student = student_crud.get_student_by_matric_no(db, matric_no=matric_no)
elif tag_id:
db_student = student_crud.get_student_by_tag_id(db, tag_id=tag_id)
if not db_student:
raise HTTPException(status_code=404, detail="Student not found with the provided identifier.")
return db_student
@router.get("/students/{student_id}", response_model=StudentReadWithClearance)
def read_single_student(student_id: int, db: Session = Depends(get_session)):
"""(Admin & Staff) Retrieves a single student's complete record by their internal ID."""
db_student = student_crud.get_student_by_id(db, student_id=student_id)
if not db_student:
raise HTTPException(status_code=404, detail="Student not found")
return db_student
@router.put("/students/{student_id}", response_model=StudentReadWithClearance)
def update_student_details(student_id: int, student: StudentUpdate, db: Session = Depends(get_session)):
"""(Admin & Staff) Updates a student's information."""
updated_student = student_crud.update_student(db, student_id=student_id, student_update=student)
if not updated_student:
raise HTTPException(status_code=404, detail="Student not found")
return updated_student
# --- Tag Management (Admin + Staff) ---
@router.post("/tags/link", response_model=RFIDTagRead)
def link_rfid_tag(link_data: TagLink, db: Session = Depends(get_session)):
"""(Admin & Staff) Links an RFID tag to a student or user."""
new_tag = tag_crud.link_tag(db, link_data)
if not new_tag:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Could not link tag. The tag may already be in use, or the user/student already has a tag."
)
return new_tag
@router.delete("/tags/{tag_id}/unlink", response_model=RFIDTagRead)
def unlink_rfid_tag(tag_id: str, db: Session = Depends(get_session)):
"""(Admin & Staff) Unlinks an RFID tag, making it available again."""
deleted_tag = tag_crud.unlink_tag(db, tag_id)
if not deleted_tag:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="RFID Tag not found."
)
return deleted_tag
# --- Super Admin Only Functions ---
def require_super_admin(current_user: User = Depends(get_current_active_user())):
"""Dependency to ensure a user has the ADMIN role."""
if current_user.role != Role.ADMIN:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="This action requires Super Admin privileges."
)
@router.post(
"/users/",
response_model=UserRead,
status_code=status.HTTP_201_CREATED,
dependencies=[Depends(get_current_active_user(required_roles=[Role.ADMIN]))]
)
def create_user_as_admin(user: UserCreate, db: Session = Depends(get_session)):
"""(Super Admin Only) Creates a new user (admin or staff)."""
if user_crud.get_user_by_username(db, username=user.username):
raise HTTPException(status_code=400, detail="Username already registered.")
if user_crud.get_user_by_email(db, email=user.email):
raise HTTPException(status_code=400, detail="Email already registered.")
return user_crud.create_user(db=db, user=user)
@router.get("/users/", response_model=List[UserRead], dependencies=[Depends(require_super_admin)])
def read_all_users(db: Session = Depends(get_session)):
"""(Super Admin Only) Retrieves a list of all users."""
return user_crud.get_all_users(db)
@router.get("/users/lookup", response_model=UserRead, dependencies=[Depends(require_super_admin)])
def lookup_user(
username: Optional[str] = Query(None, description="Username of the user."),
tag_id: Optional[str] = Query(None, description="RFID tag ID linked to the user."),
db: Session = Depends(get_session)
):
"""(Super Admin Only) Looks up a single user by Username OR Tag ID."""
if not username and not tag_id:
raise HTTPException(status_code=400, detail="A username or tag_id must be provided.")
if username and tag_id:
raise HTTPException(status_code=400, detail="Provide either username or tag_id, not both.")
db_user = None
if username:
db_user = user_crud.get_user_by_username(db, username=username)
elif tag_id:
db_user = user_crud.get_user_by_tag_id(db, tag_id=tag_id)
if not db_user:
raise HTTPException(status_code=404, detail="User not found with the provided identifier.")
return db_user
@router.put("/users/{user_id}", response_model=UserRead, dependencies=[Depends(require_super_admin)])
def update_user_details(user_id: int, user: UserUpdate, db: Session = Depends(get_session)):
"""(Super Admin Only) Updates a user's details (e.g., role)."""
db_user = user_crud.get_user_by_id(db, user_id=user_id)
if not db_user:
raise HTTPException(status_code=404, detail="User not found")
updated_user = user_crud.update_user(db, user=db_user, updates=user)
if not updated_user:
raise HTTPException(status_code=404, detail="User not found")
return updated_user
@router.delete("/users/{user_id}", response_model=UserRead, dependencies=[Depends(require_super_admin)])
def delete_user_account(user_id: int, db: Session = Depends(get_session), current_user: User = Depends(get_current_active_user())):
"""(Super Admin Only) Deletes a user account."""
if current_user.id == user_id:
raise HTTPException(status_code=400, detail="Cannot delete your own account.")
deleted_user = user_crud.delete_user(db, user_id=user_id)
if not deleted_user:
raise HTTPException(status_code=404, detail="User not found")
return deleted_user
@router.delete("/students/{student_id}", response_model=StudentRead, dependencies=[Depends(require_super_admin)])
def delete_student_record(student_id: int, db: Session = Depends(get_session)):
"""(Super Admin Only) Deletes a student record and all associated data."""
deleted_student = student_crud.delete_student(db, student_id=student_id)
if not deleted_student:
raise HTTPException(status_code=404, detail="Student not found")
return deleted_student
@router.post("/devices/", response_model=DeviceRead, status_code=status.HTTP_201_CREATED, dependencies=[Depends(require_super_admin)])
def create_device(device: DeviceCreate, db: Session = Depends(get_session)):
"""(Super Admin Only) Registers a new RFID hardware device."""
db_device = device_crud.get_device_by_location(db, location=device.location)
if db_device:
raise HTTPException(status_code=400, detail=f"A device at location '{device.location}' already exists.")
return device_crud.create_device(db=db, device=device)
@router.get("/devices/", response_model=List[DeviceRead], dependencies=[Depends(require_super_admin)])
def read_all_devices(skip: int = 0, limit: int = 100, db: Session = Depends(get_session)):
return device_crud.get_all_devices(db, skip=skip, limit=limit)
@router.delete("/devices/{device_id}", response_model=DeviceRead, dependencies=[Depends(require_super_admin)])
def delete_device_registration(device_id: int, db: Session = Depends(get_session)):
"""(Super Admin Only) De-authorizes a hardware device."""
deleted_device = device_crud.delete_device(db, device_id=device_id)
if not deleted_device:
raise HTTPException(status_code=404, detail="Device not found")
return deleted_device