Merge branch 'dev' into 'master'

Dev

See merge request !221
master
Andrey 8 years ago
commit f265006d98
  1. 11
      access/serializers.py
  2. 6
      access/urls.py
  3. 21
      courses/models.py
  4. 5
      courses/serializers.py
  5. 13
      courses/tasks.py
  6. 2
      courses/urls.py
  7. 79
      courses/views.py
  8. 11
      finance/views.py
  9. 2
      lms/settings.py
  10. 6
      lms/urls.py
  11. 23
      progress/serializers.py
  12. 61
      progress/views.py
  13. 22
      storage/views.py

@ -1,10 +1,9 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from rest_framework.generics import get_object_or_404
from access.models.other import Account from access.models.other import Account
from achievements.serialers import DiplomaSerializer, AchievementsSerializer from achievements.serialers import DiplomaSerializer, AchievementsSerializer
from progress.serializers import ProgressSerializer from progress.serializers import SecureProgressSerializer
class AccountSerializer(serializers.ModelSerializer): class AccountSerializer(serializers.ModelSerializer):
@ -22,13 +21,13 @@ class AccountSerializer(serializers.ModelSerializer):
class UserSelfSerializer(serializers.ModelSerializer): class UserSelfSerializer(serializers.ModelSerializer):
account = serializers.SerializerMethodField() account = serializers.SerializerMethodField()
groups = serializers.SerializerMethodField() groups = serializers.SerializerMethodField()
progress = serializers.SerializerMethodField() progresses = serializers.SerializerMethodField()
diplomas = serializers.SerializerMethodField() diplomas = serializers.SerializerMethodField()
achievements = serializers.SerializerMethodField() achievements = serializers.SerializerMethodField()
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = ('out_key', 'email', 'first_name', 'last_name', 'progress', 'achievements', fields = ('out_key', 'email', 'first_name', 'last_name', 'progresses', 'achievements',
'account', 'groups', 'is_staff', 'is_superuser', 'diplomas', 'is_active') 'account', 'groups', 'is_staff', 'is_superuser', 'diplomas', 'is_active')
@staticmethod @staticmethod
@ -48,8 +47,8 @@ class UserSelfSerializer(serializers.ModelSerializer):
return [group.name for group in self.groups.all()] return [group.name for group in self.groups.all()]
@staticmethod @staticmethod
def get_progress(self): def get_progresses(self):
return [ProgressSerializer(i).data for i in self.progress_set.all()] return [SecureProgressSerializer(i).data for i in self.progress_set.all()]
class UserProfileSerializer(serializers.ModelSerializer): class UserProfileSerializer(serializers.ModelSerializer):

@ -18,10 +18,6 @@ urlpatterns = [
url(r'logout/$', views.LogoutView.as_view()), url(r'logout/$', views.LogoutView.as_view()),
url(r'reset/$', views.ResetPasswordView.as_view()), url(r'reset/$', views.ResetPasswordView.as_view()),
url(r'progress_detail/upload/(?P<token>[0-9A-Fa-f-]+)/$', progress.views.UploadCourseProgressUserView.as_view()), url(r'progress_detail/upload/(?P<token>[0-9A-Fa-f-]+)/$', progress.views.UploadCourseProgressUserView.as_view()),
url( url(r'management/password/$', views.ManagementPassword.as_view(), name='management-password')
r'management/password/$',
views.ManagementPassword.as_view(),
name='management-password'
)
] ]

@ -154,19 +154,26 @@ class Course(models.Model):
lesson_list += list(topic.lesson_set.all()) lesson_list += list(topic.lesson_set.all())
return lesson_list return lesson_list
def get_next(self, lesson: Lesson) -> Lesson: def get_next(self, lesson: Lesson, f=None) -> Lesson:
lessons = self.get_lesson_list() lessons = self.get_lesson_list()
try: try:
return lessons[lessons.index(lesson)] n = lessons[lessons.index(lesson)+1]
if f is None or f(n):
return n
else:
return self.get_next(n, f)
except IndexError: except IndexError:
pass pass
def get_previous(self, lesson: Lesson): def get_previous(self, lesson: Lesson, f=None):
lessons = self.get_lesson_list() lessons = self.get_lesson_list()
try: idx = lessons.index(lesson) - 1
return lessons[lessons.index(lesson) - 2] if idx > -1:
except IndexError: prev = lessons[idx]
pass if f is None or f(prev):
return prev
else:
return self.get_previous(prev, f)
def get_first_lesson(self) -> Lesson: def get_first_lesson(self) -> Lesson:
lessons = self.get_lesson_list() lessons = self.get_lesson_list()

