parent
deb0c6142c
commit
5c2a9d33cc
24 changed files with 323 additions and 309 deletions
@ -1,7 +1,7 @@ |
|||||||
# encoding=utf-8 |
# encoding=utf-8 |
||||||
|
|
||||||
from .other import Account, Invite |
from .other import Account, Invite |
||||||
from access.models.progress import Progress |
from progress.models import Progress |
||||||
from .user import User |
from .user import User |
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@ |
|||||||
|
default_app_config = "progress.apps.ProgressAppConfig" |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from django.apps import AppConfig |
||||||
|
|
||||||
|
|
||||||
|
class ProgressAppConfig(AppConfig): |
||||||
|
name = "progress" |
||||||
|
verbose_name = "Прогрес" |
||||||
@ -0,0 +1,60 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
# Generated by Django 1.11.6 on 2018-01-31 18:55 |
||||||
|
from __future__ import unicode_literals |
||||||
|
|
||||||
|
from django.conf import settings |
||||||
|
import django.contrib.postgres.fields |
||||||
|
from django.db import migrations, models |
||||||
|
import django.db.models.deletion |
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration): |
||||||
|
|
||||||
|
initial = True |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
||||||
|
] |
||||||
|
|
||||||
|
operations = [ |
||||||
|
migrations.CreateModel( |
||||||
|
name='Progress', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('hidden_lessons', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, unique=True, verbose_name='Токен урока'), default=[], size=None, verbose_name='Список скрытых уроков')), |
||||||
|
('course_token', models.UUIDField(editable=False, verbose_name='Токен курса')), |
||||||
|
('active_lesson', models.UUIDField(blank=True, null=True, verbose_name='Токен активного урока')), |
||||||
|
('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teacher_progress', to=settings.AUTH_USER_MODEL, verbose_name='Преподователь по умолчанию')), |
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент')), |
||||||
|
], |
||||||
|
options={ |
||||||
|
'verbose_name': 'Прогресс пользователя', |
||||||
|
'verbose_name_plural': 'Прогресс пользователя', |
||||||
|
}, |
||||||
|
), |
||||||
|
migrations.CreateModel( |
||||||
|
name='ProgressLesson', |
||||||
|
fields=[ |
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||||
|
('lesson_token', models.UUIDField(editable=False, verbose_name='Токен урока')), |
||||||
|
('finish_date', models.DateTimeField(blank=True, null=True, verbose_name='Дата зачтения задания')), |
||||||
|
('start_date', models.DateTimeField(auto_now_add=True, verbose_name='Дата начала прохождения задания')), |
||||||
|
('status', models.CharField(choices=[('done', 'done'), ('wait', 'wait'), ('fail', 'fail')], default='wait', max_length=20)), |
||||||
|
('comment_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, verbose_name='Токен комента'), default=[], size=None)), |
||||||
|
('checker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Преподователь')), |
||||||
|
('progress', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='progress.Progress')), |
||||||
|
], |
||||||
|
options={ |
||||||
|
'verbose_name': 'Прохождение уроков', |
||||||
|
'verbose_name_plural': 'Прохождение урока', |
||||||
|
}, |
||||||
|
), |
||||||
|
migrations.AlterUniqueTogether( |
||||||
|
name='progresslesson', |
||||||
|
unique_together=set([('progress', 'lesson_token')]), |
||||||
|
), |
||||||
|
migrations.AlterUniqueTogether( |
||||||
|
name='progress', |
||||||
|
unique_together=set([('user', 'course_token')]), |
||||||
|
), |
||||||
|
] |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
from rest_framework import serializers |
||||||
|
|
||||||
|
from progress.models import Progress, ProgressLesson |
||||||
|
|
||||||
|
|
||||||
|
class ProgressSerializer(serializers.ModelSerializer): |
||||||
|
lessons = serializers.SerializerMethodField() |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Progress |
||||||
|
fields = ('lessons', 'course_token') |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def get_lessons(self): |
||||||
|
return [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()] |
||||||
|
|
||||||
|
|
||||||
|
class ProgressAnalyticSerializer(serializers.ModelSerializer): |
||||||
|
name = serializers.SerializerMethodField() |
||||||
|
email = serializers.SerializerMethodField() |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = Progress |
||||||
|
fields = ('name', 'email',) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def get_name(self): |
||||||
|
return self.user.get_full_name() |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def get_email(self): |
||||||
|
return self.user.email |
||||||
|
|
||||||
|
|
||||||
|
class ProgressLessonSerializer(serializers.ModelSerializer): |
||||||
|
teacher = serializers.SerializerMethodField() |
||||||
|
|
||||||
|
class Meta: |
||||||
|
model = ProgressLesson |
||||||
|
exclude = ('id', 'progress') |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def get_teacher(self): |
||||||
|
return self.teacher.get_full_name() |
||||||
@ -0,0 +1,184 @@ |
|||||||
|
import csv |
||||||
|
import datetime |
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model |
||||||
|
from django.core.exceptions import ValidationError |
||||||
|
from django.http import HttpResponse |
||||||
|
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 progress.models import ProgressLesson, Progress |
||||||
|
from progress.serializers import ProgressAnalyticSerializer, ProgressLessonSerializer |
||||||
|
from courses.api import CourseProgressApi |
||||||
|
from progress.tasks import add_next_lesson |
||||||
|
|
||||||
|
|
||||||
|
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() |
||||||
|
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 UpdateProgress(APIView): |
||||||
|
renderer_classes = (JSONRenderer,) |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def post(request): |
||||||
|
""" |
||||||
|
На вход обязательно передаётся параметр id (id узла). |
||||||
|
""" |
||||||
|
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', 'wait') |
||||||
|
comment = request.JSON.get('comment', None) |
||||||
|
|
||||||
|
if lesson_token is None or course_token is None: |
||||||
|
return Response('Не передан слаг курса или токен урока', status=400) |
||||||
|
try: |
||||||
|
is_student = student_out_key is None |
||||||
|
student = request.user if is_student else get_user_model().objects.get(out_key=student_out_key) |
||||||
|
|
||||||
|
if is_student: |
||||||
|
p = Progress.objects.get(user=student, course_token=course_token) |
||||||
|
else: |
||||||
|
p = Progress.objects.get( |
||||||
|
user=student, |
||||||
|
teacher=request.user, |
||||||
|
course_token=course_token, |
||||||
|
) |
||||||
|
|
||||||
|
if p.active_lesson == lesson_token: |
||||||
|
return Response("Ошибка доступа", status=403) |
||||||
|
|
||||||
|
try: |
||||||
|
pv = ProgressLesson.objects.get( |
||||||
|
progress=p, |
||||||
|
lesson_token=p.active_lesson, |
||||||
|
) |
||||||
|
|
||||||
|
if is_student and pv.status == ProgressLesson.STATUSES.fail and action == "wait": |
||||||
|
pv.status = ProgressLesson.STATUSES.wait |
||||||
|
|
||||||
|
elif not is_student and pv.status == ProgressLesson.STATUSES.wait: |
||||||
|
pv.status = action |
||||||
|
|
||||||
|
else: |
||||||
|
return Response("Ошибка прав доступа", status=403) |
||||||
|
|
||||||
|
pv.comment_tokens.append(comment) |
||||||
|
|
||||||
|
except ProgressLesson.DoesNotExist: |
||||||
|
pv = ProgressLesson.objects.create( |
||||||
|
date=datetime.datetime.now(), |
||||||
|
teacher=p.teacher, |
||||||
|
progress=p, |
||||||
|
lesson_token=p.active_lesson, |
||||||
|
status=ProgressLesson.STATUSES.done |
||||||
|
) |
||||||
|
|
||||||
|
pv.save() |
||||||
|
|
||||||
|
if pv.status == ProgressLesson.STATUSES.done: |
||||||
|
#TODO: Ассинхроннаязадача для celery |
||||||
|
add_next_lesson(p) |
||||||
|
|
||||||
|
return Response(ProgressLessonSerializer(pv).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 |
||||||
Loading…
Reference in new issue