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.
 
 
 
 
 
 

251 lines
9.6 KiB

# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
import json
import unidecode
from django.template.defaultfilters import slugify
from maps.models import CourseRoute, CourseMap
from lms.tools import decode_base64, get_real_name
from lms.global_decorators import transaction_decorator
from library.models import Tags
from storage.models import File
import random
COURSE_LEVEL = (
('B', 'Базовый'),
('A', 'Продвинутый'),
('E', 'Экспертный'),
('B+A', 'Базовый + Продвинутый'),
)
COURSE_DIRECTION = (
(3, 'Бизнес'),
(2, 'Веб-дизайн'),
(1, 'Разработка'),
(4, 'Рисование'),
)
class CourseManager(models.Manager):
@transaction_decorator
def update_or_create_course(self, image=None, big_image=None, id=0, route=None,
big_mobile_image=None, mentors=None, slug=None,
teachers=None, level=None, direction=None, **kwargs):
slug = slug if slug else slugify(unidecode.unidecode(kwargs['title']))
if image:
kwargs['image'] = decode_base64(image, 'course/image%s.png' % slug)
if big_image:
kwargs['big_image'] = decode_base64(big_image, 'course/big_image%s.png' % slug)
if big_mobile_image:
kwargs['big_mobile_image'] = decode_base64(big_mobile_image, 'course/big_mobile_image%s.png' % slug)
if level:
kwargs['level'] = get_real_name(COURSE_LEVEL, level)
if direction:
kwargs['direction'] = get_real_name(COURSE_DIRECTION, direction)
if route:
kwargs['route'] = CourseRoute.objects.get(id=route)
try:
course = self.get(id=id)
for i in kwargs:
if kwargs[i]:
setattr(course, i, kwargs[i])
course.save()
except ObjectDoesNotExist:
kwargs['slug'] = slug
kwargs['route'] = CourseRoute.objects.create(name="Основной шаблон")
course = self.create(**kwargs)
CourseMap.objects.create(course=course)
if mentors:
for email in mentors:
course.mentors.add(get_user_model().objects.get(email=email))
if teachers:
for email in teachers:
course.teachers.add(get_user_model().objects.get(email=email))
return course
class Course(models.Model):
hidden = models.BooleanField(verbose_name='Видно только оплатившим', default=False)
level = models.CharField(verbose_name='Уровень', choices=COURSE_LEVEL, default='B', max_length=3)
slug = models.SlugField(max_length=255, blank=True, default='', unique=True, editable=False)
direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление', null=True)
sort = models.SmallIntegerField(null=True, verbose_name="Порядок сортировки")
public = models.BooleanField(verbose_name='Опубликовать', default=False)
title = models.CharField(verbose_name="Заголовок", max_length=255)
description = models.TextField(verbose_name='Описание', blank=True)
image = models.URLField(verbose_name='Изображение', blank=True, max_length=255)
big_image = models.URLField(verbose_name='Большое изображение', blank=True, max_length=255)
big_mobile_image = models.URLField(verbose_name='Под мобилку', blank=True, null=True,
help_text='Большая картинка для мобильной версии', max_length=255)
teachers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели',
related_name='course_teachers')
route = models.OneToOneField(to=CourseRoute, verbose_name="Порядок прохождения по умолчанию", blank=True, null=True)
def __str__(self):
return self.title
def get_teacher(self):
return random.choice(self.teachers.all())
def get_maps(self, user):
return user.progress_set.get(course=self).get_template().get_maps()
def get_vertexes(self, vertex_type=None):
if vertex_type:
return self.vertex_set.filter(content_type__model=vertex_type, course=self)
return self.vertex_set.filter(course=self)
def get_statistic(self):
"""
Минималистичная статистика по уроку,
количество тем, уроков, домашек.
"""
return {
"topic_count": self.get_vertexes('topic').count(),
"tutorial_count": self.get_vertexes('tutorial').count(),
"task_count": self.get_vertexes('task').count(),
}
def get_first(self, vertex_model_list=None):
if vertex_model_list is None:
vertex_model_list = ['topic', 'tutorial', 'task']
else:
for i in vertex_model_list:
if i not in ['topic', 'tutorial', 'task']:
raise ValueError('undefined model: ' + i)
vertex = Vertex.objects.get(id=self.coursemap_set.get(sort=0).get_first())
if vertex.content_type.model in vertex_model_list:
return vertex
return vertex.get_next(vertex_model_list)
def get_last(self, vertex_model_list=None):
if vertex_model_list is None:
vertex_model_list = ['topic', 'tutorial', 'task']
else:
for i in vertex_model_list:
if i not in ['topic', 'tutorial', 'task']:
raise ValueError('undefined model: ' + i)
vertex = Vertex.objects.get(id=self.coursemap_set.get(sort=0).get_last())
if vertex.content_type.model in vertex_model_list:
return vertex
return vertex.get_previous(vertex_model_list)
objects = CourseManager()
class Meta:
verbose_name = "Курс"
verbose_name_plural = "Курсы"
class Topic(models.Model):
title = models.CharField(verbose_name='Название', max_length=255)
icon = models.ImageField(verbose_name='Иконка темы', null=True, blank=True)
course = models.ForeignKey(to=Course)
class Meta:
verbose_name = "Тема"
verbose_name_plural = "Темы"
class Vertex(models.Model):
VALID_TYPE = (
(3, 'Автаматическая валидация'),
(2, 'Полуавтаматическая валидация'),
(1, 'Ручная валидация'),
(0, 'Без валидации'),
)
topic = models.ForeignKey(to=Topic)
title = models.CharField(verbose_name='Название', max_length=255)
free = models.BooleanField(default=True, verbose_name='Привилегии для узла не будут проверяться')
description = models.TextField(verbose_name='Описание', default='', blank=True, null=True)
video = models.TextField(verbose_name='Код видео', default='', blank=True, null=True)
materials = models.ManyToManyField(File, verbose_name='Материалы урока', blank=True)
valid_type = models.SmallIntegerField(choices=VALID_TYPE, default=0)
def __str__(self):
return self.title
def get_next(self, route):
return route.get_first().get_next(self)
def get_previous(self, vertex_model_list=None):
if vertex_model_list is None:
vertex_model_list = ['topic', 'tutorial', 'task']
else:
for i in vertex_model_list:
if i not in ['topic', 'tutorial', 'task']:
raise ValueError('undefined model: ' + i)
vertex_id = CourseMap.objects.get(course=self.course).get_previous(self.id)
vertex = Vertex.objects.get(id=int(vertex_id), )
if vertex.content_type.model in vertex_model_list:
return vertex
return vertex.get_previous(vertex_model_list)
def is_more(self, vertex) -> bool:
if not self.course == vertex.course:
raise ValueError('Vertexes of different course')
course_map = CourseMap.objects.get(course=self.course)
return course_map.map_to_list().index(self.id) > course_map.map_to_list().index(vertex.id)
def check_vertex(self, user) -> bool:
if self.free:
return True
if not user.is_authenticated:
return False
try:
progress = self.course.progress_set.get(user=user)
except ObjectDoesNotExist:
return False
return progress.is_access(self)
def get_number(self, vertex_type=None):
"""
Возврощает порядковый номер узла с определённым типом.
Пример мы хотим определит какой по счёту теме принадлежит конкретно взятый урок.
"""
vertex_list = list(self.course.get_vertexes(vertex_type))
try:
res = vertex_list.index(self)
except ValueError:
parents = self.vertex_set.all()
if parents.count() == 1:
res = vertex_list.index(parents[0])
else:
res = None
return res
class Meta:
verbose_name = "Урок"
verbose_name_plural = "Уроки"