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.
 
 
 
 
 
 

270 lines
8.7 KiB

from datetime import datetime, time
from urllib.parse import urlparse
from django.conf import settings
from django.db import models
from django.contrib.auth import get_user_model
from django.db.models.expressions import RawSQL
from django.urls import reverse_lazy
from django.utils import timezone
from django.contrib.postgres.fields import ArrayField
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToCover
from polymorphic.models import PolymorphicModel
User = get_user_model()
class ImageObject(models.Model):
image = models.ImageField('Изображение', upload_to='content/imageobject')
image_thumbnail = ImageSpecField(source='image',
processors=[ResizeToCover(300, 200, False)],
format='JPEG',
options={'quality': 85})
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Объект изображения'
verbose_name_plural = 'Объекты изображения'
ordering = ('-created_at',)
class Content(PolymorphicModel):
uuid = models.UUIDField(null=True, blank=True)
course = models.ForeignKey(
'course.Course', on_delete=models.CASCADE,
null=True, blank=True,
verbose_name='Курс',
related_name='content',
)
lesson = models.ForeignKey(
'course.Lesson', on_delete=models.CASCADE,
null=True, blank=True,
verbose_name='Урок',
related_name='content',
)
live_lesson = models.ForeignKey(
'school.LiveLesson', on_delete=models.CASCADE,
null=True, blank=True,
verbose_name='Урок онлайн школы',
related_name='content',
)
contest = models.ForeignKey(
'Contest', on_delete=models.CASCADE,
null=True, blank=True,
verbose_name='Конкурс',
related_name='content',
)
title = models.CharField('Заголовок', max_length=100, default='')
position = models.PositiveSmallIntegerField(
'Положение на странице',
default=1,
)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Контент'
verbose_name_plural = 'Контент'
ordering = ('position', '-created_at',)
def ctype(self):
return self.__class__.__name__.lower()
class Image(Content):
img = models.ForeignKey(
ImageObject, related_name='content_images',
verbose_name='Объект изображения', on_delete=models.CASCADE,
null=True
)
class Text(Content):
txt = models.TextField('Текст', default='')
class ImageText(Content):
img = models.ForeignKey(
ImageObject, related_name='content_imagetexts',
verbose_name='Объект изображения', on_delete=models.CASCADE,
null=True
)
txt = models.TextField('Текст', default='')
class Video(Content):
url = models.URLField('Ссылка')
def video_index(self):
if self.is_youtube_video:
url = urlparse(self.url)
query = url.query.split('&')
for q in query:
if 'v=' in q:
return q.split('=')[-1]
return self.url.split('/')[-1]
@property
def is_youtube_video(self):
return 'youtu.be' in self.url or 'youtube.com' in self.url and 'watch' in self.url
@property
def is_vimeo_video(self):
return 'vimeo.com' in self.url
class Gallery(Content):
pass
class GalleryImage(models.Model):
gallery = models.ForeignKey(
Gallery, on_delete=models.CASCADE,
verbose_name='Галерея', related_name='gallery_images'
)
img = models.ForeignKey(
ImageObject, related_name='gallery_images',
verbose_name='Объект изображения', on_delete=models.CASCADE,
null=True, blank=True,
)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Изображение в галерее'
verbose_name_plural = 'Изображения в галерее'
ordering = ('-created_at',)
class Banner(models.Model):
PAGE_INDEX = 1
PAGE_COURSES = 2
PAGE_SCHOOL = 3
PAGE_PACKAGES = 4
PAGE_CHOICES = (
(PAGE_INDEX, 'Главная'),
(PAGE_COURSES, 'Курсы'),
(PAGE_SCHOOL, 'Школа'),
(PAGE_PACKAGES, 'Пакеты')
)
text = models.TextField(blank=True, default='')
button_text = models.CharField(max_length=50, blank=True, default='')
url = models.URLField(blank=True, default='')
image = models.ImageField()
use = models.BooleanField(default=False)
color = models.CharField(max_length=7, blank=True, default='')
color2 = models.CharField(max_length=7, blank=True, default='')
stretch_image = models.BooleanField(default=True)
future_date = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
pages = ArrayField(models.IntegerField(choices=PAGE_CHOICES), default=[], blank=True)
main_banner = ArrayField(models.IntegerField(choices=PAGE_CHOICES), default=[], blank=True)
class Meta:
verbose_name = 'Банер'
verbose_name_plural = 'Банеры'
ordering = ('-created_at',)
@classmethod
def get_for_page(cls, page):
return Banner.objects.filter(use=True, pages__contains=[page]).annotate(
is_main=RawSQL('main_banner @> %s', ([page],))
).order_by('-is_main')
@property
def is_video_url(self):
return self.url and ('vimeo.com' in self.url
or 'youtube.com' in self.url and 'watch' in self.url
or 'youtu.be' in self.url)
class Contest(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(max_length=1000, blank=True, default='')
slug = models.SlugField(
allow_unicode=True, null=True, blank=True,
max_length=100, unique=True, db_index=True,
)
cover = models.ForeignKey(
ImageObject, related_name='contest_covers',
verbose_name='Фоновая картинка', on_delete=models.CASCADE,
null=True, blank=True,
)
date_start = models.DateField('Дата начала', null=True, blank=True)
date_end = models.DateField('Дата окончания', null=True, blank=True)
active = models.BooleanField(default=True)
# TODO? banner
@property
def finished(self):
# FIXME
return datetime(2018, 8, 29, 21) < timezone.now()
def save(self, *args, **kwargs):
if self.active:
Contest.objects.filter(active=True).update(active=False)
return super().save(*args, **kwargs)
class ContestWork(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE
)
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
image = models.ForeignKey(
ImageObject, related_name='contest_work_images',
verbose_name='Работа участника', on_delete=models.CASCADE,
)
child_full_name = models.CharField(max_length=255)
age = models.SmallIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField('course.Like', blank=True)
class Meta:
verbose_name = 'Конкурсная работа'
verbose_name_plural = 'Конкурсные работы'
ordering = ('-created_at',)
@property
def img_width(self):
return self.image.image.width if self.image and self.image.image else None
@property
def img_height(self):
return self.image.image.height if self.image and self.image.image else None
def get_absolute_url(self):
return reverse_lazy('contest_work', args=[self.id])
class FAQ(models.Model):
question = models.TextField(max_length=1000,)
answer = models.TextField(max_length=1000,)
class Package(models.Model):
price = models.DecimalField(max_digits=10, decimal_places=2,)
high_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
description = models.TextField(
'Описание', default='', db_index=True
)
duration = models.PositiveSmallIntegerField()
options = models.TextField(
'Опции', default='', db_index=True
)
class Meta:
ordering = ('duration',)
@property
def options_html(self):
return ''.join(map(lambda x: '<p>%s</p>' % x, self.options.split('\n')))