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.
143 lines
6.2 KiB
143 lines
6.2 KiB
from django.db import models
|
|
|
|
from course_service.maps.exeptions import MapTypeError
|
|
from lms.global_decorators import transaction_decorator
|
|
|
|
|
|
class CourseRoute(models.Model):
|
|
"""
|
|
Объединение нескольких мап курса, одназначно
|
|
определяет способ прохождения по курсу.
|
|
"""
|
|
out_key = models.CharField(max_length=15, unique=True, verbose_name="Ключ для внешних сервисов", editable=False)
|
|
name = models.CharField(max_length=255, verbose_name='Имя шаблона', blank=True, null=True)
|
|
is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')
|
|
|
|
def is_finish(self, user):
|
|
return len([1 for i in self.pivotcoursemap_set.all() if i.map_course.is_finish(user)]) > 0
|
|
|
|
def get_active_objects(self, user):
|
|
return (i.map_course.get_active_object(user) for i in self.pivotcoursemap_set.all()) \
|
|
if not self.is_finish(user) else []
|
|
|
|
def get_maps(self):
|
|
return (i.map_course for i in self.pivotcoursemap_set.all())
|
|
|
|
def get_first(self):
|
|
return self.pivotcoursemap_set.first().map_course
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Meta:
|
|
verbose_name = 'Маршрут прохождения'
|
|
verbose_name_plural = 'Маршруты прохождения'
|
|
|
|
|
|
class CourseMap(models.Model):
|
|
"""
|
|
Способы отображения курса. Упорядочены в порядке возрастания приоретета.
|
|
"""
|
|
course = models.ForeignKey(to='courses.Course', verbose_name='К какому курсу привязан')
|
|
name = models.CharField(max_length=255, verbose_name="Имя прохождения", default="Линейное прохождение")
|
|
|
|
def get_next(self, vertex):
|
|
pivot = self.pivotvertex_set.get(vertex=vertex)
|
|
pivots = self.pivotvertex_set.filter(sort__gt=pivot.sort).exclude(vertex__content_type__model='topic')
|
|
return pivots.first().vertex if pivots.exists() else None
|
|
|
|
@transaction_decorator
|
|
def add_vertex(self, vertex, sort):
|
|
if sort > self.pivotvertex_set.count() + 1:
|
|
raise ValueError("list index out of range")
|
|
for i in self.pivotvertex_set.filter(sort__gte=sort):
|
|
i.sort += 1
|
|
i.save()
|
|
|
|
pivot = PivotVertex.objects.create(vertex=vertex, sort=sort, map_course=self)
|
|
pivot.save()
|
|
return pivot
|
|
|
|
def get_objects(self, vertex=None):
|
|
if vertex and not self.course == vertex.course:
|
|
raise MapTypeError('''Переданный узел принадлежит курсу "%s", а должен принадлежать курсу "%s"'''
|
|
% (self.course.title, vertex.course.title))
|
|
|
|
full_list = [i.vertex for i in self.pivotvertex_set.all()]
|
|
|
|
if vertex:
|
|
idx = full_list.index(vertex) + 1
|
|
return full_list[:idx]
|
|
|
|
return full_list
|
|
|
|
def get_tree(self, serializer=None):
|
|
from course_service.courses.serializers import MiniVertexSerializer
|
|
serializer = serializer if serializer is not None else MiniVertexSerializer
|
|
|
|
def helper(v_list):
|
|
new_list = []
|
|
for i in v_list:
|
|
val = serializer(i).data
|
|
new_list.append(val)
|
|
if i.children.all().count():
|
|
val['children'] = helper(i.children.all())
|
|
return new_list
|
|
|
|
return helper([i.vertex for i in self.pivotvertex_set.filter(vertex__vertex__isnull=True)])
|
|
|
|
def get_difference(self, user) -> list:
|
|
success_list = user.progress_set.get(course=self.course).progress_list.all()
|
|
all_vertex = [i.vertex for i in self.pivotvertex_set.all()]
|
|
res = list(set(all_vertex).difference(set(success_list)))
|
|
return res
|
|
|
|
def is_finish(self, user) -> bool:
|
|
return self.get_difference(user) == []
|
|
|
|
def get_active_object(self, user):
|
|
try:
|
|
return self.pivotvertex_set.exclude(
|
|
vertex__content_type__model='topic'
|
|
).filter(vertex__in=self.get_difference(user)).first().vertex
|
|
except AttributeError:
|
|
return None
|
|
|
|
def __str__(self):
|
|
return '''Линейное прохождение по курсу "%s"''' % self.course.title
|
|
|
|
class Meta:
|
|
verbose_name = 'Карта линейного прохождения курсов'
|
|
verbose_name_plural = 'Карты линейного прохождения курсов'
|
|
|
|
|
|
class PivotCourseMap(models.Model):
|
|
route = models.ForeignKey(to=CourseRoute, verbose_name="К какому узлу")
|
|
sort = models.SmallIntegerField(verbose_name='Порядок сортировки')
|
|
map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортеровке имеетотношение', blank=True,
|
|
null=True)
|
|
|
|
def __str__(self):
|
|
return '''Карта с №%s по маршруту ID%s''' % (self.sort, self.route_id)
|
|
|
|
class Meta:
|
|
verbose_name = 'Порядок сортировки маршрута'
|
|
verbose_name_plural = 'Порядки сортировок маршрутов'
|
|
unique_together = (('map_course', 'route'), ('sort', 'route'),)
|
|
ordering = ('sort',)
|
|
|
|
|
|
class PivotVertex(models.Model):
|
|
vertex = models.ForeignKey(to='courses.Vertex', verbose_name="К какому узлу")
|
|
sort = models.SmallIntegerField(verbose_name='Порядок сортировки')
|
|
map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортеровке имеетотношение', blank=True,
|
|
null=True)
|
|
|
|
def __str__(self):
|
|
return '''Карта с №%s по линейному прохождению ID%s''' % (self.sort, self.map_course_id)
|
|
|
|
class Meta:
|
|
verbose_name = 'Порядок сортировки узла'
|
|
verbose_name_plural = 'Порядки сортировок узла'
|
|
unique_together = (('map_course', 'vertex'), ('sort', 'map_course'))
|
|
ordering = ('sort',)
|
|
|