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.
 
 
 
 
 
 

240 lines
9.0 KiB

# -*- coding: utf-8 -*-
import random
import string
import unidecode
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template.defaultfilters import slugify
from course_service.maps.models import CourseRoute, CourseMap
from lms.global_decorators import transaction_decorator
from lms.tools import decode_base64, get_real_name
COURSE_LEVEL = (
('B', 'Базовый'),
('A', 'Продвинутый'),
('E', 'Экспертный'),
('B+A', 'Базовый + Продвинутый'),
)
COURSE_DIRECTION = (
(3, 'Бизнес'),
(2, 'Веб-дизайн'),
(1, 'Разработка'),
(4, 'Рисование'),
)
class CourseManager(models.Manager):
def update_or_create_course(self, image=None, big_image=None, id=0,
big_mobile_image=None, slug=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)
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
course = self.create(**kwargs)
return course
def change_route(self, pk, route):
route = CourseRoute.objects.get(id=route)
course = self.get(id=pk)
course.route = route
course.save()
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)
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 = ArrayField(
models.CharField(max_length=15, blank=True, verbose_name='Ссылки на преподов'), default=[])
route = models.OneToOneField(to=CourseRoute, verbose_name="Порядок прохождения по умолчанию", blank=True, null=True)
def __str__(self):
return self.title
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)
description = models.TextField(verbose_name='Описание', blank=True, null=True)
course = models.ForeignKey(to=Course, verbose_name='курс')
class Meta:
verbose_name = "Тема"
verbose_name_plural = "Темы"
class Vertex(models.Model):
VALID_TYPE = (
(3, 'Автаматическая валидация'),
(2, 'Полуавтаматическая валидация'),
(1, 'Ручная валидация'),
(0, 'Без валидации'),
)
topic = models.ForeignKey(to=Topic, verbose_name='Тема')
title = models.CharField(verbose_name='Название', max_length=255)
free = models.BooleanField(default=True, verbose_name='Привилегии для узла не будут проверяться')
description = models.TextField(verbose_name='Описание', blank=True, null=True)
video = models.TextField(verbose_name='Код видео', default='', blank=True, null=True)
materials = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Материалы урока'))
valid_type = models.SmallIntegerField(choices=VALID_TYPE, default=0)
token = models.CharField(max_length=15, verbose_name="Ключ доступа к узлу", unique=True)
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 = "Уроки"