diff --git a/access/urls.py b/access/urls.py index c4cdb3d..5378687 100644 --- a/access/urls.py +++ b/access/urls.py @@ -15,7 +15,6 @@ urlpatterns = [ url(r'login/$', views.LoginView.as_view()), url(r'logout/$', views.LogoutView.as_view()), url(r'reset/$', views.ResetPasswordView.as_view()), - url(r'progress/$', progress.views.UpdateProgress.as_view()), url(r'progress_detail/upload/(?P[0-9A-Fa-f-]+)/$', progress.views.UploadCourseProgressUserView.as_view()), url(r'progress_detail/(?P[0-9A-Fa-f-]+)/$', progress.views.CourseProgressUserView.as_view()), url(r'progress_dynamic/(?P[0-9A-Fa-f-]+)/$', progress.views.CourseProgressDynamicView.as_view()), diff --git a/api_v1/urls.py b/api_v1/urls.py index 0208884..0dcd8e6 100644 --- a/api_v1/urls.py +++ b/api_v1/urls.py @@ -6,4 +6,5 @@ urlpatterns = [ url(r'library/', include('library.urls')), url(r'finance/', include('finance.urls')), url(r'storage/', include('storage.urls')), + url(r'progress/', include('progress.urls')), ] \ No newline at end of file diff --git a/courses/models.py b/courses/models.py index bf0ad21..10cd8e0 100755 --- a/courses/models.py +++ b/courses/models.py @@ -6,7 +6,6 @@ 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 model_utils import Choices from lms.tools import decode_base64, get_real_name @@ -144,7 +143,14 @@ class Course(models.Model): except IndexError: pass - def get_first(self) -> Lesson: + def get_previous(self, lesson: Lesson): + lessons = self.get_lesson_list() + try: + return lessons[lessons.index(lesson) - 2] + except IndexError: + pass + + def get_first_lesson(self) -> Lesson: lessons = self.get_lesson_list() try: return lessons[0] diff --git a/courses/views.py b/courses/views.py index 5cf60a8..1000b5c 100644 --- a/courses/views.py +++ b/courses/views.py @@ -4,6 +4,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer +from progress.models import ProgressLesson class TreeView(APIView): @@ -39,15 +40,20 @@ class LessonDetail(APIView): def get(request, token): try: - vertex = Lesson.objects.get(token=token) + lesson = Lesson.objects.get(token=token) except Lesson.DoesNotExist: - return Response("Vertex doesn't exist", status=404) + return Response("Lesson doesn't exist", status=404) - # if not vertex.check_vertex(request.user): - # return Response("permission denied", status=403) - # TODO: Доделать систему прав на курс + if not lesson.free and not ProgressLesson.objects.filter(lesson_token=lesson.token).exists(): + previous_lesson = lesson.topic.course.get_previous(lesson) - res = LessonSerializer(vertex).data + if not previous_lesson is None or not ProgressLesson.objects.filter( + lesson_token=previous_lesson.token, status=ProgressLesson.STATUSES.done).exists(): + return Response("Lesson doesn't access", status=403) + + # TODO: Доделать систему прав на курс + + res = LessonSerializer(lesson).data # progress = vertex.course.progress_set.filter(user=request.user) # try: # if progress.exists(): diff --git a/csv/final_perm.py b/csv/final_perm.py index 12d559a..769c29a 100644 --- a/csv/final_perm.py +++ b/csv/final_perm.py @@ -1,3 +1,4 @@ +import datetime import django import os import sys @@ -9,16 +10,68 @@ django.setup() from progress.models import ProgressLesson, Progress from courses.models import Course + if __name__ == '__main__': i = 0 for course in Course.objects.all(): - print(i) - i += 1 - first_lesson = course.get_first() + lessons = course.get_lesson_list() + + if not lessons: + continue + + first_lesson = lessons[0] for progress in Progress.objects.filter(course_token=course.token): - if not progress.progresslesson_set.count() and not first_lesson is None: - ProgressLesson.objects.get( + print(i) + i += 1 + progress_lessons = progress.progresslesson_set.all() + if progress_lessons.count() == 0: + ProgressLesson.objects.create( progress=progress, lesson_token=first_lesson.token, checker=progress.teacher if first_lesson.is_hm else progress.user, ) + continue + + lesson_find = False + for l in lessons[::-1]: + pl = progress_lessons.filter(lesson_token=l.token) + is_done = pl.exists() and pl[0].status == ProgressLesson.STATUSES.done + + if is_done and not lesson_find: + lesson_find = True + next_lesson = course.get_next(l) + if next_lesson is None: + progress.is_finish = True + progress.save() + + if not progress_lessons.filter(lesson_token=next_lesson.token,).exists(): + ProgressLesson.objects.create( + progress=progress, + lesson_token=next_lesson.token, + checker=progress.teacher if next_lesson.is_hm else progress.user, + ) + continue + + elif not lesson_find: + continue + + elif not pl.exists(): + ProgressLesson.objects.create( + progress=progress, + lesson_token=l.token, + checker=progress.teacher if l.is_hm else progress.user, + status=ProgressLesson.STATUSES.done, + finish_date=datetime.datetime.now(), + ) + + elif not is_done: + omg = pl[0] + omg.status = ProgressLesson.STATUSES.done + omg.finish_date = datetime.datetime.now() + omg.save() + + elif is_done: + continue + + else: + raise ValueError("что-то пошло не так") diff --git a/progress/migrations/0004_progress_is_finish.py b/progress/migrations/0004_progress_is_finish.py new file mode 100644 index 0000000..1070cc1 --- /dev/null +++ b/progress/migrations/0004_progress_is_finish.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-02-13 11:49 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('progress', '0003_remove_progress_active_lesson'), + ] + + operations = [ + migrations.AddField( + model_name='progress', + name='is_finish', + field=models.BooleanField(default=False, verbose_name='Окончен ли курс'), + ), + ] diff --git a/progress/models.py b/progress/models.py index b9261ce..8bf49c5 100644 --- a/progress/models.py +++ b/progress/models.py @@ -14,6 +14,7 @@ class Progress(models.Model): related_name='teacher_progress') user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент') course_token = models.UUIDField(verbose_name="Токен курса", editable=False) + is_finish = models.BooleanField(verbose_name="Окончен ли курс", default=False) def progress_status(self, sorted_token_list): """ @@ -46,12 +47,13 @@ class Progress(models.Model): class ProgressLesson(models.Model): + STATUSES = Choices('start', 'done', 'wait', 'fail') + progress = models.ForeignKey(to=Progress) lesson_token = models.UUIDField(verbose_name="Токен урока", editable=False) checker = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь", ) finish_date = models.DateTimeField(verbose_name='Дата зачтения задания', blank=True, null=True) start_date = models.DateTimeField(verbose_name='Дата начала прохождения задания', auto_now_add=True) - STATUSES = Choices('start', 'done', 'wait', 'fail') status = models.CharField(choices=STATUSES, default=STATUSES.start, max_length=20) comment_tokens = ArrayField(models.UUIDField(verbose_name="Токен комента", editable=False), default=[]) diff --git a/progress/serializers.py b/progress/serializers.py index 00a32a9..ac0c0dd 100644 --- a/progress/serializers.py +++ b/progress/serializers.py @@ -34,11 +34,16 @@ class ProgressAnalyticSerializer(serializers.ModelSerializer): class ProgressLessonSerializer(serializers.ModelSerializer): teacher = serializers.SerializerMethodField() + student = serializers.SerializerMethodField() class Meta: model = ProgressLesson - exclude = ('id', 'progress') + exclude = ('id', 'progress', 'checker') @staticmethod def get_teacher(self): - return self.checker.get_full_name() \ No newline at end of file + return self.checker.get_full_name() + + @staticmethod + def get_student(self): + return self.progress.user.get_full_name() \ No newline at end of file diff --git a/progress/urls.py b/progress/urls.py index e69de29..cfba5ec 100644 --- a/progress/urls.py +++ b/progress/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls import url + +from progress import views + +urlpatterns = [ + url(r'students/(?P[0-9A-Fa-f-]+)/$', views.StudentWorkView.as_view()), + url(r'$', views.UpdateProgress.as_view()), +] \ No newline at end of file diff --git a/progress/views.py b/progress/views.py index 82477db..74bc81d 100644 --- a/progress/views.py +++ b/progress/views.py @@ -8,6 +8,7 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView +from django.db.models import Q from progress.models import ProgressLesson, Progress from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer @@ -15,6 +16,26 @@ from courses.api import CourseProgressApi from progress.tasks import add_next_lesson +class StudentWorkView(APIView): + renderer_classes = (JSONRenderer,) + + @staticmethod + def get(request, teacher_token): + if request.user.is_authenticated() and request.user.groups.filter(name__in=['teachers', 'admin']).exists(): + try: + progress_lessons = ProgressLesson.objects.filter( + ~Q(progress__user__out_key=teacher_token), + Q(status=ProgressLesson.STATUSES.wait) | + Q(status=ProgressLesson.STATUSES.fail), + checker__out_key=teacher_token, + ) + return Response([ProgressLessonSerializer(i).data for i in progress_lessons], status=200) + except ValidationError: + return Response("Bad request", status=400) + + return Response(status=403) + + class CourseProgressDynamicView(APIView): renderer_classes = (JSONRenderer,) @@ -120,14 +141,12 @@ class UpdateProgress(APIView): pv.save() - pl = None + res = {"current": ProgressLessonSerializer(pv).data} if pv.status == ProgressLesson.STATUSES.done: # TODO: Ассинхроннаязадача для celery - pl = add_next_lesson(p) + res['next'] = add_next_lesson(p) - return Response({ - "current": ProgressLessonSerializer(pv).data, - "next": ProgressLessonSerializer(pl).data}, status=200) + return Response(res, status=200) except Progress.DoesNotExist: return Response('Не найден прогресс по заданным параметрам', status=404)