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.
 
 
 
 
 
 

368 lines
12 KiB

import json
from jwt import DecodeError
from rest_framework.parsers import MultiPartParser
from courses.models import Course, Lesson, Topic, upload_material, COURSE_LEVEL, COURSE_DIRECTION
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
from lms.tools import get_real_name
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,)
parser_classes = (MultiPartParser,)
status_code = 200
@staticmethod
def bool_transformer(val):
if val == 'false':
return False
if val == 'true':
return True
return val
@staticmethod
def get_img(request, key):
image = request.FILES.get(key)
if image is None:
image = request.POST.get(key)
image = None if image is None else image.split('media/')[-1]
return image
def post(self, request):
title = request.POST.get('title')
slug = request.POST.get('slug')
old_slug = request.POST.get('old_slug')
if title is None:
return Response("title не передан", status=400)
course = None
if old_slug:
try:
course = Course.objects.get(slug=old_slug)
except Course.DoesNotExist:
pass
if course is None and slug:
try:
course = Course.objects.get(slug=slug)
except Course.DoesNotExist:
pass
if course is None:
course = Course()
if slug:
course.slug = slug
course.title = title
level = request.POST.get('level')
direction = request.POST.get('direction')
description = request.POST.get('description')
course.producer = request.POST.get('producer', 'skillbox')
if description:
course.description = description
if level:
course.level = get_real_name(COURSE_LEVEL, level)
if direction:
course.direction = get_real_name(COURSE_DIRECTION, direction)
course.teacher_tokens = request.POST.get('teacher_tokens', '').split(',')
course.hidden = self.bool_transformer(request.POST.get('hidden', False))
course.public = self.bool_transformer(request.POST.get('public', False))
course.image = self.get_img(request, 'image')
course.big_image = self.get_img(request, 'big_image')
course.big_mobile_image = self.get_img(request, 'big_mobile_image')
course.save()
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,)
#TODO убрать эти костыли
@staticmethod
def bool_transformer(val):
if val == 'false':
return False
if val == 'true':
return True
return val
@staticmethod
def none_transformer(val):
if val == 'null':
return None
if val == 'undefined':
return None
return val
def post(self, request):
lesson_token = self.none_transformer(request.POST.get('token', None))
sort = self.none_transformer(request.POST.get('sort', None))
topic_id = self.none_transformer(request.POST.get('topic', None))
title = self.none_transformer(request.POST.get('title', None))
description = self.none_transformer(request.POST.get('description', None))
video = self.none_transformer(request.POST.get('video', None))
materials_url = self.none_transformer(request.POST.get('materials_url', ''))
free = self.none_transformer(self.bool_transformer(request.POST.get('free', None)))
is_hm = self.none_transformer(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 = []
if not materials_url == '':
materials_url_media = json.loads(materials_url)
for material_url_media in materials_url_media:
materials.append(material_url_media.split(settings.MEDIA_URL)[1])
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)