1025 lines
37 KiB
Python
1025 lines
37 KiB
Python
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.http import JsonResponse, HttpResponse
|
|
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 .models import (
|
|
Staff,
|
|
Student,
|
|
Routine,
|
|
Attendance,
|
|
AttendanceRecord,
|
|
StudentFeedback,
|
|
Staff_leave,
|
|
Notice,
|
|
Institute,
|
|
StaffInstituteFeedback,
|
|
Subject,
|
|
SubjectFile,
|
|
InstituteFeedback,
|
|
CourseTracking,
|
|
FEEDBACK_TYPE_CHOICES
|
|
)
|
|
from app.admin import custom_admin_site
|
|
|
|
|
|
@login_required
|
|
def teacherDashboard(request):
|
|
"""View to display the teacher dashboard with their classes and student information"""
|
|
teacher = Staff.objects.filter(phone=request.user.phone).first()
|
|
if not teacher:
|
|
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 students from active course trackings
|
|
total_students = CourseTracking.objects.filter(
|
|
course=routine.subject.course,
|
|
current_semester=routine.semester_or_year,
|
|
progress_status="In Progress"
|
|
).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,
|
|
"semester_or_year": routine.semester_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_semester=routine.semester_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 = Staff_leave.objects.filter(staff=teacher, status=0).count()
|
|
approved_leaves = Staff_leave.objects.filter(staff=teacher, status=1).count()
|
|
rejected_leaves = Staff_leave.objects.filter(staff=teacher, status=2).count()
|
|
|
|
# Get recent leaves
|
|
recent_leaves = Staff_leave.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,
|
|
semester_or_year__in=CourseTracking.objects.filter(
|
|
progress_status="In Progress"
|
|
).values_list('current_semester', 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_semester=routine.semester_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 = Staff_leave.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 = Staff_leave.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", "semester_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')
|
|
profile_picture = request.FILES.get("profile_picture")
|
|
|
|
if profile_picture:
|
|
try:
|
|
# Update the teacher's profile picture
|
|
teacher.image = profile_picture
|
|
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")
|
|
|
|
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 students for this routine
|
|
students = Student.objects.filter(
|
|
course=routine.subject.course,
|
|
current_semester=routine.semester_or_year
|
|
).order_by('name')
|
|
|
|
# If no students found, try a more flexible approach
|
|
if not students.exists():
|
|
students = Student.objects.filter(
|
|
course=routine.subject.course
|
|
).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"""
|
|
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 this teacher
|
|
subject_filter = request.GET.get("subject")
|
|
|
|
subjects_query = Routine.objects.filter(
|
|
teacher=teacher,
|
|
is_active=True
|
|
).values_list('subject', flat=True).distinct()
|
|
|
|
# Apply subject filter if provided
|
|
try:
|
|
if subject_filter:
|
|
subjects = Subject.objects.filter(
|
|
id=subject_filter,
|
|
id__in=subjects_query
|
|
).order_by('course__name', 'semester_or_year', 'name')
|
|
|
|
# If no subject found with the provided ID, return a helpful error
|
|
if not subjects.exists():
|
|
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
return JsonResponse({
|
|
"success": False,
|
|
"message": f"Subject with ID {subject_filter} not found or you don't have permission to access it"
|
|
}, status=404)
|
|
else:
|
|
return render(request, 'subject_files.html', {
|
|
"subjects": [],
|
|
"teacher": teacher,
|
|
"selected_subject": None,
|
|
"error_message": f"Subject with ID {subject_filter} not found or you don't have permission to access it"
|
|
})
|
|
else:
|
|
subjects = Subject.objects.filter(
|
|
id__in=subjects_query
|
|
).order_by('course__name', 'semester_or_year', 'name')
|
|
|
|
# If no subjects found at all, return a message
|
|
if not subjects.exists():
|
|
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
return JsonResponse({
|
|
"success": False,
|
|
"message": "You are not assigned to any subjects yet"
|
|
})
|
|
else:
|
|
return render(request, 'subject_files.html', {
|
|
"subjects": [],
|
|
"teacher": teacher,
|
|
"selected_subject": None,
|
|
"error_message": "You are not assigned to any subjects yet"
|
|
})
|
|
except Exception as e:
|
|
# Handle any unexpected errors
|
|
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
return JsonResponse({
|
|
"success": False,
|
|
"message": f"Error loading subjects: {str(e)}"
|
|
}, status=500)
|
|
else:
|
|
return render(request, 'subject_files.html', {
|
|
"subjects": [],
|
|
"teacher": teacher,
|
|
"selected_subject": None,
|
|
"error_message": f"Error loading subjects: {str(e)}"
|
|
})
|
|
|
|
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")
|
|
|
|
if not subject_id or not title or not file:
|
|
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():
|
|
return JsonResponse({
|
|
"success": False,
|
|
"message": "You don't have permission to upload files to this subject"
|
|
})
|
|
|
|
# Create new subject file
|
|
subject_file = SubjectFile.objects.create(
|
|
subject=subject,
|
|
title=title,
|
|
description=description,
|
|
file=file,
|
|
uploaded_by=teacher
|
|
)
|
|
|
|
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 Subject.DoesNotExist:
|
|
return JsonResponse({
|
|
"success": False,
|
|
"message": "Subject not found"
|
|
})
|
|
except Exception as e:
|
|
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:
|
|
subject_files = subject.get_all_files()
|
|
subjects_data.append({
|
|
"id": subject.id,
|
|
"name": subject.name,
|
|
"course": subject.course.name,
|
|
"semester_or_year": subject.semester_or_year,
|
|
"files": subject_files,
|
|
"files_count": len(subject_files)
|
|
})
|
|
except Exception as e:
|
|
# If there's an error with a specific subject, log it but continue
|
|
print(f"Error loading files for subject {subject.id}: {str(e)}")
|
|
|
|
return render(request, 'subject_files.html', {
|
|
"subjects": subjects_data,
|
|
"teacher": teacher,
|
|
"selected_subject": subject_filter
|
|
})
|
|
|
|
|
|
@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,
|
|
'semester_or_year': subject.semester_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 = Staff_leave.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, "app/staff/dashboard.html", context)
|