From 152cd63fc252e98e6af10e2415f58a78cc39de36 Mon Sep 17 00:00:00 2001 From: Ivlev Denis Date: Tue, 10 Apr 2018 12:03:25 +0300 Subject: [PATCH] LIL-384. Add LiveLesson model --- api/v1/serializers/content.py | 89 ++------- api/v1/serializers/school.py | 177 +++++++++++++++++- api/v1/urls.py | 3 +- api/v1/views.py | 24 ++- .../migrations/0018_content_live_lesson.py | 20 ++ apps/content/models.py | 6 + apps/school/migrations/0008_livelesson.py | 31 +++ apps/school/models.py | 21 ++- 8 files changed, 296 insertions(+), 75 deletions(-) create mode 100644 apps/content/migrations/0018_content_live_lesson.py create mode 100644 apps/school/migrations/0008_livelesson.py diff --git a/api/v1/serializers/content.py b/api/v1/serializers/content.py index c9c42bbd..9b67d1a1 100644 --- a/api/v1/serializers/content.py +++ b/api/v1/serializers/content.py @@ -8,6 +8,20 @@ from apps.content.models import ( from . import Base64ImageField +BASE_CONTENT_FIELDS = ( + 'id', + 'uuid', + 'course', + 'lesson', + 'live_lesson', + 'title', + 'position', + 'type', + 'created_at', + 'update_at', +) + + class ContentCreateSerializer(serializers.Serializer): TYPE_CHOICES = ( 'text', @@ -59,18 +73,7 @@ class ImageCreateSerializer(serializers.ModelSerializer): class Meta: model = Image - fields = ( - 'id', - 'uuid', - 'course', - 'lesson', - 'title', - 'position', - 'img', - 'type', - 'created_at', - 'update_at', - ) + fields = BASE_CONTENT_FIELDS + ('img',) read_only_fields = ( 'id', @@ -92,17 +95,7 @@ class TextCreateSerializer(serializers.ModelSerializer): class Meta: model = Text - fields = ( - 'id', - 'uuid', - 'course', - 'lesson', - 'title', - 'position', - 'type', - 'created_at', - 'update_at', - ) + ('txt',) + fields = BASE_CONTENT_FIELDS + ('txt',) read_only_fields = ( 'id', @@ -124,19 +117,7 @@ class ImageTextCreateSerializer(serializers.ModelSerializer): class Meta: model = ImageText - fields = ( - 'id', - 'uuid', - 'course', - 'lesson', - 'title', - 'position', - 'img', - 'txt', - 'type', - 'created_at', - 'update_at', - ) + fields = BASE_CONTENT_FIELDS + ('img', 'txt',) read_only_fields = ( 'id', @@ -158,17 +139,7 @@ class VideoCreateSerializer(serializers.ModelSerializer): class Meta: model = Video - fields = ( - 'id', - 'uuid', - 'course', - 'lesson', - 'title', - 'position', - 'type', - 'created_at', - 'update_at', - ) + ('url',) + fields = BASE_CONTENT_FIELDS + ('url',) read_only_fields = ( 'id', @@ -214,18 +185,7 @@ class GallerySerializer(serializers.ModelSerializer): class Meta: model = Gallery - fields = ( - 'id', - 'uuid', - 'course', - 'lesson', - 'title', - 'position', - 'gallery_images', - 'type', - 'created_at', - 'update_at', - ) + fields = BASE_CONTENT_FIELDS + ('gallery_images',) read_only_fields = ( 'id', @@ -242,16 +202,7 @@ class ContentSerializer(serializers.ModelSerializer): class Meta: model = Content - fields = ( - 'id', - 'uuid', - 'course', - 'lesson', - 'title', - 'position', - 'created_at', - 'update_at', - ) + fields = BASE_CONTENT_FIELDS def to_representation(self, obj): if isinstance(obj, Image): diff --git a/api/v1/serializers/school.py b/api/v1/serializers/school.py index ec4d8932..a6706d40 100644 --- a/api/v1/serializers/school.py +++ b/api/v1/serializers/school.py @@ -1,11 +1,12 @@ from rest_framework import serializers from apps.school.models import ( - SchoolSchedule, SchoolScheduleImage, ImageObject + SchoolSchedule, SchoolScheduleImage, + ImageObject, LiveLesson, ) from .content import ( - ImageObjectSerializer + ContentSerializer, ContentCreateSerializer, ImageObjectSerializer ) from pprint import pprint @@ -114,3 +115,175 @@ class SchoolScheduleSerializerImg(serializers.ModelSerializer): read_only_fields = ( 'id', ) + + +class LiveLessonCreateSerializer(serializers.ModelSerializer): + content = serializers.ListSerializer( + child=ContentCreateSerializer(), + required=False, + ) + + class Meta: + model = LiveLesson + fields = ( + 'id', + 'title', + 'short_description', + 'stream', + 'date', + 'content', + 'created_at', + 'update_at', + 'deactivated_at', + ) + + read_only_fields = ( + 'id', + 'created_at', + 'update_at', + 'deactivated_at', + ) + + def dispatch_content(self, lesson, content): + for c in content: + if 'type' not in c or not c['type'] or 'data' not in c or not c['data']: + continue + ctype = c['type'] + cdata = c['data'] + if ctype == 'text': + if 'id' in cdata and cdata['id']: + t = Text.objects.get(id=cdata['id']) + t.position = cdata['position'] + t.title = cdata['title'] + t.lesson = lesson + t.txt = cdata['txt'] + t.uuid = cdata['uuid'] + t.save() + else: + t = Text.objects.create( + position=cdata['position'], + title=cdata['title'], + lesson=lesson, + txt=cdata['txt'], + uuid=cdata['uuid'], + ) + elif ctype == 'image': + if 'id' in cdata and cdata['id']: + image = Image.objects.get(id=cdata['id']) + image.position = cdata['position'] + image.title = cdata['title'] + image.lesson = lesson + image.img = ImageObject.objects.get(id=cdata['img']) + image.uuid = cdata['uuid'] + image.save() + else: + image = Image.objects.create( + position=cdata['position'], + title=cdata['title'], + lesson=lesson, + img=ImageObject.objects.get(id=cdata['img']), + uuid=cdata['uuid'], + ) + elif ctype == 'image-text': + if 'id' in cdata and cdata['id']: + it = ImageText.objects.get(id=cdata['id']) + it.position = cdata['position'] + it.title = cdata['title'] + it.lesson = lesson + it.img = ImageObject.objects.get(id=cdata['img']) + it.txt = cdata['txt'] + it.uuid = cdata['uuid'] + it.save() + else: + it = ImageText.objects.create( + position=cdata['position'], + title=cdata['title'], + lesson=lesson, + img=ImageObject.objects.get(id=cdata['img']), + txt=cdata['txt'], + uuid=cdata['uuid'], + ) + elif ctype == 'video': + if 'id' in cdata and cdata['id']: + v = Video.objects.get(id=cdata['id']) + v.position = cdata['position'] + v.title = cdata['title'] + v.lesson = lesson + v.url = cdata['url'] + v.uuid = cdata['uuid'] + v.save() + else: + v = Video.objects.create( + position=cdata['position'], + title=cdata['title'], + lesson=lesson, + url=cdata['url'], + uuid=cdata['uuid'], + ) + elif ctype == 'images': + if 'id' in cdata and cdata['id']: + g = Gallery.objects.get(id=cdata['id']) + g.position = cdata['position'] + g.title = cdata['title'] + g.lesson = lesson + g.uuid = cdata['uuid'] + g.save() + if 'images' in cdata: + for image in cdata['images']: + gi = GalleryImage.objects.create( + gallery=g, + img=ImageObject.objects.get(id=image['img']), + ) + else: + g = Gallery.objects.create( + lesson=lesson, + position=cdata['position'], + title=cdata['title'], + uuid=cdata['uuid'], + ) + if 'images' in cdata: + for image in cdata['images']: + gi = GalleryImage.objects.create( + gallery=g, + img=ImageObject.objects.get(id=image['img']), + ) + + def create(self, validated_data): + content = validated_data.pop('content', []) + lesson = super().create(validated_data) + self.dispatch_content(lesson, content) + return lesson + + def update(self, instance, validated_data): + content = validated_data.pop('content', []) + lesson = super().update(instance, validated_data) + self.dispatch_content(lesson, content) + return lesson + + def to_representation(self, instance): + return LessonSerializer(instance, context=self.context).to_representation(instance) + + +class LiveLessonSerializer(serializers.ModelSerializer): + content = ContentSerializer(many=True) + + class Meta: + model = LiveLesson + fields = ( + 'id', + 'title', + 'short_description', + 'stream', + 'date', + 'content', + 'created_at', + 'update_at', + 'deactivated_at', + ) + + read_only_fields = ( + 'id', + 'created_at', + 'update_at', + 'deactivated_at', + ) diff --git a/api/v1/urls.py b/api/v1/urls.py index cf52179a..bcc0d0d8 100644 --- a/api/v1/urls.py +++ b/api/v1/urls.py @@ -16,7 +16,7 @@ from .views import ( ImageTextViewSet, VideoViewSet, GalleryViewSet, GalleryImageViewSet, UserViewSet, LessonViewSet, ImageObjectViewSet, - SchoolScheduleViewSet, + SchoolScheduleViewSet, LiveLessonViewSet, ) router = DefaultRouter() @@ -28,6 +28,7 @@ router.register(r'comments', CommentViewSet, base_name='comments') router.register(r'materials', MaterialViewSet, base_name='materials') router.register(r'lessons', LessonViewSet, base_name='lessons') router.register(r'likes', LikeViewSet, base_name='likes') +router.register(r'live-lesson', LiveLessonViewSet, base_name='live-lesson') router.register(r'image-objects', ImageObjectViewSet, base_name='image-objects') router.register(r'images', ImageViewSet, base_name='images') diff --git a/api/v1/views.py b/api/v1/views.py index 2782830b..407668a1 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -24,7 +24,11 @@ from .serializers.content import ( GalleryImageSerializer, GalleryImageCreateSerializer, ImageObjectSerializer, ) -from .serializers.school import SchoolScheduleSerializer +from .serializers.school import ( + SchoolScheduleSerializer, + LiveLessonCreateSerializer, + LiveLessonSerializer, +) from .serializers.payment import AuthorBalanceSerializer, AuthorBalanceCreateSerializer from .serializers.user import ( AuthorRequestSerializer, @@ -45,7 +49,7 @@ from apps.content.models import ( Gallery, GalleryImage, ImageObject, ) from apps.payment.models import AuthorBalance -from apps.school.models import SchoolSchedule +from apps.school.models import SchoolSchedule, LiveLesson from apps.user.models import AuthorRequest User = get_user_model() @@ -185,6 +189,22 @@ class LessonViewSet(ExtendedModelViewSet): # } +class LiveLessonViewSet(ExtendedModelViewSet): + queryset = LiveLesson.objects.prefetch_related('content').all() + serializer_class = LiveLessonCreateSerializer + serializer_class_map = { + 'list': LiveLessonSerializer, + 'retrieve': LiveLessonSerializer, + } + search_fields = ('title', 'short_description',) + ordering_fields = ('title', 'created_at', 'update_at',) + # permission_classes = (IsAuthorObjectOrAdmin,) + # permission_map = { + # 'create': IsAuthorOrAdmin, + # 'delete': IsAdmin, + # } + + class ImageViewSet(ExtendedModelViewSet): queryset = Image.objects.select_related( 'course', 'lesson', 'img', diff --git a/apps/content/migrations/0018_content_live_lesson.py b/apps/content/migrations/0018_content_live_lesson.py new file mode 100644 index 00000000..fb67fa72 --- /dev/null +++ b/apps/content/migrations/0018_content_live_lesson.py @@ -0,0 +1,20 @@ +# Generated by Django 2.0.3 on 2018-04-10 08:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('school', '0008_livelesson'), + ('content', '0017_auto_20180406_1847'), + ] + + operations = [ + migrations.AddField( + model_name='content', + name='live_lesson', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='content', to='school.LiveLesson', verbose_name='Урок онлайн школы'), + ), + ] diff --git a/apps/content/models.py b/apps/content/models.py index f82e3bd5..c4790843 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -30,6 +30,12 @@ class Content(PolymorphicModel): verbose_name='Урок', related_name='content', ) + live_lesson = models.ForeignKey( + 'school.LiveLesson', on_delete=models.CASCADE, + null=True, blank=True, + verbose_name='Урок онлайн школы', + related_name='content', + ) title = models.CharField('Заголовок', max_length=100, default='') position = models.PositiveSmallIntegerField( 'Положение на странице', diff --git a/apps/school/migrations/0008_livelesson.py b/apps/school/migrations/0008_livelesson.py new file mode 100644 index 00000000..a7205867 --- /dev/null +++ b/apps/school/migrations/0008_livelesson.py @@ -0,0 +1,31 @@ +# Generated by Django 2.0.3 on 2018-04-10 08:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('school', '0007_schoolscheduleimage'), + ] + + operations = [ + migrations.CreateModel( + name='LiveLesson', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deactivated_at', models.DateTimeField(blank=True, default=None, null=True)), + ('title', models.CharField(max_length=100, verbose_name='Название урока')), + ('short_description', models.TextField(verbose_name='Краткое описание урока')), + ('stream', models.URLField(verbose_name='Ссылка на VIMEO')), + ('date', models.DateField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('update_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Урок', + 'verbose_name_plural': 'Уроки', + 'ordering': ('title',), + }, + ), + ] diff --git a/apps/school/models.py b/apps/school/models.py index 01830f01..89bc53e4 100644 --- a/apps/school/models.py +++ b/apps/school/models.py @@ -1,7 +1,8 @@ from django.db import models -from apps.content.models import ImageObject +from project.mixins import BaseModel, DeactivatedMixin +from apps.content.models import ImageObject class SchoolSchedule(models.Model): WEEKDAY_CHOICES = ( @@ -49,3 +50,21 @@ class SchoolScheduleImage(models.Model): verbose_name = 'Изображение в галерее' verbose_name_plural = 'Изображения в галерее' ordering = ('-created_at',) + + +class LiveLesson(BaseModel, DeactivatedMixin): + title = models.CharField('Название урока', max_length=100) + short_description = models.TextField('Краткое описание урока') + stream = models.URLField('Ссылка на VIMEO') + date = models.DateField(null=True, blank=True) + + created_at = models.DateTimeField(auto_now_add=True) + update_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + class Meta: + verbose_name = 'Урок' + verbose_name_plural = 'Уроки' + ordering = ('title',)