diff --git a/apps/collection/views/common.py b/apps/collection/views/common.py index 5c7b2720..aa3c991a 100644 --- a/apps/collection/views/common.py +++ b/apps/collection/views/common.py @@ -17,8 +17,8 @@ class CollectionViewMixin(generics.GenericAPIView): def get_queryset(self): """Override get_queryset method.""" return models.Collection.objects.published() \ - .by_country_code(code=self.request.country_code) \ - .order_by('-on_top', '-modified') + .by_country_code(code=self.request.country_code) \ + .order_by('-on_top', '-created') class GuideViewMixin(generics.GenericAPIView): @@ -39,7 +39,7 @@ class CollectionHomePageView(CollectionListView): def get_queryset(self): """Override get_queryset.""" return super(CollectionHomePageView, self).get_queryset() \ - .filter_all_related_gt(3) + .filter_all_related_gt(3) class CollectionDetailView(CollectionViewMixin, generics.RetrieveAPIView): diff --git a/apps/main/methods.py b/apps/main/methods.py index f19d595a..14583660 100644 --- a/apps/main/methods.py +++ b/apps/main/methods.py @@ -37,6 +37,13 @@ def determine_country_code(request): return country_code.lower() +def determine_country_name(request): + """Determine country name.""" + META = request.META + return META.get('X-GeoIP-Country-Name', + META.get('HTTP_X_GEOIP_COUNTRY_NAME')) + + def determine_coordinates(request): META = request.META longitude = META.get('X-GeoIP-Longitude', diff --git a/apps/main/models.py b/apps/main/models.py index 9b778834..06e2355b 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -210,6 +210,25 @@ class CarouselQuerySet(models.QuerySet): """Filter collection by country code.""" return self.filter(country__code=code) + def create_or_destroy(self, instance_to_bind, country): + """Creates or destroys Carousel instance depending on instance fields""" + toggle = True + kwargs = { + 'content_type': ContentType.objects.get_for_model(instance_to_bind), + 'object_id': instance_to_bind.pk, + 'country': country, + } + if toggle is None: + return + elif toggle: + kwargs.update({ + 'is_parse': True, + 'active': True, + }) + self.create(**kwargs) + else: + self.filter(**kwargs).delete() + class Carousel(models.Model): """Carousel model.""" @@ -279,6 +298,11 @@ class Carousel(models.Model): @property def slug(self): + if hasattr(self.content_object, 'slugs'): + try: + return next(iter(self.content_object.slugs.values())) + except StopIteration: + return None if hasattr(self.content_object, 'slug'): return self.content_object.slug diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 20033661..becf2869 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -85,8 +85,9 @@ class DetermineLocation(generics.GenericAPIView): def get(self, request, *args, **kwargs): longitude, latitude = methods.determine_coordinates(request) city = methods.determine_user_city(request) - if longitude and latitude and city: - return Response(data={'latitude': latitude, 'longitude': longitude, 'city': city}) - else: - raise Http404 + country_name = methods.determine_country_name(request) + if longitude and latitude and city and country_name: + return Response(data={'latitude': latitude, 'longitude': longitude, + 'city': city, 'country_name': country_name}) + raise Http404 diff --git a/apps/news/migrations/0045_news_must_of_the_week.py b/apps/news/migrations/0045_news_must_of_the_week.py new file mode 100644 index 00000000..57f5f351 --- /dev/null +++ b/apps/news/migrations/0045_news_must_of_the_week.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2019-12-17 17:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0044_auto_20191216_2044'), + ] + + operations = [ + migrations.AddField( + model_name='news', + name='must_of_the_week', + field=models.BooleanField(default=False), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 19a27463..d39962c5 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -223,6 +223,7 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi verbose_name=_('Duplication datetime')) duplication_uuid = models.UUIDField(default=uuid.uuid4, editable=True, unique=False, verbose_name=_('Field to detect doubles')) + must_of_the_week = models.BooleanField(default=False, verbose_name=_('Show in the carousel')) objects = NewsQuerySet.as_manager() class Meta: @@ -248,6 +249,11 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi """Duplicates for this news item excluding same country code labeled""" return News.objects.filter(duplication_uuid=self.duplication_uuid).exclude(country=self.country) + @property + def has_any_desc_active(self): + """Detects whether news item has any active description""" + return any(list(map(lambda v: v.lower() == 'true', self.locale_to_description_is_active.values()))) + @property def is_publish(self): return self.state in self.PUBLISHED_STATES diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 87acde91..78d048c5 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -5,7 +5,7 @@ from rest_framework.fields import SerializerMethodField from account.serializers.common import UserBaseSerializer from gallery.models import Image -from main.models import SiteSettings +from main.models import SiteSettings, Carousel from location import models as location_models from location.serializers import CountrySimpleSerializer, AddressBaseSerializer from news import models @@ -185,6 +185,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): 'locale_to_description_is_active', 'is_published', 'duplication_date', + 'must_of_the_week', ) extra_kwargs = { 'backoffice_title': {'allow_null': False}, @@ -199,7 +200,9 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): slugs__values__contains=list(slugs.values()) ).exists(): raise serializers.ValidationError({'slugs': _('News with this slug already exists.')}) - return super().create(validated_data) + instance = super().create(validated_data) + Carousel.objects.create_or_destroy(instance, instance.address.city.country) + return instance def update(self, instance, validated_data): slugs = validated_data.get('slugs') @@ -208,7 +211,10 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): slugs__values__contains=list(slugs.values()) ).exclude(pk=instance.pk).exists(): raise serializers.ValidationError({'slugs': _('News with this slug already exists.')}) - return super().update(instance, validated_data) + ret = super().update(instance, validated_data) + if ret.must_of_the_week != instance.must_of_the_week: + Carousel.objects.create_or_destroy(instance, instance.address.city.country) + return ret class NewsBackOfficeDuplicationInfoSerializer(serializers.ModelSerializer): @@ -380,5 +386,5 @@ class NewsCloneCreateSerializer(NewsBackOfficeBaseSerializer, 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 instance + return get_object_or_404(models.News, pk=kwargs['pk']) diff --git a/apps/news/views.py b/apps/news/views.py index c234c10a..a8f3e389 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -54,7 +54,8 @@ class NewsListView(NewsMixinView, generics.ListAPIView): 'international_preferred': True, 'locale': locale, }) - return super().get_queryset(*args, **kwargs) + return super().get_queryset(*args, **kwargs)\ + .filter(locale_to_description_is_active__values__contains=['True']) class NewsDetailView(NewsMixinView, generics.RetrieveAPIView): diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index 62e3e984..b6b6e77e 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -3,6 +3,7 @@ from django.conf import settings from django_elasticsearch_dsl import Document, Index, fields from search_indexes.utils import OBJECT_FIELD_PROPERTIES from news import models +from json import dumps NewsIndex = Index(settings.ELASTICSEARCH_INDEX_NAMES.get(__name__, 'news')) @@ -17,7 +18,7 @@ class NewsDocument(Document): 'name': fields.KeywordField()}) title = fields.ObjectField(attr='title_indexing', properties=OBJECT_FIELD_PROPERTIES) - slugs = fields.ObjectField(properties=OBJECT_FIELD_PROPERTIES) + slugs = fields.KeywordField() backoffice_title = fields.TextField(analyzer='english') subtitle = fields.ObjectField(attr='subtitle_indexing', properties=OBJECT_FIELD_PROPERTIES) @@ -45,9 +46,10 @@ class NewsDocument(Document): multi=True) favorites_for_users = fields.ListField(field=fields.IntegerField()) start = fields.DateField(attr='start') + has_any_desc_active = fields.BooleanField() def prepare_slugs(self, instance): - return {locale: instance.slugs.get(locale) for locale in OBJECT_FIELD_PROPERTIES} + return dumps(instance.slugs or {}) class Django: diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index 4cad05fc..2a183c10 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -6,6 +6,7 @@ from news.serializers import NewsTypeSerializer from search_indexes.documents import EstablishmentDocument, NewsDocument from search_indexes.documents.product import ProductDocument from search_indexes.utils import get_translated_value +from json import loads class TagsDocumentSerializer(serializers.Serializer): @@ -243,7 +244,7 @@ class NewsDocumentSerializer(InFavoritesMixin, DocumentSerializer): @staticmethod def get_slug(obj): - return get_translated_value(obj.slugs) + return get_translated_value(loads(obj.slugs)) @staticmethod def get_title_translated(obj): diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index f088bf8b..7ebecfb3 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -17,6 +17,11 @@ from utils.pagination import ESDocumentPagination class NewsDocumentViewSet(BaseDocumentViewSet): """News document ViewSet.""" + def get_queryset(self): + qs = super(NewsDocumentViewSet, self).get_queryset() + qs = qs.filter('match', has_any_desc_active=True) + return qs + document = NewsDocument lookup_field = 'slug' pagination_class = ESDocumentPagination diff --git a/apps/utils/models.py b/apps/utils/models.py index eb41f7a1..07891330 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -67,16 +67,23 @@ def get_default_locale(): settings.FALLBACK_LOCALE -def translate_field(self, field_name): +def translate_field(self, field_name, toggle_field_name=None): def translate(self): field = getattr(self, field_name) + toggler = getattr(self, toggle_field_name, None) if isinstance(field, dict): + if toggler: + field = {locale: v for locale, v in field.items() if toggler.get(locale) in [True, 'True', 'true']} value = field.get(to_locale(get_language())) # fallback if value is None: value = field.get(get_default_locale()) if value is None: - value = field.get(next(iter(field.keys()), None)) + try: + value = next(iter(field.values())) + except StopIteration: + # field values are absent + return None return value return None return translate @@ -114,7 +121,7 @@ class TranslatedFieldsMixin: field_name = field.name if isinstance(field, TJSONField): setattr(cls, f'{field.name}_translated', - property(translate_field(self, field_name))) + property(translate_field(self, field_name, f'locale_to_{field_name}_is_active'))) setattr(cls, f'{field_name}_indexing', property(index_field(self, field_name)))