@ -23,11 +23,16 @@ class MiniLessonSerializer(serializers.ModelSerializer):
class LessonSerializer(MiniLessonSerializer): class LessonSerializer(MiniLessonSerializer):
course_slug = serializers.SerializerMethodField()
class Meta: class Meta:
model = Lesson model = Lesson
exclude = ('id', 'topic', 'key') exclude = ('id', 'topic', 'key')
@staticmethod
def get_course_slug(self):
return self.topic.course.slug
class TeacherLessonSerializer(MiniLessonSerializer): class TeacherLessonSerializer(MiniLessonSerializer):
topic_sort = serializers.SerializerMethodField() topic_sort = serializers.SerializerMethodField()

@ -0,0 +1,13 @@
from progress.models import ProgressLesson, Progress
from django.contrib.auth import get_user_model
def add_lesson(user_out_key: str, course_token: str, lesson_token: str, teacher_key: str, is_hm: bool):
p = Progress.objects.get(course_token=course_token, user__out_key=user_out_key)
ProgressLesson.objects.get_or_create(
progress=p,
lesson_token=lesson_token,
checker=get_user_model().objects.get(out_key=(teacher_key if is_hm else user_out_key)),
)

@ -3,8 +3,8 @@ from django.conf.urls import url
from courses import views as views from courses import views as views
urlpatterns = [ urlpatterns = [
url(r'vertex/(?P<token>.+)/$', views.LessonDetail.as_view()),
url(r'lesson/teacher/(?P<token>.+)/$', views.LessonInfoView.as_view()), url(r'lesson/teacher/(?P<token>.+)/$', views.LessonInfoView.as_view()),
url(r'lesson/(?P<token>.+)/$', views.LessonDetail.as_view()),
url(r'tree/(?P<slug>.+)/$', views.TreeView.as_view()), url(r'tree/(?P<slug>.+)/$', views.TreeView.as_view()),
url(r'detail/(?P<slug>.+)/$', views.CourseDetailView.as_view()), url(r'detail/(?P<slug>.+)/$', views.CourseDetailView.as_view()),
url(r'^$', views.CourseListView.as_view()), url(r'^$', views.CourseListView.as_view()),

@ -1,3 +1,5 @@
from jwt import DecodeError
from courses.models import Course, Lesson from courses.models import Course, Lesson
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response from rest_framework.response import Response
@ -5,7 +7,10 @@ from rest_framework.views import APIView
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer, TeacherLessonSerializer from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer, TeacherLessonSerializer
from progress.models import ProgressLesson import jwt
from courses.tasks import add_lesson
from lms import settings
class TreeView(APIView): class TreeView(APIView):
@ -93,33 +98,53 @@ class LessonDetail(APIView):
renderer_classes = (JSONRenderer,) renderer_classes = (JSONRenderer,)
@staticmethod @staticmethod
def get(request, token): def post(request, token):
jwt_token = request.JSON.get('jwt_token', None)
try: try:
lesson = Lesson.objects.get(token=token) lesson = Lesson.objects.get(token=token)
except Lesson.DoesNotExist: except Lesson.DoesNotExist:
return Response("Lesson doesn't exist", status=404) return Response("Урока не существует", status=404)
if not lesson.free and not ProgressLesson.objects.filter(lesson_token=lesson.token).exists(): l = LessonSerializer(lesson).data
previous_lesson = lesson.topic.course.get_previous(lesson)
try:
if not previous_lesson is None or not ProgressLesson.objects.filter( payload = None if jwt_token is None\
lesson_token=previous_lesson.token, status=ProgressLesson.STATUSES.done).exists(): else jwt.decode(jwt_token, settings.COURSE_PROGRESS_SECRET_KEY, algorithms=['HS256'])
return Response("Lesson doesn't access", status=403) except DecodeError:
payload = None
# TODO: Доделать систему прав на курс
course = lesson.topic.course
res = LessonSerializer(lesson).data
# progress = vertex.course.progress_set.filter(user=request.user) if payload is None:
# try: if not lesson.free:
# if progress.exists(): return Response("Bad token", status=400)
# next_vertex = vertex.get_next(progress[0].get_template())
# if next_vertex: else:
# res['next'] = MiniVertexSerializer(next_vertex).data return Response(l, status=200)
# res['is_in_progress'] = vertex in progress[0].get_objects_in_progress()
# else: prev_lesson = course.get_previous(lesson, (lambda x: not x.is_hm) if payload['only_watch'] else None)
# res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data next_lesson = course.get_next(lesson, (lambda x: not x.is_hm) if payload['only_watch'] else None)
# except Thread.DoesNotExist or Vertex.DoesNotExist:
# res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data if not prev_lesson is None:
l['prev_token'] = prev_lesson.token
return Response(res, status=200)
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, course.get_teacher(), lesson.is_hm)
return Response(l, status=200)

@ -295,4 +295,13 @@ class YandexFailView(APIView):
logger_yandex.error(data) logger_yandex.error(data)
return redirect(to=settings.DOMAIN) return redirect(to=settings.DOMAIN)
class DemoYandexCheckView(YandexCheckView):
"""для тестирования платежей"""
pass
class DemoYandexAvisoView(YandexAvisoView):
pass

@ -68,6 +68,8 @@ DATABASES = {
'default': env.db(), 'default': env.db(),
} }
COURSE_PROGRESS_SECRET_KEY = "!gf?s3@4Hr5#J#&%Kfr@56s"
SESSION_ENGINE = 'redis_sessions.session' SESSION_ENGINE = 'redis_sessions.session'
CELERY_EMAIL_CHUNK_SIZE = 1 CELERY_EMAIL_CHUNK_SIZE = 1

