from django.shortcuts import render, redirect, get_object_or_404 from django.http import JsonResponse from django.contrib.auth.decorators import login_required from django.contrib import messages from django.db.models import Count, Q, Avg from django.utils import timezone from datetime import timedelta, datetime from django.views.decorators.http import require_http_methods, require_GET from django.contrib.auth.hashers import check_password, make_password from app.models import ( Staff, Student, Routine, Attendance, AttendanceRecord, StudentFeedback, StaffLeave, Notice, Institute, StaffInstituteFeedback, Subject, SubjectFile, InstituteFeedback, CourseTracking, FEEDBACK_TYPE_CHOICES, ) import logging from app.utils import ( handle_file_upload, cleanup_failed_upload, FileUploadError, ALLOWED_DOCUMENT_TYPES, MAX_DOCUMENT_SIZE ) @login_required def teacherDashboard(request): """View to display the teacher dashboard with their classes and student information""" # Check if user is in Teacher group if not request.user.groups.filter(name="Teacher").exists(): print(request, "You don't have permission to access the teacher dashboard.") return redirect("dashboard") # Get or create a teacher object try: # Try to get the teacher object directly if user is already a Staff instance if isinstance(request.user, Staff): teacher = request.user else: # Try to get the staff object by ID teacher = Staff.objects.get(id=request.user.id) except (Staff.DoesNotExist, Exception) as e: print(f"Error getting staff profile: {str(e)}") messages.error( request, "Teacher profile not found. Please contact administrator." ) return redirect("dashboard") today = timezone.now().date() current_time = timezone.now().time() last_week = today - timedelta(days=7) # Get only the active routines assigned to this teacher today_routines = Routine.objects.filter( teacher=teacher, # Filter by the current teacher is_active=True, # Only include active routines ).order_by("start_time") # Process routines to include status and attendance processed_routines = [] for routine in today_routines: attendance = routine.attendances.filter(date=today).first() # Get total active students for this routine - matching the get_students logic students = Student.objects.filter( course=routine.subject.course, current_period=routine.period_or_year, status="Active", # Only count active students ) # If no students found with exact period match, try more flexible approach if not students.exists(): students = Student.objects.filter( course=routine.subject.course, status="Active", # Still only count active students ) total_students = students.count() attendance_count = ( 0 if not attendance else AttendanceRecord.objects.filter( attendance=attendance, student_attend=True ).count() ) # Add routine to processed_routines processed_routines.append( { "id": routine.id, "name": routine.subject.name, "start_time": routine.start_time, "end_time": routine.end_time, "is_completed": attendance is not None, "is_ongoing": routine.start_time <= current_time <= routine.end_time, "total_students": total_students, "attendance_count": attendance_count, "period_or_year": routine.period_or_year, "course_name": routine.subject.course.name, "has_attendance": attendance is not None, "subject_id": routine.subject.id, } ) # Calculate class statistics total_classes = today_routines.count() completed_classes = len([r for r in processed_routines if r["is_completed"]]) remaining_classes = total_classes - completed_classes # Get student statistics from course tracking total_students = ( CourseTracking.objects.filter( course__in=today_routines.values_list("subject__course", flat=True), progress_status="In Progress", ) .values("student") .distinct() .count() ) # Get present and absent students for today present_students = ( AttendanceRecord.objects.filter( attendance__date=today, student_attend=True, attendance__routine__teacher=teacher, ) .values("student") .distinct() .count() ) # Calculate absent students based on teacher's classes today today_class_students = set() for routine in today_routines: students = CourseTracking.objects.filter( course=routine.subject.course, current_period=routine.period_or_year, progress_status="In Progress", ).values_list("student", flat=True) today_class_students.update(students) # Make sure absent students doesn't go negative total_today_students = len(today_class_students) absent_students = ( max(0, total_today_students - present_students) if total_today_students else 0 ) # Get average rating from student feedback avg_rating = ( StudentFeedback.objects.filter(teacher=teacher).aggregate(Avg("rating"))[ "rating__avg" ] or 0 ) avg_rating_rounded = round(avg_rating) remaining_stars = 5 - avg_rating_rounded # Get leave statistics pending_leaves = StaffLeave.objects.filter(staff=teacher, status=0).count() approved_leaves = StaffLeave.objects.filter(staff=teacher, status=1).count() rejected_leaves = StaffLeave.objects.filter(staff=teacher, status=2).count() # Get recent leaves recent_leaves = StaffLeave.objects.filter(staff=teacher).order_by("-created_at")[:5] # Get recent feedback recent_feedback = StudentFeedback.objects.filter(teacher=teacher).order_by( "-created_at" )[:5] # Get notices notices = Notice.objects.all().order_by("-created_at")[:5] # Get feedback types for institute feedback feedback_types = FEEDBACK_TYPE_CHOICES # Get institute feedback institute_feedback = StaffInstituteFeedback.objects.filter(staff=teacher).order_by( "-created_at" )[:5] # Get all routines for the teacher (for the schedule) all_routines = Routine.objects.filter( teacher=teacher, is_active=True, period_or_year__in=CourseTracking.objects.filter(progress_status="In Progress") .values_list("current_period", flat=True) .distinct(), ).order_by("start_time") # Get all attendance records for quick access recent_attendance = ( Attendance.objects.filter(teacher=teacher, date__gte=last_week) .select_related("routine") .order_by("-date") ) # Add present/total counts to each attendance record for attendance in recent_attendance: attendance.present_count = AttendanceRecord.objects.filter( attendance=attendance, student_attend=True ).count() attendance.total_count = AttendanceRecord.objects.filter( attendance=attendance ).count() # Attendance Management Data routines = Routine.objects.filter(teacher=teacher, is_active=True).order_by( "start_time" ) # Get all subjects taught by this teacher for the subjects section subject_ids = ( Routine.objects.filter(teacher=teacher, is_active=True) .values_list("subject", flat=True) .distinct() ) teacher_subjects = ( Routine.objects.filter( teacher=teacher, is_active=True, subject_id__in=subject_ids ) .select_related("subject", "course") .order_by("subject__name") ) # Add student count to each routine for routine in teacher_subjects: routine.students_count = CourseTracking.objects.filter( course=routine.course, current_period=routine.period_or_year, progress_status="In Progress", ).count() context = { "teacher": teacher, "title": "Teacher Dashboard", "today_routines": processed_routines, "total_classes": total_classes, "completed_classes": completed_classes, "remaining_classes": remaining_classes, "total_students": total_students, "present_students": present_students, "absent_students": absent_students, "avg_rating": avg_rating, "avg_rating_rounded": avg_rating_rounded, "remaining_stars": remaining_stars, "pending_leaves": pending_leaves, "approved_leaves": approved_leaves, "rejected_leaves": rejected_leaves, "recent_leaves": recent_leaves, "recent_feedback": recent_feedback, "notices": notices, "feedback_types": feedback_types, "institute_feedback": institute_feedback, "all_routines": all_routines, "recent_attendance": recent_attendance, "routines": routines, "today": today, "teacher_subjects": teacher_subjects, } return render(request, "teacher/dashboard.html", context) @login_required def request_staff_leave(request): """View to handle leave requests from staff""" if request.method == "POST": staff = request.user start_date = request.POST.get("start_date") end_date = request.POST.get("end_date") message = request.POST.get("message") try: # Convert date strings to timezone-aware datetime start_date = timezone.datetime.strptime(start_date, "%Y-%m-%d") end_date = timezone.datetime.strptime(end_date, "%Y-%m-%d") start_date = timezone.make_aware(start_date) end_date = timezone.make_aware(end_date) today = timezone.now() if start_date.date() < today.date(): messages.error(request, "Start date cannot be in the past.") return redirect("teacherDashboard") if end_date.date() < start_date.date(): messages.error(request, "End date cannot be before start date.") return redirect("teacherDashboard") # Check if leave already exists for these dates existing_leave = StaffLeave.objects.filter( staff=staff, start_date__lte=end_date.date(), end_date__gte=start_date.date(), ).first() if existing_leave: messages.error( request, "You have already requested leave for these dates." ) return redirect("teacherDashboard") # Create leave request leave = StaffLeave.objects.create( staff=staff, start_date=start_date.date(), end_date=end_date.date(), message=message, status=0, # Pending status ) messages.success(request, "Leave request submitted successfully.") except Exception as e: messages.error(request, f"Error submitting leave request: {str(e)}") return redirect("teacherDashboard") return redirect("teacherDashboard") @login_required def submit_staff_institute_feedback(request): """View to handle feedback submission from staff to institute""" if request.method == "POST": staff = request.user feedback_type = request.POST.get("feedback_type") rating = request.POST.get("rating") feedback_text = request.POST.get("feedback_text") is_anonymous = request.POST.get("is_anonymous") == "on" try: # Convert to float first to handle half-stars rating_value = float(rating) if rating_value < 0.5 or rating_value > 5: messages.error(request, "Rating must be between 0.5 and 5") return redirect("teacherDashboard") # Check if it's a valid half or full star rating if rating_value % 0.5 != 0: messages.error(request, "Rating must be a whole or half number") return redirect("teacherDashboard") # Get the institute (assuming one institute for now) institute = Institute.objects.first() if not institute: # Create a default institute if none exists institute = Institute.objects.create( name="Default Institute", phone="123456789", email="default@example.com", ) # Get the staff object - FIX: Use filter().first() instead of get() to avoid MultipleObjectsReturned error teacher = Staff.objects.filter(phone=staff.phone).first() if not teacher: messages.error(request, "Staff account not found") return redirect("teacherDashboard") # Check if feedback already exists for this type existing_feedback = StaffInstituteFeedback.objects.filter( staff=teacher, institute=institute, feedback_type=feedback_type ).first() if existing_feedback: # Update existing feedback existing_feedback.rating = rating_value existing_feedback.feedback_text = feedback_text existing_feedback.is_anonymous = is_anonymous existing_feedback.save() messages.success(request, "Institute feedback updated successfully.") else: # Create new feedback feedback = StaffInstituteFeedback.objects.create( staff=teacher, institute=institute, feedback_type=feedback_type, rating=rating_value, feedback_text=feedback_text, is_anonymous=is_anonymous, ) messages.success(request, "Institute feedback submitted successfully.") except ValueError: messages.error(request, "Invalid rating value") except Exception as e: messages.error(request, f"Error submitting institute feedback: {str(e)}") return redirect("teacherDashboard") return redirect("teacherDashboard") @login_required def save_attendance(request): """View to save attendance data""" if request.method == "POST": teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return redirect("login") routine_id = request.POST.get("routine_id") date_str = request.POST.get("date") # Convert class_status to boolean - "Conducted" is True, others are False class_status_str = request.POST.get("class_status", "True") class_status = class_status_str == "True" teacher_attend = request.POST.get("teacher_attend") == "on" try: # Validate routine - strict check to ensure teacher can only access their assigned routines routine = Routine.objects.get( id=routine_id, teacher=teacher, # Ensure this routine is assigned to this teacher is_active=True, # Only allow active routines ) # Validate date attendance_date = timezone.datetime.strptime(date_str, "%Y-%m-%d").date() # Get or create attendance record attendance, created = Attendance.objects.get_or_create( routine=routine, date=attendance_date, defaults={ "teacher": teacher, "class_status": class_status, "teacher_attend": teacher_attend, }, ) if not created: # Update existing attendance attendance.class_status = class_status attendance.teacher_attend = teacher_attend attendance.save() # Process student attendance for key, value in request.POST.items(): if key.startswith("student_"): student_id = key.replace("student_", "") student_attend = value == "on" try: # Convert student_id to integer to ensure proper lookup student_id_int = int(student_id) student = Student.objects.get(id=student_id_int) # Get or create attendance record for this student record, _ = AttendanceRecord.objects.get_or_create( attendance=attendance, student=student, defaults={"student_attend": student_attend}, ) # Update if record already exists if record.student_attend != student_attend: record.student_attend = student_attend record.save() except Student.DoesNotExist: continue messages.success( request, f"Attendance for {attendance_date.strftime('%d %b, %Y')} saved successfully.", ) except Routine.DoesNotExist: messages.error( request, "You do not have permission to take attendance for this class. Only teachers assigned to this class can manage its attendance.", ) except ValueError: messages.error(request, "Invalid date format.") except Exception as e: messages.error(request, f"Error saving attendance: {str(e)}") # Redirect to teacher dashboard after saving attendance return redirect("teacherDashboard") return redirect("teacherDashboard") @login_required @require_GET def get_subject_schedule(request): """View to get schedule for a subject""" teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return JsonResponse({"success": False, "message": "Teacher account not found"}) subject_id = request.GET.get("subject_id") if not subject_id: return JsonResponse({"success": False, "message": "Subject ID is required"}) try: routines = Routine.objects.filter( teacher=teacher, subject_id=subject_id, is_active=True ).values("id", "start_time", "end_time", "period_or_year") return JsonResponse({"success": True, "schedule": list(routines)}) except Exception as e: return JsonResponse({"success": False, "message": str(e)}) @login_required def teacher_update_profile_picture(request): """View to handle profile picture update for teachers""" if request.method == "POST": teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return redirect("login") image = request.FILES.get("image") if image: try: # Update the teacher's profile picture teacher.image = image teacher.save() messages.success(request, "Profile picture updated successfully.") except Exception as e: messages.error(request, f"Error updating profile picture: {str(e)}") else: messages.error(request, "No profile picture uploaded.") return redirect("teacherDashboard") @login_required def teacher_change_password(request): """View to handle password change for teachers""" if request.method == "POST": current_password = request.POST.get("current_password") new_password = request.POST.get("new_password") confirm_password = request.POST.get("confirm_password") # Get the current user user = request.user # Validate inputs if not current_password or not new_password or not confirm_password: messages.error(request, "All password fields are required.") return redirect("teacherDashboard") # Check if current password is correct if not check_password(current_password, user.password): messages.error(request, "Current password is incorrect.") return redirect("teacherDashboard") # Check if new passwords match if new_password != confirm_password: messages.error(request, "New passwords do not match.") return redirect("teacherDashboard") # Check if new password is strong enough (add more criteria as needed) if len(new_password) < 8: messages.error(request, "New password must be at least 8 characters long.") return redirect("teacherDashboard") try: # Update the password user.password = make_password(new_password) user.save() messages.success( request, "Password changed successfully. Please log in again with your new password.", ) return redirect( "login" ) # Redirect to login page to log in with new password except Exception as e: messages.error(request, f"Error changing password: {str(e)}") return redirect("teacherDashboard") return redirect("teacherDashboard") @login_required def get_attendance_form(request): """View to handle AJAX request for getting attendance form""" routine_id = request.GET.get("routine_id") date_str = request.GET.get("date") if not routine_id or not date_str: return JsonResponse({"error": "Missing required parameters"}) try: teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return JsonResponse({"error": "Teacher account not found"}, status=404) routine = Routine.objects.get(id=routine_id, teacher=teacher) date = datetime.strptime(date_str, "%Y-%m-%d").date() # Get existing attendance data if any attendance_data = {"teacher_attend": False, "class_status": True, "records": {}} try: # Get attendance record with all related data in one query attendance = Attendance.objects.select_related("routine").get( routine=routine, date=date ) # Get all attendance records for this attendance in one query records = AttendanceRecord.objects.filter( attendance=attendance ).select_related("student") attendance_data = { "id": attendance.id, "date": attendance.date.strftime("%Y-%m-%d"), "teacher_attend": attendance.teacher_attend, "class_status": attendance.class_status, "records": { str(record.student.id): record.student_attend for record in records }, } except Attendance.DoesNotExist: # No existing attendance record found - return default values pass return JsonResponse(attendance_data) except Routine.DoesNotExist: return JsonResponse({"error": "Invalid routine selected"}, status=404) except ValueError: return JsonResponse({"error": "Invalid date format"}, status=400) except Exception as e: return JsonResponse({"error": str(e)}, status=500) @login_required def get_students(request): """View to handle AJAX request for getting students list""" from django.http import JsonResponse routine_id = request.GET.get("routine_id") if not routine_id: return JsonResponse({"error": "Missing routine_id parameter"}, status=400) try: teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return JsonResponse({"error": "Teacher account not found"}, status=404) routine = Routine.objects.get(id=routine_id, teacher=teacher) # Get only active students for this routine students = Student.objects.filter( course=routine.subject.course, current_period=routine.period_or_year, status="Active", # Only get active students ).order_by("name") # If no students found, try a more flexible approach but still only active students if not students.exists(): students = Student.objects.filter( course=routine.subject.course, status="Active", # Maintain active student filter ).order_by("name") # Format student data for JSON response student_data = [ {"id": student.id, "name": student.name} for student in students ] return JsonResponse({"students": student_data}) except Routine.DoesNotExist: return JsonResponse({"error": "Invalid routine selected"}, status=404) except Exception as e: return JsonResponse({"error": str(e)}, status=500) @login_required @require_http_methods(["GET", "POST"]) def manage_subject_files(request): """View to manage files for subjects taught by the teacher""" logger = logging.getLogger(__name__) # First check if the user is authenticated and in the Teacher group if not request.user.is_authenticated: logger.error("User not authenticated") return redirect("login") # Try to get the teacher object teacher = None try: if hasattr(request.user, "phone"): # If the user model has phone attribute (Staff model) teacher = Staff.objects.get(phone=request.user.phone) else: # If request.user is a different model, try to get Staff by ID teacher = Staff.objects.get(id=request.user.id) except Staff.DoesNotExist: logger.error(f"Teacher not found for user: {request.user}") messages.error( request, "Staff account not found. Please contact administrator." ) return redirect("dashboard") except Exception as e: logger.error(f"Error getting teacher: {e}", exc_info=True) messages.error(request, f"Error retrieving staff profile: {str(e)}") return redirect("dashboard") logger.info(f"Teacher found: {teacher.name}") # Get all subjects taught by this teacher subjects = Subject.objects.filter( routine__teacher=teacher, routine__is_active=True ).distinct() if request.method == "POST": subject_id = request.POST.get("subject_id") title = request.POST.get("title") description = request.POST.get("description") file = request.FILES.get("file") logger.info( f"Processing file upload - subject_id: {subject_id}, title: {title}" ) if not subject_id or not title or not file: logger.warning("Missing required fields for file upload") return JsonResponse( {"success": False, "message": "Subject, title and file are required"} ) try: subject = Subject.objects.get(id=subject_id) # Check if subject is taught by this teacher if not Routine.objects.filter( teacher=teacher, subject=subject, is_active=True ).exists(): logger.warning( f"Teacher {teacher.id} does not have permission for subject {subject_id}" ) return JsonResponse( { "success": False, "message": "You don't have permission to upload files to this subject", } ) # Handle file upload with security checks try: file_path = handle_file_upload( file, "subject_files", ALLOWED_DOCUMENT_TYPES, MAX_DOCUMENT_SIZE ) except FileUploadError as e: logger.error(f"File upload validation failed: {e}") return JsonResponse({"success": False, "message": str(e)}) # Create new subject file try: subject_file = SubjectFile.objects.create( subject=subject, title=title, description=description, file=file_path, uploaded_by=teacher, ) logger.info(f"File uploaded successfully: {subject_file.id}") return JsonResponse( { "success": True, "message": "File uploaded successfully", "file": { "id": subject_file.id, "title": subject_file.title, "description": subject_file.description, "file_url": subject_file.file.url, "uploaded_at": subject_file.uploaded_at.strftime( "%b %d, %Y %H:%M" ), }, } ) except Exception as e: # Clean up the uploaded file if database operation fails cleanup_failed_upload(file_path) logger.error(f"Error creating subject file: {e}", exc_info=True) return JsonResponse( {"success": False, "message": f"Error creating subject file: {str(e)}"} ) except Subject.DoesNotExist: logger.error(f"Subject {subject_id} not found") return JsonResponse({"success": False, "message": "Subject not found"}) except Exception as e: logger.error(f"Error uploading file: {e}", exc_info=True) return JsonResponse( {"success": False, "message": f"Error uploading file: {str(e)}"} ) # For GET requests, return a list of subjects and their files subjects_data = [] for subject in subjects: try: logger.info(f"Processing subject: {subject.id} - {subject.name}") # Manually get the files instead of relying on get_all_files method files = [] # Add syllabus if it exists if subject.syllabus_pdf: try: files.append( { "id": "syllabus", "title": f"{subject.name} Syllabus", "description": f"Syllabus PDF for {subject.name}", "file_url": subject.syllabus_pdf.url, "file_type": "syllabus", "uploaded_by": None, "uploaded_at": None, } ) except Exception as syllabus_error: logger.error( f"Error adding syllabus for subject {subject.id}: {syllabus_error}", exc_info=True, ) # Add subject files try: subject_files = SubjectFile.objects.filter(subject=subject).order_by( "-uploaded_at" ) logger.info( f"Found {subject_files.count()} additional files for subject {subject.id}" ) for file in subject_files: try: files.append( { "id": file.id, "title": file.title, "description": file.description, "file_url": file.file.url, "file_type": "notes", "uploaded_by": file.uploaded_by.name if file.uploaded_by else None, "uploaded_at": file.uploaded_at, } ) except Exception as file_error: logger.error( f"Error adding file {file.id} for subject {subject.id}: {file_error}", exc_info=True, ) except Exception as files_error: logger.error( f"Error getting subject files for subject {subject.id}: {files_error}", exc_info=True, ) logger.info( f"Successfully processed {len(files)} files for subject {subject.id}" ) subjects_data.append( { "id": subject.id, "name": subject.name, "course": subject.course.name, "semester_or_year": subject.period_or_year, "files": files, "files_count": len(files), } ) except Exception as e: logger.error( f"Error processing subject {subject.id}: {e}", exc_info=True ) return JsonResponse({"success": True, "subjects": subjects_data}) @login_required @require_http_methods(["POST"]) def delete_subject_file(request): """View to delete a subject file""" teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return JsonResponse( {"success": False, "message": "Staff account not found"}, status=404 ) file_id = request.POST.get("file_id") if not file_id: return JsonResponse({"success": False, "message": "File ID is required"}) try: # Check if the file exists and is owned by this teacher or from their subject subject_file = SubjectFile.objects.get(id=file_id) # Check if the file is from a subject taught by this teacher if ( not Routine.objects.filter( teacher=teacher, subject=subject_file.subject, is_active=True ).exists() and subject_file.uploaded_by != teacher ): return JsonResponse( { "success": False, "message": "You don't have permission to delete this file", } ) # Delete the file subject_file.delete() return JsonResponse({"success": True, "message": "File deleted successfully"}) except SubjectFile.DoesNotExist: return JsonResponse({"success": False, "message": "File not found"}) except Exception as e: return JsonResponse( {"success": False, "message": f"Error deleting file: {str(e)}"} ) @login_required @require_GET def get_teacher_subjects(request): """View to get a list of subjects taught by the teacher""" teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return JsonResponse( {"success": False, "message": "Staff account not found"}, status=404 ) # Get all subjects taught by the teacher subjects_query = Subject.objects.filter( routine__teacher=teacher, routine__is_active=True ).distinct() # Prepare results with additional has_syllabus field subjects_data = [] for subject in subjects_query: subjects_data.append( { "id": subject.id, "name": subject.name, "course__name": subject.course.name, "period_or_year": subject.period_or_year, "has_syllabus": bool(subject.syllabus_pdf), } ) return JsonResponse({"success": True, "subjects": subjects_data}) @login_required @require_GET def view_subject_syllabus(request, subject_id): """View to display syllabus PDF for a subject""" teacher = Staff.objects.filter(phone=request.user.phone).first() if not teacher: return JsonResponse( {"success": False, "message": "Staff account not found"}, status=404 ) try: # Check if the subject_id is valid if not subject_id or not str(subject_id).isdigit(): return JsonResponse( {"success": False, "message": "Invalid subject ID provided"}, status=400 ) subject_id = int(subject_id) # First check if the teacher teaches this subject if not Routine.objects.filter( teacher=teacher, subject_id=subject_id, is_active=True ).exists(): return JsonResponse( { "success": False, "message": "You don't have permission to view this subject's syllabus", }, status=403, ) try: subject = Subject.objects.filter(id=subject_id).first() except Subject.DoesNotExist: return JsonResponse( {"success": False, "message": "Subject not found"}, status=404 ) if not subject.syllabus_pdf: return JsonResponse( {"success": False, "message": "No syllabus available for this subject"}, status=404, ) # Return JSON with the file URL return JsonResponse( { "success": True, "file_url": subject.syllabus_pdf.url, "file_name": f"{subject.name} Syllabus.pdf", } ) except Exception as e: return JsonResponse( {"success": False, "message": f"Error retrieving syllabus: {str(e)}"}, status=500, ) @login_required def staffDashboard(request): """View to display the staff dashboard with their personal information and progress""" staff = Staff.objects.get(phone=request.user.phone) # Get the Staff object today = timezone.now().date() last_week = today - timedelta(days=7) last_month = today - timedelta(days=30) # Clear section states on page load if request.method == "GET": request.session.pop("active_section", None) request.session.pop("active_subsection", None) # Get feedback types for institute feedback feedback_types = FEEDBACK_TYPE_CHOICES # Get institute feedback institute_feedback = StaffInstituteFeedback.objects.filter(staff=staff).order_by( "-created_at" )[:5] # Get recent notices recent_notices = Notice.objects.all().order_by("-created_at")[:5] # Get recent feedback recent_feedback = StudentFeedback.objects.filter(teacher=staff).order_by( "-created_at" )[:5] # Get leave requests leave_requests = StaffLeave.objects.filter(staff=staff).order_by("-created_at")[:5] # Get routines assigned to this teacher routines = Routine.objects.filter(teacher=staff, is_active=True).order_by( "start_time" ) # Get today's routines today_routines = routines.filter( start_time__lte=timezone.now().time(), end_time__gte=timezone.now().time() ) # Get attendance records for today's routines today_attendance = Attendance.objects.filter( routine__in=today_routines, date=today ).select_related("routine") # Calculate attendance statistics total_students = 0 present_students = 0 absent_students = 0 for attendance in today_attendance: records = attendance.records.all() total_students += records.count() present_students += records.filter(student_attend=True).count() absent_students += records.filter(student_attend=False).count() # Get average rating from student feedback avg_rating = ( StudentFeedback.objects.filter(teacher=staff).aggregate(Avg("rating"))[ "rating__avg" ] or 0 ) context = { "staff": staff, "recent_notices": recent_notices, "recent_feedback": recent_feedback, "institute_feedback": institute_feedback, "leave_requests": leave_requests, "today_routines": today_routines, "today_attendance": today_attendance, "total_students": total_students, "present_students": present_students, "absent_students": absent_students, "avg_rating": avg_rating, "feedback_types": feedback_types, "last_week": last_week, "last_month": last_month, } return render(request, "teacher/dashboard.html", context)