280 lines
7.2 KiB
Python
280 lines
7.2 KiB
Python
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']
|