You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

344 lines
12 KiB

import os
from decimal import Decimal
import math
from django.db import models
from django.db.models import TextChoices
from django.utils.translation import gettext_lazy as _
<<<<<<< HEAD
from filer.fields.image import FilerImageField
from filer.fields.file import FilerFileField
=======
>>>>>>> develop
from apps.account.models import ProfessorUser
from utils.schema import default_timing
from utils import generate_slug_for_model
def course_file_upload_to(instance, filename):
return os.path.join(f"courses/{instance.slug}/videos/{filename}")
<<<<<<< HEAD
def attachment_file_upload_to(instance, filename):
=======
def attachment_file_upload_to(instance, filename):
return os.path.join(f"attachments/{filename}")
def course_attachment_file_upload_to(instance, filename):
>>>>>>> develop
return os.path.join(f"courses/{instance.course.slug}/attachments/{filename}")
class CourseCategory(models.Model):
name = models.CharField(max_length=255, verbose_name='Category Name')
slug = models.SlugField(unique=True, max_length=255)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
<<<<<<< HEAD
self.slug = generate_slug_for_model(CourseCategory, self.name)
=======
if not self.slug:
self.slug = generate_slug_for_model(CourseCategory, self.name)
>>>>>>> develop
super().save(*args, **kwargs)
@property
def course_count(self):
return self.courses.exclude(status="inactive").count()
<<<<<<< HEAD
=======
>>>>>>> develop
class Course(models.Model):
class LevelChoices(TextChoices):
BEGINNER = 'beginner', 'Beginner'
MID = 'mid', 'Mid Level'
ADVANCED = 'advanced', 'Advanced'
class StatusChoices(TextChoices):
INACTIVE = 'inactive', 'Inactive' # Not Active (does not show)
UPCOMING = 'upcoming', 'Upcoming' # Upcoming (visible but registration not allowed)-Предстоящие
REGISTERING = 'registering', 'Registering' # Registering (registration is open)-регистрация
ONGOING = 'ongoing', 'Ongoing' # Ongoing (course has started, registration closed)-В процессе
FINISHED = 'finished', 'Finished' # Finished (course has ended)-закончился
class VedioTypeChoices(models.TextChoices):
<<<<<<< HEAD
VIDEO_FILE = 'video_file', 'Video File'
VIDEO_LINK = 'video_link', 'Video Link'
=======
YOUTUBE_LINK = 'youtube_link', 'Youtube Link'
VIDEO_FILE = 'video_file', 'Video File'
>>>>>>> develop
title = models.CharField(max_length=255, verbose_name='Course Title')
slug = models.SlugField(allow_unicode=True, unique=True)
category = models.ForeignKey(CourseCategory, on_delete=models.CASCADE, related_name='courses', verbose_name='Category')
professor = models.ForeignKey(
ProfessorUser,
on_delete=models.CASCADE,
related_name="courses"
)
<<<<<<< HEAD
thumbnail = FilerImageField(
related_name='+', on_delete=models.PROTECT, null=True, blank=True,
verbose_name=_('thumbnail')
)
video_type = models.CharField(max_length=20, choices=VedioTypeChoices.choices, verbose_name='Vedio Type')
=======
thumbnail = models.ImageField(upload_to="courses/thumbnails/", verbose_name=_('Thumbnail'))
video_type = models.CharField(
max_length=20,
choices=VedioTypeChoices.choices,
verbose_name='Preview Video Type (YouTube Link or File Upload)'
)
>>>>>>> develop
video_file = models.FileField(
upload_to=course_file_upload_to,
null=True,
blank=True
)
<<<<<<< HEAD
video_link = models.CharField(max_length=500, null=True, blank=True, verbose_name='Video Link')
is_online = models.BooleanField(default=True, verbose_name='Is Online Course')
=======
video_link = models.CharField(max_length=500, null=True, blank=True)
is_online = models.BooleanField(default=False, verbose_name='Is Online Course')
>>>>>>> develop
online_link = models.CharField(max_length=500, null=True, blank=True, verbose_name='Online Class Link')
level = models.CharField(max_length=10, choices=LevelChoices.choices, verbose_name='Course Level')
duration = models.PositiveIntegerField(verbose_name='Duration (in hours)')
lessons_count = models.PositiveIntegerField(verbose_name='Number of Lessons')
description = models.TextField(verbose_name='Course Description')
short_description = models.CharField(max_length=500, blank=True, null=True, verbose_name="Short Description")
status = models.CharField(max_length=15, choices=StatusChoices.choices, default=StatusChoices.INACTIVE, verbose_name='Course Status')
is_free = models.BooleanField(default=True, verbose_name='Is Free')
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='Course Price')
discount_percentage = models.PositiveIntegerField(default=0, verbose_name='Discount Percentage')
final_price = models.DecimalField(
verbose_name=_('Course Final Price'), decimal_places=2, max_digits=10, default=0.00, blank=True,
help_text=_('This field is automatically calculated based on the discount percentage.')
)
<<<<<<< HEAD
timing = models.JSONField(blank=True, null=True, default=default_timing, verbose_name=_("Timing"), help_text=_("The Timing information in JSON format."))
=======
timing = models.JSONField(blank=True, null=True, default=default_timing, verbose_name=_("Timing"))
>>>>>>> develop
features = models.JSONField(verbose_name=_('Course features'), default=dict, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
def __str__(self):
return self.title
def get_completed_lessons_count(self, student):
return self.lessons.filter(completions__student=student).count()
def is_student_participant(self, student):
return self.participants.filter(student=student).exists()
def save(self, *args, **kwargs):
<<<<<<< HEAD
self.slug = generate_slug_for_model(Course, self.title)
if self.discount_percentage > 0:
=======
if not self.slug:
self.slug = generate_slug_for_model(Course, self.title)
# Ensure consistency: if price is 0, set is_free to True and discount_percentage to 0
if self.price == 0:
self.is_free = True
self.discount_percentage = 0
self.final_price = Decimal('0.00')
elif self.is_free:
self.price = Decimal('0.00')
self.discount_percentage = 0
self.final_price = Decimal('0.00')
elif self.discount_percentage > 0:
>>>>>>> develop
discount_amount = (self.price * self.discount_percentage) / 100
final_price = self.price - discount_amount
self.final_price = Decimal(math.ceil(final_price)).quantize(Decimal('0.00'))
else:
self.final_price = Decimal(math.ceil(self.price)).quantize(Decimal('0.00'))
super().save(*args, **kwargs)
class Meta:
verbose_name = "Course"
verbose_name_plural = "Courses"
<<<<<<< HEAD
class Glossary(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='glossaries', verbose_name='Course')
title = models.CharField(max_length=555, verbose_name='Glossary Title')
description = models.TextField(verbose_name='Description')
def __str__(self):
return f"{self.course.title} - {self.title}"
class Meta:
ordering = ("-id",)
verbose_name = "Glossary"
verbose_name_plural = "Glossary"
class Attachment(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='attachments', verbose_name='Course')
=======
indexes = [
models.Index(fields=['status']),
models.Index(fields=['is_free']),
models.Index(fields=['created_at']),
models.Index(fields=['slug']),
models.Index(fields=['status', 'created_at']),
models.Index(fields=['category', 'status']),
models.Index(fields=['professor', 'status']),
]
class Glossary(models.Model):
"""
Base Glossary model that contains the actual content
"""
title = models.CharField(max_length=555, verbose_name='Glossary Title')
description = models.TextField(verbose_name='Description')
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
def __str__(self):
return self.title
class Meta:
verbose_name = "Glossary"
verbose_name_plural = "Glossaries"
class CourseGlossary(models.Model):
"""
Intermediate model that connects Course with Glossary
"""
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='glossaries', verbose_name='Course')
glossary = models.ForeignKey(Glossary, on_delete=models.CASCADE, related_name='course_glossaries', verbose_name='Glossary')
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
def __str__(self):
return f"{self.course.title} - {self.glossary.title}"
@property
def title(self):
return self.glossary.title
@property
def description(self):
return self.glossary.description
class Meta:
ordering = ("-id",)
verbose_name = "Course Glossary"
verbose_name_plural = "Course Glossaries"
class Attachment(models.Model):
"""
Base Attachment model that contains the actual file
"""
>>>>>>> develop
title = models.CharField(max_length=255, verbose_name='Attachment Title')
file = models.FileField(
upload_to=attachment_file_upload_to,
verbose_name='Attachment File'
)
<<<<<<< HEAD
file_size = models.PositiveIntegerField(verbose_name='File Size (in bytes)', null=True, blank=True)
=======
file_size = models.PositiveIntegerField(verbose_name='File Size (in bytes)', null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
>>>>>>> develop
def save(self, *args, **kwargs):
# Calculate the file size before saving
if self.file and not self.file_size:
self.file_size = self.file.size
super().save(*args, **kwargs)
<<<<<<< HEAD
def __str__(self):
return f"{self.course.title} - {self.title}"
class Meta:
ordering = ("-id",)
verbose_name = "Attachment"
verbose_name_plural = "Attachments"
=======
def __str__(self):
return self.title
class Meta:
verbose_name = "Attachment"
verbose_name_plural = "Attachments"
class CourseAttachment(models.Model):
"""
Intermediate model that connects Course with Attachment
"""
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='attachments', verbose_name='Course')
attachment = models.ForeignKey(Attachment, on_delete=models.CASCADE, related_name='course_attachments', verbose_name='Attachment')
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
def __str__(self):
return f"{self.course.title} - {self.attachment.title}"
@property
def title(self):
return self.attachment.title
@property
def file(self):
return self.attachment.file
@property
def file_size(self):
return self.attachment.file_size
class Meta:
ordering = ("-id",)
verbose_name = "Course Attachment"
verbose_name_plural = "Course Attachments"
indexes = [
models.Index(fields=['course']),
models.Index(fields=['attachment']),
]
>>>>>>> develop