models(update): remove meeting field from student
This commit is contained in:
1 parent
3cdaf2ab0c
commit
e8b306a4f6
1 file changed
+127
-103
+127
-103
@@ -126,12 +126,14 @@ class Course(models.Model):
|
||||
verbose_name = "Course"
|
||||
verbose_name_plural = "Courses"
|
||||
indexes = [
|
||||
models.Index(fields=['name']),
|
||||
models.Index(fields=['code']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['duration_type']),
|
||||
models.Index(fields=['name', 'is_active']), # For active course lookups
|
||||
models.Index(fields=['code', 'is_active']), # For active course lookups by code
|
||||
models.Index(fields=["name"]),
|
||||
models.Index(fields=["code"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
models.Index(fields=["duration_type"]),
|
||||
# For active course lookups
|
||||
models.Index(fields=["name", "is_active"]),
|
||||
# For active course lookups by code
|
||||
models.Index(fields=["code", "is_active"]),
|
||||
]
|
||||
|
||||
def cleanup_orphaned_data(self):
|
||||
@@ -222,11 +224,13 @@ class Subject(models.Model):
|
||||
unique_together = ["name", "course", "period_or_year"]
|
||||
ordering = ["course", "period_or_year", "name"]
|
||||
indexes = [
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['period_or_year']),
|
||||
models.Index(fields=['name']),
|
||||
models.Index(fields=['course', 'period_or_year']), # For course period subjects
|
||||
models.Index(fields=['name', 'course']), # For subject lookups
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["period_or_year"]),
|
||||
models.Index(fields=["name"]),
|
||||
# For course period subjects
|
||||
models.Index(fields=["course", "period_or_year"]),
|
||||
# For subject lookups
|
||||
models.Index(fields=["name", "course"]),
|
||||
]
|
||||
|
||||
def cleanup_orphaned_data(self):
|
||||
@@ -323,7 +327,6 @@ class Student(AbstractUser):
|
||||
joining_date = models.DateField(null=True, blank=True)
|
||||
password = models.CharField(max_length=128, editable=False, null=True)
|
||||
fcm_token = models.CharField(max_length=500, null=True, blank=True)
|
||||
meetings = models.ManyToManyField("TeacherParentMeeting", related_name="students", blank=True)
|
||||
|
||||
USERNAME_FIELD = "phone"
|
||||
REQUIRED_FIELDS = ["name"]
|
||||
@@ -408,7 +411,7 @@ class Student(AbstractUser):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# Update related records in the same transaction
|
||||
if hasattr(self, 'course_trackings'):
|
||||
if hasattr(self, "course_trackings"):
|
||||
for tracking in self.course_trackings.all():
|
||||
tracking.update_completion_percentage()
|
||||
except ValidationError as e:
|
||||
@@ -421,7 +424,7 @@ class Student(AbstractUser):
|
||||
if self.phone and not self.phone.isdigit():
|
||||
raise ValidationError("Phone number must contain only digits")
|
||||
|
||||
if self.email and '@' not in self.email:
|
||||
if self.email and "@" not in self.email:
|
||||
raise ValidationError("Invalid email format")
|
||||
|
||||
if self.birth_date and self.birth_date > date.today():
|
||||
@@ -479,11 +482,11 @@ class Student(AbstractUser):
|
||||
verbose_name = "Student"
|
||||
verbose_name_plural = "Students"
|
||||
indexes = [
|
||||
models.Index(fields=['phone']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['current_period']),
|
||||
models.Index(fields=['joining_date']),
|
||||
models.Index(fields=["phone"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["current_period"]),
|
||||
models.Index(fields=["joining_date"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -546,7 +549,9 @@ class Staff(AbstractUser):
|
||||
help_text="Course for which this staff member is HOD",
|
||||
)
|
||||
courses_taught = models.ManyToManyField(Course, related_name="teachers", blank=True)
|
||||
meetings = models.ManyToManyField("TeacherParentMeeting", related_name="staff_members", blank=True)
|
||||
meetings = models.ManyToManyField(
|
||||
"TeacherParentMeeting", related_name="staff_members", blank=True
|
||||
)
|
||||
|
||||
USERNAME_FIELD = "phone"
|
||||
REQUIRED_FIELDS = ["name"]
|
||||
@@ -607,7 +612,7 @@ class Staff(AbstractUser):
|
||||
if self.phone and not self.phone.isdigit():
|
||||
raise ValidationError("Phone number must contain only digits")
|
||||
|
||||
if self.email and '@' not in self.email:
|
||||
if self.email and "@" not in self.email:
|
||||
raise ValidationError("Invalid email format")
|
||||
|
||||
if self.birth_date and self.birth_date > date.today():
|
||||
@@ -658,14 +663,16 @@ class Staff(AbstractUser):
|
||||
verbose_name = "Staff"
|
||||
verbose_name_plural = "Staffs"
|
||||
indexes = [
|
||||
models.Index(fields=['phone']),
|
||||
models.Index(fields=['designation']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=["phone"]),
|
||||
models.Index(fields=["designation"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
models.Index(fields=["course"]),
|
||||
# Add these composite indexes
|
||||
models.Index(fields=['designation', 'is_active']), # For active staff by role
|
||||
models.Index(fields=['course', 'is_active']), # For course staff
|
||||
models.Index(fields=['phone', 'is_active']), # For active staff lookups
|
||||
# For active staff by role
|
||||
models.Index(fields=["designation", "is_active"]),
|
||||
models.Index(fields=["course", "is_active"]), # For course staff
|
||||
# For active staff lookups
|
||||
models.Index(fields=["phone", "is_active"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -696,14 +703,15 @@ class Routine(models.Model):
|
||||
"period_or_year",
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['subject']),
|
||||
models.Index(fields=['teacher']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['period_or_year']),
|
||||
models.Index(fields=['course', 'period_or_year']), # For course routines
|
||||
models.Index(fields=['teacher', 'is_active']), # For teacher schedules
|
||||
models.Index(fields=['subject', 'is_active']), # For subject schedules
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["subject"]),
|
||||
models.Index(fields=["teacher"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
models.Index(fields=["period_or_year"]),
|
||||
# For course routines
|
||||
models.Index(fields=["course", "period_or_year"]),
|
||||
models.Index(fields=["teacher", "is_active"]), # For teacher schedules
|
||||
models.Index(fields=["subject", "is_active"]), # For subject schedules
|
||||
]
|
||||
|
||||
|
||||
@@ -737,14 +745,17 @@ class Attendance(models.Model):
|
||||
verbose_name = "Attendance"
|
||||
verbose_name_plural = "Attendances"
|
||||
indexes = [
|
||||
models.Index(fields=['date']),
|
||||
models.Index(fields=['routine']),
|
||||
models.Index(fields=['teacher']),
|
||||
models.Index(fields=['class_status']),
|
||||
models.Index(fields=["date"]),
|
||||
models.Index(fields=["routine"]),
|
||||
models.Index(fields=["teacher"]),
|
||||
models.Index(fields=["class_status"]),
|
||||
# Add these composite indexes
|
||||
models.Index(fields=['date', 'routine']), # For date-based routine lookups
|
||||
models.Index(fields=['teacher', 'date']), # For teacher attendance history
|
||||
models.Index(fields=['routine', 'class_status']), # For class status filtering
|
||||
# For date-based routine lookups
|
||||
models.Index(fields=["date", "routine"]),
|
||||
# For teacher attendance history
|
||||
models.Index(fields=["teacher", "date"]),
|
||||
# For class status filtering
|
||||
models.Index(fields=["routine", "class_status"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -770,13 +781,15 @@ class AttendanceRecord(models.Model):
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=['attendance']),
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['student_attend']),
|
||||
models.Index(fields=["attendance"]),
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["student_attend"]),
|
||||
# Add these composite indexes
|
||||
models.Index(fields=['student', 'student_attend']), # For attendance filtering
|
||||
models.Index(fields=['attendance', 'student']), # For attendance lookups
|
||||
models.Index(fields=['attendance', 'student_attend']), # For attendance status
|
||||
# For attendance filtering
|
||||
models.Index(fields=["student", "student_attend"]),
|
||||
models.Index(fields=["attendance", "student"]), # For attendance lookups
|
||||
# For attendance status
|
||||
models.Index(fields=["attendance", "student_attend"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -841,13 +854,14 @@ class StudentLeave(models.Model):
|
||||
verbose_name_plural = "Student Leaves"
|
||||
ordering = ["-created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['start_date']),
|
||||
models.Index(fields=['end_date']),
|
||||
models.Index(fields=['student', 'status']), # For student leave status
|
||||
models.Index(fields=['start_date', 'end_date']), # For date range queries
|
||||
models.Index(fields=['status', 'start_date']), # For pending leaves
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["start_date"]),
|
||||
models.Index(fields=["end_date"]),
|
||||
# For student leave status
|
||||
models.Index(fields=["student", "status"]),
|
||||
models.Index(fields=["start_date", "end_date"]), # For date range queries
|
||||
models.Index(fields=["status", "start_date"]), # For pending leaves
|
||||
]
|
||||
|
||||
|
||||
@@ -1084,34 +1098,35 @@ class CourseTracking(models.Model):
|
||||
|
||||
# Get completed subjects from SubjectProgress
|
||||
completed_subjects = SubjectProgress.objects.filter(
|
||||
student=self.student,
|
||||
subject__course=self.course,
|
||||
status="Completed"
|
||||
student=self.student, subject__course=self.course, status="Completed"
|
||||
).count()
|
||||
|
||||
# Get attendance percentage for active subjects
|
||||
attended_classes = AttendanceRecord.objects.filter(
|
||||
student=self.student,
|
||||
attendance__routine__subject__course=self.course,
|
||||
student_attend=True
|
||||
student_attend=True,
|
||||
).count()
|
||||
|
||||
total_classes = AttendanceRecord.objects.filter(
|
||||
student=self.student,
|
||||
attendance__routine__subject__course=self.course
|
||||
student=self.student, attendance__routine__subject__course=self.course
|
||||
).count()
|
||||
|
||||
# Calculate weighted completion percentage
|
||||
subject_weight = 0.7 # 70% weight to subject completion
|
||||
attendance_weight = 0.3 # 30% weight to attendance
|
||||
|
||||
subject_percentage = (completed_subjects / total_subjects) * 100 if total_subjects > 0 else 0
|
||||
attendance_percentage = (attended_classes / total_classes) * 100 if total_classes > 0 else 0
|
||||
subject_percentage = (
|
||||
(completed_subjects / total_subjects) * 100 if total_subjects > 0 else 0
|
||||
)
|
||||
attendance_percentage = (
|
||||
(attended_classes / total_classes) * 100 if total_classes > 0 else 0
|
||||
)
|
||||
|
||||
# Calculate final percentage
|
||||
completion_percentage = int(
|
||||
(subject_percentage * subject_weight) +
|
||||
(attendance_percentage * attendance_weight)
|
||||
(subject_percentage * subject_weight)
|
||||
+ (attendance_percentage * attendance_weight)
|
||||
)
|
||||
|
||||
# Ensure percentage is between 0 and 100
|
||||
@@ -1193,12 +1208,12 @@ class CourseTracking(models.Model):
|
||||
try:
|
||||
self.clean()
|
||||
# Update completion percentage without triggering save again
|
||||
if not hasattr(self, '_updating_completion'):
|
||||
if not hasattr(self, "_updating_completion"):
|
||||
self._updating_completion = True
|
||||
try:
|
||||
self.completion_percentage = self.update_completion_percentage()
|
||||
finally:
|
||||
delattr(self, '_updating_completion')
|
||||
delattr(self, "_updating_completion")
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
except Exception as e:
|
||||
@@ -1274,17 +1289,17 @@ class CourseTracking(models.Model):
|
||||
unique_together = ["student", "course"]
|
||||
ordering = ["-created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['progress_status']),
|
||||
models.Index(fields=['current_period']),
|
||||
models.Index(fields=['enrollment_date']),
|
||||
models.Index(fields=['student', 'progress_status']),
|
||||
models.Index(fields=['course', 'progress_status']),
|
||||
models.Index(fields=['student', 'course', 'progress_status']),
|
||||
models.Index(fields=['completion_percentage']),
|
||||
models.Index(fields=['start_date', 'expected_end_date']),
|
||||
models.Index(fields=['current_period', 'progress_status']),
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["progress_status"]),
|
||||
models.Index(fields=["current_period"]),
|
||||
models.Index(fields=["enrollment_date"]),
|
||||
models.Index(fields=["student", "progress_status"]),
|
||||
models.Index(fields=["course", "progress_status"]),
|
||||
models.Index(fields=["student", "course", "progress_status"]),
|
||||
models.Index(fields=["completion_percentage"]),
|
||||
models.Index(fields=["start_date", "expected_end_date"]),
|
||||
models.Index(fields=["current_period", "progress_status"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -1327,12 +1342,13 @@ class ResetToken(models.Model):
|
||||
|
||||
class OTPAttempt(models.Model):
|
||||
"""Model to track OTP attempts for rate limiting"""
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='otp_attempts',
|
||||
related_name="otp_attempts",
|
||||
null=True,
|
||||
blank=True
|
||||
blank=True,
|
||||
)
|
||||
identifier = models.CharField(max_length=255) # phone or email
|
||||
attempts = models.IntegerField(default=0)
|
||||
@@ -1342,9 +1358,9 @@ class OTPAttempt(models.Model):
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=['identifier']),
|
||||
models.Index(fields=['is_locked']),
|
||||
models.Index(fields=['lock_until']),
|
||||
models.Index(fields=["identifier"]),
|
||||
models.Index(fields=["is_locked"]),
|
||||
models.Index(fields=["lock_until"]),
|
||||
]
|
||||
|
||||
def increment_attempts(self):
|
||||
@@ -1466,15 +1482,16 @@ class Parent(AbstractUser):
|
||||
|
||||
# Clean up notification tokens
|
||||
self.fcm_token = None
|
||||
self.save(update_fields=['fcm_token'])
|
||||
self.save(update_fields=["fcm_token"])
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Parent"
|
||||
verbose_name_plural = "Parents"
|
||||
indexes = [
|
||||
models.Index(fields=['phone']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['phone', 'is_active']), # For active parent lookups
|
||||
models.Index(fields=["phone"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
# For active parent lookups
|
||||
models.Index(fields=["phone", "is_active"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -1508,11 +1525,11 @@ class TeacherParentMeeting(models.Model):
|
||||
ordering = ["-meeting_date", "-meeting_time"]
|
||||
unique_together = ["meeting_date", "meeting_time"]
|
||||
indexes = [
|
||||
models.Index(fields=['meeting_date']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['is_online']),
|
||||
models.Index(fields=['meeting_date', 'status']), # For upcoming meetings
|
||||
models.Index(fields=['status', 'is_online']), # For online meetings
|
||||
models.Index(fields=["meeting_date"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["is_online"]),
|
||||
models.Index(fields=["meeting_date", "status"]), # For upcoming meetings
|
||||
models.Index(fields=["status", "is_online"]), # For online meetings
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
@@ -1569,22 +1586,29 @@ class TeacherParentMeeting(models.Model):
|
||||
|
||||
class SubjectProgress(models.Model):
|
||||
"""Model for tracking student progress in individual subjects"""
|
||||
student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='subject_progress')
|
||||
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='student_progress')
|
||||
status = models.CharField(max_length=20, choices=PROGRESS_STATUS_CHOICES, default="Not Started")
|
||||
|
||||
student = models.ForeignKey(
|
||||
Student, on_delete=models.CASCADE, related_name="subject_progress"
|
||||
)
|
||||
subject = models.ForeignKey(
|
||||
Subject, on_delete=models.CASCADE, related_name="student_progress"
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=PROGRESS_STATUS_CHOICES, default="Not Started"
|
||||
)
|
||||
completion_percentage = models.IntegerField(default=0)
|
||||
last_updated = models.DateTimeField(auto_now=True)
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['student', 'subject']
|
||||
ordering = ['-last_updated']
|
||||
unique_together = ["student", "subject"]
|
||||
ordering = ["-last_updated"]
|
||||
indexes = [
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['subject']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['student', 'subject']),
|
||||
models.Index(fields=['student', 'status']),
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["subject"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["student", "subject"]),
|
||||
models.Index(fields=["student", "status"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
|
||||
Reference in new issue
Block a user