diff --git a/access/admin.py b/access/admin.py index 278435d..091e649 100755 --- a/access/admin.py +++ b/access/admin.py @@ -1,12 +1,9 @@ from django.contrib import admin from access.models.other import Invite, Account, ResetPassword, UserActivity -from access.models.progress import ProgressLesson, Progress from access.models.user import User admin.site.register(User) admin.site.register(Account) -admin.site.register(Progress) admin.site.register(Invite) admin.site.register(ResetPassword) -admin.site.register(ProgressLesson) admin.site.register(UserActivity) diff --git a/access/migrations/0004_auto_20180129_0933.py b/access/migrations/0004_auto_20180129_0933.py new file mode 100644 index 0000000..7440266 --- /dev/null +++ b/access/migrations/0004_auto_20180129_0933.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-01-29 09:33 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0003_useractivity'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='progress', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='progress', + name='teacher', + ), + migrations.RemoveField( + model_name='progress', + name='user', + ), + migrations.AlterUniqueTogether( + name='progresslesson', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='progresslesson', + name='progress', + ), + migrations.RemoveField( + model_name='progresslesson', + name='teacher', + ), + migrations.DeleteModel( + name='Progress', + ), + migrations.DeleteModel( + name='ProgressLesson', + ), + ] diff --git a/access/models/__init__.py b/access/models/__init__.py index 666ece4..ae43d1e 100755 --- a/access/models/__init__.py +++ b/access/models/__init__.py @@ -1,7 +1,6 @@ # encoding=utf-8 from .other import Account, Invite -from access.models.progress import Progress from .user import User diff --git a/access/serializers.py b/access/serializers.py index 54d8690..425ffd5 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -2,52 +2,8 @@ from django.contrib.auth import get_user_model from rest_framework import serializers from access.models.other import Account -from access.models.progress import ProgressLesson -from access.models import Progress from achievements.serialers import DiplomaSerializer, AchievementsSerializer -from courses.api import CourseProgressApi - - -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() - - -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 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()] +from progress.serializers import ProgressSerializer class AccountSerializer(serializers.ModelSerializer): diff --git a/access/urls.py b/access/urls.py index 59946d8..3851074 100644 --- a/access/urls.py +++ b/access/urls.py @@ -13,8 +13,4 @@ urlpatterns = [ url(r'login/$', views.LoginView.as_view()), url(r'logout/$', views.LogoutView.as_view()), url(r'reset/$', views.ResetPasswordView.as_view()), - url(r'progress/$', views.UpdateProgress.as_view()), - url(r'progress_detail/upload/(?P[0-9A-Fa-f-]+)/$', views.UploadCourseProgressUserView.as_view()), - url(r'progress_detail/(?P[0-9A-Fa-f-]+)/$', views.CourseProgressUserView.as_view()), - url(r'progress_dynamic/(?P[0-9A-Fa-f-]+)/$', views.CourseProgressDynamicView.as_view()), ] \ No newline at end of file diff --git a/access/views.py b/access/views.py index 7a0087c..f7ef4eb 100644 --- a/access/views.py +++ b/access/views.py @@ -16,10 +16,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from access.models.other import Invite, ResetPassword -from access.models.progress import ProgressLesson -from access.models import Progress -from access.serializers import UserSelfSerializer, UserSearchSerializer, ProgressLessonSerializer, \ - ProgressAnalyticSerializer +from access.serializers import UserSelfSerializer, UserSearchSerializer from courses.api import CourseProgressApi @@ -311,73 +308,6 @@ class UserGuardView(APIView): return res_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() - 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 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 MinUserView(APIView): renderer_classes = (JSONRenderer,) diff --git a/api_v1/urls.py b/api_v1/urls.py index 0208884..bcfb4d0 100644 --- a/api_v1/urls.py +++ b/api_v1/urls.py @@ -6,4 +6,5 @@ urlpatterns = [ url(r'library/', include('library.urls')), url(r'finance/', include('finance.urls')), url(r'storage/', include('storage.urls')), -] \ No newline at end of file + url(r'progress/', include('progress.urls')), +] diff --git a/csv/load_bills.py b/csv/load_bills.py index 01ffb2c..75fa3eb 100644 --- a/csv/load_bills.py +++ b/csv/load_bills.py @@ -15,7 +15,7 @@ django.setup() from yandex_money.models import Payment from finance.models import Bill, Invoice -from access.models import Progress +from progress.models import Progress from courses.models import Course if __name__ == '__main__': diff --git a/csv/load_comments.py b/csv/load_comments.py index 150e2a6..6922d78 100644 --- a/csv/load_comments.py +++ b/csv/load_comments.py @@ -1,6 +1,4 @@ import csv -import random -import string import django import os @@ -15,7 +13,7 @@ django.setup() from storage.models import Comment, File from courses.models import Lesson -from access.models.progress import ProgressLesson, Progress +from progress.models import ProgressLesson, Progress if __name__ == '__main__': csv.field_size_limit(500 * 1024 * 1024) diff --git a/csv/load_perm.py b/csv/load_perm.py index 78d42cd..f5bba11 100644 --- a/csv/load_perm.py +++ b/csv/load_perm.py @@ -14,8 +14,7 @@ django.setup() from django.contrib.auth.models import Group from django.contrib.auth import get_user_model -from access.models.progress import ProgressLesson -from access.models import Progress +from progress.models import ProgressLesson, Progress from courses.models import Lesson, Course diff --git a/lms/settings.py b/lms/settings.py index 52935f1..841a358 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -113,6 +113,7 @@ apps = ( 'library', 'achievements', 'config_app', + 'progress', ) INSTALLED_APPS = libs + apps @@ -185,3 +186,6 @@ STATIC_URL = '/static/' RAVEN_CONFIG = { 'dsn': 'http://caaea487274f4e23a9107862484c79f3:3d463ad4717942508536f7a659921950@sentry.skillbox.ru/3' } + + +DEBUG = env('DEBUG') diff --git a/progress/__init__.py b/progress/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/progress/admin.py b/progress/admin.py new file mode 100644 index 0000000..fc4f9eb --- /dev/null +++ b/progress/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin + +# Register your models here. +from progress.models import ProgressLesson, Progress + + +admin.site.register(Progress) +admin.site.register(ProgressLesson) diff --git a/progress/apps.py b/progress/apps.py new file mode 100644 index 0000000..1e24c0b --- /dev/null +++ b/progress/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ProgressConfig(AppConfig): + name = 'progress' + verbose_name = 'Прогресс' diff --git a/progress/migrations/0001_initial.py b/progress/migrations/0001_initial.py new file mode 100644 index 0000000..9abd9d4 --- /dev/null +++ b/progress/migrations/0001_initial.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-01-29 09:31 +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='Токен курса')), + ('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='Токен урока')), + ('date', models.DateTimeField(blank=True, null=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)), + ('progress', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='progress.Progress')), + ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Преподователь')), + ], + 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')]), + ), + ] diff --git a/progress/migrations/__init__.py b/progress/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/access/models/progress.py b/progress/models.py similarity index 100% rename from access/models/progress.py rename to progress/models.py diff --git a/progress/serializers.py b/progress/serializers.py new file mode 100644 index 0000000..d4bb4c4 --- /dev/null +++ b/progress/serializers.py @@ -0,0 +1,44 @@ +from rest_framework import serializers + +from progress.models import ProgressLesson, Progress + + +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() + + +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 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()] diff --git a/progress/tests.py b/progress/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/progress/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/progress/urls.py b/progress/urls.py new file mode 100644 index 0000000..3f43770 --- /dev/null +++ b/progress/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url + +from progress import views + + +urlpatterns = [ + url(r'$', views.UpdateProgress.as_view()), + url(r'progress_detail/upload/(?P[0-9A-Fa-f-]+)/$', views.UploadCourseProgressUserView.as_view()), + url(r'progress_detail/(?P[0-9A-Fa-f-]+)/$', views.CourseProgressUserView.as_view()), + url(r'progress_dynamic/(?P[0-9A-Fa-f-]+)/$', views.CourseProgressDynamicView.as_view()), +] \ No newline at end of file diff --git a/progress/views.py b/progress/views.py new file mode 100644 index 0000000..0bc2a92 --- /dev/null +++ b/progress/views.py @@ -0,0 +1,117 @@ +import datetime + +import csv +from django.http import HttpResponse +from django.shortcuts import render + +# Create your views here. +from rest_framework.exceptions import ValidationError +from rest_framework.renderers import JSONRenderer +from rest_framework.response import Response +from rest_framework.views import APIView + +from courses.api import CourseProgressApi +from progress.models import Progress, ProgressLesson +from progress.serializers import ProgressLessonSerializer, ProgressAnalyticSerializer + + +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) + if lesson_token is None or course_token is None: + return Response('Не передан слаг курса или токен урока', status=400) + try: + p = Progress.objects.get(user=request.user, course_token=course_token) + try: + pv = ProgressLesson.objects.get( + progress=p, + lesson_token=lesson_token, + ) + + except ProgressLesson.DoesNotExist: + pv = ProgressLesson.objects.create( + date=datetime.datetime.now(), + teacher=p.teacher, + progress=p, + lesson_token=lesson_token, + ) + pv.status = ProgressLesson.STATUSES.done + pv.save() + return Response(ProgressLessonSerializer(pv).data, status=200) + + except Progress.DoesNotExist: + return Response('Не найден прогресс по заданным параметрам', status=404) + + +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 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)