Merge remote-tracking branch 'origin/develop' into recipe

This commit is contained in:
alex 2019-12-17 08:56:09 +03:00
commit 959eb33043
12 changed files with 207 additions and 29 deletions

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.7 on 2019-12-15 21:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('collection', '0023_advertorial'),
]
operations = [
migrations.AddField(
model_name='collection',
name='rank',
field=models.IntegerField(default=None, null=True),
),
migrations.AlterField(
model_name='collection',
name='start',
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='start'),
),
migrations.RemoveField(
model_name='collection',
name='description',
)
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.7 on 2019-12-16 17:25
from django.db import migrations
import utils.models
class Migration(migrations.Migration):
dependencies = [
('collection', '0024_auto_20191215_2156'),
]
operations = [
migrations.AddField(
model_name='collection',
name='description',
field=utils.models.TJSONField(blank=True, default=None,
help_text='{"en-GB":"some text"}', null=True,
verbose_name='description'),
),
]

View File

@ -6,9 +6,10 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from utils.models import ProjectBaseMixin, URLImageMixin from utils.models import (
from utils.models import TJSONField ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
from utils.models import TranslatedFieldsMixin URLImageMixin,
)
from utils.querysets import RelatedObjectsCountMixin from utils.querysets import RelatedObjectsCountMixin
@ -24,7 +25,8 @@ class CollectionNameMixin(models.Model):
class CollectionDateMixin(models.Model): class CollectionDateMixin(models.Model):
"""CollectionDate mixin""" """CollectionDate mixin"""
start = models.DateTimeField(_('start')) start = models.DateTimeField(blank=True, null=True, default=None,
verbose_name=_('start'))
end = models.DateTimeField(blank=True, null=True, default=None, end = models.DateTimeField(blank=True, null=True, default=None,
verbose_name=_('end')) verbose_name=_('end'))
@ -80,6 +82,8 @@ class Collection(ProjectBaseMixin, CollectionDateMixin,
verbose_name=_('Collection slug'), editable=True, null=True) verbose_name=_('Collection slug'), editable=True, null=True)
old_id = models.IntegerField(null=True, blank=True) old_id = models.IntegerField(null=True, blank=True)
rank = models.IntegerField(null=True, default=None)
objects = CollectionQuerySet.as_manager() objects = CollectionQuerySet.as_manager()
class Meta: class Meta:
@ -108,20 +112,32 @@ class Collection(ProjectBaseMixin, CollectionDateMixin,
@property @property
def related_object_names(self) -> list: def related_object_names(self) -> list:
"""Return related object names.""" """Return related object names."""
raw_object_names = [] raw_objects = []
for related_object in [related_object.name for related_object in self._related_objects]: for related_object in [related_object.name for related_object in self._related_objects]:
instances = getattr(self, f'{related_object}') instances = getattr(self, f'{related_object}')
if instances.exists(): if instances.exists():
for instance in instances.all(): for instance in instances.all():
raw_object_names.append(instance.slug if hasattr(instance, 'slug') else None) raw_object = (instance.id, instance.slug) if hasattr(instance, 'slug') else (
instance.id, None
)
raw_objects.append(raw_object)
# parse slugs # parse slugs
object_names = [] related_objects = []
object_names = set()
re_pattern = r'[\w]+' re_pattern = r'[\w]+'
for raw_name in raw_object_names: for object_id, raw_name, in raw_objects:
result = re.findall(re_pattern, raw_name) result = re.findall(re_pattern, raw_name)
if result: object_names.append(' '.join(result).capitalize()) if result:
return set(object_names) name = ' '.join(result).capitalize()
if name not in object_names:
related_objects.append({
'id': object_id,
'name': name
})
object_names.add(name)
return related_objects
class GuideTypeQuerySet(models.QuerySet): class GuideTypeQuerySet(models.QuerySet):

View File

@ -7,7 +7,8 @@ from location.models import Country
from location.serializers import CountrySimpleSerializer from location.serializers import CountrySimpleSerializer
from product.models import Product from product.models import Product
from utils.exceptions import ( from utils.exceptions import (
BindingObjectNotFound, RemovedBindingObjectNotFound, ObjectAlreadyAdded BindingObjectNotFound, ObjectAlreadyAdded,
RemovedBindingObjectNotFound,
) )
@ -33,13 +34,14 @@ class CollectionBackOfficeSerializer(CollectionBaseSerializer):
'on_top', 'on_top',
'country', 'country',
'country_id', 'country_id',
'block_size', # 'block_size',
'description', 'description',
'slug', 'slug',
'start', # 'start',
'end', # 'end',
'count_related_objects', 'count_related_objects',
'related_object_names', 'related_object_names',
'rank',
] ]
@ -68,15 +70,15 @@ class CollectionBindObjectSerializer(serializers.Serializer):
attrs['collection'] = collection attrs['collection'] = collection
if obj_type == self.ESTABLISHMENT: if obj_type == self.ESTABLISHMENT:
establishment = Establishment.objects.filter(pk=obj_id).\ establishment = Establishment.objects.filter(pk=obj_id). \
first() first()
if not establishment: if not establishment:
raise BindingObjectNotFound() raise BindingObjectNotFound()
if request.method == 'POST' and collection.establishments.\ if request.method == 'POST' and collection.establishments. \
filter(pk=establishment.pk).exists(): filter(pk=establishment.pk).exists():
raise ObjectAlreadyAdded() raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not collection.\ if request.method == 'DELETE' and not collection. \
establishments.filter(pk=establishment.pk).\ establishments.filter(pk=establishment.pk). \
exists(): exists():
raise RemovedBindingObjectNotFound() raise RemovedBindingObjectNotFound()
attrs['related_object'] = establishment attrs['related_object'] = establishment
@ -84,10 +86,10 @@ class CollectionBindObjectSerializer(serializers.Serializer):
product = Product.objects.filter(pk=obj_id).first() product = Product.objects.filter(pk=obj_id).first()
if not product: if not product:
raise BindingObjectNotFound() raise BindingObjectNotFound()
if request.method == 'POST' and collection.products.\ if request.method == 'POST' and collection.products. \
filter(pk=product.pk).exists(): filter(pk=product.pk).exists():
raise ObjectAlreadyAdded() raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not collection.products.\ if request.method == 'DELETE' and not collection.products. \
filter(pk=product.pk).exists(): filter(pk=product.pk).exists():
raise RemovedBindingObjectNotFound() raise RemovedBindingObjectNotFound()
attrs['related_object'] = product attrs['related_object'] = product

