247 lines
9.1 KiB
Python

from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Path, Response, status
from sqlalchemy.orm import Session
from datetime import datetime
from app import crud, models, schemas
from app.api.v1.deps import get_db, get_current_active_user, get_admin_user
router = APIRouter()
@router.get("/", response_model=List[schemas.ClassEnrollment])
def read_enrollments(
db: Session = Depends(get_db),
skip: int = 0,
limit: int = 100,
student_id: Optional[int] = None,
course_id: Optional[int] = None,
current_user: models.User = Depends(get_current_active_user),
) -> Any:
"""
Retrieve class enrollments.
"""
# Regular users can only see their own enrollments if they are a student
if not crud.user.is_admin(current_user):
user_student = db.query(models.Student).filter(models.Student.user_id == current_user.id).first()
if user_student:
student_id = user_student.id
else:
# Check if they are a teacher, in which case they can see enrollments for their courses
user_teacher = db.query(models.Teacher).filter(models.Teacher.user_id == current_user.id).first()
if not user_teacher:
return []
# Get courses taught by this teacher
teacher_courses = db.query(models.Course).filter(models.Course.teacher_id == user_teacher.id).all()
if not teacher_courses:
return []
course_ids = [course.id for course in teacher_courses]
enrollments = db.query(models.ClassEnrollment).filter(
models.ClassEnrollment.course_id.in_(course_ids)
).offset(skip).limit(limit).all()
return enrollments
# Apply filters
query = db.query(models.ClassEnrollment)
if student_id:
query = query.filter(models.ClassEnrollment.student_id == student_id)
if course_id:
query = query.filter(models.ClassEnrollment.course_id == course_id)
enrollments = query.offset(skip).limit(limit).all()
return enrollments
@router.get("/{enrollment_id}", response_model=schemas.ClassEnrollmentWithDetails)
def read_enrollment(
*,
db: Session = Depends(get_db),
enrollment_id: int = Path(..., title="The ID of the enrollment to get"),
current_user: models.User = Depends(get_current_active_user),
) -> Any:
"""
Get class enrollment by ID with student and course details.
"""
enrollment = db.query(models.ClassEnrollment).filter(models.ClassEnrollment.id == enrollment_id).first()
if not enrollment:
raise HTTPException(
status_code=404,
detail="Class enrollment not found",
)
# Check permissions
if not crud.user.is_admin(current_user):
# Students can only see their own enrollments
user_student = db.query(models.Student).filter(models.Student.user_id == current_user.id).first()
if user_student and user_student.id == enrollment.student_id:
pass # OK, student is viewing their own enrollment
else:
# Check if they are a teacher for this course
user_teacher = db.query(models.Teacher).filter(models.Teacher.user_id == current_user.id).first()
if not user_teacher:
raise HTTPException(
status_code=403,
detail="Not enough permissions to view this enrollment",
)
course = db.query(models.Course).filter(
models.Course.id == enrollment.course_id,
models.Course.teacher_id == user_teacher.id
).first()
if not course:
raise HTTPException(
status_code=403,
detail="Not enough permissions to view this enrollment",
)
# Get student and course details
student = db.query(models.Student).filter(models.Student.id == enrollment.student_id).first()
course = db.query(models.Course).filter(models.Course.id == enrollment.course_id).first()
# Create a combined response
result = schemas.ClassEnrollmentWithDetails.from_orm(enrollment)
result.student = schemas.Student.from_orm(student) if student else None
result.course = schemas.Course.from_orm(course) if course else None
return result
@router.post("/", response_model=schemas.ClassEnrollment)
def create_enrollment(
*,
db: Session = Depends(get_db),
enrollment_in: schemas.ClassEnrollmentCreate,
current_user: models.User = Depends(get_admin_user),
) -> Any:
"""
Create new class enrollment.
"""
# Check if student exists
student = db.query(models.Student).filter(models.Student.id == enrollment_in.student_id).first()
if not student:
raise HTTPException(
status_code=404,
detail=f"Student with ID {enrollment_in.student_id} not found",
)
# Check if course exists
course = db.query(models.Course).filter(models.Course.id == enrollment_in.course_id).first()
if not course:
raise HTTPException(
status_code=404,
detail=f"Course with ID {enrollment_in.course_id} not found",
)
# Check if student is already enrolled in this course for this semester/academic year
existing_enrollment = db.query(models.ClassEnrollment).filter(
models.ClassEnrollment.student_id == enrollment_in.student_id,
models.ClassEnrollment.course_id == enrollment_in.course_id,
models.ClassEnrollment.semester == enrollment_in.semester,
models.ClassEnrollment.academic_year == enrollment_in.academic_year,
).first()
if existing_enrollment:
raise HTTPException(
status_code=400,
detail=f"Student is already enrolled in this course for {enrollment_in.semester} {enrollment_in.academic_year}",
)
# Create enrollment with current date if not provided
enrollment_data = enrollment_in.dict()
if not enrollment_data.get("enrollment_date"):
enrollment_data["enrollment_date"] = datetime.utcnow()
db_enrollment = models.ClassEnrollment(**enrollment_data)
db.add(db_enrollment)
db.commit()
db.refresh(db_enrollment)
return db_enrollment
@router.put("/{enrollment_id}", response_model=schemas.ClassEnrollment)
def update_enrollment(
*,
db: Session = Depends(get_db),
enrollment_id: int = Path(..., title="The ID of the enrollment to update"),
enrollment_in: schemas.ClassEnrollmentUpdate,
current_user: models.User = Depends(get_current_active_user),
) -> Any:
"""
Update a class enrollment.
"""
enrollment = db.query(models.ClassEnrollment).filter(models.ClassEnrollment.id == enrollment_id).first()
if not enrollment:
raise HTTPException(
status_code=404,
detail="Class enrollment not found",
)
# Check permissions - only teachers of the course or admins can update enrollments
if not crud.user.is_admin(current_user):
user_teacher = db.query(models.Teacher).filter(models.Teacher.user_id == current_user.id).first()
if not user_teacher:
raise HTTPException(
status_code=403,
detail="Not enough permissions to update this enrollment",
)
course = db.query(models.Course).filter(
models.Course.id == enrollment.course_id,
models.Course.teacher_id == user_teacher.id
).first()
if not course:
raise HTTPException(
status_code=403,
detail="Not enough permissions to update this enrollment",
)
# Update enrollment fields
update_data = enrollment_in.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(enrollment, field, value)
db.add(enrollment)
db.commit()
db.refresh(enrollment)
return enrollment
@router.delete("/{enrollment_id}", response_model=None, status_code=status.HTTP_204_NO_CONTENT)
def delete_enrollment(
*,
db: Session = Depends(get_db),
enrollment_id: int = Path(..., title="The ID of the enrollment to delete"),
current_user: models.User = Depends(get_admin_user),
) -> Any:
"""
Delete a class enrollment.
"""
enrollment = db.query(models.ClassEnrollment).filter(models.ClassEnrollment.id == enrollment_id).first()
if not enrollment:
raise HTTPException(
status_code=404,
detail="Class enrollment not found",
)
# Check if there are any exam results for this student in this course
exam_results = db.query(models.ExamResult).join(
models.Exam, models.Exam.id == models.ExamResult.exam_id
).filter(
models.ExamResult.student_id == enrollment.student_id,
models.Exam.course_id == enrollment.course_id
).first()
if exam_results:
raise HTTPException(
status_code=400,
detail="Cannot delete enrollment with associated exam results",
)
db.delete(enrollment)
db.commit()
return Response(status_code=status.HTTP_204_NO_CONTENT)