import csv import datetime from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.core.mail import EmailMessage from django.http import HttpResponse, HttpResponseForbidden from rest_framework import permissions from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView from django.db.models import Q from courses.models import Course from progress.models import ProgressLesson, Progress from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer, ProgressSerializer, \ SecureProgressSerializer from courses.api import CourseProgressApi, CourseParamsApi from progress.tasks import add_next_lesson class StudentWorkView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def get(request, teacher_token): client_status = request.GET.get('status', 'in_progress') if request.user.is_authenticated() and request.user.groups.filter(name__in=['teachers', 'admin']).exists(): try: if client_status == 'done': date_from = datetime.datetime.now() - datetime.timedelta(days=7) progress_lessons = ProgressLesson.objects.filter( ~Q(progress__user__out_key=teacher_token), ~Q(comment_tokens__len=0), status='done', checker__out_key=teacher_token, finish_date__gte=date_from, ).order_by('finish_date') else: server_status = Q(status='fail') if client_status == 'not_done' else Q(status='wait') progress_lessons = ProgressLesson.objects.filter( ~Q(progress__user__out_key=teacher_token), server_status, checker__out_key=teacher_token, ).order_by('-last_update') return Response([ProgressLessonSerializer(i).data for i in progress_lessons], status=200) except ValidationError: return Response("Bad request", status=400) return Response(status=403) class CourseProgressDynamicView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def get(request, token): if request.user.is_authenticated() and request.user.is_staff: try: progresses = Progress.objects.filter(course_token=token) res = {} for i in progresses: key = i.progresslesson_set.filter(status="done").count() + 1 res[key] = 1 if not key in res.keys() else res[key] + 1 return Response(res, status=200) except ValidationError: return Response("Bad request", status=400) return Response(status=403) class CourseProgressUserView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def get(request, token): if request.user.is_authenticated() and request.user.is_staff: try: res = [] sorted_token_list = CourseProgressApi.get_topic_lesson(token) for p in Progress.objects.filter(course_token=token): progress = ProgressAnalyticSerializer(p).data progress['progress_course'] = p.progress_status(sorted_token_list) res.append(progress) return Response(res, status=200) except ValidationError: return Response("Bad request", status=400) return Response(status=403) class TeacherUpdateProgress(APIView): renderer_classes = (JSONRenderer,) @staticmethod def post(request): lesson_token = request.JSON.get('lesson_token', None) course_token = request.JSON.get('course_token', None) student_out_key = request.JSON.get('student_out_key', None) action = request.JSON.get('action', None) comment = request.JSON.get('comment', None) if lesson_token is None or course_token is None: return Response('Не передан слаг курса или токен урока', status=400) if student_out_key is None: return Response('Не передан student_out_key', status=400) try: student = get_user_model().objects.get(out_key=student_out_key) p = Progress.objects.get( user=student, teacher=request.user, course_token=course_token, ) try: pv = ProgressLesson.objects.get( progress=p, lesson_token=lesson_token, ) if pv.status == ProgressLesson.STATUSES.wait: if action == "no": pv.status = ProgressLesson.STATUSES.fail msg = EmailMessage( 'Ваша работа отправлена на доработку', '''Преподаватель "%s" отклонил вашу работу''' % request.user.get_full_name(), 'robo@skillbox.ru', [student.email], ) msg.send() elif action == "yes": pv.status = ProgressLesson.STATUSES.done pv.finish_date = datetime.datetime.now() msg = EmailMessage( 'Ваша работа принята', '''Преподователь "%s" принял вашу работу''' % request.user.get_full_name(), 'robo@skillbox.ru', [student.email], ) msg.send() else: Response("Свойство action должно иметь значение либо done, либо fail", status=400) else: return Response("Ошибка прав доступа", status=403) if not comment is None: pv.comment_tokens.append(comment) except ProgressLesson.DoesNotExist: return Response('Урок не проходится этим пользователем', status=403) pv.save() res = {"current": ProgressLessonSerializer(pv).data} if pv.status == ProgressLesson.STATUSES.done: # TODO: Ассинхроннаязадача для celery res['next'] = ProgressLessonSerializer(add_next_lesson(p)).data return Response(res, status=200) except Progress.DoesNotExist: return Response('Не найден прогресс по заданным параметрам', status=404) class StudentUpdateProgress(APIView): permission_classes = (permissions.IsAuthenticated,) renderer_classes = (JSONRenderer,) @staticmethod def post(request): lesson_token = request.JSON.get('lesson_token', None) comment = request.JSON.get('comment', None) if lesson_token is None: return Response('Не передан токен урока', status=400) student = request.user try: pv = ProgressLesson.objects.get( progress__user=student, lesson_token=lesson_token, ) if pv.status == ProgressLesson.STATUSES.done: return Response(SecureProgressSerializer(pv.progress).data, status=200) 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 and\ not pv.progress.progresslesson_set.filter(status=ProgressLesson.STATUSES.wait).exists(): pv.status = ProgressLesson.STATUSES.wait pv.comment_tokens.append(comment) elif comment is None: return Response("Не преложен комментарий", status=400) else: return Response("В настоящее время, мы уже проверяем одно из ваших домашних заданий.
Как " "только оно будет успешно сдано - вы сможете продолжить.", status=403) elif not comment is None: pv.comment_tokens.append(comment) pv.save() return Response(SecureProgressSerializer(pv.progress).data, status=200) except Progress.DoesNotExist: return Response('Не найден прогресс по заданным параметрам', status=404) class UploadCourseProgressUserView(APIView): renderer_classes = (JSONRenderer,) @staticmethod def get(request, token): if request.user.is_authenticated() and request.user.is_staff: try: response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="%s.csv"' % token sorted_token_list = CourseProgressApi.get_topic_lesson(token) writer = csv.writer(response) writer.writerow(['Имя', 'Почта', 'Последняя тема', 'Последний урок']) for p in Progress.objects.filter(course_token=token): progress = ProgressAnalyticSerializer(p).data progress['progress_course'] = p.progress_status(sorted_token_list) writer.writerow([ progress['name'], progress['email'], progress['progress_course'] and progress['progress_course'][0], progress['progress_course'] and progress['progress_course'][1], ]) return response except ValidationError: return Response("Bad request", status=400) return Response(status=403) class UserGuardView(APIView): renderer_classes = (JSONRenderer,) permission_classes = (IsAuthenticated,) @staticmethod def get(request, pk, page): try: user = get_user_model().objects.get(out_key=pk) except get_user_model().DoesNotExist: return Response("User doesn't exist", status=404) is_i = request.user == user res_403 = Response('Permission denied', status=403) res_204 = Response(status=204) if is_i and not request.user.groups.filter(name='teachers').exists() and page == 'homeworks': return res_403 if is_i and not \ request.user.groups.filter(name__in=['students', 'managers', 'lead_managers']).exists() \ and page == 'payment': return res_403 if is_i: return res_204 if page == 'profile' and (request.user.is_superuser or request.user.is_staff): return res_204 return res_403 class SetProgress(APIView): renderer_classes = (JSONRenderer,) permission_classes = (IsAuthenticated,) @staticmethod def post(request): if request.user.is_staff: email = request.JSON.get('email', None) 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: return Response('course_slug не передан', status=400) if email is None: return Response('email не передан', status=400) try: student = get_user_model().objects.get(email=email.lower()) except get_user_model().DoesNotExist: return Response("User doesn't exist", status=404) try: course = Course.objects.get(slug=course_slug) except get_user_model().DoesNotExist: return Response("Course doesn't exist", status=404) try: progress = Progress.objects.get(course_token=course.token, user=student) except Progress.DoesNotExist: if not force: return Response("Студент не проходит этот курс", status=403) 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()): topic_find = topic_idx == (topic_sort - 1) for lesson_idx, lesson in enumerate(topic.lesson_set.all()): token_list.append(lesson.token) lesson_list.append(lesson) if lesson_idx == (lesson_sort - 1) and topic_find: break if topic_find: break if progress.progresslesson_set.filter( ~Q(lesson_token__in=token_list)).exists() and not force: return Response("Пользователь, прошёл дальше по курсу", status=403) progress.progresslesson_set.filter(~Q(lesson_token__in=token_list)).delete() for lesson_idx, lesson_token in enumerate(token_list[:-1]): try: pl = ProgressLesson.objects.get(progress__user=student, lesson_token=lesson_token) if pl.finish_date is None: pl.finish_date = datetime.datetime.now() pl.status = 'done' pl.save() except ProgressLesson.DoesNotExist: ProgressLesson.objects.create( progress=progress, lesson_token=lesson_token, checker=progress.teacher if lesson_list[lesson_idx].is_hm else student, status="done", finish_date=datetime.datetime.now() ) try: pl = ProgressLesson.objects.get(progress=progress, lesson_token=token_list[-1:][0]) if pl.status == "done": pl.status = "start" pl.finish_date = None pl.save() except ProgressLesson.DoesNotExist: ProgressLesson.objects.create( progress=progress, lesson_token=token_list[-1:][0], checker=progress.teacher if lesson_list[-1:][0].is_hm else student ) return Response(status=204) else: return Response("Эта функция доступна только сотрудникам персонала", status=403) def get_teachers_pay(request): if not request.user.is_authenticated and (request.user.groups.filter(name="support") or request.user.is_superuser): return HttpResponseForbidden() date_from = request.GET.get('from', None) date_to = request.GET.get('to', None) email = request.GET.get('email', None) file_name = "teacher_pay_%s" % email file_name = file_name + "__from_%s" % date_from if date_from else file_name file_name = file_name + "__to_%s" % date_to if date_to else file_name progress_lessons = ProgressLesson.objects.filter( ~Q(comment_tokens__len=0), checker__email=email, status='done', ) progress_lessons = progress_lessons.filter(finish_date__lt=date_to) if date_to else progress_lessons progress_lessons = progress_lessons.filter(finish_date__gte=date_from) if date_from else progress_lessons response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="%s.csv"' % file_name writer = csv.writer(response) writer.writerow(['Почта студента', 'Имя студента', 'Курс', 'Дата', 'Время',]) for i in progress_lessons.order_by('-finish_date'): course_api = CourseParamsApi(i.progress.course_token) writer.writerow([ i.progress.user.email, i.progress.user.get_full_name(), course_api.get_slug_and_title()['title'], i.finish_date.date(), i.finish_date.time(), ]) return response