import base64 import os from jwt import DecodeError from rest_framework.parsers import MultiPartParser from courses.models import Course, Lesson, Topic, upload_material from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer from rest_framework.response import Response from rest_framework.views import APIView from django.core.files.storage import default_storage from django.core.files.base import ContentFile from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer, TeacherLessonSerializer import jwt from courses.tasks import add_lesson from lms import settings class TreeView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 def get(self, request, slug): try: course = Course.objects.get(slug=slug) return Response(CourseTreeSerializer(course).data, self.status_code) except Course.DoesNotExist: return Response("Course doesn't exist", status=404) class CourseListView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 def post(self, request): course = Course.objects.update_or_create_course(**request.JSON.dict()) return Response(CourseDetailSerializer(course).data, status=self.status_code) def get(self, request): """ This API endpoint return list of courses. --- """ res = [CourseDetailSerializer(course).data for course in Course.objects.all()] return Response(res, self.status_code) class CourseDetailView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 @staticmethod def delete(request, slug): try: Course.objects.get(slug=slug).delete() except Course.DoesNotExist: return Response("Курса не существует", status=404) return Response(status=204) def get(self, request, slug): return Response(CourseDetailSerializer(Course.objects.get(slug=slug)).data, self.status_code) class DeleteTopicView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def delete(request, topic_id): if request.user and request.user.is_staff: try: t = Topic.objects.get(id=topic_id) except Topic.DoesNotExist: return Response("Темы не существует", status=404) t.delete() return Response(CourseTreeSerializer(t.course).data, status=200) class DeleteLessonView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def delete(request, lesson_token): if request.user and request.user.is_staff: try: l = Lesson.objects.get(token=lesson_token) except Lesson.DoesNotExist: return Response("Темы не существует", status=404) l.delete() return Response(CourseTreeSerializer(l.topic.course).data, status=200) class UpdateLessonView(APIView): renderer_classes = (JSONRenderer, BrowsableAPIRenderer) parser_classes = (MultiPartParser,) @staticmethod def bool_transformer(val): if val == 'false': return False if val == 'true': return True return val def post(self, request): lesson_token = request.POST.get('token', None) sort = request.POST.get('sort', None) topic_id = request.POST.get('topic', None) title = request.POST.get('title', None) description = request.POST.get('description', None) video = request.POST.get('video', None) materials_url = request.POST.get('materials_url', '') free = self.bool_transformer(request.POST.get('free', None)) is_hm = self.bool_transformer(request.POST.get('is_hm', None)) if topic_id is None: return Response("topic не передан", status=400) if sort is None: return Response("sort не передан", status=400) try: topic = Topic.objects.get(id=topic_id) except Topic.DoesNotExist: return Response("Тема не найдена", status=404) if lesson_token is None: if title is None: return Response("Название урока не переданно", status=400) for lesson in reversed(topic.lesson_set.filter(sort__gte=sort)): lesson.sort = lesson.sort + 1 lesson.save() l = Lesson.objects.create( title=title, topic=topic, sort=sort, ) else: try: l = Lesson.objects.get(token=lesson_token) except Lesson.DoesNotExist: return Response("Урок не найден", status=404) l.title = l.title if title is None else title l.video = l.video if video is None else video l.free = l.free if free is None else free l.is_hm = l.is_hm if is_hm is None else is_hm l.description = l.description if description is None else description if not l.sort == sort: for lesson in reversed(topic.lesson_set.filter(sort__gte=sort)): lesson.sort = lesson.sort + 1 lesson.save() l.sort = sort materials = list(l.materials) if not materials_url == '': materials = base64.b64decode(materials_url).decode("utf-8").split(',') for key in request.FILES: f = request.FILES[key] path = default_storage.save(upload_material(l, f.name), ContentFile(f.read())) materials.append(path) l.materials = materials l.save() return Response(CourseTreeSerializer(topic.course).data, status=200) class UpdateTopicView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def post(request): topic_id = request.JSON.get('id', None) sort = request.JSON.get('sort', None) course_token = request.JSON.get('course_token', None) title = request.JSON.get('title', None) if course_token is None: return Response("Не передан course_token", status=400) if sort is None: return Response("Не передан sort", status=400) try: course = Course.objects.get(token=course_token) except Course.DoesNotExist: return Response("Курс не найден", status=404) try: if topic_id: t = Topic.objects.get(id=topic_id) if not t.sort == sort: for topic in reversed(course.topic_set.filter(sort__gte=sort)): topic.sort = topic.sort + 1 topic.save() t.sort = sort t.title = t.title if title is None else title t.save() else: raise Topic.DoesNotExist() except Topic.DoesNotExist: if title is None: return Response("Не передан title", status=400) for topic in reversed(course.topic_set.filter(sort__gte=sort)): topic.sort = topic.sort + 1 topic.save() Topic.objects.create( course=course, title=title, sort=sort, ) return Response(CourseTreeSerializer(course).data, status=200) class LessonInfoView(APIView): renderer_classes = (JSONRenderer,) status_code = 200 def get(self, request, token): try: lesson = Lesson.objects.get(token=token) except Lesson.DoesNotExist: return Response('Урок не найден', status=404) if request.user.is_authenticated: return Response(TeacherLessonSerializer(lesson).data, self.status_code) return Response("Пользователь не является преподователем по курсу", status=403) class LessonDetail(APIView): renderer_classes = (JSONRenderer,) @staticmethod def post(request, token): jwt_token = request.JSON.get('jwt_token', None) try: lesson = Lesson.objects.get(token=token) except Lesson.DoesNotExist: return Response("Урока не существует", status=404) l = LessonSerializer(lesson).data try: payload = None if jwt_token is None \ else jwt.decode(jwt_token, settings.COURSE_PROGRESS_SECRET_KEY, algorithms=['HS256']) except DecodeError: payload = None course = lesson.topic.course if payload is None: if not (lesson.free or request.user.is_authenticated and request.user.is_staff): return Response("Bad token", status=400) else: return Response(l, status=200) prev_lesson = course.get_previous(lesson, (lambda x: not x.is_hm) if payload['only_watch'] else None) next_lesson = course.get_next(lesson, (lambda x: not x.is_hm) if payload['only_watch'] else None) if not prev_lesson is None: l['prev_token'] = prev_lesson.token if not next_lesson is None: l['next_token'] = next_lesson.token new_lesson = False for payload_lesson in payload['lessons']: if payload_lesson['lesson_token'] == str(lesson.token): return Response(l, status=200) if not prev_lesson is None and str(prev_lesson.token) == payload_lesson['lesson_token']: new_lesson = True if prev_lesson is None else \ (payload_lesson['status'] == "done" or payload_lesson['status'] == "wait") if not new_lesson: return Response("Permission denied", status=403) # TODO Задача для селери add_lesson(request.user.out_key, course.token, lesson.token, lesson.is_hm) return Response(l, status=200)