"""News app common serializers.""" from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from rest_framework.fields import SerializerMethodField from account.serializers.common import UserBaseSerializer from gallery.models import Image from location import models as location_models from location.serializers import AddressBaseSerializer, CountrySimpleSerializer from main.models import SiteSettings from news import models from rating import models as rating_models from tag.serializers import TagBaseSerializer from utils import exceptions as utils_exceptions from utils.models import get_current_locale, get_default_locale from utils.serializers import ( CarouselCreateSerializer, FavoritesCreateSerializer, ImageBaseSerializer, ProjectModelSerializer, TranslatedField, ) class AgendaSerializer(ProjectModelSerializer): start_date = serializers.DateField() start_time = serializers.TimeField(required=False) end_date = serializers.DateField(required=False) end_time = serializers.TimeField(required=False) event_name_translated = TranslatedField(required=False) content_translated = TranslatedField(required=False) class Meta: """Meta class.""" model = models.Agenda fields = ( 'id', 'start_date', 'start_time', 'end_date', 'end_time', 'address', 'content_translated', 'event_name_translated', ) class NewsBannerSerializer(ProjectModelSerializer): title_translated = TranslatedField() image_url = serializers.URLField() content_url = serializers.URLField() class Meta: """Meta class.""" model = models.NewsBanner fields = ( 'id', 'title_translated', 'image_url', 'content_url' ) class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" default_image_url = serializers.ImageField(source='default_image.image', allow_null=True) class Meta: """Meta class.""" model = models.NewsType fields = ('id', 'name', 'default_image_url') class NewsBaseSerializer(ProjectModelSerializer): """Base serializer for News model.""" title_translated = TranslatedField() subtitle_translated = TranslatedField() news_type = NewsTypeSerializer(read_only=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags') in_favorites = serializers.BooleanField(allow_null=True, read_only=True) view_counter = serializers.IntegerField(read_only=True) slug = serializers.SerializerMethodField(read_only=True, allow_null=True) def get_slug(self, obj): if obj.slugs: return obj.slugs.get(get_current_locale()) \ or obj.slugs.get(get_default_locale()) \ or next(iter(obj.slugs.values())) class Meta: """Meta class.""" model = models.News fields = ( 'id', 'slug', 'title_translated', 'subtitle_translated', 'is_highlighted', 'news_type', 'tags', 'view_counter', ) class NewsSimilarListSerializer(NewsBaseSerializer): """List serializer for News model.""" preview_image_url = serializers.URLField() class Meta(NewsBaseSerializer.Meta): """Meta class.""" fields = NewsBaseSerializer.Meta.fields + ( 'preview_image_url', ) class NewsListSerializer(NewsBaseSerializer): """List serializer for News model.""" image = ImageBaseSerializer(source='crop_main_image', allow_null=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" fields = NewsBaseSerializer.Meta.fields + ( 'image', 'in_favorites', ) class NewsDetailSerializer(NewsBaseSerializer): """News detail serializer.""" description_translated = TranslatedField() country = CountrySimpleSerializer(read_only=True) author = UserBaseSerializer(source='created_by', read_only=True) state_display = serializers.CharField(source='get_state_display', read_only=True) gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) start = serializers.DateTimeField(source='publication_datetime', read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" fields = NewsBaseSerializer.Meta.fields + ( 'description_translated', 'start', 'end', 'is_publish', 'state', 'state_display', 'author', 'country', 'gallery', ) def update(self, instance, validated_data): return super().update(instance, validated_data) class NewsDetailWebSerializer(NewsDetailSerializer): """News detail serializer for web users..""" same_theme = SerializerMethodField() should_read = SerializerMethodField() agenda = AgendaSerializer() banner = NewsBannerSerializer() in_favorites = serializers.BooleanField(read_only=True) class Meta(NewsDetailSerializer.Meta): """Meta class.""" fields = NewsDetailSerializer.Meta.fields + ( 'same_theme', 'should_read', 'agenda', 'banner', 'in_favorites', ) def get_same_theme(self, obj): return NewsSimilarListSerializer(obj.same_theme(self.context['request'].user), many=True, read_only=True).data def get_should_read(self, obj): return NewsSimilarListSerializer(obj.should_read(self.context['request'].user), many=True, read_only=True).data class NewsPreviewWebSerializer(NewsDetailSerializer): """News preview serializer for web users..""" same_theme = SerializerMethodField() agenda = AgendaSerializer() banner = NewsBannerSerializer() class Meta(NewsDetailSerializer.Meta): """Meta class.""" fields = NewsDetailSerializer.Meta.fields + ( 'same_theme', 'agenda', 'banner', ) def get_same_theme(self, obj): return NewsSimilarListSerializer(obj.same_theme(self.context['request'].user), many=True, read_only=True).data class NewsBackOfficeBaseSerializer(NewsBaseSerializer): """News back office base serializer.""" is_published = serializers.BooleanField(source='is_publish', read_only=True) descriptions = serializers.ListField(required=False) agenda = AgendaSerializer(required=False, allow_null=True) state_display = serializers.CharField(source='get_state_display', read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" fields = NewsBaseSerializer.Meta.fields + ( 'title', 'backoffice_title', 'subtitle', 'slugs', 'locale_to_description_is_active', 'is_published', 'duplication_date', 'must_of_the_week', 'publication_date', 'publication_time', 'created', 'modified', 'descriptions', 'agenda', 'state', 'state_display', ) extra_kwargs = { 'created': {'read_only': True}, 'modified': {'read_only': True}, 'duplication_date': {'read_only': True}, 'locale_to_description_is_active': {'allow_null': False}, 'must_of_the_week': {'read_only': True}, # 'state': {'read_only': True}, 'state_display': {'read_only': True}, } def validate(self, attrs): """Overridden validate method.""" if 'descriptions' in attrs: descriptions = attrs.pop('descriptions') locales = list(map(lambda x: x['locale'], descriptions)) status_to_bool = { 'active': True, 'inactive': False, } attrs['slugs'] = {obj['locale']: obj['slug'] for obj in descriptions if 'slug' in obj} attrs['title'] = {obj['locale']: obj['title'] for obj in descriptions if 'title' in obj} attrs['locale_to_description_is_active'] = { obj['locale']: status_to_bool[obj['status']] for obj in descriptions } attrs['description'] = {obj['locale']: obj['text'] for obj in descriptions if 'text' in obj} if self.context['request'].method == 'PATCH': instance = models.News.objects.get(pk=self.context['request'].data['id']) for key in ['slugs', 'title', 'locale_to_description_is_active', 'description']: for locale in locales: if locale not in attrs[key]: attrs[key][locale] = getattr(instance, key).get(locale) return attrs def create(self, validated_data): slugs = validated_data.get('slugs') if slugs: slugs_list = list(map(lambda x: x.lower(), slugs.values())) slugs_set = set(slugs_list) if models.News.objects.filter( slugs__values__contains=list(slugs.values()) ).exists() or len(slugs_list) != len(slugs_set): raise serializers.ValidationError({'slugs': _('Slug should be unique')}) request = self.context.get("request") if request and hasattr(request, "user"): user = request.user validated_data['created_by'] = user agenda_data = validated_data.get('agenda') agenda = None if agenda_data is not None: agenda_serializer = AgendaSerializer(data=agenda_data) agenda_serializer.is_valid(raise_exception=True) agenda = agenda_serializer.save() instance = super().create(validated_data) instance.agenda = agenda instance.save() return instance def update(self, instance, validated_data): slugs = validated_data.get('slugs') slugs_list = list(map(lambda x: x.lower(), slugs.values() if slugs else ())) slugs_set = set(slugs_list) if slugs: slugs_list = list(map(lambda x: x.lower(), slugs.values())) slugs_set = set(slugs_list) if models.News.objects.filter( slugs__values__contains=list(slugs.values()) ).exclude(pk=instance.pk).exists() or len(slugs_list) != len(slugs_set): raise serializers.ValidationError({'slugs': _('Slug should be unique')}) agenda_data = validated_data.get('agenda') agenda = instance.agenda if agenda is None and agenda_data is not None: agenda_serializer = AgendaSerializer(data=agenda_data) agenda_serializer.is_valid(raise_exception=True) agenda_serializer.save() elif agenda_data is not None: agenda.start_datetime = agenda_data.pop( 'start_datetime') if 'start_datetime' in agenda_data else agenda.start_datetime agenda.end_datetime = agenda_data.pop( 'end_datetime') if 'end_datetime' in agenda_data else agenda.end_datetime agenda.address = agenda_data.pop( 'address') if 'address' in agenda_data else agenda.address agenda.event_name = agenda_data.pop( 'event_name') if 'event_time' in agenda_data else agenda.event_name agenda.content = agenda_data.pop( 'content') if 'content' in agenda_data else agenda.content agenda.save() return super().update(instance, validated_data) class NewsBackOfficeDuplicationInfoSerializer(serializers.ModelSerializer): """Duplication info for news detail.""" country = CountrySimpleSerializer(read_only=True) class Meta: model = models.News fields = ( 'id', 'duplication_date', 'country', ) class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, NewsDetailSerializer): """News detail serializer for back-office users.""" news_type_id = serializers.PrimaryKeyRelatedField( source='news_type', write_only=True, queryset=models.NewsType.objects.all()) country_id = serializers.PrimaryKeyRelatedField( source='country', write_only=True, queryset=location_models.Country.objects.all()) site_id = serializers.PrimaryKeyRelatedField( source='site', write_only=True, queryset=SiteSettings.objects.all()) template_display = serializers.CharField(source='get_template_display', read_only=True) duplicates = NewsBackOfficeDuplicationInfoSerializer(many=True, allow_null=True, read_only=True) agenda = AgendaSerializer(required=False, allow_null=True, read_only=True) class Meta(NewsDetailSerializer.Meta, NewsBackOfficeBaseSerializer.Meta): """Meta class.""" fields = NewsBackOfficeBaseSerializer.Meta.fields + \ NewsDetailSerializer.Meta.fields + ( 'description', 'news_type_id', 'country_id', 'site_id', 'template', 'template_display', 'is_international', 'duplicates', ) def validate(self, attrs): """Overridden validate method.""" return super().validate(attrs) def update(self, instance, validated_data): return super().update(instance, validated_data) class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): """Serializer class for model NewsGallery.""" class Meta: """Meta class""" model = models.NewsGallery fields = [ 'id', 'is_main', ] @property def request_kwargs(self): """Get url kwargs from request.""" return self.context.get('request').parser_context.get('kwargs') def create(self, validated_data): news_pk = self.request_kwargs.get('pk') image_id = self.request_kwargs.get('image_id') qs = models.NewsGallery.objects.filter(image_id=image_id, news_id=news_pk) instance = qs.first() if instance: qs.update(**validated_data) return instance return super().create(validated_data) def validate(self, attrs): """Override validate method.""" news_pk = self.request_kwargs.get('pk') image_id = self.request_kwargs.get('image_id') news_qs = models.News.objects.filter(pk=news_pk) image_qs = Image.objects.filter(id=image_id) if not news_qs.exists(): raise serializers.ValidationError({'detail': _('News not found')}) if not image_qs.exists(): raise serializers.ValidationError({'detail': _('Image not found')}) news = news_qs.first() image = image_qs.first() # if image in news.gallery.all(): # raise serializers.ValidationError({'detail': _('Image is already added.')}) attrs['news'] = news attrs['image'] = image return attrs class NewsFavoritesCreateSerializer(FavoritesCreateSerializer): """Serializer to favorite object w/ model News.""" def validate(self, attrs): """Overridden validate method""" # Check establishment object news_qs = models.News.objects.filter(slugs__values__contains=[self.slug]) # Check establishment obj by slug from lookup_kwarg if not news_qs.exists(): raise serializers.ValidationError({'detail': _('Object not found.')}) else: news = news_qs.first() # Check existence in favorites if news.favorites.filter(user=self.user).exists(): raise utils_exceptions.FavoritesError() attrs['news'] = news return attrs def create(self, validated_data, *args, **kwargs): """Overridden create method""" validated_data.update({ 'user': self.user, 'content_object': validated_data.pop('news') }) return super().create(validated_data) class NewsCarouselCreateSerializer(CarouselCreateSerializer): """Serializer to carousel object w/ model News.""" def validate(self, attrs): news = models.News.objects.filter(pk=self.pk).first() if not news: raise serializers.ValidationError({'detail': _('Object not found.')}) if news.carousels.exists(): raise utils_exceptions.CarouselError() attrs['news'] = news return attrs def create(self, validated_data, *args, **kwargs): validated_data.update({ 'country': validated_data['news'].country }) validated_data.update({ 'content_object': validated_data.pop('news'), 'is_parse': True, 'active': True, }) return super().create(validated_data) class NewsCloneCreateSerializer(NewsBackOfficeBaseSerializer, NewsDetailSerializer): """Serializer for creating news clone.""" template_display = serializers.CharField(source='get_template_display', read_only=True) duplicates = NewsBackOfficeDuplicationInfoSerializer(many=True, allow_null=True, read_only=True) class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta): fields = NewsBackOfficeBaseSerializer.Meta.fields + NewsDetailSerializer.Meta.fields + ( 'template_display', 'duplicates', ) read_only_fields = fields def create(self, validated_data): kwargs = self.context.get('request').parser_context.get('kwargs') instance = get_object_or_404(models.News, pk=kwargs['pk']) new_country = get_object_or_404(location_models.Country, code=kwargs['country_code']) view_count_model = rating_models.ViewCount.objects.create(count=0) instance.create_duplicate(new_country, view_count_model) return get_object_or_404(models.News, pk=kwargs['pk']) class NewsStatesSerializer(serializers.Serializer): value = serializers.IntegerField() state_translated = serializers.CharField()