From 89150bb29400a38e7ac88d6b4bd98797f73f0a31 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 16 Jan 2018 15:52:34 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- access/admin.py | 6 +- access/migrations/0001_initial.py | 41 ++----- access/migrations/0003_auto_20180115_1953.py | 20 +++ access/models/progress.py | 52 ++++---- access/models/user.py | 2 +- access/serializers.py | 12 +- access/views.py | 4 +- achievements/migrations/0001_initial.py | 4 +- courses/admin.py | 8 +- courses/migrations/0001_initial.py | 84 ++----------- courses/migrations/0002_init_demands.py | 42 ------- courses/migrations/0003_lesson_old_id.py | 20 --- courses/models.py | 123 ++++++++++--------- courses/serializers.py | 15 +-- courses/views.py | 4 +- csv/load_comments.py | 47 ++++--- csv/load_courses.py | 13 +- csv/load_storage.py | 2 +- finance/migrations/0001_initial.py | 2 +- library/migrations/0001_initial.py | 2 +- storage/api.py | 14 +-- storage/migrations/0001_initial.py | 7 +- storage/models.py | 7 +- storage/tests.py | 20 +-- 24 files changed, 213 insertions(+), 338 deletions(-) create mode 100644 access/migrations/0003_auto_20180115_1953.py delete mode 100644 courses/migrations/0002_init_demands.py delete mode 100644 courses/migrations/0003_lesson_old_id.py diff --git a/access/admin.py b/access/admin.py index 6b38215..6a51244 100755 --- a/access/admin.py +++ b/access/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from access.models.other import Invite, Account, ResetPassword -from access.models.progress import ProgressLesson, UserLessonAnswer, AnswerItem +from access.models.progress import ProgressLesson from access.models import Progress from access.models.user import User @@ -9,6 +9,4 @@ admin.site.register(Account) admin.site.register(Progress) admin.site.register(Invite) admin.site.register(ResetPassword) -admin.site.register(ProgressLesson) -admin.site.register(AnswerItem) -admin.site.register(UserLessonAnswer) \ No newline at end of file +admin.site.register(ProgressLesson) \ No newline at end of file diff --git a/access/migrations/0001_initial.py b/access/migrations/0001_initial.py index fc35d65..ebe5f42 100644 --- a/access/migrations/0001_initial.py +++ b/access/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 15:44 +# Generated by Django 1.11.6 on 2018-01-15 17:54 from __future__ import unicode_literals import access.models.user @@ -23,10 +23,11 @@ class Migration(migrations.Migration): migrations.CreateModel( name='User', fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('out_key', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Токен')), + ('out_key', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')), ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), ('first_name', models.CharField(blank=True, default='Гость', max_length=63, verbose_name='first name')), ('last_name', models.CharField(blank=True, max_length=63, verbose_name='last name')), @@ -61,20 +62,6 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Дополнительная информация о пользователе', }, ), - migrations.CreateModel( - name='AnswerItem', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('question', models.TextField(verbose_name='Вопрос')), - ('value', models.TextField(verbose_name='Ответ')), - ('comment', models.TextField(blank=True, null=True, verbose_name='Комент')), - ('status', models.CharField(choices=[('done', 'done'), ('wait', 'wait'), ('fail', 'fail')], default='wait', max_length=20)), - ], - options={ - 'verbose_name': 'Ответ пользователя', - 'verbose_name_plural': 'Ответы пользователя', - }, - ), migrations.CreateModel( name='Invite', fields=[ @@ -107,6 +94,8 @@ class Migration(migrations.Migration): ('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='access.Progress')), ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Преподователь')), ], @@ -115,19 +104,6 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Прохождение урока', }, ), - migrations.CreateModel( - name='UserLessonAnswer', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата сдачи')), - ('progress_lesson', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.ProgressLesson')), - ('reviewer', 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='ResetPassword', fields=[ @@ -145,10 +121,9 @@ class Migration(migrations.Migration): name='owner', field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'), ), - migrations.AddField( - model_name='answeritem', - name='lesson_answer', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.UserLessonAnswer'), + migrations.AlterUniqueTogether( + name='progresslesson', + unique_together=set([('progress', 'lesson_token')]), ), migrations.AlterUniqueTogether( name='progress', diff --git a/access/migrations/0003_auto_20180115_1953.py b/access/migrations/0003_auto_20180115_1953.py new file mode 100644 index 0000000..1b40b47 --- /dev/null +++ b/access/migrations/0003_auto_20180115_1953.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-01-15 19:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0002_init_group'), + ] + + operations = [ + migrations.AlterField( + model_name='progresslesson', + name='date', + field=models.DateTimeField(auto_now_add=True, verbose_name='Дата зачтения задания'), + ), + ] diff --git a/access/models/progress.py b/access/models/progress.py index ffc908b..feda799 100644 --- a/access/models/progress.py +++ b/access/models/progress.py @@ -28,35 +28,39 @@ class ProgressLesson(models.Model): progress = models.ForeignKey(to=Progress) lesson_token = models.UUIDField(verbose_name="Токен урока", editable=False) teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь",) - date = models.DateTimeField(verbose_name='Дата зачтения задания', blank=True, null=True) + date = models.DateTimeField(verbose_name='Дата зачтения задания', auto_now_add=True) + STATUSES = Choices('done', 'wait', 'fail') + status = models.CharField(choices=STATUSES, default=STATUSES.wait, max_length=20) + comment_tokens = ArrayField(models.UUIDField(verbose_name="Токен комента", editable=False), default=[]) def __str__(self): - return self.lesson_token + return self.progress.user.email class Meta: verbose_name = 'Прохождение уроков' verbose_name_plural = 'Прохождение урока' + unique_together = ('progress', 'lesson_token') -class UserLessonAnswer(models.Model): - progress_lesson = models.ForeignKey(to=ProgressLesson) - date = models.DateTimeField(verbose_name='Дата сдачи', auto_now_add=True) - reviewer = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Проверяющий",) - - class Meta: - verbose_name = 'Блок ответов пользователя' - verbose_name_plural = 'Блоки ответов пользователя' - - -class AnswerItem(models.Model): - STATUSES = Choices('done', 'wait', 'fail') - - lesson_answer = models.ForeignKey(to=UserLessonAnswer) - question = models.TextField(verbose_name='Вопрос') # TODO подумать над хранением токена вопроса - value = models.TextField(verbose_name='Ответ') - comment = models.TextField(verbose_name='Комент', blank=True, null=True) - status = models.CharField(choices=STATUSES, default=STATUSES.wait, max_length=20) - - class Meta: - verbose_name = 'Ответ пользователя' - verbose_name_plural = 'Ответы пользователя' +# class UserLessonAnswer(models.Model): +# progress_lesson = models.ForeignKey(to=ProgressLesson) +# date = models.DateTimeField(verbose_name='Дата сдачи', auto_now_add=True) +# reviewer = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Проверяющий",) +# +# class Meta: +# verbose_name = 'Блок ответов пользователя' +# verbose_name_plural = 'Блоки ответов пользователя' +# +# +# class AnswerItem(models.Model): +# STATUSES = Choices('done', 'wait', 'fail') +# +# lesson_answer = models.ForeignKey(to=UserLessonAnswer) +# question = models.TextField(verbose_name='Вопрос') # TODO подумать над хранением токена вопроса +# value = models.TextField(verbose_name='Ответ') +# comment = models.TextField(verbose_name='Комент', blank=True, null=True) +# status = models.CharField(choices=STATUSES, default=STATUSES.wait, max_length=20) +# +# class Meta: +# verbose_name = 'Ответ пользователя' +# verbose_name_plural = 'Ответы пользователя' diff --git a/access/models/user.py b/access/models/user.py index 449f932..207e2af 100644 --- a/access/models/user.py +++ b/access/models/user.py @@ -93,7 +93,7 @@ class CustomUserManager(BaseUserManager): class User(AbstractBaseUser, PermissionsMixin): - out_key = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, primary_key=True, editable=False) + out_key = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False) email = models.EmailField(_('email address'), unique=True) first_name = models.CharField(_('first name'), max_length=63, blank=True, default='Гость') last_name = models.CharField(_('last name'), max_length=63, blank=True) diff --git a/access/serializers.py b/access/serializers.py index eef9b37..94232b0 100644 --- a/access/serializers.py +++ b/access/serializers.py @@ -7,7 +7,7 @@ from access.models import Progress from achievements.serialers import DiplomaSerializer, AchievementsSerializer -class ProgressVertexSerializer(serializers.ModelSerializer): +class ProgressLessonSerializer(serializers.ModelSerializer): teacher = serializers.SerializerMethodField() class Meta: @@ -20,15 +20,15 @@ class ProgressVertexSerializer(serializers.ModelSerializer): class ProgressSerializer(serializers.ModelSerializer): - vertexes = serializers.SerializerMethodField() + lessons = serializers.SerializerMethodField() class Meta: model = Progress - fields = ('route', 'vertexes', 'course') + fields = ('lessons', 'course_token') @staticmethod - def get_vertexes(self): - return [ProgressVertexSerializer(i).data for i in self.progressvertex_set.all()] + def get_lessons(self): + return [ProgressLessonSerializer(i).data for i in self.progresslesson_set.all()] class AccountSerializer(serializers.ModelSerializer): @@ -61,7 +61,7 @@ class UserSelfSerializer(serializers.ModelSerializer): @staticmethod def get_achievements(self): - return [AchievementsSerializer(i).data for i in self.achievements_set.all()] + return [AchievementsSerializer(i).data for i in self.achievement_set.all()] @staticmethod def get_account(self): diff --git a/access/views.py b/access/views.py index 450ec60..067ff56 100644 --- a/access/views.py +++ b/access/views.py @@ -15,7 +15,7 @@ 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, ProgressVertexSerializer +from access.serializers import UserSelfSerializer, UserSearchSerializer, ProgressLessonSerializer class TeacherListView(APIView): @@ -260,7 +260,7 @@ class UpdateProgress(APIView): ) pv.status = 2 pv.save() - return Response(ProgressVertexSerializer(pv).data, status=200) + return Response(ProgressLessonSerializer(pv).data, status=200) except Progress.DoesNotExist: return Response('Не найден прогресс по заданным параметрам', status=404) diff --git a/achievements/migrations/0001_initial.py b/achievements/migrations/0001_initial.py index d706d00..7c0edd4 100644 --- a/achievements/migrations/0001_initial.py +++ b/achievements/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 15:44 +# Generated by Django 1.11.6 on 2018-01-15 17:54 from __future__ import unicode_literals from django.conf import settings @@ -12,8 +12,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('courses', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ diff --git a/courses/admin.py b/courses/admin.py index 6a3c5e2..f378846 100755 --- a/courses/admin.py +++ b/courses/admin.py @@ -1,10 +1,6 @@ from django.contrib import admin -from courses.models import Course, Topic, Lesson, Requirement, Question, RightAnswer - +from courses.models import Course, Topic, Lesson admin.site.register(Topic) admin.site.register(Lesson) -admin.site.register(Course) -admin.site.register(Requirement) -admin.site.register(Question) -admin.site.register(RightAnswer) \ No newline at end of file +admin.site.register(Course) \ No newline at end of file diff --git a/courses/migrations/0001_initial.py b/courses/migrations/0001_initial.py index 11a2d02..bcf0023 100644 --- a/courses/migrations/0001_initial.py +++ b/courses/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 15:44 +# Generated by Django 1.11.6 on 2018-01-15 17:54 from __future__ import unicode_literals import django.contrib.postgres.fields @@ -19,14 +19,15 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Course', fields=[ - ('token', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Токен')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')), ('slug', models.SlugField(editable=False, max_length=127, unique=True)), ('title', models.CharField(max_length=255, unique=True, verbose_name='Заголовок')), ('description', models.TextField(blank=True, verbose_name='Описание')), ('level', models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень')), ('direction', models.SmallIntegerField(choices=[(3, 'Бизнес'), (2, 'Веб-дизайн'), (1, 'Разработка'), (4, 'Рисование'), (5, 'Музыка')], verbose_name='Направление')), ('public', models.BooleanField(default=False, verbose_name='Опубликовать')), - ('teacher_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, primary_key=True, verbose_name='Токен препода'), default=[], size=None, verbose_name='Преподователи курса')), + ('teacher_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, verbose_name='Токен препода'), default=[], size=None, verbose_name='Преподователи курса')), ('image', models.URLField(blank=True, max_length=255, verbose_name='Изображение')), ('big_image', models.URLField(blank=True, max_length=255, verbose_name='Большое изображение')), ('big_mobile_image', models.URLField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, verbose_name='Под мобилку')), @@ -40,13 +41,17 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Lesson', fields=[ - ('token', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Токен')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Токен')), + ('key', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Внутрений ключ используется для расшивровки')), ('title', models.CharField(max_length=255, verbose_name='Название')), ('description', models.TextField(blank=True, null=True, verbose_name='Описание')), ('video', models.TextField(blank=True, null=True, verbose_name='Код видео')), - ('material_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=15, verbose_name='Материалы урока'), default=[], size=None)), + ('material_tokens', django.contrib.postgres.fields.ArrayField(base_field=models.UUIDField(editable=False, verbose_name='Токен материала'), default=[], size=None, verbose_name='Материалы курса')), ('free', models.BooleanField(default=False, verbose_name='Привилегии для узла не будут проверяться')), ('sort', models.SmallIntegerField(unique=True)), + ('is_hm', models.BooleanField(default=False)), + ('old_id', models.IntegerField(blank=True, null=True)), ], options={ 'verbose_name': 'Урок', @@ -54,61 +59,6 @@ class Migration(migrations.Migration): 'ordering': ('sort',), }, ), - migrations.CreateModel( - name='LessonRequirement', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sort', models.SmallIntegerField(default=1)), - ('lesson', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Lesson')), - ], - options={ - 'verbose_name': 'Порядок требований', - 'verbose_name_plural': 'Порядок требований', - 'ordering': ('sort',), - }, - ), - migrations.CreateModel( - name='Question', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField(verbose_name='Вопрос')), - ('type', models.CharField(choices=[('text', 'text'), ('char', 'char'), ('boolean', 'boolean'), ('file', 'file')], default='char', max_length=20)), - ('multiple', models.BooleanField(default=False)), - ('null', models.BooleanField(default=False)), - ('balls', models.SmallIntegerField(default=100, verbose_name='Вознаграждение')), - ], - options={ - 'verbose_name': 'Вопрос', - 'verbose_name_plural': 'Вопросы', - }, - ), - migrations.CreateModel( - name='Requirement', - fields=[ - ('token', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Токен')), - ('name', models.CharField(max_length=31, unique=True, verbose_name='Название')), - ('checker', models.CharField(choices=[('student', 'student'), ('teacher', 'teacher'), ('auto', 'auto')], default='teacher', max_length=15, verbose_name='Проверяющий')), - ('min_balls', models.SmallIntegerField(default=50, verbose_name='Проходной бал')), - ], - options={ - 'verbose_name': 'Требования', - 'verbose_name_plural': 'Требования', - }, - ), - migrations.CreateModel( - name='RightAnswer', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField(verbose_name='Верный ответ')), - ('success_comment', models.TextField(blank=True, null=True, verbose_name='Комментарий при верном ответе')), - ('error_comment', models.TextField(blank=True, null=True, verbose_name='Комментарий при ошибке')), - ('question', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='courses.Question')), - ], - options={ - 'verbose_name': 'Верный ответ', - 'verbose_name_plural': 'Верные ответы', - }, - ), migrations.CreateModel( name='Topic', fields=[ @@ -124,23 +74,9 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Темы', }, ), - migrations.AddField( - model_name='question', - name='requirement', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Requirement'), - ), - migrations.AddField( - model_name='lessonrequirement', - name='requirement', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Requirement'), - ), migrations.AddField( model_name='lesson', name='topic', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Тема'), ), - migrations.AlterUniqueTogether( - name='lessonrequirement', - unique_together=set([('lesson', 'requirement', 'sort')]), - ), ] diff --git a/courses/migrations/0002_init_demands.py b/courses/migrations/0002_init_demands.py deleted file mode 100644 index d0623f6..0000000 --- a/courses/migrations/0002_init_demands.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import unicode_literals - -from courses.models import Requirement, Question - -from django.db import migrations - - -def init_demands(*_args, **_kwargs): - requirement, created = Requirement.objects.get_or_create( - min_balls=51, - name="Стандартные требования", - ) - - Question.objects.get_or_create( - requirement=requirement, - text='Комментарий', - type='text', - null=True, - balls=50, - ) - - Question.objects.get_or_create( - requirement=requirement, - text='Приложенные файлы', - type='files', - null=True, - multiple=True, - balls=50, - ) - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('courses', '0001_initial'), - ] - - operations = [ - migrations.RunPython(init_demands) - ] \ No newline at end of file diff --git a/courses/migrations/0003_lesson_old_id.py b/courses/migrations/0003_lesson_old_id.py deleted file mode 100644 index e2dd994..0000000 --- a/courses/migrations/0003_lesson_old_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 16:02 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('courses', '0002_init_demands'), - ] - - operations = [ - migrations.AddField( - model_name='lesson', - name='old_id', - field=models.IntegerField(blank=True, null=True), - ), - ] diff --git a/courses/models.py b/courses/models.py index 49febc8..700c853 100755 --- a/courses/models.py +++ b/courses/models.py @@ -64,7 +64,7 @@ class CourseManager(models.Manager): class Course(models.Model): - token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, primary_key=True, editable=False) + token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False) slug = models.SlugField(unique=True, editable=False, max_length=127) title = models.CharField(verbose_name="Заголовок", max_length=255, unique=True) description = models.TextField(verbose_name='Описание', blank=True) @@ -72,7 +72,7 @@ class Course(models.Model): direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление') public = models.BooleanField(verbose_name='Опубликовать', default=False) teacher_tokens = ArrayField( - models.UUIDField(verbose_name="Токен препода", primary_key=True, editable=False), + models.UUIDField(verbose_name="Токен препода", editable=False), default=[], verbose_name='Преподователи курса', ) @@ -88,7 +88,7 @@ class Course(models.Model): def get_statistic(self): return { 'topic_count': self.topic_set.all().count(), - 'task_count': sum([topic.vertex_set.count() for topic in self.topic_set.all()]) + 'task_count': sum([topic.lesson_set.count() for topic in self.topic_set.all()]) } objects = CourseManager() @@ -111,15 +111,22 @@ class Topic(models.Model): class Lesson(models.Model): - token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, primary_key=True, editable=False) + token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, editable=False) + key = models.UUIDField( + verbose_name="Внутрений ключ используется для расшивровки", default=uuid.uuid4, editable=False) topic = models.ForeignKey(to=Topic, verbose_name='Тема') title = models.CharField(verbose_name='Название', max_length=255) description = models.TextField(verbose_name='Описание', blank=True, null=True) video = models.TextField(verbose_name='Код видео', blank=True, null=True) - material_tokens = ArrayField(models.CharField(max_length=15, blank=True, verbose_name='Материалы урока'), default=[]) + material_tokens = ArrayField( + models.UUIDField(verbose_name="Токен материала", editable=False), + default=[], + verbose_name='Материалы курса', + ) free = models.BooleanField(default=False, verbose_name='Привилегии для узла не будут проверяться') sort = models.SmallIntegerField(unique=True) + is_hm = models.BooleanField(default=False) #TODO костыли old_id = models.IntegerField(null=True, blank=True) def __str__(self): @@ -131,56 +138,56 @@ class Lesson(models.Model): ordering = ('sort', ) -class LessonRequirement(models.Model): - lesson = models.ForeignKey(to=Lesson) - requirement = models.ForeignKey(to='courses.Requirement') - sort = models.SmallIntegerField(default=1) - - class Meta: - verbose_name = "Порядок требований" - verbose_name_plural = "Порядок требований" - ordering = ('sort', ) - unique_together = ('lesson', 'requirement', 'sort') - - -class Requirement(models.Model): - CHECK_TYPES = Choices('student', 'teacher', 'auto',) - - token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, primary_key=True, editable=False) - name = models.CharField(max_length=31, verbose_name="Название", unique=True) - checker = models.CharField( - choices=CHECK_TYPES, default=CHECK_TYPES.teacher, max_length=15, verbose_name="Проверяющий",) - min_balls = models.SmallIntegerField(default=50, verbose_name='Проходной бал') - - def __str__(self): - return self.name - - class Meta: - verbose_name = "Требования" - verbose_name_plural = "Требования" - - -class Question(models.Model): - FIELD_TYPES = Choices('text', 'char', 'boolean', 'file') - - requirement = models.ForeignKey(to=Requirement) - text = models.TextField(verbose_name="Вопрос") - type = models.CharField(choices=FIELD_TYPES, default=FIELD_TYPES.char, max_length=20) - multiple = models.BooleanField(default=False) - null = models.BooleanField(default=False) - balls = models.SmallIntegerField(default=100, verbose_name='Вознаграждение') - - class Meta: - verbose_name = "Вопрос" - verbose_name_plural = "Вопросы" - - -class RightAnswer(models.Model): - question = models.OneToOneField(to=Question) - text = models.TextField(verbose_name="Верный ответ") - success_comment = models.TextField(blank=True, null=True, verbose_name="Комментарий при верном ответе") - error_comment = models.TextField(blank=True, null=True, verbose_name="Комментарий при ошибке") - - class Meta: - verbose_name = "Верный ответ" - verbose_name_plural = "Верные ответы" +# class LessonRequirement(models.Model): +# lesson = models.ForeignKey(to=Lesson) +# requirement = models.ForeignKey(to='courses.Requirement') +# sort = models.SmallIntegerField(default=1) +# +# class Meta: +# verbose_name = "Порядок требований" +# verbose_name_plural = "Порядок требований" +# ordering = ('sort', ) +# unique_together = ('lesson', 'requirement', 'sort') +# +# +# class Requirement(models.Model): +# CHECK_TYPES = Choices('student', 'teacher', 'auto',) +# +# token = models.UUIDField(verbose_name="Токен", default=uuid.uuid4, primary_key=True, editable=False) +# name = models.CharField(max_length=31, verbose_name="Название", unique=True) +# checker = models.CharField( +# choices=CHECK_TYPES, default=CHECK_TYPES.teacher, max_length=15, verbose_name="Проверяющий",) +# min_balls = models.SmallIntegerField(default=50, verbose_name='Проходной бал') +# +# def __str__(self): +# return self.name +# +# class Meta: +# verbose_name = "Требования" +# verbose_name_plural = "Требования" +# +# +# class Question(models.Model): +# FIELD_TYPES = Choices('text', 'char', 'boolean', 'file') +# +# requirement = models.ForeignKey(to=Requirement) +# text = models.TextField(verbose_name="Вопрос") +# type = models.CharField(choices=FIELD_TYPES, default=FIELD_TYPES.char, max_length=20) +# multiple = models.BooleanField(default=False) +# null = models.BooleanField(default=False) +# balls = models.SmallIntegerField(default=100, verbose_name='Вознаграждение') +# +# class Meta: +# verbose_name = "Вопрос" +# verbose_name_plural = "Вопросы" +# +# +# class RightAnswer(models.Model): +# question = models.OneToOneField(to=Question) +# text = models.TextField(verbose_name="Верный ответ") +# success_comment = models.TextField(blank=True, null=True, verbose_name="Комментарий при верном ответе") +# error_comment = models.TextField(blank=True, null=True, verbose_name="Комментарий при ошибке") +# +# class Meta: +# verbose_name = "Верный ответ" +# verbose_name_plural = "Верные ответы" diff --git a/courses/serializers.py b/courses/serializers.py index e28683e..3c9d26f 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -12,27 +12,22 @@ class TopicSerializer(serializers.ModelSerializer): @staticmethod def get_children(self): - return [MiniVertexSerializer(i).data for i in self.vertex_set.all()] + return [MiniLessonSerializer(i).data for i in self.lesson_set.all()] -class MiniVertexSerializer(serializers.ModelSerializer): +class MiniLessonSerializer(serializers.ModelSerializer): class Meta: model = Lesson fields = ('title', 'free', 'token') -class VertexSerializer(MiniVertexSerializer): - valid_type = serializers.SerializerMethodField() +class LessonSerializer(MiniLessonSerializer): class Meta: model = Lesson exclude = ('id', 'topic', 'free') - @staticmethod - def get_valid_type(self): - return self.get_valid_type_display() - class CourseInitSerializer(serializers.ModelSerializer): @@ -46,7 +41,7 @@ class CourseTreeSerializer(serializers.ModelSerializer): class Meta: model = Course - fields = ('tree', 'route', 'slug') + fields = ('tree', 'slug') @staticmethod def get_tree(self): @@ -60,7 +55,7 @@ class CourseDetailSerializer(serializers.ModelSerializer): class Meta: model = Course - exclude = ('route', 'id') + exclude = ('id', ) @staticmethod def get_level(self): diff --git a/courses/views.py b/courses/views.py index d4035f5..1901580 100644 --- a/courses/views.py +++ b/courses/views.py @@ -3,7 +3,7 @@ from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView -from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, VertexSerializer +from courses.serializers import CourseDetailSerializer, CourseTreeSerializer, LessonSerializer class TreeView(APIView): @@ -47,7 +47,7 @@ class VertexDetail(APIView): # return Response("permission denied", status=403) # TODO: Доделать систему прав на курс - res = VertexSerializer(vertex).data + res = LessonSerializer(vertex).data # progress = vertex.course.progress_set.filter(user=request.user) # try: # if progress.exists(): diff --git a/csv/load_comments.py b/csv/load_comments.py index 0e65d8e..f8f83cd 100644 --- a/csv/load_comments.py +++ b/csv/load_comments.py @@ -6,38 +6,53 @@ import django import os import sys +from django.contrib.auth import get_user_model from django.db import IntegrityError sys.path.append("../") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings") django.setup() -from storage.models import File -from access.models.progress import UserLessonAnswer, AnswerItem, ProgressLesson +from storage.models import Comment, File +from courses.models import Lesson +from access.models.progress import ProgressLesson if __name__ == '__main__': csv.field_size_limit(500 * 1024 * 1024) + Comment.objects.all().delete() with open('./management/comment.csv') as comment_csv: comment_reader = csv.DictReader(comment_csv) for row in comment_reader: if row['type'] == 'task' or row['type'] == 'exam': + l = Lesson.objects.get(old_id=row['parent_id']) + try: - c = Comment.objects.create( - id=row['id'], - email=row['owner__email'], + p = ProgressLesson.objects.get( + lesson_token=l.token, + progress__user__email=row['student'], + ) + files = [File.objects.get(id=file) for file in row['files'].split("[")[1].split("]")[0].split(',') + if not file == ''] + + comment = Comment.objects.create( text=row['text'], - key=''.join(random.choice(string.ascii_letters) for x in range(15)), + email=row['owner__email'], ) - except IntegrityError: - c = Comment.objects.get(id=row['id']) - for file_id in row['files'].split("[")[1].split("]")[0].split(", "): - if file_id: - c.files.add(File.objects.get(id=file_id)) + if row['status'] == 'Одобренно': + p.status = ProgressLesson.STATUSES.done + p.date = row['date'] + elif row['status'] == 'Отклонено': + p.status = ProgressLesson.STATUSES.fail + else: + p.status = ProgressLesson.STATUSES.wait + + p.save() - c.date = row['date'] - c.save() + [comment.files.add(file) for file in files] + comment.date = row['date'] + comment.save() - parent_id = int(row['parent_id']) - if row['type'] == 'task': - parent_id += 50 + p.comment_tokens.append(comment.token) + except ProgressLesson.DoesNotExist: + pass diff --git a/csv/load_courses.py b/csv/load_courses.py index f6bfa92..e03357f 100644 --- a/csv/load_courses.py +++ b/csv/load_courses.py @@ -10,7 +10,7 @@ django.setup() from courses.api import InApiTeacher from django.contrib.auth import get_user_model -from courses.models import Course, Lesson, Topic, Requirement, LessonRequirement +from courses.models import Course, Lesson, Topic from storage.models import File if __name__ == '__main__': @@ -47,7 +47,7 @@ if __name__ == '__main__': try: m = row.pop('materials', None) if m: - materials = [File.objects.get(id=i).key for i in m.split("[")[1].split("]")[0].split(", ")] + materials = [File.objects.get(id=i).token for i in m.split("[")[1].split("]")[0].split(", ")] except ValueError: pass @@ -76,10 +76,5 @@ if __name__ == '__main__': title=title, sort=l_sort, old_id=pk, - ) - - if model_type == 'task': - LessonRequirement.objects.create( - lesson=small_vertex, - requirement=Requirement.objects.get(name="Стандартные требования"), - ) \ No newline at end of file + is_hm=model_type == 'task', + ) \ No newline at end of file diff --git a/csv/load_storage.py b/csv/load_storage.py index 7a059bd..97f9f0a 100644 --- a/csv/load_storage.py +++ b/csv/load_storage.py @@ -19,4 +19,4 @@ if __name__ == '__main__': for row in storage_reader: if row['original']: key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) - File.objects.create(original=row['original'], id=row['id'], key=key) \ No newline at end of file + File.objects.create(original=row['original'], id=row['id']) \ No newline at end of file diff --git a/finance/migrations/0001_initial.py b/finance/migrations/0001_initial.py index 0e172e4..062533b 100644 --- a/finance/migrations/0001_initial.py +++ b/finance/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 15:44 +# Generated by Django 1.11.6 on 2018-01-15 17:54 from __future__ import unicode_literals from django.conf import settings diff --git a/library/migrations/0001_initial.py b/library/migrations/0001_initial.py index 651b76f..2158001 100644 --- a/library/migrations/0001_initial.py +++ b/library/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 15:44 +# Generated by Django 1.11.6 on 2018-01-15 17:54 from __future__ import unicode_literals import datetime diff --git a/storage/api.py b/storage/api.py index d0e0f60..0624c47 100644 --- a/storage/api.py +++ b/storage/api.py @@ -1,13 +1,9 @@ -import random -import string - from storage.models import Comment, File def upload_file(original=None, name=None, base64=None) -> File: - key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) if original: - new_file = File.objects.create(key=key, original=original) + new_file = File.objects.create(original=original) else: new_file = File.objects.upload_as_base64(base64) @@ -27,11 +23,9 @@ def add_comment(text: str, email: str, files=None) -> Comment: files = [] if files is None else files - key = ''.join(random.choice(string.ascii_letters) for _x in range(15)) comment = Comment.objects.create( text=text, email=email, - key=key, ) for file in files: @@ -42,17 +36,17 @@ def add_comment(text: str, email: str, files=None) -> Comment: def get_comment(key): - comment = Comment.objects.get(key=key) + comment = Comment.objects.get(token=key) return comment def update_comment(key, **kwargs): - comment = Comment.objects.get(key=key) + comment = Comment.objects.get(token=key) comment.__dict__.update(kwargs) comment.save() return comment def delete_comment(key): - comment = Comment.objects.get(key=key).delete() + comment = Comment.objects.get(token=key).delete() return comment diff --git a/storage/migrations/0001_initial.py b/storage/migrations/0001_initial.py index 4a5a967..70e15bd 100644 --- a/storage/migrations/0001_initial.py +++ b/storage/migrations/0001_initial.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2018-01-12 15:44 +# Generated by Django 1.11.6 on 2018-01-15 17:54 from __future__ import unicode_literals from django.db import migrations, models +import uuid class Migration(migrations.Migration): @@ -19,7 +20,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('email', models.CharField(max_length=63, verbose_name='email автора')), ('text', models.TextField(default='', verbose_name='Текст комментария')), - ('key', models.SlugField(editable=False, unique=True, verbose_name='Получения комментария по ключу')), + ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Ключ')), ('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата коментария')), ], options={ @@ -31,7 +32,7 @@ class Migration(migrations.Migration): name='File', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('key', models.CharField(editable=False, max_length=15, unique=True, verbose_name='Внешний ключ')), + ('token', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='Ключ')), ('original', models.FileField(max_length=255, unique=True, upload_to='files', verbose_name='Файл')), ('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Видимое имя файла')), ], diff --git a/storage/models.py b/storage/models.py index 69a0173..b76bb72 100755 --- a/storage/models.py +++ b/storage/models.py @@ -2,6 +2,7 @@ import base64 import random import string +import uuid from django.core.files.base import ContentFile from django.db import models @@ -20,7 +21,7 @@ class FileManager(models.Manager): class File(models.Model): - key = models.CharField(max_length=15, verbose_name="Внешний ключ", unique=True, editable=False) + token = models.UUIDField(verbose_name="Ключ", default=uuid.uuid4, editable=False) original = models.FileField(max_length=255, verbose_name='Файл', upload_to="files", unique=True) name = models.CharField(max_length=255, null=True, blank=True, verbose_name='Видимое имя файла') @@ -38,11 +39,11 @@ class Comment(models.Model): email = models.CharField(verbose_name="email автора", max_length=63) text = models.TextField(default="", verbose_name="Текст комментария") files = models.ManyToManyField(to=File, blank=True, verbose_name='Файлы') - key = models.SlugField(unique=True, verbose_name="Получения комментария по ключу", editable=False) + token = models.UUIDField(verbose_name="Ключ", default=uuid.uuid4, editable=False) date = models.DateTimeField(auto_now_add=True, verbose_name="Дата коментария") def __str__(self): - return '%s' % self.key + return '%s' % self.token class Meta: verbose_name = 'Коммент' diff --git a/storage/tests.py b/storage/tests.py index 396e8ca..1e1c0a0 100644 --- a/storage/tests.py +++ b/storage/tests.py @@ -17,12 +17,12 @@ class CommentTestCase(TestCase): Comment.objects.all().delete() def test_comment_get(self): - self.assertEqual(self.first_comment, get_comment(self.first_comment.key)) + self.assertEqual(self.first_comment, get_comment(self.first_comment.token)) def test_comment_update(self): new_text = "Новый текст для коммента" - update_comment(key=self.first_comment.key, text=new_text) - self.assertEqual(get_comment(self.first_comment.key).text, new_text) + update_comment(key=self.first_comment.token, text=new_text) + self.assertEqual(get_comment(self.first_comment.token).text, new_text) # def test_comment_create(self): # token = 'fskjfskj' @@ -37,9 +37,9 @@ class CommentTestCase(TestCase): # self.assertEqual(comment2.files.all()[0].name, file_name) def test_comment_delete(self): - delete_comment(self.first_comment.key) + delete_comment(self.first_comment.token) try: - comment = get_comment(self.first_comment.id) + comment = get_comment(self.first_comment.token) except Comment.DoesNotExist: comment = None @@ -52,12 +52,12 @@ class FileTestCase(TestCase): self.second_comment = add_comment(text="Привет, отличная работа", email="artem4000@gmail.com") def test_comment_get(self): - self.assertEqual(self.first_comment, get_comment(self.first_comment.key)) + self.assertEqual(self.first_comment, get_comment(self.first_comment.token)) def test_comment_update(self): new_text = "Новый текст для коммента" - update_comment(key=self.first_comment.key, text=new_text) - self.assertEqual(get_comment(self.first_comment.key).text, new_text) + update_comment(key=self.first_comment.token, text=new_text) + self.assertEqual(get_comment(self.first_comment.token).text, new_text) def test_comment_create(self): token = 'fskjfskj' @@ -71,9 +71,9 @@ class FileTestCase(TestCase): self.assertEqual(comment2.files.all()[0].name, file_name) def test_comment_delete(self): - delete_comment(self.first_comment.key) + delete_comment(self.first_comment.token) try: - comment = get_comment(self.first_comment.id) + comment = get_comment(self.first_comment.token) except Comment.DoesNotExist: comment = None