View File

@ -1,5 +1,6 @@
from rest_framework import permissions from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, mixins from rest_framework import mixins, permissions, viewsets
from rest_framework.filters import OrderingFilter
from collection import models from collection import models
from collection.serializers import back as serializers from collection.serializers import back as serializers
@ -31,9 +32,13 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
permission_classes = (permissions.IsAuthenticated,) permission_classes = (permissions.IsAuthenticated,)
queryset = models.Collection.objects.all() queryset = models.Collection.objects.all()
filter_backends = [DjangoFilterBackend, OrderingFilter]
serializer_class = serializers.CollectionBackOfficeSerializer serializer_class = serializers.CollectionBackOfficeSerializer
bind_object_serializer_class = serializers.CollectionBindObjectSerializer bind_object_serializer_class = serializers.CollectionBindObjectSerializer
ordering_fields = ('rank', 'start')
ordering = ('-start', )
def perform_binding(self, serializer): def perform_binding(self, serializer):
data = serializer.validated_data data = serializer.validated_data
collection = data.pop('collection') collection = data.pop('collection')

View File

@ -541,9 +541,15 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
def visible_tags(self): def visible_tags(self):
return super().visible_tags \ return super().visible_tags \
.exclude(category__index_name__in=['guide', 'collection', 'purchased_item', .exclude(category__index_name__in=['guide', 'collection', 'purchased_item',
'business_tag', 'business_tags_de', 'tag']) 'business_tag', 'business_tags_de']) \
.exclude(value__in=['rss', 'rss_selection'])
# todo: recalculate toque_number # todo: recalculate toque_number
@property
def visible_tags_detail(self):
"""Removes some tags from detail Establishment representation"""
return self.visible_tags.exclude(category__index_name__in=['tag'])
def recalculate_toque_number(self): def recalculate_toque_number(self):
toque_number = 0 toque_number = 0
if self.address and self.public_mark: if self.address and self.public_mark:

