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.
 
 
 
 
 
 

260 lines
12 KiB

# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
import json
from library.models import Tags
from management.models import Comment
from storage.models import Storage
class MaterialDirection(models.Model):
title = models.CharField(verbose_name=u'Заголовок', max_length=255)
color = models.CharField(verbose_name=u'Цвет', max_length=50)
description = models.TextField(verbose_name=u'Описание', blank=True)
mentors = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Кураторы')
def __str__(self):
return self.title
class Meta:
verbose_name = u'Направление'
verbose_name_plural = u'Направления'
class Course(models.Model):
COURSE_LEVEL = (
('B', u'Базовый'),
('A', u'Продвинутый'),
('E', u'Экспертный'),
('B+A', u'Базовый + Продвинутый')
)
hidden = models.BooleanField(verbose_name=u'Видно только оплатившим', default=False)
level = models.CharField(verbose_name=u'Уровень', choices=COURSE_LEVEL, default='B', max_length=3)
slug = models.SlugField(max_length=255, editable=False, blank=True, default='', unique=True)
direction = models.ForeignKey(MaterialDirection, verbose_name=u'Направление', null=True)
mentors = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Кураторы', blank=True,
related_name='course_mentors')
public = models.BooleanField(verbose_name=u'Опубликовать', default=False)
title = models.CharField(verbose_name=u"Заголовок", max_length=255)
description = models.TextField(verbose_name=u'Описание', blank=True)
image = models.ImageField(verbose_name=u'Изображение', upload_to='course', blank=True)
big_image = models.ImageField(verbose_name=u'Большое изображение', upload_to='course', blank=True)
big_mobile_image = models.ImageField(verbose_name=u'Под мобилку', upload_to='course', blank=True, null=True,
help_text=u'Большая картинка для мобильной версии')
teachers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name=u'Преподаватели',
related_name='course_teachers')
def __str__(self):
return self.title
def get_tree(self, serializer):
"""
Способ отображения дочерних элементов.
Принимает на вход сериалайзер узла
"""
course_map = json.loads(CourseMap.objects.get(course=self).dependent_elements)
def helper(tree_id):
acc = []
for j, i in enumerate(tree_id):
if type([]) == type(i):
acc[-1]['children'] = helper(i)
else:
acc.append(serializer(Vertex.manager.get(id=i)).data)
return acc
return helper(course_map)
def get_statistic(self):
"""
Минималистичная статистика по уроку,
количество тем, уроков, домашек.
"""
topic_count = Vertex.objects.filter(course=self, content_type__model='topic').count()
task_count = Vertex.objects.filter(course=self, content_type__model='task').count()
tutorial_count = Vertex.objects.filter(course=self, content_type__model='tutorial').count()
return {"topic_count": topic_count, "tutorial_count": tutorial_count, "task_count": task_count}
class Meta:
verbose_name = u"Курс"
verbose_name_plural = u"Курсы"
ordering = ['sort']
class Skills(models.Model):
title = models.CharField(verbose_name=u'Наименование', max_length=255)
color = models.CharField(verbose_name=u'Цвет', max_length=255)
icon = models.ImageField(verbose_name=u'Большая картинка', upload_to='skills', null=True, help_text='65x65')
description = models.TextField(verbose_name=u'Описание', blank=True)
def __str__(self): return '%s' % self.title
class Meta:
verbose_name = u'Навык'
verbose_name_plural = u'Навыки'
class SkillJ(models.Model):
skill = models.ForeignKey(to="Skills", verbose_name=u'Навык')
lesson = models.ForeignKey(to="Vertex", verbose_name=u'Урок')
size = models.IntegerField(verbose_name=u'Размер', default=0)
def __str__(self): return '%s %s' % (self.skill, self.size)
class Meta:
verbose_name = u'Размер навыка'
verbose_name_plural = u'Размеры навыков'
ordering = ['id']
class Achievements(models.Model):
course = models.ForeignKey(to="Course")
icon = models.ImageField(verbose_name='Отображение достижения', upload_to='diplomas', blank=True, null=True)
user = models.ForeignKey(to=settings.AUTH_USER_MODEL)
def __str__(self):
return 'Студенту %s за курс %s' % (self.user.username, self.course.title)
class Meta:
verbose_name = u'Достижение'
verbose_name_plural = u'Достижения'
class Diploma(models.Model):
icon = models.ImageField(verbose_name=u'Иконка', upload_to='diplomas')
course = models.ForeignKey(to=Course)
user = models.ForeignKey(to=settings.AUTH_USER_MODEL)
def __str__(self):
return 'Студенту %s за курс %s' % (self.user.username, self.course.title)
class Meta:
verbose_name = 'Диплом'
verbose_name_plural = 'Дипломы'
class VertexManager(models.Manager):
# Менеджер вершин графа.
def create_with_dependencies(self, model, course, old_id, title, description,
free=True, materials=None, **kwargs):
content_type = ContentType.objects.get(app_label='courses', model=model)
obj, _is_create = content_type.model_class().objects.update_or_create(**kwargs)
[obj.materials.add(i) for i in materials] if materials else None
res, _is_create = self.update_or_create(
content_type=content_type,
object_id=obj.id,
course=course,
title=title,
description=description,
free=free,
old_id=old_id,
)
return res
class Vertex(models.Model):
"""
Основная структурная единица узел графа курса.
Позволяет работать со структурой курса на более высоком уровне абстракции.
"""
course = models.ForeignKey(to=Course)
title = models.CharField(verbose_name=u'Название', max_length=255)
free = models.BooleanField(default=True, verbose_name=u'Привилегии для узла не будут проверяться')
description = models.TextField(verbose_name=u'Описание', default='', blank=True, null=True)
children = models.ManyToManyField(to='Vertex', blank=True)
content_type = models.ForeignKey(to=ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
objects = VertexManager()
def __str__(self):
return self.title + ': ' + str(self.content_type.model)
def get_next(self):
vertex_id = CourseMap.objects.get(course=self.course).get_next(self.id)
return Vertex.objects.get(id=int(vertex_id), )
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)
# Модели нового API со временем всё, что выше будет выпилено
class Tutorial(Vertex):
"""
Модель урока.
Урок может быть открыт для комментирования и закрыт, по дефолту открыт,
вероятно закрывать нужно будет крайне редко.
Видео к уроку фрейм который лежит прямо в базе, конечно же костыль и со временем
мы уйдём от этого, все видео хостятся на двух онлайн сервисах на клиент нужно передовать
id и сервис на котором, это дело хостится, а правило отображения оставить клиенту.
Материалы для урока по сути FileField, нужна только для создания лишней связи в таблице
и дублирования метазаголовков файла
"""
on_comment = models.BooleanField(verbose_name=u'Комментарии', default=True)
video = models.TextField(verbose_name=u'Код видео', default='', blank=True)
materials = models.ManyToManyField(Storage, verbose_name=u'Материалы урока', blank=True)
class Task(Vertex):
"""
Модель таска.
Исторически сложилось, что на сервере хостятся два типа тасков отличающихся лишь наименованием
домашние работы и экзамены, не нужно быть гением, чтобы понять для чего нужно булево значение
is_exam
Материалы для урока по сути FileField, нужна только для создания лишней связи в таблице
и дублирования метазаголовков файла
"""
materials = models.ManyToManyField(Storage, verbose_name=u'Материалы для домашней работы', blank=True)
is_exam = models.BooleanField(default=False, verbose_name=u'Экзамен или домашка')
class Topic(Vertex):
"""
Модель темы, нужно просто для объединения тасков и уроков.
У некоторых тем есть иконка.
Возможно поле icon перекачует в Vertex, а данная модель отвалится за ненадобностью
"""
icon = models.ImageField(verbose_name=u'Иконка темы', upload_to='CourseTheme', null=True, blank=True)
class CourseMap(models.Model):
"""
Так как курс евляется связным графом мы можем отобразить его бесконечным количеством способов,
а нам нужен один самый красивый, мы должный в явном виде указать способ отображения.
"""
course = models.OneToOneField(to=Course)
dependent_elements = models.TextField(default='[]')
independent_elements = models.TextField(default='[]')
def map_to_list(self) -> list:
def helper(root_list):
res = []
for i in root_list:
if type(i) == type([]):
res += helper(i)
else:
res.append(i)
return res
return helper(json.loads(self.dependent_elements))
def get_next(self, vertex_id) -> str:
res_list = self.map_to_list()
try:
return res_list[res_list.index(vertex_id)]
except ValueError:
raise ValueError("vertex_id " + str(vertex_id) + " not in list " + ",".join(res_list))