text: from django.db import models
  import math
  from django.core.validators import MinValueValidator, MaxValueValidator
  from django.contrib.auth.models import AbstractUser
  from django.db.models.signals import post_save, post_delete
  from datetime import timedelta, date
  
  # Create your models here.
  sex_choice = (
      ('Male', 'Male'),
      ('Female', 'Female')
  )
  
  time_slots = (
      ('7:30 - 8:30', '7:30 - 8:30'),
      ('8:30 - 9:30', '8:30 - 9:30'),
      ('9:30 - 10:30', '9:30 - 10:30'),
      ('11:00 - 11:50', '11:00 - 11:50'),
      ('11:50 - 12:40', '11:50 - 12:40'),
      ('12:40 - 1:30', '12:40 - 1:30'),
      ('2:30 - 3:30', '2:30 - 3:30'),
      ('3:30 - 4:30', '3:30 - 4:30'),
      ('4:30 - 5:30', '4:30 - 5:30'),
  )
  
  DAYS_OF_WEEK = (
      ('Monday', 'Monday'),
      ('Tuesday', 'Tuesday'),
      ('Wednesday', 'Wednesday'),
      ('Thursday', 'Thursday'),
      ('Friday', 'Friday'),
      ('Saturday', 'Saturday'),
  )
  
  test_name = (
      ('Internal test 1', 'Internal test 1'),
      ('Internal test 2', 'Internal test 2'),
      ('Internal test 3', 'Internal test 3'),
      ('Event 1', 'Event 1'),
      ('Event 2', 'Event 2'),
      ('Semester End Exam', 'Semester End Exam'),
  )
  
  
  class User(AbstractUser):
      @property
      def is_student(self):
          if hasattr(self, 'student'):
              return True
          return False
  
      @property
      def is_teacher(self):
          if hasattr(self, 'teacher'):
              return True
          return False
  
  
  class Dept(models.Model):
      id = models.CharField(primary_key='True', max_length=100)
      name = models.CharField(max_length=200)
  
      def __str__(self):
          return self.name
  
  
  class Course(models.Model):
      dept = models.ForeignKey(Dept, on_delete=models.CASCADE)
      id = models.CharField(primary_key='True', max_length=50)
      name = models.CharField(max_length=50)
      shortname = models.CharField(max_length=50, default='X')
  
      def __str__(self):
          return self.name
  
  
  class Class(models.Model):
      # courses = models.ManyToManyField(Course, default=1)
      id = models.CharField(primary_key='True', max_length=100)
      dept = models.ForeignKey(Dept, on_delete=models.CASCADE)
      section = models.CharField(max_length=100)
      sem = models.IntegerField()
  
      class Meta:
          verbose_name_plural = 'classes'
  
      def __str__(self):
          d = Dept.objects.get(name=self.dept)
          return '%s : %d %s' % (d.name, self.sem, self.section)
  
  
  class Student(models.Model):
      user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
      class_id = models.ForeignKey(Class, on_delete=models.CASCADE, default=1)
      USN = models.CharField(primary_key='True', max_length=100)
      name = models.CharField(max_length=200)
      sex = models.CharField(max_length=50, choices=sex_choice, default='Male')
      DOB = models.DateField(default='1998-01-01')
  
      def __str__(self):
          return self.name
  
  
  class Teacher(models.Model):
      user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
      id = models.CharField(primary_key=True, max_length=100)
      dept = models.ForeignKey(Dept, on_delete=models.CASCADE, default=1)
      name = models.CharField(max_length=100)
      sex = models.CharField(max_length=50, choices=sex_choice, default='Male')
      DOB = models.DateField(default='1980-01-01')
  
      def __str__(self):
          return self.name
  
  
  class Assign(models.Model):
      class_id = models.ForeignKey(Class, on_delete=models.CASCADE)
      course = models.ForeignKey(Course, on_delete=models.CASCADE)
      teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
  
      class Meta:
          unique_together = (('course', 'class_id', 'teacher'),)
  
      def __str__(self):
          cl = Class.objects.get(id=self.class_id_id)
          cr = Course.objects.get(id=self.course_id)
          te = Teacher.objects.get(id=self.teacher_id)
          return '%s : %s : %s' % (te.name, cr.shortname, cl)
  
  
  class AssignTime(models.Model):
      assign = models.ForeignKey(Assign, on_delete=models.CASCADE)
      period = models.CharField(max_length=50, choices=time_slots, default='11:00 - 11:50')
      day = models.CharField(max_length=15, choices=DAYS_OF_WEEK)
  
  
  class AttendanceClass(models.Model):
      assign = models.ForeignKey(Assign, on_delete=models.CASCADE)
      date = models.DateField()
      status = models.IntegerField(default=0)
  
  
  class Attendance(models.Model):
      course = models.ForeignKey(Course, on_delete=models.CASCADE)
      student = models.ForeignKey(Student, on_delete=models.CASCADE)
      attendanceclass = models.ForeignKey(AttendanceClass, on_delete=models.CASCADE, default=1)
      date = models.DateField(default='2018-10-23')
      status = models.BooleanField(default='True')
  
      def __str__(self):
          sname = Student.objects.get(name=self.student)
          cname = Course.objects.get(name=self.course)
          return '%s : %s' % (sname.name, cname.shortname)
  
  
  class AttendanceTotal(models.Model):
      course = models.ForeignKey(Course, on_delete=models.CASCADE)
      student = models.ForeignKey(Student, on_delete=models.CASCADE)
  
      class Meta:
          unique_together = (('student', 'course'),)
  
      @property
      def att_class(self):
          stud = Student.objects.get(name=self.student)
          cr = Course.objects.get(name=self.course)
          att_class = Attendance.objects.filter(course=cr, student=stud, status='True').count()
          return att_class
  
      @property
      def total_class(self):
          stud = Student.objects.get(name=self.student)
          cr = Course.objects.get(name=self.course)
          total_class = Attendance.objects.filter(course=cr, student=stud).count()
          return total_class
  
      @property
      def attendance(self):
          stud = Student.objects.get(name=self.student)
          cr = Course.objects.get(name=self.course)
          total_class = Attendance.objects.filter(course=cr, student=stud).count()
          att_class = Attendance.objects.filter(course=cr, student=stud, status='True').count()
          if total_class == 0:
              attendance = 0
          else:
              attendance = round(att_class / total_class * 100, 2)
          return attendance
  
      @property
      def classes_to_attend(self):
          stud = Student.objects.get(name=self.student)
          cr = Course.objects.get(name=self.course)
          total_class = Attendance.objects.filter(course=cr, student=stud).count()
          att_class = Attendance.objects.filter(course=cr, student=stud, status='True').count()
          cta = math.ceil((0.75*total_class - att_class)/0.25)
          if cta < 0:
              return 0
          return cta
  
  
  class StudentCourse(models.Model):
      student = models.ForeignKey(Student, on_delete=models.CASCADE)
      course = models.ForeignKey(Course, on_delete=models.CASCADE)
  
      class Meta:
          unique_together = (('student', 'course'),)
          verbose_name_plural = 'Marks'
  
      def __str__(self):
          sname = Student.objects.get(name=self.student)
          cname = Course.objects.get(name=self.course)
          return '%s : %s' % (sname.name, cname.shortname)
  
      def get_cie(self):
          marks_list = self.marks_set.all()
          m = []
          for mk in marks_list:
              m.append(mk.marks1)
          cie = math.ceil(sum(m[:5])/2)
          return cie
  
      def get_attendance(self):
          a = AttendanceTotal.objects.get(student=self.student, course=self.course)
          return a.attendance
  
  
  class Marks(models.Model):
      studentcourse = models.ForeignKey(StudentCourse, on_delete=models.CASCADE)
      name = models.CharField(max_length=50, choices=test_name, default='Internal test 1')
      marks1 = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)])
  
      class Meta:
          unique_together = (('studentcourse', 'name'),)
  
      @property
      def total_marks(self):
          if self.name == 'Semester End Exam':
              return 100
          return 20
  
  
  class MarksClass(models.Model):
      assign = models.ForeignKey(Assign, on_delete=models.CASCADE)
      name = models.CharField(max_length=50, choices=test_name, default='Internal test 1')
      status = models.BooleanField(default='False')
  
      class Meta:
          unique_together = (('assign', 'name'),)
  
      @property
      def total_marks(self):
          if self.name == 'Semester End Exam':
              return 100
          return 20
  
  
  # Triggers
  
  
  def daterange(start_date, end_date):
      for n in range(int ((end_date - start_date).days)):
          yield start_date + timedelta(n)
  
  days = {
      'Monday': 1,
      'Tuesday': 2,
      'Wednesday': 3,
      'Thursday': 4,
      'Friday': 5,
      'Saturday': 6,
  }
  
  
  def create_attendance(sender, instance, **kwargs):
      if kwargs['created']:
          start_date = date(2018, 8, 1)
          end_date = date(2018, 11, 30)
          for single_date in daterange(start_date, end_date):
              if single_date.isoweekday() == days[instance.day]:
                  try:
                      a = AttendanceClass.objects.get(date=single_date.strftime("%Y-%m-%d"), assign=instance.assign)
                  except AttendanceClass.DoesNotExist:
                      a = AttendanceClass(date=single_date.strftime("%Y-%m-%d"), assign=instance.assign)
                      a.save()
  
  
  def create_marks(sender, instance, **kwargs):
      if kwargs['created']:
          if hasattr(instance, 'name'):
              ass_list = instance.class_id.assign_set.all()
              for ass in ass_list:
                  try:
                      StudentCourse.objects.get(student=instance, course=ass.course)
                  except StudentCourse.DoesNotExist:
                      sc = StudentCourse(student=instance, course=ass.course)
                      sc.save()
                      sc.marks_set.create(name='Internal test 1')
                      sc.marks_set.create(name='Internal test 2')
                      sc.marks_set.create(name='Internal test 3')
                      sc.marks_set.create(name='Event 1')
                      sc.marks_set.create(name='Event 2')
                      sc.marks_set.create(name='Semester End Exam')
          elif hasattr(instance, 'course'):
              stud_list = instance.class_id.student_set.all()
              cr = instance.course
              for s in stud_list:
                  try:
                      StudentCourse.objects.get(student=s, course=cr)
                  except StudentCourse.DoesNotExist:
                      sc = StudentCourse(student=s, course=cr)
                      sc.save()
                      sc.marks_set.create(name='Internal test 1')
                      sc.marks_set.create(name='Internal test 2')
                      sc.marks_set.create(name='Internal test 3')
                      sc.marks_set.create(name='Event 1')
                      sc.marks_set.create(name='Event 2')
                      sc.marks_set.create(name='Semester End Exam')
  
  
  def create_marks_class(sender, instance, **kwargs):
      if kwargs['created']:
          for name in test_name:
              try:
                  MarksClass.objects.get(assign=instance, name=name[0])
              except MarksClass.DoesNotExist:
                  m = MarksClass(assign=instance, name=name[0])
                  m.save()
  
  
  def delete_marks(sender, instance, **kwargs):
      stud_list = instance.class_id.student_set.all()
      StudentCourse.objects.filter(course=instance.course, student__in=stud_list).delete()
  
  
  post_save.connect(create_marks, sender=Student)
  post_save.connect(create_marks, sender=Assign)
  post_save.connect(create_marks_class, sender=Assign)
  post_save.connect(create_attendance, sender=AssignTime)
  post_delete.connect(delete_marks, sender=Assign)
  