View File

@ -0,0 +1,37 @@
# Generated by Django 2.2.7 on 2019-12-16 19:20
import django.contrib.postgres.fields.hstore
from django.db import migrations, models
import uuid
def fill_uuid(apps, schemaeditor):
News = apps.get_model('news', 'News')
for news in News.objects.all():
news.duplication_uuid = uuid.uuid4()
news.save()
class Migration(migrations.Migration):
dependencies = [
('news', '0042_news_duplication_date'),
]
operations = [
migrations.AddField(
model_name='news',
name='description_to_locale_is_active',
field=django.contrib.postgres.fields.hstore.HStoreField(blank=True, default=dict, help_text='{"en-GB": true, "fr-FR": false}', null=True, verbose_name='Is description for certain locale active'),
),
migrations.AddField(
model_name='news',
name='duplication_uuid',
field=models.UUIDField(default=uuid.uuid4, verbose_name='Field to detect doubles'),
),
migrations.AlterField(
model_name='news',
name='slugs',
field=django.contrib.postgres.fields.hstore.HStoreField(blank=True, default=dict, help_text='{"en-GB":"some slug"}', null=True, verbose_name='Slugs for current news obj'),
),
migrations.RunPython(fill_uuid, migrations.RunPython.noop),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.7 on 2019-12-16 20:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('news', '0043_auto_20191216_1920'),
]
operations = [
migrations.RenameField(
model_name='news',
old_name='description_to_locale_is_active',
new_name='locale_to_description_is_active',
),
]

View File

@ -1,5 +1,9 @@
"""News app models.""" """News app models."""
import uuid
from django.conf import settings
from django.contrib.contenttypes import fields as generic from django.contrib.contenttypes import fields as generic
from django.contrib.postgres.fields import HStoreField
from django.db import models from django.db import models
from django.db.models import Case, When from django.db.models import Case, When
from django.utils import timezone from django.utils import timezone
@ -11,8 +15,6 @@ from utils.models import (BaseAttributes, TJSONField, TranslatedFieldsMixin, Has
ProjectBaseMixin, GalleryModelMixin, IntermediateGalleryModelMixin, ProjectBaseMixin, GalleryModelMixin, IntermediateGalleryModelMixin,
FavoritesMixin) FavoritesMixin)
from utils.querysets import TranslationQuerysetMixin from utils.querysets import TranslationQuerysetMixin
from django.conf import settings
from django.contrib.postgres.fields import HStoreField
class Agenda(ProjectBaseMixin, TranslatedFieldsMixin): class Agenda(ProjectBaseMixin, TranslatedFieldsMixin):
@ -177,11 +179,14 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi
description = TJSONField(blank=True, null=True, default=None, description = TJSONField(blank=True, null=True, default=None,
verbose_name=_('description'), verbose_name=_('description'),
help_text='{"en-GB":"some text"}') help_text='{"en-GB":"some text"}')
locale_to_description_is_active = HStoreField(null=True, default=dict, blank=True,
verbose_name=_('Is description for certain locale active'),
help_text='{"en-GB": true, "fr-FR": false}')
start = models.DateTimeField(blank=True, null=True, default=None, start = models.DateTimeField(blank=True, null=True, default=None,
verbose_name=_('Start')) verbose_name=_('Start'))
end = models.DateTimeField(blank=True, null=True, default=None, end = models.DateTimeField(blank=True, null=True, default=None,
verbose_name=_('End')) verbose_name=_('End'))
slugs = HStoreField(null=True, blank=True, default=None, slugs = HStoreField(null=True, blank=True, default=dict,
verbose_name=_('Slugs for current news obj'), verbose_name=_('Slugs for current news obj'),
help_text='{"en-GB":"some slug"}') help_text='{"en-GB":"some slug"}')
state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES,
@ -213,6 +218,8 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi
on_delete=models.SET_NULL, verbose_name=_('site settings')) on_delete=models.SET_NULL, verbose_name=_('site settings'))
duplication_date = models.DateTimeField(blank=True, null=True, default=None, duplication_date = models.DateTimeField(blank=True, null=True, default=None,
verbose_name=_('Duplication datetime')) verbose_name=_('Duplication datetime'))
duplication_uuid = models.UUIDField(default=uuid.uuid4, editable=True, unique=False,
verbose_name=_('Field to detect doubles'))
objects = NewsQuerySet.as_manager() objects = NewsQuerySet.as_manager()
class Meta: class Meta:
@ -233,6 +240,12 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi
self.duplication_date = timezone.now() self.duplication_date = timezone.now()
self.save() self.save()
@property
def duplicates(self):
"""Duplicates for this news item excluding same country code labeled"""
return News.objects.filter(duplication_uuid=self.duplication_uuid).exclude(country=self.country)
@property @property
def is_publish(self): def is_publish(self):
return self.state in self.PUBLISHED_STATES return self.state in self.PUBLISHED_STATES

