from django.conf import settings from django.core.validators import FileExtensionValidator from django.db import models from django.db.models import Q from django.db.models.signals import pre_save, post_delete, post_save from django.dispatch import receiver from django.urls import reverse from django.utils.translation import gettext_lazy as _ from core.models import ActivityLog, Semester from core.utils import unique_slug_generator class ProgramManager(models.Manager): def search(self, query=None): queryset = self.get_queryset() if query: or_lookup = Q(title__icontains=query) | Q(summary__icontains=query) queryset = queryset.filter(or_lookup).distinct() return queryset class Program(models.Model): title = models.CharField(max_length=150, unique=True) summary = models.TextField(blank=True) objects = ProgramManager() def __str__(self): return f"{self.title}" def get_absolute_url(self): return reverse("program_detail", kwargs={"pk": self.pk}) @receiver(post_save, sender=Program) def log_program_save(sender, instance, created, **kwargs): verb = "created" if created else "updated" ActivityLog.objects.create(message=_(f"The program '{instance}' has been {verb}.")) @receiver(post_delete, sender=Program) def log_program_delete(sender, instance, **kwargs): ActivityLog.objects.create(message=_(f"The program '{instance}' has been deleted.")) class CourseManager(models.Manager): def search(self, query=None): queryset = self.get_queryset() if query: or_lookup = ( Q(title__icontains=query) | Q(summary__icontains=query) | Q(code__icontains=query) | Q(slug__icontains=query) ) queryset = queryset.filter(or_lookup).distinct() return queryset class Course(models.Model): slug = models.SlugField(unique=True, blank=True) title = models.CharField(max_length=200) code = models.CharField(max_length=200, unique=True) credit = models.IntegerField(default=0) summary = models.TextField(max_length=200, blank=True) program = models.ForeignKey(Program, on_delete=models.CASCADE) level = models.CharField(max_length=25, choices=settings.LEVEL_CHOICES) year = models.IntegerField(choices=settings.YEARS, default=1) semester = models.CharField(choices=settings.SEMESTER_CHOICES, max_length=200) is_elective = models.BooleanField(default=False) objects = CourseManager() def __str__(self): return f"{self.title} ({self.code})" def get_absolute_url(self): return reverse("course_detail", kwargs={"slug": self.slug}) @property def is_current_semester(self): current_semester = Semester.objects.filter(is_current_semester=True).first() return self.semester == current_semester.semester if current_semester else False @receiver(pre_save, sender=Course) def course_pre_save_receiver(sender, instance, **kwargs): if not instance.slug: instance.slug = unique_slug_generator(instance) @receiver(post_save, sender=Course) def log_course_save(sender, instance, created, **kwargs): verb = "created" if created else "updated" ActivityLog.objects.create(message=_(f"The course '{instance}' has been {verb}.")) @receiver(post_delete, sender=Course) def log_course_delete(sender, instance, **kwargs): ActivityLog.objects.create(message=_(f"The course '{instance}' has been deleted.")) class CourseAllocation(models.Model): lecturer = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="allocated_lecturer", ) courses = models.ManyToManyField(Course, related_name="allocated_course") session = models.ForeignKey( "core.Session", on_delete=models.CASCADE, blank=True, null=True ) def __str__(self): return self.lecturer.get_full_name def get_absolute_url(self): return reverse("edit_allocated_course", kwargs={"pk": self.pk}) class Upload(models.Model): title = models.CharField(max_length=100) course = models.ForeignKey(Course, on_delete=models.CASCADE) file = models.FileField( upload_to="course_files/", help_text=_( "Valid Files: pdf, docx, doc, xls, xlsx, ppt, pptx, zip, rar, 7zip" ), validators=[ FileExtensionValidator( [ "pdf", "docx", "doc", "xls", "xlsx", "ppt", "pptx", "zip", "rar", "7zip", ] ) ], ) updated_date = models.DateTimeField(auto_now=True) upload_time = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.title}" def get_extension_short(self): ext = self.file.name.split(".")[-1].lower() if ext in ("doc", "docx"): return "word" elif ext == "pdf": return "pdf" elif ext in ("xls", "xlsx"): return "excel" elif ext in ("ppt", "pptx"): return "powerpoint" elif ext in ("zip", "rar", "7zip"): return "archive" return "file" def delete(self, *args, **kwargs): self.file.delete(save=False) super().delete(*args, **kwargs) @receiver(post_save, sender=Upload) def log_upload_save(sender, instance, created, **kwargs): if created: message = _( f"The file '{instance.title}' has been uploaded to the course '{instance.course}'." ) else: message = _( f"The file '{instance.title}' of the course '{instance.course}' has been updated." ) ActivityLog.objects.create(message=message) @receiver(post_delete, sender=Upload) def log_upload_delete(sender, instance, **kwargs): ActivityLog.objects.create( message=_( f"The file '{instance.title}' of the course '{instance.course}' has been deleted." ) ) class UploadVideo(models.Model): title = models.CharField(max_length=100) slug = models.SlugField(unique=True, blank=True) course = models.ForeignKey(Course, on_delete=models.CASCADE) video = models.FileField( upload_to="course_videos/", help_text=_("Valid video formats: mp4, mkv, wmv, 3gp, f4v, avi, mp3"), validators=[ FileExtensionValidator(["mp4", "mkv", "wmv", "3gp", "f4v", "avi", "mp3"]) ], ) summary = models.TextField(blank=True) timestamp = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.title}" def get_absolute_url(self): return reverse( "video_single", kwargs={"slug": self.course.slug, "video_slug": self.slug} ) def delete(self, *args, **kwargs): self.video.delete(save=False) super().delete(*args, **kwargs) @receiver(pre_save, sender=UploadVideo) def video_pre_save_receiver(sender, instance, **kwargs): if not instance.slug: instance.slug = unique_slug_generator(instance) @receiver(post_save, sender=UploadVideo) def log_uploadvideo_save(sender, instance, created, **kwargs): if created: message = _( f"The video '{instance.title}' has been uploaded to the course '{instance.course}'." ) else: message = _( f"The video '{instance.title}' of the course '{instance.course}' has been updated." ) ActivityLog.objects.create(message=message) @receiver(post_delete, sender=UploadVideo) def log_uploadvideo_delete(sender, instance, **kwargs): ActivityLog.objects.create( message=_( f"The video '{instance.title}' of the course '{instance.course}' has been deleted." ) ) class CourseOffer(models.Model): """NOTE: Only department head can offer semester courses""" dep_head = models.ForeignKey("accounts.DepartmentHead", on_delete=models.CASCADE) def __str__(self): return str(self.dep_head)