from datetime import date from django.db import models from django.urls import reverse from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation ) from django.db.models import ( Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value ) class SchoolYear(models.Model): year = models.PositiveIntegerField(unique=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f'{self.year}' @property def is_current_year(self): return True if self.year == int(date.today().year) else False def get_absolute_url(self): return reverse('core:schoolyear-detail', kwargs={ 'year': self.year }) class StudentTag(models.Model): name = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name # class StudentManager(models.Manager): # def get_queryset(self): # today = date.today() # return super().get_queryset().filter( # school_year__start_date__year__gte=today.year, # school_year__end_date__year__lte=today.year # ) class Student(models.Model): school_year = models.ForeignKey( SchoolYear, blank=True, null=True, on_delete=models.SET_NULL ) student_id = models.IntegerField() first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) address = models.TextField(blank=True) dob = models.DateField() allergies = models.CharField(max_length=255, blank=True) tags = models.ManyToManyField(StudentTag, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @property def full_name(self): return f'{self.first_name} {self.last_name}' @property def age(self): today = date.today() return today.year - self.dob.year - ( (today.month, today.day) < (self.dob.month, self.dob.day) ) def __str__(self): return f'{self.first_name} {self.last_name}' def get_absolute_url(self): return reverse('core:student-detail', kwargs={ 'year': self.school_year.year, 'pk': self.pk }) class Meta: ordering = ['student_id', 'first_name'] class Subject(models.Model): school_year = models.ForeignKey( SchoolYear, blank=True, null=True, on_delete=models.SET_NULL ) name = models.CharField(max_length=250) description = models.CharField(max_length=250, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name def get_absolute_url(self): return reverse('core:subject-detail', kwargs={ 'year': self.school_year.year, 'pk': self.pk }) class Meta: ordering = ['name'] class Tag(models.Model): name = models.CharField(max_length=50) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name def get_absolute_url(self): return reverse('tag-detail', kwargs={'pk': self.pk}) class Meta: ordering = ['name'] class Component(models.Model): QZ = 'QUIZ' AS = 'ASSIGNMENT' TS = 'TEST' CATEGORY_CHOICES = [ (QZ, 'Quiz'), (AS, 'Assignment'), (TS, 'Test'), ] subject = models.ForeignKey(Subject, on_delete=models.CASCADE) name = models.CharField(max_length=50) category = models.CharField( max_length=255, choices=CATEGORY_CHOICES, default=AS, ) due_date = models.DateField() grade_total = models.PositiveIntegerField() finished_grading = models.BooleanField(default=False) tags = models.ManyToManyField(Tag, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @property def is_due(self): return True if self.due_date < date.today() else False @property def grade_avg(self): avg = Score.objects.filter(component=self.pk).aggregate( Avg('value') ) if avg['value__avg'] is not None: return round(avg['value__avg'], 2) else: return 'No scores yet.' def __str__(self): return self.name def get_absolute_url(self): return reverse('core:component-detail', kwargs={ 'year': self.school_year.year, 'pk': self.subject.pk, 'component_pk': self.pk }) class Meta: ordering = ['due_date'] class Score(models.Model): component = models.ForeignKey(Component, on_delete=models.CASCADE) student = models.ForeignKey(Student, on_delete=models.CASCADE) value = models.PositiveIntegerField() @property def grade(self): return f'{self.value} / {self.component.grade_total}' @property def grade_as_percentage(self): return round(self.value / self.component.grade_total * 100, 2) def __str__(self): return f'{self.student} scored: {self.value} / {self.component.grade_total}' def get_absolute_url(self): return reverse('core:component-detail', kwargs={ 'year': self.component.subject.school_year.year, 'pk': self.component.subject.pk, 'component_pk': self.component.pk }) class Meta: ordering = ['student'] class SchoolDay(models.Model): school_year = models.ForeignKey( SchoolYear, blank=True, null=True, on_delete=models.SET_NULL ) date = models.DateField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def get_absolute_url(self): return reverse('core:schoolday-detail', kwargs={ 'year': self.school_year.year, 'pk': self.pk }) def __str__(self): return f'{self.date}' class Meta: ordering = ['-date'] class AttendanceEntry(models.Model): PRESENT = 'P' TARDY = 'T' ABSENT = 'A' STATUS_CHOICES = [ (PRESENT, 'Present'), (TARDY, 'Tardy'), (ABSENT, 'Absent'), ] school_day = models.ForeignKey(SchoolDay, on_delete=models.CASCADE) student = models.ForeignKey(Student, on_delete=models.CASCADE) status = models.CharField( max_length=1, choices=STATUS_CHOICES, default=PRESENT ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def get_absolute_url(self): return reverse('core:schoolday-detail', kwargs={ 'year': self.school_day.school_year.year, 'pk': self.school_day.pk, 'entry_pk': self.pk }) def __str__(self): return f"{self.school_day} | {self.student} | {self.status}" class Meta: verbose_name_plural = 'entries' ordering = ['student']