# # steering, Programme/schedule management for AURA # # Copyright (C) 2011-2017, 2020, Ernesto Rico Schmidt # Copyright (C) 2017-2019, Ingo Leindecker # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more # details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist from django.utils import timezone from rest_framework import serializers from profile.models import Profile from profile.serializers import ProfileSerializer from program.models import Show, Schedule, TimeSlot, Category, FundingCategory, Host, Topic, MusicFocus, Note, Type, Language, \ RRule from steering.settings import THUMBNAIL_SIZES class UserSerializer(serializers.ModelSerializer): # Add profile fields to JSON profile = ProfileSerializer(required=False) class Meta: model = User fields = ('id', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'is_superuser', 'password', 'profile') def create(self, validated_data): """ Create and return a new User instance, given the validated data. """ profile_data = validated_data.pop('profile') if 'profile' in validated_data else None user = super(UserSerializer, self).create(validated_data) user.date_joined = timezone.now() user.set_password(validated_data['password']) user.save() if profile_data: profile = Profile(user=user, cba_username=profile_data.get('cba_username').strip(), cba_user_token=profile_data.get('cba_user_token').strip()) profile.save() return user def update(self, instance, validated_data): """ Update and return an existing User instance, given the validated data. """ user = self.context['user'] instance.first_name = validated_data.get('first_name', instance.first_name) instance.last_name = validated_data.get('last_name', instance.last_name) instance.email = validated_data.get('email', instance.email) if user.is_superuser: instance.is_active = validated_data.get('is_active', instance.is_active) instance.is_staff = validated_data.get('is_staff', instance.is_staff) instance.is_superuser = validated_data.get('is_superuser', instance.is_superuser) profile_data = validated_data.pop('profile') if 'profile' in validated_data else None if profile_data: # TODO: How to hook into this from ProfileSerializer without having to call it here? try: profile = Profile.objects.get(user=instance.id) except ObjectDoesNotExist: profile = Profile.objects.create(user=instance, **profile_data) profile.cba_username = profile_data.get('cba_username') profile.cba_user_token = profile_data.get('cba_user_token') profile.save() instance.save() return instance class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing Category instance, given the validated data. """ instance.category = validated_data.get('category', instance.category) instance.abbrev = validated_data.get('abbrev', instance.abbrev) instance.slug = validated_data.get('slug', instance.slug) instance.description = validated_data.get('description', instance.description) instance.is_active = validated_data.get('is_active', instance.is_active) instance.save() return instance class HostSerializer(serializers.ModelSerializer): thumbnails = serializers.SerializerMethodField() # Read-only @staticmethod def get_thumbnails(host): """Returns thumbnails""" thumbnails = [] if host.image.name and THUMBNAIL_SIZES: for size in THUMBNAIL_SIZES: thumbnails.append(host.image.crop[size].name) return thumbnails class Meta: model = Host fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing Host instance, given the validated data. """ instance.name = validated_data.get('name', instance.name) instance.is_active = validated_data.get('is_active', instance.is_active) instance.email = validated_data.get('email', instance.email) instance.website = validated_data.get('website', instance.website) instance.biography = validated_data.get('biography', instance.biography) instance.image = validated_data.get('image', instance.image) instance.ppoi = validated_data.get('ppoi', instance.ppoi) instance.save() return instance class LanguageSerializer(serializers.ModelSerializer): class Meta: model = Language fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing Language instance, given the validated data. """ instance.name = validated_data.get('name', instance.name) instance.is_active = validated_data.get('is_active', instance.is_active) instance.save() return instance class TopicSerializer(serializers.ModelSerializer): class Meta: model = Topic fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing Topic instance, given the validated data. """ instance.topic = validated_data.get('topic', instance.topic) instance.abbrev = validated_data.get('abbrev', instance.abbrev) instance.slug = validated_data.get('slug', instance.slug) instance.is_active = validated_data.get('is_active', instance.is_active) instance.save() return instance class MusicFocusSerializer(serializers.ModelSerializer): class Meta: model = MusicFocus fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing MusicFocus instance, given the validated data. """ instance.focus = validated_data.get('focus', instance.focus) instance.abbrev = validated_data.get('abbrev', instance.abbrev) instance.slug = validated_data.get('slug', instance.slug) instance.is_active = validated_data.get('is_active', instance.is_active) instance.save() return instance class TypeSerializer(serializers.ModelSerializer): class Meta: model = Type fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing Type instance, given the validated data. """ instance.type = validated_data.get('type', instance.type) instance.slug = validated_data.get('slug', instance.slug) instance.is_active = validated_data.get('is_active', instance.is_active) instance.save() return instance class FundingCategorySerializer(serializers.ModelSerializer): class Meta: model = FundingCategory fields = '__all__' def update(self, instance, validated_data): """ Update and return an existing FundingCategory instance, given the validated data. """ instance.fundingcategory = validated_data.get('fundingcategory', instance.fundingcategory) instance.abbrev = validated_data.get('abbrev', instance.abbrev) instance.slug = validated_data.get('slug', instance.slug) instance.is_active = validated_data.get('is_active', instance.is_active) instance.save() return instance class ShowSerializer(serializers.HyperlinkedModelSerializer): owners = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), many=True) category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all(), many=True) hosts = serializers.PrimaryKeyRelatedField(queryset=Host.objects.all(), many=True) language = serializers.PrimaryKeyRelatedField(queryset=Language.objects.all(), many=True) topic = serializers.PrimaryKeyRelatedField(queryset=Topic.objects.all(), many=True) musicfocus = serializers.PrimaryKeyRelatedField(queryset=MusicFocus.objects.all(), many=True) type = serializers.PrimaryKeyRelatedField(queryset=Type.objects.all()) fundingcategory = serializers.PrimaryKeyRelatedField(queryset=FundingCategory.objects.all()) predecessor = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all(), required=False, allow_null=True) thumbnails = serializers.SerializerMethodField() # Read-only @staticmethod def get_thumbnails(show): """Returns thumbnails""" thumbnails = [] if show.image.name and THUMBNAIL_SIZES: for size in THUMBNAIL_SIZES: thumbnails.append(show.image.crop[size].name) return thumbnails class Meta: model = Show fields = ('id', 'name', 'slug', 'image', 'ppoi', 'logo', 'short_description', 'description', 'email', 'website', 'created', 'last_updated', 'type', 'fundingcategory', 'predecessor', 'cba_series_id', 'default_playlist_id', 'category', 'hosts', 'owners', 'language', 'topic', 'musicfocus', 'thumbnails', 'is_active', 'is_public') def create(self, validated_data): """ Create and return a new Show instance, given the validated data. """ owners = validated_data.pop('owners') category = validated_data.pop('category') hosts = validated_data.pop('hosts') language = validated_data.pop('language') topic = validated_data.pop('topic') musicfocus = validated_data.pop('musicfocus') show = Show.objects.create(**validated_data) # Save many-to-many relationships show.owners.set(owners) show.category.set(category) show.hosts.set(hosts) show.language.set(language) show.topic.set(topic) show.musicfocus.set(musicfocus) show.save() return show def update(self, instance, validated_data): """ Update and return an existing Show instance, given the validated data. """ user = self.context['user'] instance.name = validated_data.get('name', instance.name) instance.slug = validated_data.get('slug', instance.slug) instance.image = validated_data.get('image', instance.image) instance.ppoi = validated_data.get('ppoi', instance.ppoi) instance.logo = validated_data.get('logo', instance.logo) instance.short_description = validated_data.get('short_description', instance.short_description) instance.description = validated_data.get('description', instance.description) instance.email = validated_data.get('email', instance.email) instance.website = validated_data.get('website', instance.website) instance.cba_series_id = validated_data.get('cba_series_id', instance.cba_series_id) instance.default_playlist_id = validated_data.get('default_playlist_id', instance.default_playlist_id) # Only superusers may update the following fields if user.is_superuser: instance.owners.set(validated_data.get('owners', instance.owners)) instance.category.set(validated_data.get('category', instance.category)) instance.hosts.set(validated_data.get('hosts', instance.hosts)) instance.language.set(validated_data.get('language', instance.language)) instance.topic.set(validated_data.get('topic', instance.topic)) instance.musicfocus.set(validated_data.get('musicfocus', instance.musicfocus)) instance.type = validated_data.get('type', instance.type) instance.fundingcategory = validated_data.get('fundingcategory', instance.fundingcategory) instance.predecessor = validated_data.get('predecessor', instance.predecessor) instance.is_active = validated_data.get('is_active', instance.is_active) instance.is_public = validated_data.get('is_public', instance.is_public) instance.save() return instance class ScheduleSerializer(serializers.ModelSerializer): rrule = serializers.PrimaryKeyRelatedField(queryset=RRule.objects.all()) show = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all()) class Meta: model = Schedule fields = '__all__' def create(self, validated_data): """Create and return a new Schedule instance, given the validated data.""" rrule = validated_data.pop('rrule') show = validated_data.pop('show') schedule = Schedule.objects.create(**validated_data) schedule.rrule = rrule schedule.show = show schedule.save() return schedule def update(self, instance, validated_data): """Update and return an existing Schedule instance, given the validated data.""" instance.byweekday = validated_data.get('byweekday', instance.byweekday) instance.dstart = validated_data.get('dstart', instance.dstart) instance.tstart = validated_data.get('tstart', instance.tstart) instance.tend = validated_data.get('tend', instance.tend) instance.until = validated_data.get('until', instance.until) instance.is_repetition = validated_data.get('is_repetition', instance.is_repetition) instance.default_playlist_id = validated_data.get('default_playlist_id', instance.default_playlist_id) instance.rrule = validated_data.get('rrule', instance.rrule) instance.show = validated_data.get('show', instance.show) instance.add_days_no = validated_data.get('add_days_no', instance.add_days_no) instance.add_business_days_only = validated_data.get('add_business_days_only', instance.add_business_days_only) instance.save() return instance class TimeSlotSerializer(serializers.ModelSerializer): show = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all()) schedule = serializers.PrimaryKeyRelatedField(queryset=Schedule.objects.all()) class Meta: model = TimeSlot fields = '__all__' def create(self, validated_data): """Create and return a new TimeSlot instance, given the validated data.""" return TimeSlot.objects.create(**validated_data) def update(self, instance, validated_data): """Update and return an existing Show instance, given the validated data.""" # Only save certain fields instance.memo = validated_data.get('memo', instance.memo) instance.is_repetition = validated_data.get('is_repetition', instance.is_repetition) instance.playlist_id = validated_data.get('playlist_id', instance.playlist_id) instance.save() return instance class NoteSerializer(serializers.ModelSerializer): show = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all()) timeslot = serializers.PrimaryKeyRelatedField(queryset=TimeSlot.objects.all()) host = serializers.PrimaryKeyRelatedField(queryset=Host.objects.all()) thumbnails = serializers.SerializerMethodField() # Read-only @staticmethod def get_thumbnails(note): """Returns thumbnails""" thumbnails = [] if note.image.name and THUMBNAIL_SIZES: for size in THUMBNAIL_SIZES: thumbnails.append(note.image.crop[size].name) return thumbnails class Meta: model = Note fields = '__all__' def create(self, validated_data): """Create and return a new Note instance, given the validated data.""" # Save the creator validated_data['user_id'] = self.context['user_id'] # Try to retrieve audio URL from CBA validated_data['audio_url'] = Note.get_audio_url(validated_data['cba_id']) note = Note.objects.create(**validated_data) # Assign note to timeslot if note.timeslot_id is not None: try: timeslot = TimeSlot.objects.get(pk=note.timeslot_id) timeslot.note_id = note.id timeslot.save(update_fields=["note_id"]) except ObjectDoesNotExist: pass return note def update(self, instance, validated_data): """Update and return an existing Note instance, given the validated data.""" instance.show = validated_data.get('show', instance.show) instance.timeslot = validated_data.get('timeslot', instance.timeslot) instance.title = validated_data.get('title', instance.title) instance.slug = validated_data.get('slug', instance.slug) instance.summary = validated_data.get('summary', instance.summary) instance.content = validated_data.get('content', instance.content) instance.image = validated_data.get('image', instance.image) instance.ppoi = validated_data.get('ppoi', instance.ppoi) instance.status = validated_data.get('status', instance.status) instance.host = validated_data.get('host', instance.host) instance.cba_id = validated_data.get('cba_id', instance.cba_id) instance.audio_url = Note.get_audio_url(instance.cba_id) instance.save() # Remove existing note connections from timeslots timeslots = TimeSlot.objects.filter(note_id=instance.id) for ts in timeslots: ts.note_id = None ts.save(update_fields=["note_id"]) # Assign note to timeslot if instance.timeslot.id is not None: try: timeslot = TimeSlot.objects.get(pk=instance.timeslot.id) timeslot.note_id = instance.id timeslot.save(update_fields=["note_id"]) except ObjectDoesNotExist: pass return instance