@ -1,7 +1,7 @@
from django.conf.urls import url, include from django.conf.urls import url, include
from django.contrib import admin from django.contrib import admin
from django.views.static import serve from django.views.static import serve
from finance.views import YandexCheckView, YandexAvisoView from finance.views import YandexCheckView, YandexAvisoView, DemoYandexCheckView, DemoYandexAvisoView
from django.conf import settings from django.conf import settings
@ -12,5 +12,7 @@ urlpatterns = [
url(r'^static/(?P<path>.*)/$', serve, {'document_root': settings.STATIC_ROOT}), url(r'^static/(?P<path>.*)/$', serve, {'document_root': settings.STATIC_ROOT}),
url(r'^wallet/pay/check/$', YandexCheckView.as_view(), name='yandex_money_check'), url(r'^wallet/pay/check/$', YandexCheckView.as_view(), name='yandex_money_check'),
url(r'^wallet/pay/result/$', YandexAvisoView.as_view(), name='yandex_money_notice'), url(r'^wallet/pay/result/$', YandexAvisoView.as_view(), name='yandex_money_notice'),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^yandex-money/check/$', DemoYandexCheckView.as_view()),
url(r'^yandex-money/aviso/$', DemoYandexAvisoView.as_view())
] ]

@ -1,6 +1,8 @@
from rest_framework import serializers from rest_framework import serializers
from progress.models import Progress, ProgressLesson from progress.models import Progress, ProgressLesson
import jwt
from django.conf import settings
class ProgressSerializer(serializers.ModelSerializer): class ProgressSerializer(serializers.ModelSerializer):
@ -15,6 +17,23 @@ class ProgressSerializer(serializers.ModelSerializer):
return [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()] return [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()]
class SecureProgressSerializer(serializers.ModelSerializer):
jwt_token = serializers.SerializerMethodField()
class Meta:
model = Progress
fields = ('jwt_token', )
@staticmethod
def get_jwt_token(self):
payload = {
'lessons': [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()],
'course_token': str(self.course_token),
'only_watch': self.only_watch,
}
return jwt.encode(payload, settings.COURSE_PROGRESS_SECRET_KEY, algorithm='HS256')
class ProgressAnalyticSerializer(serializers.ModelSerializer): class ProgressAnalyticSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField() name = serializers.SerializerMethodField()
email = serializers.SerializerMethodField() email = serializers.SerializerMethodField()
@ -47,8 +66,8 @@ class ProgressLessonSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_student(self): def get_student(self):
return {'name': self.progress.user.get_full_name(), "out_key": self.progress.user.out_key} return {'name': self.progress.user.get_full_name(), "out_key": str(self.progress.user.out_key)}
@staticmethod @staticmethod
def get_course_token(self): def get_course_token(self):
return self.progress.course_token return str(self.progress.course_token)

