remotes/origin/feature/test_courses
Dmitriy Shesterkin 8 years ago
commit 41de0175e9
  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. 81
      courses/views.py
  8. 20
      finance/migrations/0003_auto_20180315_1358.py
  9. 2
      finance/models.py
  10. 6
      finance/signals.py
  11. 6
      finance/urls.py
  12. 185
      finance/views.py
  13. 2
      lms/settings.py
  14. 6
      lms/urls.py
  15. 23
      progress/serializers.py
  16. 63
      progress/views.py
  17. 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):
@ -84,7 +89,7 @@ class LessonInfoView(APIView):
lesson = Lesson.objects.get(token=token) lesson = Lesson.objects.get(token=token)
except Lesson.DoesNotExist: except Lesson.DoesNotExist:
return Response('Урок не найден', status=404) return Response('Урок не найден', status=404)
if request.user.is_authenticated and request.user.out_key in lesson.topic.course.teacher_tokens: if request.user.is_authenticated:
return Response(TeacherLessonSerializer(lesson).data, self.status_code) return Response(TeacherLessonSerializer(lesson).data, self.status_code)
return Response("Пользователь не является преподователем по курсу", status=403) return Response("Пользователь не является преподователем по курсу", status=403)
@ -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)

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-03-15 13:58
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('finance', '0002_auto_20180202_1301'),
]
operations = [
migrations.AlterField(
model_name='invoice',
name='real_price',
field=models.FloatField(blank=True, editable=False, help_text='Сумма, минус комиссия', null=True, verbose_name='Полученная сумма'),
),
]

@ -44,7 +44,7 @@ class Invoice(models.Model):
) )
status = models.CharField(verbose_name='Статус', max_length=1, default='W', choices=BILL_STATUSES) status = models.CharField(verbose_name='Статус', max_length=1, default='W', choices=BILL_STATUSES)
price = models.IntegerField(verbose_name='Сумма', editable=False, null=True, blank=True) price = models.IntegerField(verbose_name='Сумма', editable=False, null=True, blank=True)
real_price = models.IntegerField(verbose_name='Полученная сумма', null=True, blank=True, real_price = models.FloatField(verbose_name='Полученная сумма', null=True, blank=True,
help_text='Сумма, минус комиссия', editable=False) help_text='Сумма, минус комиссия', editable=False)
method = models.CharField(verbose_name='Способ оплаты', max_length=2, default='Y', choices=BILL_METHOD) method = models.CharField(verbose_name='Способ оплаты', max_length=2, default='Y', choices=BILL_METHOD)
key = models.CharField(verbose_name='Ключ платежа', max_length=255, editable=False, blank=True) key = models.CharField(verbose_name='Ключ платежа', max_length=255, editable=False, blank=True)

@ -36,7 +36,7 @@ def invoice_signal(instance, **kwargs):
user=instance.bill.user, user=instance.bill.user,
) )
except Progress.DoesNotExist: except Progress.DoesNotExist:
p=Progress.objects.create( p = Progress.objects.create(
course_token=instance.bill.course_token, course_token=instance.bill.course_token,
user=instance.bill.user, user=instance.bill.user,
teacher=get_user_model().objects.get(out_key=course.get_teacher()) teacher=get_user_model().objects.get(out_key=course.get_teacher())
@ -54,7 +54,7 @@ def invoice_signal(instance, **kwargs):
% (course.title, settings.DOMAIN, course.slug), % (course.title, settings.DOMAIN, course.slug),
'robo@skillbox.ru', 'robo@skillbox.ru',
[instance.bill.user.email], [instance.bill.user.email],
cc=[instance.bill.opener.email], bcc=[instance.bill.opener.email],
reply_to=[instance.bill.opener.email], reply_to=[instance.bill.opener.email],
) )
else: else:
@ -63,7 +63,7 @@ def invoice_signal(instance, **kwargs):
'''Курс "%s" был забронирован''' % course.title, '''Курс "%s" был забронирован''' % course.title,
'robo@skillbox.ru', 'robo@skillbox.ru',
[instance.bill.user.email], [instance.bill.user.email],
cc=[instance.bill.opener.email], bcc=[instance.bill.opener.email],
reply_to=[instance.bill.opener.email], reply_to=[instance.bill.opener.email],
) )
msg.send() msg.send()

