from django.db import models from lms.global_decorators import transaction_decorator from maps.exeptions import MapTypeError from django.db.models import Q class CourseRoute(models.Model): """ Объединение нескольких мап курса, одназначно определяет способ прохождения по курсу. """ 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) return self.pivotvertex_set.filter(sort__gt=pivot.sort).exclude(vertex__content_type__model='topic').first().vertex @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 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',)