@ -14,7 +14,8 @@ from django.db.models import Q
from courses.models import Course from courses.models import Course
from progress.models import ProgressLesson, Progress from progress.models import ProgressLesson, Progress
from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer, ProgressSerializer from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer, ProgressSerializer, \
SecureProgressSerializer
from courses.api import CourseProgressApi, CourseParamsApi from courses.api import CourseProgressApi, CourseParamsApi
from progress.tasks import add_next_lesson from progress.tasks import add_next_lesson
@ -175,47 +176,45 @@ class StudentUpdateProgress(APIView):
@staticmethod @staticmethod
def post(request): def post(request):
lesson_token = request.JSON.get('lesson_token', None) lesson_token = request.JSON.get('lesson_token', None)
course_token = request.JSON.get('course_token', None)
comment = request.JSON.get('comment', None) comment = request.JSON.get('comment', None)
if lesson_token is None or course_token is None: if lesson_token is None:
return Response('Не передан слаг курса или токен урока', status=400) return Response('Не передан токен урока', status=400)
try:
student = request.user
p = Progress.objects.get(user=student, course_token=course_token) student = request.user
try: try:
pv = ProgressLesson.objects.get( pv = ProgressLesson.objects.get(
progress=p, progress__user=student,
lesson_token=lesson_token, lesson_token=lesson_token,
) )
if not pv.status == ProgressLesson.STATUSES.wait: if pv.status == ProgressLesson.STATUSES.done:
if pv.checker == p.teacher and not comment is None: Response(SecureProgressSerializer(pv.progress).data, status=200)
pv.status = ProgressLesson.STATUSES.wait
pv.comment_tokens.append(comment)
elif pv.checker == p.user: if not pv.status == ProgressLesson.STATUSES.wait:
pv.status = ProgressLesson.STATUSES.done if pv.checker == pv.progress.user:
pv.finish_date = datetime.datetime.now() pv.status = ProgressLesson.STATUSES.done
pv.finish_date = datetime.datetime.now()
else: elif not comment is None and\
raise ValueError("Этого никогда не должно происходить, но я уверен, что произойдёт") not pv.progress.progresslesson_set.filter(status=ProgressLesson.STATUSES.wait).exists():
pv.status = ProgressLesson.STATUSES.wait
pv.comment_tokens.append(comment)
pv.save() elif comment is None:
return Response("Не преложен комментарий", status=400)
else: else:
return Response("Ошибка прав доступа", status=403) return Response("В настоящее время, мы уже проверяем одно из ваших домашних заданий. <br> Как "
"только оно будет успешно сдано - вы сможете продолжить.", status=403)
except ProgressLesson.DoesNotExist: elif not comment is None:
return Response('Урок не проходится этим пользователем', status=403) pv.comment_tokens.append(comment)
if pv.status == ProgressLesson.STATUSES.done: pv.save()
# TODO: Ассинхроннаязадача для celery
add_next_lesson(p)
return Response(ProgressSerializer(p).data, status=200) return Response(SecureProgressSerializer(pv.progress).data, status=200)
except Progress.DoesNotExist: except Progress.DoesNotExist:
return Response('Не найден прогресс по заданным параметрам', status=404) return Response('Не найден прогресс по заданным параметрам', status=404)
@ -292,6 +291,7 @@ class SetProgress(APIView):
course_slug = request.JSON.get('course_slug', None) course_slug = request.JSON.get('course_slug', None)
topic_sort = int(request.JSON.get('topic', 1)) topic_sort = int(request.JSON.get('topic', 1))
lesson_sort = int(request.JSON.get('lesson', 1)) lesson_sort = int(request.JSON.get('lesson', 1))
only_watch = request.JSON.get('only_watch', False)
force = request.JSON.get('force', False) force = request.JSON.get('force', False)
if course_slug is None: if course_slug is None:
@ -319,6 +319,9 @@ class SetProgress(APIView):
teacher = get_user_model().objects.get(out_key=course.get_teacher()) teacher = get_user_model().objects.get(out_key=course.get_teacher())
progress = Progress.objects.create(course_token=course.token, user=student, teacher=teacher) progress = Progress.objects.create(course_token=course.token, user=student, teacher=teacher)
progress.only_watch = only_watch
progress.save()
token_list = [] token_list = []
lesson_list = [] lesson_list = []
for topic_idx, topic in enumerate(course.topic_set.all()): for topic_idx, topic in enumerate(course.topic_set.all()):

@ -1,3 +1,6 @@
import base64
import json
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
@ -44,12 +47,19 @@ class CommentView(APIView):
@staticmethod @staticmethod
def get(request): def get(request):
token = request.GET.get('token', None) base64_tokens = request.GET.get('base64_tokens', None)
if not token: if not base64_tokens:
return Response("Attribute token not set", status=400) return Response("Attribute token not set", status=400)
try: tokens = json.loads(base64.b64decode(base64_tokens).decode('utf-8'))
return Response(CommentSerializer(Comment.objects.get(token=token)).data, status=200) comments = []
except Comment.DoesNotExist:
return Response("Comment not found", status=404) for token in tokens:
try:
comment = Comment.objects.get(token=token)
comments.append(comment)
except Comment.DoesNotExist:
pass
return Response([CommentSerializer(comment).data for comment in comments], status=200)

Loading…
Cancel
Save