From bbbb8bb6f2f87c677ffa4c9d19a6fae267ec1156 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 5 Mar 2018 12:41:00 +0300 Subject: [PATCH 01/42] =?UTF-8?q?=D0=9D=D0=B5=20=D0=B2=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B5=D0=BC=20=D1=81=D0=BB=D1=83=D1=87=D0=B0=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D0=BC=D1=91=D1=80=D0=B6=D0=B8=D1=82=D1=8C!!!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- access/serializers.py | 5 ++--- access/urls.py | 6 +---- lms/settings.py | 2 ++ progress/serializers.py | 24 ++++++++++++++++++-- progress/views.py | 50 ++++++++++++++++++----------------------- 5 files changed, 49 insertions(+), 38 deletions(-) diff --git a/access/serializers.py b/access/serializers.py index a9d5c6f..5d29a96 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -1,10 +1,9 @@ from django.contrib.auth import get_user_model from rest_framework import serializers -from rest_framework.generics import get_object_or_404 from access.models.other import Account from achievements.serialers import DiplomaSerializer, AchievementsSerializer -from progress.serializers import ProgressSerializer +from progress.serializers import SecureProgressSerializer class AccountSerializer(serializers.ModelSerializer): @@ -49,7 +48,7 @@ class UserSelfSerializer(serializers.ModelSerializer): @staticmethod def get_progress(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): diff --git a/access/urls.py b/access/urls.py index 579905d..f2961b6 100644 --- a/access/urls.py +++ b/access/urls.py @@ -18,10 +18,6 @@ urlpatterns = [ url(r'logout/$', views.LogoutView.as_view()), url(r'reset/$', views.ResetPasswordView.as_view()), url(r'progress_detail/upload/(?P[0-9A-Fa-f-]+)/$', progress.views.UploadCourseProgressUserView.as_view()), - url( - r'management/password/$', - views.ManagementPassword.as_view(), - name='management-password' - ) + url(r'management/password/$', views.ManagementPassword.as_view(), name='management-password') ] diff --git a/lms/settings.py b/lms/settings.py index 92db76a..8dfbb82 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -68,6 +68,8 @@ DATABASES = { 'default': env.db(), } +COURSE_PROGRESS_SECRET_KEY = "!gf?s3@4Hr5#J#&%Kfr@56s" + SESSION_ENGINE = 'redis_sessions.session' CELERY_EMAIL_CHUNK_SIZE = 1 diff --git a/progress/serializers.py b/progress/serializers.py index ea27df1..a7a9e65 100644 --- a/progress/serializers.py +++ b/progress/serializers.py @@ -1,6 +1,8 @@ from rest_framework import serializers from progress.models import Progress, ProgressLesson +import jwt +from django.conf import settings class ProgressSerializer(serializers.ModelSerializer): @@ -15,6 +17,24 @@ class ProgressSerializer(serializers.ModelSerializer): 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, + 'is_finish': self.is_finish, + } + return jwt.encode(payload, settings.COURSE_PROGRESS_SECRET_KEY, algorithm='HS256') + + class ProgressAnalyticSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() email = serializers.SerializerMethodField() @@ -47,8 +67,8 @@ class ProgressLessonSerializer(serializers.ModelSerializer): @staticmethod 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 def get_course_token(self): - return self.progress.course_token \ No newline at end of file + return str(self.progress.course_token) diff --git a/progress/views.py b/progress/views.py index 2b43719..ed3db3f 100644 --- a/progress/views.py +++ b/progress/views.py @@ -175,47 +175,41 @@ class StudentUpdateProgress(APIView): @staticmethod def post(request): lesson_token = request.JSON.get('lesson_token', None) - course_token = request.JSON.get('course_token', None) comment = request.JSON.get('comment', None) - if lesson_token is None or course_token is None: - return Response('Не передан слаг курса или токен урока', status=400) - try: - student = request.user - - p = Progress.objects.get(user=student, course_token=course_token) - - try: - pv = ProgressLesson.objects.get( - progress=p, - lesson_token=lesson_token, - ) + if lesson_token is None: + return Response('Не передан токен урока', status=400) - if not pv.status == ProgressLesson.STATUSES.wait: - if pv.checker == p.teacher and not comment is None: - pv.status = ProgressLesson.STATUSES.wait - pv.comment_tokens.append(comment) + student = request.user - elif pv.checker == p.user: - pv.status = ProgressLesson.STATUSES.done - pv.finish_date = datetime.datetime.now() + try: + pv = ProgressLesson.objects.get( + progress__user=student, + lesson_token=lesson_token, + ) - else: - raise ValueError("Этого никогда не должно происходить, но я уверен, что произойдёт") + if not pv.status == ProgressLesson.STATUSES.wait: + if pv.checker == pv.progress.teacher and not comment is None: + pv.status = ProgressLesson.STATUSES.wait + pv.comment_tokens.append(comment) - pv.save() + elif pv.checker == pv.progress.user: + pv.status = ProgressLesson.STATUSES.done + pv.finish_date = datetime.datetime.now() else: - return Response("Ошибка прав доступа", status=403) + raise ValueError("Этого никогда не должно происходить, но я уверен, что произойдёт") - except ProgressLesson.DoesNotExist: - return Response('Урок не проходится этим пользователем', status=403) + pv.save() + + else: + return Response("Ошибка прав доступа", status=403) if pv.status == ProgressLesson.STATUSES.done: # TODO: Ассинхроннаязадача для celery - add_next_lesson(p) + add_next_lesson(pv.progress) - return Response(ProgressSerializer(p).data, status=200) + return Response(ProgressSerializer(pv.progress).data, status=200) except Progress.DoesNotExist: return Response('Не найден прогресс по заданным параметрам', status=404) From f2f0f8cc44d1f14fc5ff464345b42f396a63df5e Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 16:33:43 +0300 Subject: [PATCH 02/42] Yandex_update --- access/serializers.py | 6 ++-- courses/models.py | 21 +++++++---- courses/serializers.py | 5 +++ courses/tasks.py | 12 +++++++ courses/urls.py | 2 +- courses/views.py | 77 ++++++++++++++++++++++++++--------------- progress/serializers.py | 1 - 7 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 courses/tasks.py diff --git a/access/serializers.py b/access/serializers.py index 5d29a96..2aea54b 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -21,13 +21,13 @@ class AccountSerializer(serializers.ModelSerializer): class UserSelfSerializer(serializers.ModelSerializer): account = serializers.SerializerMethodField() groups = serializers.SerializerMethodField() - progress = serializers.SerializerMethodField() + progresses = serializers.SerializerMethodField() diplomas = serializers.SerializerMethodField() achievements = serializers.SerializerMethodField() class Meta: 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') @staticmethod @@ -47,7 +47,7 @@ class UserSelfSerializer(serializers.ModelSerializer): return [group.name for group in self.groups.all()] @staticmethod - def get_progress(self): + def get_progresses(self): return [SecureProgressSerializer(i).data for i in self.progress_set.all()] diff --git a/courses/models.py b/courses/models.py index 7785b4c..934a43b 100755 --- a/courses/models.py +++ b/courses/models.py @@ -154,19 +154,26 @@ class Course(models.Model): lesson_list += list(topic.lesson_set.all()) return lesson_list - def get_next(self, lesson: Lesson) -> Lesson: + def get_next(self, lesson: Lesson, f=None) -> Lesson: lessons = self.get_lesson_list() try: - return lessons[lessons.index(lesson)] + n = lessons[lessons.index(lesson)+1] + if f is None or f(lesson): + return n + else: + return self.get_next(lesson, f) except IndexError: pass - def get_previous(self, lesson: Lesson): + def get_previous(self, lesson: Lesson, f=None): lessons = self.get_lesson_list() - try: - return lessons[lessons.index(lesson) - 2] - except IndexError: - pass + idx = lessons.index(lesson) - 1 + if idx > -1: + prev = lessons[idx] + if f is None or f(lesson): + return prev + else: + return self.get_previous(prev, f) def get_first_lesson(self) -> Lesson: lessons = self.get_lesson_list() diff --git a/courses/serializers.py b/courses/serializers.py index 70e4ba6..359386b 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -23,11 +23,16 @@ class MiniLessonSerializer(serializers.ModelSerializer): class LessonSerializer(MiniLessonSerializer): + course_slug = serializers.SerializerMethodField() class Meta: model = Lesson exclude = ('id', 'topic', 'key') + @staticmethod + def get_course_slug(self): + return self.topic.course.slug + class TeacherLessonSerializer(MiniLessonSerializer): topic_sort = serializers.SerializerMethodField() diff --git a/courses/tasks.py b/courses/tasks.py new file mode 100644 index 0000000..9d00b7f --- /dev/null +++ b/courses/tasks.py @@ -0,0 +1,12 @@ +from progress.models import ProgressLesson, Progress + + +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=user_out_key if is_hm else teacher_key, + ) \ No newline at end of file diff --git a/courses/urls.py b/courses/urls.py index d30f49b..819f814 100644 --- a/courses/urls.py +++ b/courses/urls.py @@ -3,8 +3,8 @@ from django.conf.urls import url from courses import views as views urlpatterns = [ - url(r'vertex/(?P.+)/$', views.LessonDetail.as_view()), url(r'lesson/teacher/(?P.+)/$', views.LessonInfoView.as_view()), + url(r'lesson/(?P.+)/$', views.LessonDetail.as_view()), url(r'tree/(?P.+)/$', views.TreeView.as_view()), url(r'detail/(?P.+)/$', views.CourseDetailView.as_view()), url(r'^$', views.CourseListView.as_view()), diff --git a/courses/views.py b/courses/views.py index ea9536f..90450b6 100644 --- a/courses/views.py +++ b/courses/views.py @@ -1,3 +1,5 @@ +from jwt import DecodeError + from courses.models import Course, Lesson from rest_framework.renderers import JSONRenderer 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 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): @@ -93,33 +98,51 @@ class LessonDetail(APIView): renderer_classes = (JSONRenderer,) @staticmethod - def get(request, token): + def post(request, token): + jwt_token = request.JSON.get('jwt_token', None) try: lesson = Lesson.objects.get(token=token) except Lesson.DoesNotExist: - return Response("Lesson doesn't exist", status=404) - - if not lesson.free and not ProgressLesson.objects.filter(lesson_token=lesson.token).exists(): - previous_lesson = lesson.topic.course.get_previous(lesson) - - 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(): - # next_vertex = vertex.get_next(progress[0].get_template()) - # if next_vertex: - # res['next'] = MiniVertexSerializer(next_vertex).data - # res['is_in_progress'] = vertex in progress[0].get_objects_in_progress() - # else: - # res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data - # except Thread.DoesNotExist or Vertex.DoesNotExist: - # res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data - - return Response(res, status=200) + return Response("Урока не существует", status=404) + + l = LessonSerializer(lesson).data + + if not lesson.free: + if jwt_token is None: + return Response("Ошибка доступа", status=403) + + try: + payload = jwt.decode(jwt_token, settings.COURSE_PROGRESS_SECRET_KEY, algorithms=['HS256']) + except DecodeError: + return Response("Bad token", status=400) + + try: + course = Course.objects.get(token=payload['course_token']) + except Course.DoesNotExist: + return Response("Course not found", status=404) + + 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) + new_lesson = True if prev_lesson is None else \ + ((payload_lesson['status'] == "done" or payload_lesson['status'] == "wait") + if prev_lesson.token == payload_lesson['lesson_token'] else False) + + 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) diff --git a/progress/serializers.py b/progress/serializers.py index a7a9e65..c2570e9 100644 --- a/progress/serializers.py +++ b/progress/serializers.py @@ -30,7 +30,6 @@ class SecureProgressSerializer(serializers.ModelSerializer): 'lessons': [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()], 'course_token': str(self.course_token), 'only_watch': self.only_watch, - 'is_finish': self.is_finish, } return jwt.encode(payload, settings.COURSE_PROGRESS_SECRET_KEY, algorithm='HS256') From 5096fc02333c58e1711997ac074203c764d8cf0b Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 16:38:44 +0300 Subject: [PATCH 03/42] Yandex_update --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index 6671172..8d59e11 100644 --- a/finance/views.py +++ b/finance/views.py @@ -153,7 +153,7 @@ class YandexPay(APIView): 'scid': pay.scid, 'sum': pay.order_amount, 'customerNumber': pay.customer_number, - 'orderNumber': pay.order_number, + # 'orderNumber': pay.order_number, 'cps_email': pay.cps_email, 'shopSuccessURL': settings.YANDEX_MONEY_SUCCESS_URL, 'shopFailURL': settings.YANDEX_MONEY_FAIL_URL, From 0b1409e45d05dfc8b3ab4cd1b18118bc7f44827b Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 16:50:03 +0300 Subject: [PATCH 04/42] Yandex_update --- finance/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index 8d59e11..83f400b 100644 --- a/finance/views.py +++ b/finance/views.py @@ -62,7 +62,6 @@ class BillListView(APIView): if i['method'] == 'Y': yandex_pay, _is_create = Payment.objects.get_or_create( order_amount=i['price'], - order_number=invoice.id, shop_amount=0, customer_number=bill_obj.user.id, user=bill_obj.user, From 3cf388896163e8ad2662af563eefca9cf86c7dfd Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 16:59:01 +0300 Subject: [PATCH 05/42] Yandex_update --- finance/views.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/finance/views.py b/finance/views.py index 83f400b..3c95834 100644 --- a/finance/views.py +++ b/finance/views.py @@ -59,8 +59,8 @@ class BillListView(APIView): i['bill'] = bill_obj i['yandex_pay'] = None invoice, _is_create = Invoice.objects.update_or_create(**i) - if i['method'] == 'Y': - yandex_pay, _is_create = Payment.objects.get_or_create( + if i['method'] == 'Y' and invoice.yandex_pay is None: + yandex_pay = Payment.objects.create( order_amount=i['price'], shop_amount=0, customer_number=bill_obj.user.id, @@ -152,14 +152,12 @@ class YandexPay(APIView): 'scid': pay.scid, 'sum': pay.order_amount, 'customerNumber': pay.customer_number, - # 'orderNumber': pay.order_number, + 'orderNumber': pay.order_number, 'cps_email': pay.cps_email, 'shopSuccessURL': settings.YANDEX_MONEY_SUCCESS_URL, 'shopFailURL': settings.YANDEX_MONEY_FAIL_URL, }) - logger_yandex.info(r) - return redirect(r.url) except Payment.DoesNotExist: From 7560c09b871f34040e965e5332d5c02b69420ca4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 17:05:58 +0300 Subject: [PATCH 06/42] Yandex_update --- finance/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/finance/views.py b/finance/views.py index 3c95834..d7c3b52 100644 --- a/finance/views.py +++ b/finance/views.py @@ -240,8 +240,8 @@ class YandexCheckView(APIView): # pay.invoice_id = int(data['invoiceId']) # pay.save() - xml_res = """ - """ % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id), str(pay.order_amount)) + xml_res = """ + """ % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id)) logger_yandex.info(xml_res) @@ -279,8 +279,8 @@ class YandexAvisoView(APIView): 'performedDatetime': datetime.datetime.now(), }) - """ - """ % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id), str(pay.order_amount)) + """ + """ % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id)) logger_yandex.info(xml_res) From fa75dcf9089b09dfa58dfe7e305c0645c4f4873e Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 17:20:11 +0300 Subject: [PATCH 07/42] Yandex_update --- finance/views.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/finance/views.py b/finance/views.py index d7c3b52..ac08320 100644 --- a/finance/views.py +++ b/finance/views.py @@ -19,6 +19,7 @@ from finance.models import Bill, Invoice from finance.serializers import BillSerializer, InvoiceSerializer from lms.global_decorators import transaction_decorator from lms.tools import get_real_name +from django.utils import timezone logger_yandex = logging.getLogger('yandex_money') @@ -239,9 +240,12 @@ class YandexCheckView(APIView): # TODO Нужно решение # pay.invoice_id = int(data['invoiceId']) # pay.save() + now = timezone.now() + pay.performed_datetime = now.isoformat() + pay.save() xml_res = """ - """ % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id)) + """ % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id)) logger_yandex.info(xml_res) @@ -270,17 +274,8 @@ class YandexAvisoView(APIView): pay.shop_amount = data['shopSumAmount'] pay.status = Payment.STATUS.SUCCESS pay.save() - - xml_res = dicttoxml.dicttoxml({ - 'code': 0, - 'shopId':int(pay.shop_id), - 'invoiceId': int(data['invoiceId']), - 'orderSumAmount': pay.order_amount, - 'performedDatetime': datetime.datetime.now(), - }) - - """ - """ % (datetime.datetime.now(), str(data['invoiceId']), str(pay.shop_id)) + xml_res = """ + """ % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id)) logger_yandex.info(xml_res) From c2eff111fa57521ba199440836233503d0da315c Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 17:40:01 +0300 Subject: [PATCH 08/42] Yandex_update --- finance/signals.py | 1 - 1 file changed, 1 deletion(-) diff --git a/finance/signals.py b/finance/signals.py index 7f104c1..110a4f5 100644 --- a/finance/signals.py +++ b/finance/signals.py @@ -54,7 +54,6 @@ def invoice_signal(instance, **kwargs): % (course.title, settings.DOMAIN, course.slug), 'robo@skillbox.ru', [instance.bill.user.email], - cc=[instance.bill.opener.email], reply_to=[instance.bill.opener.email], ) else: From f997850145b03f94a90c4f329ad87d97dd963eb0 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 17:46:22 +0300 Subject: [PATCH 09/42] Yandex_update --- finance/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/urls.py b/finance/urls.py index e2f8f0d..830c0d0 100644 --- a/finance/urls.py +++ b/finance/urls.py @@ -4,8 +4,8 @@ from finance import views 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'bills_find/$', views.FindBillView.as_view()), url(r'bills/$', views.BillListView.as_view()), + url(r'bills_find/$', views.FindBillView.as_view()), url(r'yandex/fail/$', views.YandexFailView.as_view()), url(r'invoices/$', views.get_invoices), ] \ No newline at end of file From be41248e3d944723834bb61464038da200264a26 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 12 Mar 2018 17:48:24 +0300 Subject: [PATCH 10/42] Yandex_update --- finance/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/urls.py b/finance/urls.py index 830c0d0..5da326d 100644 --- a/finance/urls.py +++ b/finance/urls.py @@ -2,9 +2,9 @@ from django.conf.urls import url from finance import views 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'bills/$', views.BillListView.as_view()), + 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'invoices/$', views.get_invoices), From ef3e1324314727b0e541d972af8a27e2e20709c5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 13 Mar 2018 18:24:04 +0300 Subject: [PATCH 11/42] Yandex_update --- courses/tasks.py | 3 ++- courses/views.py | 54 ++++++++++++++++++++++++----------------------- progress/views.py | 11 ++++------ 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/courses/tasks.py b/courses/tasks.py index 9d00b7f..9384958 100644 --- a/courses/tasks.py +++ b/courses/tasks.py @@ -1,4 +1,5 @@ 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): @@ -8,5 +9,5 @@ def add_lesson(user_out_key: str, course_token: str, lesson_token: str, teacher_ ProgressLesson.objects.get_or_create( progress=p, lesson_token=lesson_token, - checker=user_out_key if is_hm else teacher_key, + checker=get_user_model().objects.get(out_key=(teacher_key if is_hm else user_out_key)), ) \ No newline at end of file diff --git a/courses/views.py b/courses/views.py index 90450b6..1a6138b 100644 --- a/courses/views.py +++ b/courses/views.py @@ -108,41 +108,43 @@ class LessonDetail(APIView): l = LessonSerializer(lesson).data - if not lesson.free: - if jwt_token is None: - return Response("Ошибка доступа", status=403) + 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 - try: - payload = jwt.decode(jwt_token, settings.COURSE_PROGRESS_SECRET_KEY, algorithms=['HS256']) - except DecodeError: + if payload is None: + if not lesson.free: return Response("Bad token", status=400) - try: - course = Course.objects.get(token=payload['course_token']) - except Course.DoesNotExist: - return Response("Course not found", status=404) + 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) - 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 prev_lesson is None: - l['prev_token'] = prev_lesson.token + if not next_lesson is None: + l['next_token'] = next_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) - 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 prev_lesson.token == payload_lesson['lesson_token'] else False) + (payload_lesson['status'] == "done" or payload_lesson['status'] == "wait") - if not new_lesson: - return Response("Permission denied", status=403) + 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) + #TODO Задача для селери + add_lesson(request.user.out_key, course.token, lesson.token, course.get_teacher(), lesson.is_hm) return Response(l, status=200) diff --git a/progress/views.py b/progress/views.py index ed3db3f..e0ae37d 100644 --- a/progress/views.py +++ b/progress/views.py @@ -14,7 +14,8 @@ from django.db.models import Q from courses.models import Course 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 progress.tasks import add_next_lesson @@ -203,13 +204,9 @@ class StudentUpdateProgress(APIView): pv.save() else: - return Response("Ошибка прав доступа", status=403) + pass - if pv.status == ProgressLesson.STATUSES.done: - # TODO: Ассинхроннаязадача для celery - add_next_lesson(pv.progress) - - return Response(ProgressSerializer(pv.progress).data, status=200) + return Response(SecureProgressSerializer(pv.progress).data, status=200) except Progress.DoesNotExist: return Response('Не найден прогресс по заданным параметрам', status=404) From cfe82db7139cfcb1025a174a239f9f3210562276 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 12:16:17 +0300 Subject: [PATCH 12/42] storage update --- progress/views.py | 6 +++--- storage/views.py | 22 ++++++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/progress/views.py b/progress/views.py index e0ae37d..6b3ee94 100644 --- a/progress/views.py +++ b/progress/views.py @@ -201,10 +201,10 @@ class StudentUpdateProgress(APIView): else: raise ValueError("Этого никогда не должно происходить, но я уверен, что произойдёт") - pv.save() + elif not comment is None: + pv.comment_tokens.append(comment) - else: - pass + pv.save() return Response(SecureProgressSerializer(pv.progress).data, status=200) diff --git a/storage/views.py b/storage/views.py index 55770d6..1b7c14c 100644 --- a/storage/views.py +++ b/storage/views.py @@ -1,3 +1,6 @@ +import base64 +import json + from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView @@ -44,12 +47,19 @@ class CommentView(APIView): @staticmethod 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) - try: - return Response(CommentSerializer(Comment.objects.get(token=token)).data, status=200) - except Comment.DoesNotExist: - return Response("Comment not found", status=404) + tokens = json.loads(base64.b64decode(base64_tokens).decode('utf-8')) + comments = [] + + 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) From 5981e69f4d16d8f6a3bea198e7315d008effdb85 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 15:38:32 +0300 Subject: [PATCH 13/42] storage update --- progress/views.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/progress/views.py b/progress/views.py index 6b3ee94..66f7a6e 100644 --- a/progress/views.py +++ b/progress/views.py @@ -189,15 +189,18 @@ class StudentUpdateProgress(APIView): lesson_token=lesson_token, ) - if not pv.status == ProgressLesson.STATUSES.wait: - if pv.checker == pv.progress.teacher and not comment is None: - pv.status = ProgressLesson.STATUSES.wait - pv.comment_tokens.append(comment) + if pv.status == ProgressLesson.STATUSES.done: + Response(SecureProgressSerializer(pv.progress).data, status=200) - elif pv.checker == pv.progress.user: + if not pv.status == ProgressLesson.STATUSES.wait: + if pv.checker == pv.progress.user: pv.status = ProgressLesson.STATUSES.done pv.finish_date = datetime.datetime.now() + elif not comment is None: + pv.status = ProgressLesson.STATUSES.wait + pv.comment_tokens.append(comment) + else: raise ValueError("Этого никогда не должно происходить, но я уверен, что произойдёт") From ab411b615fdac088edfa3ec668d2563e9c5a6474 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 15:49:05 +0300 Subject: [PATCH 14/42] storage update --- progress/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/progress/views.py b/progress/views.py index 66f7a6e..7fe4dc3 100644 --- a/progress/views.py +++ b/progress/views.py @@ -197,12 +197,13 @@ class StudentUpdateProgress(APIView): pv.status = ProgressLesson.STATUSES.done pv.finish_date = datetime.datetime.now() - elif not comment is None: + elif not comment is None and\ + not pv.progress.progresslesson_set.filter(status=ProgressLesson.STATUSES.wait).exists(): pv.status = ProgressLesson.STATUSES.wait pv.comment_tokens.append(comment) else: - raise ValueError("Этого никогда не должно происходить, но я уверен, что произойдёт") + raise ValueError("Вероятней всего тут отшили студента который пытался здать вторую домашку подряд") elif not comment is None: pv.comment_tokens.append(comment) From 5bd0e0bb1357f4fd09a23c560ecd165111d4fae6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 16:09:00 +0300 Subject: [PATCH 15/42] storage update --- progress/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/progress/views.py b/progress/views.py index 7fe4dc3..b1a1e08 100644 --- a/progress/views.py +++ b/progress/views.py @@ -202,8 +202,12 @@ class StudentUpdateProgress(APIView): pv.status = ProgressLesson.STATUSES.wait pv.comment_tokens.append(comment) + elif comment is None: + return Response("Не преложен комментарий", status=400) + else: - raise ValueError("Вероятней всего тут отшили студента который пытался здать вторую домашку подряд") + return Response("""В настоящее время, мы уже проверяем одно из ваших домашних заданий. Как только оно будет + успешно сдано - вы сможете продолжить.""", status=403) elif not comment is None: pv.comment_tokens.append(comment) From b9eb5c552dd296b32970134963b968be070e9b2a Mon Sep 17 00:00:00 2001 From: Evgeniy Shabanov Date: Wed, 14 Mar 2018 16:15:03 +0300 Subject: [PATCH 16/42] Demo urls --- finance/views.py | 11 ++++++++++- lms/urls.py | 6 ++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/finance/views.py b/finance/views.py index ac08320..c97513f 100644 --- a/finance/views.py +++ b/finance/views.py @@ -295,4 +295,13 @@ class YandexFailView(APIView): logger_yandex.error(data) - return redirect(to=settings.DOMAIN) \ No newline at end of file + return redirect(to=settings.DOMAIN) + + +class DemoYandexCheckView(YandexCheckView): + """для тестирования платежей""" + pass + + +class DemoYandexAvisoView(YandexAvisoView): + pass \ No newline at end of file diff --git a/lms/urls.py b/lms/urls.py index 9ed625b..9acba7b 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import url, include from django.contrib import admin 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 @@ -12,5 +12,7 @@ urlpatterns = [ url(r'^static/(?P.*)/$', serve, {'document_root': settings.STATIC_ROOT}), 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'^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()) ] From c8f65dc71a1dcd776f307b1fef049e83f09d49f4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 16:31:47 +0300 Subject: [PATCH 17/42] storage update --- progress/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/progress/views.py b/progress/views.py index b1a1e08..495f542 100644 --- a/progress/views.py +++ b/progress/views.py @@ -206,8 +206,8 @@ class StudentUpdateProgress(APIView): return Response("Не преложен комментарий", status=400) else: - return Response("""В настоящее время, мы уже проверяем одно из ваших домашних заданий. Как только оно будет - успешно сдано - вы сможете продолжить.""", status=403) + return Response("В настоящее время, мы уже проверяем одно из ваших домашних заданий.
Как " + "только оно будет успешно сдано - вы сможете продолжить.", status=403) elif not comment is None: pv.comment_tokens.append(comment) From 543410d92c5396b34dd32faf9f0a739d185da011 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 16:47:43 +0300 Subject: [PATCH 18/42] storage update --- progress/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/progress/views.py b/progress/views.py index 495f542..842a3ec 100644 --- a/progress/views.py +++ b/progress/views.py @@ -291,6 +291,7 @@ class SetProgress(APIView): course_slug = request.JSON.get('course_slug', None) topic_sort = int(request.JSON.get('topic', 1)) lesson_sort = int(request.JSON.get('lesson', 1)) + only_watch = request.JSON.get('only_watch', False) force = request.JSON.get('force', False) if course_slug is None: @@ -318,6 +319,9 @@ class SetProgress(APIView): teacher = get_user_model().objects.get(out_key=course.get_teacher()) progress = Progress.objects.create(course_token=course.token, user=student, teacher=teacher) + progress.only_watch = only_watch + progress.save() + token_list = [] lesson_list = [] for topic_idx, topic in enumerate(course.topic_set.all()): From dbbcb508bced9f71c35900a21acb907e73258206 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 14 Mar 2018 17:47:42 +0300 Subject: [PATCH 19/42] storage update --- courses/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/courses/models.py b/courses/models.py index 934a43b..3bc61ac 100755 --- a/courses/models.py +++ b/courses/models.py @@ -158,10 +158,10 @@ class Course(models.Model): lessons = self.get_lesson_list() try: n = lessons[lessons.index(lesson)+1] - if f is None or f(lesson): + if f is None or f(n): return n else: - return self.get_next(lesson, f) + return self.get_next(n, f) except IndexError: pass @@ -170,7 +170,7 @@ class Course(models.Model): idx = lessons.index(lesson) - 1 if idx > -1: prev = lessons[idx] - if f is None or f(lesson): + if f is None or f(prev): return prev else: return self.get_previous(prev, f) From c708310e432c772c46af20bc2053c91d0e7de9c5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 13:56:08 +0300 Subject: [PATCH 20/42] storage update --- finance/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/models.py b/finance/models.py index 9516873..ae56954 100755 --- a/finance/models.py +++ b/finance/models.py @@ -44,7 +44,7 @@ class Invoice(models.Model): ) status = models.CharField(verbose_name='Статус', max_length=1, default='W', choices=BILL_STATUSES) 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) 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) From 6de90bb28629c33f87aaa7963903c679b2e998b6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 14:00:00 +0300 Subject: [PATCH 21/42] pay --- finance/migrations/0003_auto_20180315_1358.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 finance/migrations/0003_auto_20180315_1358.py diff --git a/finance/migrations/0003_auto_20180315_1358.py b/finance/migrations/0003_auto_20180315_1358.py new file mode 100644 index 0000000..67c8bd1 --- /dev/null +++ b/finance/migrations/0003_auto_20180315_1358.py @@ -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='Полученная сумма'), + ), + ] From 084d8661724876220dadc8e2226fcfad431aa894 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 14:46:24 +0300 Subject: [PATCH 22/42] pay --- progress/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/progress/views.py b/progress/views.py index 842a3ec..50d2b61 100644 --- a/progress/views.py +++ b/progress/views.py @@ -190,7 +190,7 @@ class StudentUpdateProgress(APIView): ) if pv.status == ProgressLesson.STATUSES.done: - Response(SecureProgressSerializer(pv.progress).data, status=200) + return Response(SecureProgressSerializer(pv.progress).data, status=200) if not pv.status == ProgressLesson.STATUSES.wait: if pv.checker == pv.progress.user: From fad1ac9b0419988a0125705457326653e5463bcb Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 16:30:40 +0300 Subject: [PATCH 23/42] pay --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index c97513f..c6bdf3c 100644 --- a/finance/views.py +++ b/finance/views.py @@ -269,7 +269,7 @@ class YandexAvisoView(APIView): logger_yandex.error("Payment with invoice_id=%s not found" % data['orderNumber']) return Response(status=204) - logger_yandex.info('Get success pay with invoice_id(yandex) %s' % pay.invoice_id) + logger_yandex.info('Get success pay with invoice_id(yandex) %s' % str(data['invoiceId'])) pay.shop_amount = data['shopSumAmount'] pay.status = Payment.STATUS.SUCCESS From 35c0f4c5916ef4c5cb7b0c4e0d796ed3e520c52f Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 17:30:59 +0300 Subject: [PATCH 24/42] pay --- finance/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/finance/views.py b/finance/views.py index c6bdf3c..e83f7d5 100644 --- a/finance/views.py +++ b/finance/views.py @@ -273,6 +273,8 @@ class YandexAvisoView(APIView): pay.shop_amount = data['shopSumAmount'] pay.status = Payment.STATUS.SUCCESS + now = timezone.now() + pay.performed_datetime = now.isoformat() pay.save() xml_res = """ """ % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id)) From 0adfde03b42d5caf3968271d176976d2c3a6fe90 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 18:38:01 +0300 Subject: [PATCH 25/42] pay --- finance/signals.py | 5 +++-- finance/views.py | 29 ++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/finance/signals.py b/finance/signals.py index 110a4f5..bd8d330 100644 --- a/finance/signals.py +++ b/finance/signals.py @@ -36,7 +36,7 @@ def invoice_signal(instance, **kwargs): user=instance.bill.user, ) except Progress.DoesNotExist: - p=Progress.objects.create( + p = Progress.objects.create( course_token=instance.bill.course_token, user=instance.bill.user, teacher=get_user_model().objects.get(out_key=course.get_teacher()) @@ -54,6 +54,7 @@ def invoice_signal(instance, **kwargs): % (course.title, settings.DOMAIN, course.slug), 'robo@skillbox.ru', [instance.bill.user.email], + bcc=[instance.bill.opener.email], reply_to=[instance.bill.opener.email], ) else: @@ -62,7 +63,7 @@ def invoice_signal(instance, **kwargs): '''Курс "%s" был забронирован''' % course.title, 'robo@skillbox.ru', [instance.bill.user.email], - cc=[instance.bill.opener.email], + bcc=[instance.bill.opener.email], reply_to=[instance.bill.opener.email], ) msg.send() diff --git a/finance/views.py b/finance/views.py index e83f7d5..8ae4166 100644 --- a/finance/views.py +++ b/finance/views.py @@ -1,10 +1,9 @@ import csv import logging -import datetime -import dicttoxml import requests from django.contrib.auth import get_user_model +from django.core.mail import EmailMessage from django.db.models import Q from django.http import HttpResponse, HttpResponseForbidden from django.shortcuts import redirect @@ -14,6 +13,7 @@ from rest_framework.views import APIView from yandex_money.models import Payment from django.conf import settings +from courses.models import Course from courses.api import CourseParamsApi from finance.models import Bill, Invoice from finance.serializers import BillSerializer, InvoiceSerializer @@ -237,9 +237,6 @@ class YandexCheckView(APIView): % (pay.order_amount, data['orderSumAmount'])) return Response(status=204) - # TODO Нужно решение - # pay.invoice_id = int(data['invoiceId']) - # pay.save() now = timezone.now() pay.performed_datetime = now.isoformat() pay.save() @@ -249,6 +246,17 @@ class YandexCheckView(APIView): logger_yandex.info(xml_res) + msg = EmailMessage( + 'Списание средств с карты пользователя.', + '''Пользователь "%s", переволит %s рублей. Номер платежа в яндекс кассе %s''' + % (pay.invoice.bill.user.email, str(pay.invoice.real_price), str(data['invoiceId'])), + 'robo@skillbox.ru', + [pay.invoice.bill.opener.email], + bcc='dmitry.dolya@skillbox.ru', + ) + + msg.send() + return HttpResponse(xml_res, content_type='application/xml') @@ -281,6 +289,17 @@ class YandexAvisoView(APIView): 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', + ) + + msg.send() + return HttpResponse(xml_res, content_type='application/xml') From fa7261821c54376c4e75868213c7f110286056f9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 18:44:54 +0300 Subject: [PATCH 26/42] pay --- finance/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/finance/views.py b/finance/views.py index 8ae4166..9d52610 100644 --- a/finance/views.py +++ b/finance/views.py @@ -252,7 +252,7 @@ class YandexCheckView(APIView): % (pay.invoice.bill.user.email, str(pay.invoice.real_price), str(data['invoiceId'])), 'robo@skillbox.ru', [pay.invoice.bill.opener.email], - bcc='dmitry.dolya@skillbox.ru', + bcc=['dmitry.dolya@skillbox.ru'], ) msg.send() @@ -295,7 +295,7 @@ class YandexAvisoView(APIView): % (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', + bcc=['dmitry.dolya@skillbox.ru'], ) msg.send() From 8da2c4205d4ef4f4e50c0f37de871d71fccb93cb Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Mar 2018 18:53:19 +0300 Subject: [PATCH 27/42] pay --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index 9d52610..3c03ac0 100644 --- a/finance/views.py +++ b/finance/views.py @@ -292,7 +292,7 @@ class YandexAvisoView(APIView): msg = EmailMessage( 'Успешная оплата.', '''Пользователь "%s", перевёл %s рублей. Номер платежа в яндекс кассе %s''' - % (pay.invoice.bill.user.email, str(pay.invoice.price), str(data['invoiceId'])), + % (pay.invoice.bill.user.email, str(pay.shop_amount), str(data['invoiceId'])), 'robo@skillbox.ru', [pay.invoice.bill.opener.email], bcc=['dmitry.dolya@skillbox.ru'], From 53cb094345c7307c18005449e6beac33d5e6566a Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 11:41:54 +0300 Subject: [PATCH 28/42] pay --- finance/views.py | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/finance/views.py b/finance/views.py index 3c03ac0..1656baa 100644 --- a/finance/views.py +++ b/finance/views.py @@ -71,6 +71,21 @@ class BillListView(APIView): invoice.yandex_pay = yandex_pay invoice.save() + msg = EmailMessage( + 'Выставден новый счёт.', + '''Менеджер %s выставил счёт пользователю %s на курс "%s".''' + % ( + invoice.bill.opener.full_name(), + invoice.bill.user.email, + Course.objects.get(invoice.bill.course_token).title, + ), + 'robo@skillbox.ru', + [invoice.bill.opener.email], + bcc=['dmitry.dolya@skillbox.ru'], + ) + + msg.send() + invoices = [j for j in invoices if not j.id == invoice.id] [i.delete() for i in invoices] @@ -159,6 +174,17 @@ class YandexPay(APIView): 'shopFailURL': settings.YANDEX_MONEY_FAIL_URL, }) + msg = EmailMessage( + 'Пользователь перешёл на страницу оплаты.', + '''Пользователь "%s", переводит %s рублей.''' + % (pay.invoice.bill.user.email, str(pay.invoice.real_price)), + 'robo@skillbox.ru', + [pay.invoice.bill.opener.email], + bcc=['dmitry.dolya@skillbox.ru'], + ) + + msg.send() + return redirect(r.url) except Payment.DoesNotExist: @@ -177,8 +203,8 @@ def get_invoices(request): file_name = file_name + "__to_%s" % date_to if date_to else file_name invoices = Invoice.objects.filter(method="Y", status="F") - invoices = invoices.filter(date__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__lt=date_to) if date_to else invoices + invoices = invoices.filter(yandex_pay__performed_datetime__gte=date_from) if date_from else invoices response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="%s.csv"' % file_name @@ -189,8 +215,8 @@ def get_invoices(request): for i in invoices.order_by('-date'): course_api = CourseParamsApi(i.bill.course_token) writer.writerow([ - i.date.date(), - i.date.time(), + i.yandex_pay.performed_datetime.date(), + i.yandex_pay.performed_datetime.time(), i.bill.user.email, i.bill.user.get_full_name(), course_api.get_slug_and_title()['title'], @@ -246,17 +272,6 @@ class YandexCheckView(APIView): logger_yandex.info(xml_res) - msg = EmailMessage( - 'Списание средств с карты пользователя.', - '''Пользователь "%s", переволит %s рублей. Номер платежа в яндекс кассе %s''' - % (pay.invoice.bill.user.email, str(pay.invoice.real_price), str(data['invoiceId'])), - 'robo@skillbox.ru', - [pay.invoice.bill.opener.email], - bcc=['dmitry.dolya@skillbox.ru'], - ) - - msg.send() - return HttpResponse(xml_res, content_type='application/xml') From 19021a4eab12a51cfdc0fb526c330002ebc70888 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 11:50:03 +0300 Subject: [PATCH 29/42] pay --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index 1656baa..9b99799 100644 --- a/finance/views.py +++ b/finance/views.py @@ -75,7 +75,7 @@ class BillListView(APIView): 'Выставден новый счёт.', '''Менеджер %s выставил счёт пользователю %s на курс "%s".''' % ( - invoice.bill.opener.full_name(), + invoice.bill.opener.get_full_name(), invoice.bill.user.email, Course.objects.get(invoice.bill.course_token).title, ), From a11d6c698fe21c322614fe34c8508ec524c1b99b Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 12:04:18 +0300 Subject: [PATCH 30/42] pay --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index 9b99799..d43b591 100644 --- a/finance/views.py +++ b/finance/views.py @@ -77,7 +77,7 @@ class BillListView(APIView): % ( invoice.bill.opener.get_full_name(), invoice.bill.user.email, - Course.objects.get(invoice.bill.course_token).title, + Course.objects.get(token=invoice.bill.course_token).title, ), 'robo@skillbox.ru', [invoice.bill.opener.email], From f1b91d347ed3d003bf8546e948ba309c762a607b Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 12:13:38 +0300 Subject: [PATCH 31/42] pay --- finance/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/finance/views.py b/finance/views.py index d43b591..c0f9505 100644 --- a/finance/views.py +++ b/finance/views.py @@ -72,7 +72,7 @@ class BillListView(APIView): invoice.save() msg = EmailMessage( - 'Выставден новый счёт.', + 'Выставлен новый счёт.', '''Менеджер %s выставил счёт пользователю %s на курс "%s".''' % ( invoice.bill.opener.get_full_name(), @@ -176,8 +176,8 @@ class YandexPay(APIView): msg = EmailMessage( 'Пользователь перешёл на страницу оплаты.', - '''Пользователь "%s", переводит %s рублей.''' - % (pay.invoice.bill.user.email, str(pay.invoice.real_price)), + '''Пользователь "%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'], From b4d4402c73897db372e0b9f8c9ec081c6a3888b5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 13:23:21 +0300 Subject: [PATCH 32/42] pay --- finance/views.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/finance/views.py b/finance/views.py index c0f9505..98a0725 100644 --- a/finance/views.py +++ b/finance/views.py @@ -42,24 +42,40 @@ class BillListView(APIView): or request.user.is_superuser): bill = request.JSON.get('bill') children = request.JSON.get('children', []) - bill_kwarg = dict() if bill: - bill_kwarg['user'] = get_user_model().objects.get(email=bill['user']) - bill_kwarg['opener'] = get_user_model().objects.get(email=bill['opener']) - bill_kwarg['description'] = bill['description'] - bill_kwarg['comment'] = bill['comment'] - bill_kwarg['course_token'] = bill['course_token'] + user = get_user_model().objects.get(email=bill['user']) + opener = get_user_model().objects.get(email=bill['opener']) + description = bill['description'] + comment = bill['comment'] + course_token = bill['course_token'] + + try: + 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() - bill_obj, is_create = Bill.objects.update_or_create(**bill_kwarg) invoices = bill_obj.invoice_set.all() for i in children: - i['method'] = get_real_name(elem=i['method'], array=Invoice.BILL_METHOD) - i['status'] = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) - i['bill'] = bill_obj - i['yandex_pay'] = None - invoice, _is_create = Invoice.objects.update_or_create(**i) + status = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) + try: + invoice = Invoice.objects.get(id=i['id']) + if invoice.status == "P" or invoice.status == status: + continue + + 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'], From 9364171ac05c618b1cc7f3ce1ad5a67305442b74 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 13:30:50 +0300 Subject: [PATCH 33/42] pay --- finance/views.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/finance/views.py b/finance/views.py index 98a0725..800786c 100644 --- a/finance/views.py +++ b/finance/views.py @@ -60,8 +60,6 @@ class BillListView(APIView): bill_obj.comment = comment bill_obj.save() - invoices = bill_obj.invoice_set.all() - for i in children: status = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) try: @@ -102,10 +100,6 @@ class BillListView(APIView): msg.send() - invoices = [j for j in invoices if not j.id == invoice.id] - - [i.delete() for i in invoices] - res = { "bill": BillSerializer(bill_obj).data, "children": [InvoiceSerializer(i).data for i in bill_obj.invoice_set.all()], From 3c28c27bd55b9665187571c9e2129b63e4fb7ef7 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 13:37:04 +0300 Subject: [PATCH 34/42] pay --- finance/views.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/finance/views.py b/finance/views.py index 800786c..5d6f2ae 100644 --- a/finance/views.py +++ b/finance/views.py @@ -63,9 +63,17 @@ class BillListView(APIView): for i in children: status = get_real_name(elem=i['status'], array=Invoice.BILL_STATUSES) try: - invoice = Invoice.objects.get(id=i['id']) - if invoice.status == "P" or invoice.status == status: - continue + invoice_id = i['id'] + except KeyError: + invoice_id = None + + 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) From 36fce7172c9f71c2b78454c3276f323b0ad397b2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 15:42:10 +0300 Subject: [PATCH 35/42] pay --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index 5d6f2ae..c785ca9 100644 --- a/finance/views.py +++ b/finance/views.py @@ -325,7 +325,7 @@ class YandexAvisoView(APIView): msg = EmailMessage( 'Успешная оплата.', '''Пользователь "%s", перевёл %s рублей. Номер платежа в яндекс кассе %s''' - % (pay.invoice.bill.user.email, str(pay.shop_amount), str(data['invoiceId'])), + % (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'], From 9293ae5697c0824d98ee300b15f2fa16fff50659 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 16 Mar 2018 18:11:21 +0300 Subject: [PATCH 36/42] pay --- finance/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finance/views.py b/finance/views.py index c785ca9..ea823cb 100644 --- a/finance/views.py +++ b/finance/views.py @@ -328,7 +328,7 @@ class YandexAvisoView(APIView): % (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'], + bcc=['dmitry.dolya@skillbox.ru', 'vera.procenko@skillbox.ru'], ) msg.send() From 71fd94d61faf63f5c120b87c48c06102adabafca Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 19 Mar 2018 17:50:22 +0300 Subject: [PATCH 37/42] pay --- courses/serializers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/courses/serializers.py b/courses/serializers.py index 359386b..e4fe7ce 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -1,3 +1,4 @@ +from django.contrib.auth import get_user_model from rest_framework import serializers from courses.models import Course, Lesson, Topic @@ -82,11 +83,16 @@ class CourseDetailSerializer(serializers.ModelSerializer): level = serializers.SerializerMethodField() direction = serializers.SerializerMethodField() statistic = serializers.SerializerMethodField() + teacher_emails = serializers.SerializerMethodField() class Meta: model = Course exclude = ('id', ) + @staticmethod + def get_teacher_emails(self): + return [get_user_model().objects.get(out_key=i).email for i in self.teacher_tokens] + @staticmethod def get_level(self): return self.get_level_display() From 77bcaa95f6c39d5df6085d919c1d39fc758fd1ba Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 19 Mar 2018 17:55:59 +0300 Subject: [PATCH 38/42] pay --- courses/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/courses/serializers.py b/courses/serializers.py index e4fe7ce..139b545 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -91,7 +91,7 @@ class CourseDetailSerializer(serializers.ModelSerializer): @staticmethod def get_teacher_emails(self): - return [get_user_model().objects.get(out_key=i).email for i in self.teacher_tokens] + return [i.email for i in get_user_model().objects.filter(out_key__in=self.teacher_tokens)] @staticmethod def get_level(self): From 63ccf9f0853ffde282b3a11b23ff3819a565c69f Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 19 Mar 2018 18:13:20 +0300 Subject: [PATCH 39/42] pay --- courses/serializers.py | 6 ------ courses/views.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/courses/serializers.py b/courses/serializers.py index 139b545..359386b 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -1,4 +1,3 @@ -from django.contrib.auth import get_user_model from rest_framework import serializers from courses.models import Course, Lesson, Topic @@ -83,16 +82,11 @@ class CourseDetailSerializer(serializers.ModelSerializer): level = serializers.SerializerMethodField() direction = serializers.SerializerMethodField() statistic = serializers.SerializerMethodField() - teacher_emails = serializers.SerializerMethodField() class Meta: model = Course exclude = ('id', ) - @staticmethod - def get_teacher_emails(self): - return [i.email for i in get_user_model().objects.filter(out_key__in=self.teacher_tokens)] - @staticmethod def get_level(self): return self.get_level_display() diff --git a/courses/views.py b/courses/views.py index 1a6138b..5db4617 100644 --- a/courses/views.py +++ b/courses/views.py @@ -89,7 +89,7 @@ class LessonInfoView(APIView): lesson = Lesson.objects.get(token=token) except Lesson.DoesNotExist: 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("Пользователь не является преподователем по курсу", status=403) From a0fc46bdb2acb5743f2e9beb20663c331cdfbcd1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Mar 2018 11:40:42 +0300 Subject: [PATCH 40/42] pay --- progress/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/progress/views.py b/progress/views.py index 50d2b61..01513e8 100644 --- a/progress/views.py +++ b/progress/views.py @@ -127,7 +127,7 @@ class TeacherUpdateProgress(APIView): pv.status = ProgressLesson.STATUSES.fail msg = EmailMessage( 'Ваша работа отправлена на доработку', - '''Преподователь "%s" отклонил вашу работу''' % request.user.get_full_name(), + '''Преподаватель "%s" отклонил вашу работу''' % request.user.get_full_name(), 'robo@skillbox.ru', [student.email], ) From 8c4aec26fad06cbae5a3b5e6182bd6fe28c92987 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Mar 2018 11:55:20 +0300 Subject: [PATCH 41/42] pay --- finance/views.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/finance/views.py b/finance/views.py index ea823cb..0020e53 100644 --- a/finance/views.py +++ b/finance/views.py @@ -257,28 +257,41 @@ class YandexCheckView(APIView): val = i.split('=')[1] data[key] = val - logger_yandex.info(data) + logger_yandex.info('Проверка платежа запрос', exc_info=True, extra={ + 'request': data, + }) + try: pay = Payment.objects.get(order_number=data['orderNumber']) 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) 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) 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) 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) if not pay.order_amount == float(data['orderSumAmount']): - logger_yandex.error("Expected amount is %s received amount is %s" - % (pay.order_amount, data['orderSumAmount'])) + logger_yandex.error('Ошибка проверки платежа', exc_info=True, extra={ + 'request': "Expected amount is %s received amount is %s" + % (pay.order_amount, data['orderSumAmount']), + }) return Response(status=204) now = timezone.now() @@ -288,7 +301,9 @@ class YandexCheckView(APIView): xml_res = """ """ % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id)) - logger_yandex.info(xml_res) + logger_yandex.info('Проверка платежа ответ', exc_info=True, extra={ + 'request': xml_res, + }) return HttpResponse(xml_res, content_type='application/xml') From a3a5011a06fb2a498026378229d8354de12aee35 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 20 Mar 2018 12:07:28 +0300 Subject: [PATCH 42/42] finance logging --- finance/views.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/finance/views.py b/finance/views.py index 0020e53..4a4de84 100644 --- a/finance/views.py +++ b/finance/views.py @@ -302,7 +302,7 @@ class YandexCheckView(APIView): """ % (pay.performed_datetime, str(data['invoiceId']), str(pay.shop_id)) logger_yandex.info('Проверка платежа ответ', exc_info=True, extra={ - 'request': xml_res, + 'response': xml_res, }) return HttpResponse(xml_res, content_type='application/xml') @@ -322,10 +322,14 @@ class YandexAvisoView(APIView): try: pay = Payment.objects.get(order_number=data['orderNumber']) 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) - logger_yandex.info('Get success pay with invoice_id(yandex) %s' % str(data['invoiceId'])) + 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.status = Payment.STATUS.SUCCESS @@ -335,7 +339,10 @@ class YandexAvisoView(APIView): xml_res = """ """ % (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, + }) + msg = EmailMessage( 'Успешная оплата.',