@ -2,10 +2,10 @@ from django.conf.urls import url
from finance import views from finance import views
urlpatterns = [ urlpatterns = [
url(r'bills/([0-9]{1,99})/$', views.BillDetailView.as_view()),
url(r'payment/([0-9]{1,99})/$', views.YandexPay.as_view()), url(r'payment/([0-9]{1,99})/$', views.YandexPay.as_view()),
url(r'bills_find/$', views.FindBillView.as_view()),
url(r'bills/$', views.BillListView.as_view(), name='bills'), url(r'bills/$', views.BillListView.as_view(), name='bills'),
url(r'bills/([0-9]{1,99})/$', views.BillDetailView.as_view()),
url(r'bills_find/$', views.FindBillView.as_view()),
url(r'yandex/fail/$', views.YandexFailView.as_view()), url(r'yandex/fail/$', views.YandexFailView.as_view()),
url(r'invoices/$', views.get_invoices), url(r'invoices/$', views.get_invoices),
] ]

@ -1,10 +1,9 @@
import csv import csv
import logging import logging
import datetime
import dicttoxml
import requests import requests
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.mail import EmailMessage
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponse, HttpResponseForbidden from django.http import HttpResponse, HttpResponseForbidden
from django.shortcuts import redirect from django.shortcuts import redirect
@ -14,11 +13,13 @@ from rest_framework.views import APIView
from yandex_money.models import Payment from yandex_money.models import Payment
from django.conf import settings from django.conf import settings
from courses.models import Course
from courses.api import CourseParamsApi from courses.api import CourseParamsApi
from finance.models import Bill, Invoice from finance.models import Bill, Invoice
from finance.serializers import BillSerializer, InvoiceSerializer from finance.serializers import BillSerializer, InvoiceSerializer
from lms.global_decorators import transaction_decorator from lms.global_decorators import transaction_decorator
from lms.tools import get_real_name from lms.tools import get_real_name
from django.utils import timezone
logger_yandex = logging.getLogger('yandex_money') logger_yandex = logging.getLogger('yandex_money')
@ -28,11 +29,6 @@ class BillListView(APIView):
@staticmethod @staticmethod
def get(request): def get(request):
"""
This API endpoint return list of bills.
---
Returns a list of accounts for an authorized user or responsible user
"""
if request.user.is_authenticated: if request.user.is_authenticated:
return Response( return Response(
[BillSerializer(i).data for i in Bill.objects.filter(Q(user=request.user) | Q(opener=request.user))], [BillSerializer(i).data for i in Bill.objects.filter(Q(user=request.user) | Q(opener=request.user))],
@ -46,28 +42,49 @@ class BillListView(APIView):
or request.user.is_superuser): or request.user.is_superuser):
bill = request.JSON.get('bill') bill = request.JSON.get('bill')
children = request.JSON.get('children', []) children = request.JSON.get('children', [])
bill_kwarg = dict()
if bill: if bill:
bill_kwarg['user'] = get_user_model().objects.get(email=bill['user']) user = get_user_model().objects.get(email=bill['user'])
bill_kwarg['opener'] = get_user_model().objects.get(email=bill['opener']) opener = get_user_model().objects.get(email=bill['opener'])
bill_kwarg['description'] = bill['description'] description = bill['description']
bill_kwarg['comment'] = bill['comment'] comment = bill['comment']
bill_kwarg['course_token'] = bill['course_token'] course_token = bill['course_token']
bill_obj, is_create = Bill.objects.update_or_create(**bill_kwarg) try:
invoices = bill_obj.invoice_set.all() bill_obj = Bill.objects.get(user=user, course_token=course_token)
except Bill.DoesNotExist:
bill_obj = Bill.objects.create(user=user, course_token=course_token)
bill_obj.opener = opener
bill_obj.description = description
bill_obj.comment = comment
bill_obj.save()
for i in children: for i in children:
i['method'] = get_real_name(elem=i['method'], array=Invoice.BILL_METHOD) status = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES)
i['status'] = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) try:
i['bill'] = bill_obj invoice_id = i['id']
i['yandex_pay'] = None except KeyError:
invoice, _is_create = Invoice.objects.update_or_create(**i) invoice_id = None
if i['method'] == 'Y':
yandex_pay, _is_create = Payment.objects.get_or_create( try:
if not invoice_id is None:
invoice = Invoice.objects.get(id=i['id'])
if invoice.status == "P" or invoice.status == status:
continue
else:
raise Invoice.DoesNotExist
except Invoice.DoesNotExist:
i['method'] = get_real_name(elem=i['method'], array=Invoice.BILL_METHOD)
i['status'] = status
i['bill'] = bill_obj
i['yandex_pay'] = None
invoice = Invoice.objects.create(**i)
if i['method'] == 'Y' and invoice.yandex_pay is None:
yandex_pay = Payment.objects.create(
order_amount=i['price'], order_amount=i['price'],
order_number=invoice.id,
shop_amount=0, shop_amount=0,
customer_number=bill_obj.user.id, customer_number=bill_obj.user.id,
user=bill_obj.user, user=bill_obj.user,
@ -75,9 +92,21 @@ class BillListView(APIView):
) )
invoice.yandex_pay = yandex_pay invoice.yandex_pay = yandex_pay
invoice.save() invoice.save()
invoices = [j for j in invoices if not j.id == invoice.id]
[i.delete() for i in invoices] msg = EmailMessage(
'Выставлен новый счёт.',
'''Менеджер %s выставил счёт пользователю %s на курс "%s".'''
% (
invoice.bill.opener.get_full_name(),
invoice.bill.user.email,
Course.objects.get(token=invoice.bill.course_token).title,
),
'robo@skillbox.ru',
[invoice.bill.opener.email],
bcc=['dmitry.dolya@skillbox.ru'],
)
msg.send()
res = { res = {
"bill": BillSerializer(bill_obj).data, "bill": BillSerializer(bill_obj).data,
@ -163,7 +192,16 @@ class YandexPay(APIView):
'shopFailURL': settings.YANDEX_MONEY_FAIL_URL, 'shopFailURL': settings.YANDEX_MONEY_FAIL_URL,
}) })
logger_yandex.info(r) msg = EmailMessage(
'Пользователь перешёл на страницу оплаты.',
'''Пользователь "%s" перешёл на страницу оплаты курса "%s".'''
% (pay.invoice.bill.user.email, Course.objects.get(token=pay.invoice.bill.course_token).title),
'robo@skillbox.ru',
[pay.invoice.bill.opener.email],
bcc=['dmitry.dolya@skillbox.ru'],
)
msg.send()
return redirect(r.url) return redirect(r.url)
@ -183,8 +221,8 @@ def get_invoices(request):
file_name = file_name + "__to_%s" % date_to if date_to else file_name file_name = file_name + "__to_%s" % date_to if date_to else file_name
invoices = Invoice.objects.filter(method="Y", status="F") invoices = Invoice.objects.filter(method="Y", status="F")
invoices = invoices.filter(date__lt=date_to) if date_to else invoices invoices = invoices.filter(yandex_pay__performed_datetime__lt=date_to) if date_to else invoices
invoices = invoices.filter(date__gte=date_from) if date_from else invoices invoices = invoices.filter(yandex_pay__performed_datetime__gte=date_from) if date_from else invoices
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % file_name response['Content-Disposition'] = 'attachment; filename="%s.csv"' % file_name
@ -195,8 +233,8 @@ def get_invoices(request):
for i in invoices.order_by('-date'): for i in invoices.order_by('-date'):
course_api = CourseParamsApi(i.bill.course_token) course_api = CourseParamsApi(i.bill.course_token)
writer.writerow([ writer.writerow([
i.date.date(), i.yandex_pay.performed_datetime.date(),
i.date.time(), i.yandex_pay.performed_datetime.time(),
i.bill.user.email, i.bill.user.email,
i.bill.user.get_full_name(), i.bill.user.get_full_name(),
course_api.get_slug_and_title()['title'], course_api.get_slug_and_title()['title'],
@ -219,38 +257,53 @@ class YandexCheckView(APIView):
val = i.split('=')[1] val = i.split('=')[1]
data[key] = val data[key] = val
logger_yandex.info(data) logger_yandex.info('Проверка платежа запрос', exc_info=True, extra={
'request': data,
})
try: try:
pay = Payment.objects.get(order_number=data['orderNumber']) pay = Payment.objects.get(order_number=data['orderNumber'])
except Payment.DoesNotExist: except Payment.DoesNotExist:
logger_yandex.error("Payment with id=%s not found" % data['orderNumber']) logger_yandex.error('Ошибка проверки платежа', exc_info=True, extra={
'request': "Payment with id=%s not found" % data['orderNumber'],
})
return Response(status=204) return Response(status=204)
if not pay.status == Payment.STATUS.PROCESSED: if not pay.status == Payment.STATUS.PROCESSED:
logger_yandex.error("Payment with id=%s have status %s" % (data['orderNumber'], pay.status)) logger_yandex.error('Ошибка проверки платежа', exc_info=True, extra={
'request': "Payment with id=%s have status %s" % (data['orderNumber'], pay.status),
})
return Response(status=204) return Response(status=204)
if not pay.shop_id == int(data['shopId']): if not pay.shop_id == int(data['shopId']):
logger_yandex.error("ShopId=%s not match" % (data['shopId'],)) logger_yandex.error('Ошибка проверки платежа', exc_info=True, extra={
'request': "ShopId=%s not match" % (data['shopId'],),
})
return Response(status=204) return Response(status=204)
if not pay.scid == int(data['scid']): if not pay.scid == int(data['scid']):
logger_yandex.error("scid=%s not match" % (data['scid'],)) logger_yandex.error('Ошибка проверки платежа', exc_info=True, extra={
'request': "scid=%s not match" % (data['scid'],)
})
return Response(status=204) return Response(status=204)
if not pay.order_amount == float(data['orderSumAmount']): if not pay.order_amount == float(data['orderSumAmount']):
logger_yandex.error("Expected amount is %s received amount is %s" logger_yandex.error('Ошибка проверки платежа', exc_info=True, extra={
% (pay.order_amount, data['orderSumAmount'])) 'request': "Expected amount is %s received amount is %s"
% (pay.order_amount, data['orderSumAmount']),
})
return Response(status=204) return Response(status=204)
# TODO Нужно решение now = timezone.now()
# pay.invoice_id = int(data['invoiceId']) pay.performed_datetime = now.isoformat()
# pay.save() pay.save()
xml_res = """<checkOrderResponse performedDatetime="%s" code="0" invoiceId="%s" shopId="%s" orderSumAmount="%s"/> xml_res = """<checkOrderResponse performedDatetime="%s" code="0" invoiceId="%s" shopId="%s"/>
""" % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id), str(pay.order_amount)) """ % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id))
logger_yandex.info(xml_res) logger_yandex.info('Проверка платежа ответ', exc_info=True, extra={
'response': xml_res,
})
return HttpResponse(xml_res, content_type='application/xml') return HttpResponse(xml_res, content_type='application/xml')
@ -269,27 +322,38 @@ class YandexAvisoView(APIView):
try: try:
pay = Payment.objects.get(order_number=data['orderNumber']) pay = Payment.objects.get(order_number=data['orderNumber'])
except Payment.DoesNotExist: except Payment.DoesNotExist:
logger_yandex.error("Payment with invoice_id=%s not found" % data['orderNumber']) logger_yandex.error('Ошибка подтверждения платежа', exc_info=True, extra={
'request': "Payment with invoice_id=%s not found" % data['orderNumber'],
})
return Response(status=204) return Response(status=204)
logger_yandex.info('Get success pay with invoice_id(yandex) %s' % pay.invoice_id) logger_yandex.info('Подтверждение платежа запрос', exc_info=True, extra={
'request': 'Get success pay with invoice_id(yandex) %s' % str(data['invoiceId']),
})
pay.shop_amount = data['shopSumAmount'] pay.shop_amount = data['shopSumAmount']
pay.status = Payment.STATUS.SUCCESS pay.status = Payment.STATUS.SUCCESS
now = timezone.now()
pay.performed_datetime = now.isoformat()
pay.save() pay.save()
xml_res = """<paymentAvisoResponse performedDatetime="%s" code="0" invoiceId="%s" shopId="%s"/>
""" % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id))
xml_res = dicttoxml.dicttoxml({ logger_yandex.info('Подтверждение платежа ответ', exc_info=True, extra={
'code': 0, 'response': xml_res,
'shopId':int(pay.shop_id),
'invoiceId': int(data['invoiceId']),
'orderSumAmount': pay.order_amount,
'performedDatetime': datetime.datetime.now(),
}) })
"""<paymentAvisoResponse performedDatetime="%s" code="0" invoiceId="%s" shopId="%s" orderSumAmount="%s"/>
""" % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id), str(pay.order_amount))
logger_yandex.info(xml_res) msg = EmailMessage(
'Успешная оплата.',
'''Пользователь "%s", перевёл %s рублей. Номер платежа в яндекс кассе %s'''
% (pay.invoice.bill.user.email, str(pay.invoice.price), str(data['invoiceId'])),
'robo@skillbox.ru',
[pay.invoice.bill.opener.email],
bcc=['dmitry.dolya@skillbox.ru', 'vera.procenko@skillbox.ru'],
)
msg.send()
return HttpResponse(xml_res, content_type='application/xml') return HttpResponse(xml_res, content_type='application/xml')
@ -307,4 +371,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

@ -70,6 +70,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
@ -126,7 +127,7 @@ class TeacherUpdateProgress(APIView):
pv.status = ProgressLesson.STATUSES.fail pv.status = ProgressLesson.STATUSES.fail
msg = EmailMessage( msg = EmailMessage(
'Ваша работа отправлена на доработку', 'Ваша работа отправлена на доработку',
'''Преподователь "%s" отклонил вашу работу''' % request.user.get_full_name(), '''Преподаватель "%s" отклонил вашу работу''' % request.user.get_full_name(),
'robo@skillbox.ru', 'robo@skillbox.ru',
[student.email], [student.email],
) )
@ -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: return 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