Spaces:
Configuration error
Configuration error
| from datetime import datetime | |
| from typing import Optional | |
| from fastapi import APIRouter, Depends, HTTPException, status | |
| from prisma.models import Teacher | |
| from app.api.dependencies import get_current_teacher | |
| from app.db.client import db | |
| from app.repositories.attendance_repo import AttendanceRepository | |
| from app.repositories.leave_repo import LeaveRepository | |
| from app.schemas.teacher import ( | |
| SessionResponse, SessionStart, GeofenceUpsert, GeofenceResponse, | |
| AcademicClassWithGeofenceResponse, SessionAttendanceResponse, | |
| ClassStatsResponse, AttendanceManualOverride, SessionWithClassResponse, | |
| BulkMarkRequest, AbsentStudentItem, DeviceChangeResponse, DeviceChangeApprove | |
| ) | |
| from app.schemas.attendance import AttendanceReview, FlaggedAttendanceResponse | |
| from app.schemas.leave import LeaveRequestResponse, LeaveRequestApprove | |
| from app.services.session_service import SessionService | |
| from app.services.attendance_service import AttendanceService | |
| from app.services.teacher_service import TeacherService | |
| from app.services.leave_service import LeaveService | |
| from app.services.device_change_service import DeviceChangeService | |
| router = APIRouter(prefix="/teacher", tags=["Teacher Features"]) | |
| def _make_leave_response(leave) -> LeaveRequestResponse: | |
| name = f"{leave.student.firstName or ''} {leave.student.lastName or ''}".strip() | |
| return LeaveRequestResponse( | |
| id=leave.id, student_id=leave.studentId, student_name=name or "Unknown", | |
| enrollment_number=leave.student.enrollmentNumber, | |
| start_date=leave.startDate, end_date=leave.endDate, reason=leave.reason, | |
| document_url=leave.documentUrl, status=leave.status, approved_by=leave.approvedBy, | |
| approver_note=leave.approverNote, created_at=leave.createdAt, updated_at=leave.updatedAt, | |
| ) | |
| async def start_session( | |
| data: SessionStart, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| session_service: SessionService = Depends(), | |
| ) -> SessionResponse: | |
| session = await session_service.start_session(data, teacher.id) | |
| if not session: | |
| raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Could not open session. Class not found or unauthorized.") | |
| return session | |
| async def stop_session( | |
| id: str, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| session_service: SessionService = Depends(), | |
| ) -> dict: | |
| if not await session_service.stop_session(id, teacher.id): | |
| raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Session not found, already stopped, or unauthorized.") | |
| return {"status": "success", "message": "Session closed successfully."} | |
| async def get_flagged_attendance( | |
| teacher: Teacher = Depends(get_current_teacher), | |
| attendance_repo: AttendanceRepository = Depends(), | |
| ) -> list[FlaggedAttendanceResponse]: | |
| classes = await db.academicclass.find_many(where={"teacherId": teacher.id}) | |
| my_class_ids = [c.id for c in classes] | |
| records = await db.attendance.find_many( | |
| where={"status": "Flagged", "session": {"is": {"academicClassId": {"in": my_class_ids}}}}, | |
| include={ | |
| "student": {"include": {"user": True}}, | |
| "session": {"include": {"academicClass": {"include": {"subject": True}}}}, | |
| }, | |
| ) | |
| return [ | |
| FlaggedAttendanceResponse( | |
| id=r.id, | |
| enrollment_number=r.student.enrollmentNumber if r.student else "N/A", | |
| student_name=( | |
| f"{r.student.firstName or ''} {r.student.lastName or ''}".strip() | |
| if r.student else "Unknown Student" | |
| ), | |
| class_name=ac.subject.name if (ac := r.session.academicClass if r.session else None) and ac.subject else (ac.name if ac else "N/A"), | |
| subject=ac.subject.name if ac and ac.subject else (ac.name if ac else "N/A"), | |
| face_score=r.faceScore, liveness_score=r.livenessScore, | |
| background_score=r.backgroundScore, final_ai_score=r.finalAiScore, | |
| gps_latitude=r.gpsLatitude, gps_longitude=r.gpsLongitude, | |
| created_at=r.createdAt, | |
| student_note=r.studentNote, | |
| ) | |
| for r in records | |
| ] | |
| async def get_attendance_by_id(id: str, attendance_repo: AttendanceRepository = Depends()): | |
| r = await attendance_repo.get_by_id(id) | |
| if not r: | |
| raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Attendance record not found") | |
| ac = r.session.academicClass if r.session else None | |
| return FlaggedAttendanceResponse( | |
| id=r.id, enrollment_number=r.student.enrollmentNumber if r.student else "N/A", | |
| student_name=(f"{r.student.firstName or ''} {r.student.lastName or ''}".strip() if r.student else "Unknown Student"), | |
| class_name=ac.name if ac else "N/A", subject=ac.subject.name if ac and ac.subject else (ac.name if ac else "N/A"), | |
| face_score=r.faceScore, liveness_score=r.livenessScore, | |
| background_score=r.backgroundScore, final_ai_score=r.finalAiScore, | |
| gps_latitude=r.gpsLatitude, gps_longitude=r.gpsLongitude, created_at=r.createdAt, | |
| student_note=r.studentNote, | |
| ) | |
| async def review_flagged_attendance( | |
| id: str, | |
| review: AttendanceReview, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| attendance_service: AttendanceService = Depends(), | |
| ) -> dict: | |
| if not await attendance_service.review_attendance(attendance_id=id, status=review.status, remarks=review.remarks): | |
| raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Attendance record not found, is not currently flagged, or review failed.") | |
| return {"status": "success", "message": f"Attendance record has been {review.status}."} | |
| async def get_my_classes( | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> list[AcademicClassWithGeofenceResponse]: | |
| return await teacher_service.get_classes_by_teacher_user_id(teacher.userId) | |
| async def upsert_geofence( | |
| class_id: str, data: GeofenceUpsert, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> GeofenceResponse: | |
| return await teacher_service.upsert_geofence(user_id=teacher.userId, class_id=class_id, data=data) | |
| async def get_session_attendance( | |
| session_id: str, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> SessionAttendanceResponse: | |
| return await teacher_service.get_session_attendance_roster(user_id=teacher.userId, session_id=session_id) | |
| async def get_class_stats( | |
| class_id: str, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> ClassStatsResponse: | |
| return await teacher_service.get_class_stats(user_id=teacher.userId, class_id=class_id) | |
| async def manual_override_attendance( | |
| session_id: str, data: AttendanceManualOverride, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> dict: | |
| if not await teacher_service.manual_override_attendance(user_id=teacher.userId, session_id=session_id, student_id=data.student_id, status_val=data.status): | |
| raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to apply attendance manual override.") | |
| return {"status": "success", "message": f"Attendance overridden to {data.status}."} | |
| async def get_teacher_sessions( | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> list[SessionWithClassResponse]: | |
| return await teacher_service.get_teacher_sessions(user_id=teacher.userId) | |
| async def get_absent_students( | |
| session_id: str, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> list[AbsentStudentItem]: | |
| return await teacher_service.get_absent_students(session_id=session_id, user_id=teacher.userId) | |
| async def bulk_mark_attendance( | |
| session_id: str, data: BulkMarkRequest, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> dict: | |
| count = await teacher_service.bulk_mark_attendance(session_id=session_id, user_id=teacher.userId, request=data) | |
| return {"status": "success", "count": count} | |
| async def export_class_attendance( | |
| class_id: str, | |
| from_date: Optional[datetime] = None, | |
| to_date: Optional[datetime] = None, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| teacher_service: TeacherService = Depends(), | |
| ) -> list[dict]: | |
| return await teacher_service.export_class_attendance(class_id=class_id, user_id=teacher.userId, from_date=from_date, to_date=to_date) | |
| async def get_pending_leaves( | |
| teacher: Teacher = Depends(get_current_teacher), | |
| leave_repo: LeaveRepository = Depends(), | |
| ) -> list[LeaveRequestResponse]: | |
| return [_make_leave_response(leave) for leave in await leave_repo.get_pending_for_teacher(teacher.id)] | |
| async def approve_leave( | |
| leave_id: str, data: LeaveRequestApprove, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| leave_service: LeaveService = Depends(), | |
| ) -> dict: | |
| result = await leave_service.approve_leave(leave_id=leave_id, teacher_id=teacher.id, status=data.status, approver_note=data.approver_note) | |
| if not result: | |
| raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Leave request not found.") | |
| return {"status": "success", "message": f"Leave request {data.status.lower()} successfully."} | |
| async def get_pending_device_changes( | |
| teacher: Teacher = Depends(get_current_teacher), | |
| device_change_service: DeviceChangeService = Depends(), | |
| ) -> list[DeviceChangeResponse]: | |
| return await device_change_service.get_pending_requests(teacher_id=teacher.id) | |
| async def approve_device_change( | |
| request_id: str, data: DeviceChangeApprove, | |
| teacher: Teacher = Depends(get_current_teacher), | |
| device_change_service: DeviceChangeService = Depends(), | |
| ) -> dict: | |
| result = await device_change_service.approve_request(request_id=request_id, teacher_id=teacher.id, new_status=data.status) | |
| if not result: | |
| raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Device change request not found or not pending.") | |
| return {"status": "success", "message": f"Device change request {data.status.lower()} successfully."} | |