View File

@ -182,6 +182,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
'backoffice_title', 'backoffice_title',
'subtitle', 'subtitle',
'slugs', 'slugs',
'locale_to_description_is_active',
'is_published', 'is_published',
'duplication_date', 'duplication_date',
) )
@ -209,6 +210,20 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
return super().update(instance, validated_data) 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, class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
NewsDetailSerializer): NewsDetailSerializer):
"""News detail serializer for back-office users.""" """News detail serializer for back-office users."""
@ -224,6 +239,7 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
queryset=SiteSettings.objects.all()) queryset=SiteSettings.objects.all())
template_display = serializers.CharField(source='get_template_display', template_display = serializers.CharField(source='get_template_display',
read_only=True) read_only=True)
duplicates = NewsBackOfficeDuplicationInfoSerializer(many=True, allow_null=True, read_only=True)
class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta): class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta):
"""Meta class.""" """Meta class."""
@ -237,6 +253,7 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
'template', 'template',
'template_display', 'template_display',
'is_international', 'is_international',
'duplicates',
) )

View File

@ -1,7 +1,7 @@
"""News app views.""" """News app views."""
from django.conf import settings from django.conf import settings
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import generics, permissions from rest_framework import generics, permissions, response
from news import filters, models, serializers from news import filters, models, serializers
from rating.tasks import add_rating from rating.tasks import add_rating
@ -99,7 +99,10 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView,
def get_queryset(self): def get_queryset(self):
"""Override get_queryset method.""" """Override get_queryset method."""
return super().get_queryset().with_extended_related() qs = super().get_queryset().with_extended_related()
if self.request.country_code:
qs = qs.by_country_code(self.request.country_code)
return qs
class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
@ -107,6 +110,13 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView,
"""Resource for a create gallery for news for back-office users.""" """Resource for a create gallery for news for back-office users."""
serializer_class = serializers.NewsBackOfficeGallerySerializer serializer_class = serializers.NewsBackOfficeGallerySerializer
def create(self, request, *args, **kwargs):
_ = super().create(request, *args, **kwargs)
news_qs = self.filter_queryset(self.get_queryset())
return response.Response(
data=serializers.NewsDetailSerializer(get_object_or_404(news_qs, pk=kwargs.get('pk'))).data
)
def get_object(self): def get_object(self):
""" """
Returns the object the view is displaying. Returns the object the view is displaying.

View File

@ -61,11 +61,18 @@ class NewsDocumentViewSet(BaseDocumentViewSet):
) )
filter_fields = { filter_fields = {
'tags_id': {
'field': 'tags.id',
'lookups': [
constants.LOOKUP_QUERY_IN,
constants.LOOKUP_QUERY_EXCLUDE,
],
},
'tag': { 'tag': {
'field': 'tags.id', 'field': 'tags.id',
'lookups': [ 'lookups': [
constants.LOOKUP_QUERY_IN, constants.LOOKUP_QUERY_IN,
constants.LOOKUP_QUERY_EXCLUDE constants.LOOKUP_QUERY_EXCLUDE,
] ]
}, },
'tag_value': { 'tag_value': {