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

This commit is contained in:
alex 2019-12-11 17:10:56 +03:00
commit bbb72dabbe
26 changed files with 459 additions and 102 deletions

View File

@ -10,4 +10,5 @@ urlpatterns = [
path('user-role/', views.UserRoleLstView.as_view(), name='user-role-list-create'), path('user-role/', views.UserRoleLstView.as_view(), name='user-role-list-create'),
path('user/', views.UserLstView.as_view(), name='user-create-list'), path('user/', views.UserLstView.as_view(), name='user-create-list'),
path('user/<int:id>/', views.UserRUDView.as_view(), name='user-rud'), path('user/<int:id>/', views.UserRUDView.as_view(), name='user-rud'),
path('user/<int:id>/csv', views.get_user_csv, name='user-csv'),
] ]

View File

@ -1,6 +1,9 @@
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics, permissions from rest_framework import generics, permissions
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
import csv
from django.http import HttpResponse, HttpResponseNotFound
from rest_framework.authtoken.models import Token
from account import models from account import models
from account.models import User from account.models import User
@ -46,3 +49,69 @@ class UserRUDView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = serializers.BackDetailUserSerializer serializer_class = serializers.BackDetailUserSerializer
permission_classes = (permissions.IsAdminUser,) permission_classes = (permissions.IsAdminUser,)
lookup_field = 'id' lookup_field = 'id'
def get_user_csv(request, id):
# fields = ["id", "uuid", "nickname", "locale", "country_code", "city", "role", "consent_purpose", "consent_at",
# "last_seen_at", "created_at", "updated_at", "email", "is_admin", "ezuser_id", "ez_user_id",
# "encrypted_password", "reset_password_token", "reset_password_sent_at", "remember_created_at",
# "sign_in_count", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip",
# "confirmation_token", "confirmed_at", "confirmation_sent_at", "unconfirmed_email", "webpush_subscription"]
# uuid == id
#
# Не найдены:
# consent_purpose
# consent_at
# ezuser_id
# ez_user_id
# remember_created_at
# sign_in_count
# current_sign_in_at
# current_sign_in_ip
# last_sign_in_ip
# confirmed_at
# confirmation_sent_at
# webpush_subscription
#
# country_code не получить - клиент не привязан к стране
try:
user = User.objects.get(id=id)
except User.DoesNotExist:
return HttpResponseNotFound("User not found")
try:
roles = " ".join([role for role in user.roles])
except:
roles = ""
token, _ = Token.objects.get_or_create(user=user)
fields = {
"id": user.id,
"uuid": user.id,
"username": getattr(user, "username", ""),
"locale": getattr(user, "locale", ""),
"city": getattr(user, "city", ""),
"role": roles,
"created_at": getattr(user, "date_joined", ""),
"updated_at": user.last_login,
"email": user.email,
"is_admin": user.is_superuser,
"encrypted_password": user.password,
"reset_password_token": token.key,
"reset_password_sent_at": token.created, # TODO: не уверен в назначении поля, лучше проверить
"last_sign_in_at": user.last_login, # Повтор?
"confirmation_token": user.confirm_email_token,
"unconfirmed_email": 1 if user.unconfirmed_email else 0
}
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = f'attachment; filename="{user.email}.csv"'
writer = csv.writer(response)
writer.writerow(fields.keys())
writer.writerow(fields.values())
return response

View File

@ -14,3 +14,8 @@ class PageInline(admin.TabularInline):
class AdvertisementModelAdmin(admin.ModelAdmin): class AdvertisementModelAdmin(admin.ModelAdmin):
"""Admin model for model Advertisement""" """Admin model for model Advertisement"""
inlines = (PageInline, ) inlines = (PageInline, )
list_display = ('id', '__str__', 'block_level',
'start', 'end', 'page_type')
list_filter = ('url', 'block_level', 'start', 'end', 'page_type',
'pages__source')
date_hierarchy = 'created'

View File

@ -71,11 +71,11 @@ class Advertisement(ProjectBaseMixin):
return super().delete(using, keep_parents) return super().delete(using, keep_parents)
@property @property
def mobile_page(self): def mobile_pages(self):
"""Return mobile page""" """Return mobile page"""
return self.pages.by_platform(Page.MOBILE).first() return self.pages.by_platform(Page.MOBILE)
@property @property
def web_page(self): def web_pages(self):
"""Return web page""" """Return web page"""
return self.pages.by_platform(Page.WEB).first() return self.pages.by_platform(Page.WEB)

View File

@ -1,26 +1,14 @@
"""Serializers for back office app advertisements""" """Serializers for back office app advertisements"""
from main.serializers import PageBaseSerializer from advertisement.serializers import AdvertisementBaseSerializer
from main.serializers import PageExtendedSerializer
class AdvertisementPageBaseSerializer(PageBaseSerializer): class AdvertisementDetailSerializer(AdvertisementBaseSerializer):
"""Base serializer for linking page w/ advertisement.""" """Advertisement serializer for back office."""
pages = PageExtendedSerializer(many=True, read_only=True)
class Meta(PageBaseSerializer.Meta): class Meta(AdvertisementBaseSerializer.Meta):
"""Meta class.""" """Meta class."""
fields = AdvertisementBaseSerializer.Meta.fields + [
PageBaseSerializer.Meta.extra_kwargs.update({ 'pages',
'advertisement': {'write_only': True}, ]
'image_url': {'required': True},
'width': {'required': True},
'height': {'required': True},
})
class AdvertisementPageListCreateSerializer(AdvertisementPageBaseSerializer):
"""Serializer for linking page w/ advertisement."""
def create(self, validated_data):
"""Overridden create method."""
validated_data['advertisement'] = self.context.get('view').get_object()
return super().create(validated_data)

View File

@ -2,15 +2,15 @@
from rest_framework import serializers from rest_framework import serializers
from advertisement import models from advertisement import models
from translation.serializers import LanguageSerializer
from main.serializers import SiteShortSerializer, PageBaseSerializer
from translation.models import Language
from main.models import SiteSettings from main.models import SiteSettings
from main.serializers import PageTypeBaseSerializer
from translation.models import Language
class AdvertisementBaseSerializer(serializers.ModelSerializer): class AdvertisementBaseSerializer(serializers.ModelSerializer):
"""Base serializer for model Advertisement.""" """Base serializer for model Advertisement."""
page_type_detail = PageTypeBaseSerializer(read_only=True,
source='page_type')
target_languages = serializers.PrimaryKeyRelatedField( target_languages = serializers.PrimaryKeyRelatedField(
queryset=Language.objects.all(), queryset=Language.objects.all(),
many=True, many=True,
@ -34,16 +34,17 @@ class AdvertisementBaseSerializer(serializers.ModelSerializer):
'target_sites', 'target_sites',
'start', 'start',
'end', 'end',
'page_type',
'page_type_detail',
] ]
extra_kwargs = {
'page_type': {'required': True, 'write_only': True}
}
class AdvertisementPageTypeCommonListSerializer(AdvertisementBaseSerializer): class AdvertisementSerializer(AdvertisementBaseSerializer):
"""Serializer for AdvertisementPageTypeCommonView.""" """Serializer for model Advertisement."""
page = PageBaseSerializer(source='common_page', read_only=True)
class Meta(AdvertisementBaseSerializer.Meta): class Meta(AdvertisementBaseSerializer.Meta):
"""Meta class.""" """Meta class."""
fields = AdvertisementBaseSerializer.Meta.fields + [ fields = AdvertisementBaseSerializer.Meta.fields.copy()
'page', fields.pop(fields.index('page_type_detail'))
]

View File

@ -1,15 +1,15 @@
"""Serializers for mobile app advertisements""" """Serializers for mobile app advertisements"""
from advertisement.serializers import AdvertisementBaseSerializer from advertisement.serializers import AdvertisementSerializer
from main.serializers import PageBaseSerializer from main.serializers import PageBaseSerializer
class AdvertisementPageTypeMobileListSerializer(AdvertisementBaseSerializer): class AdvertisementPageTypeMobileListSerializer(AdvertisementSerializer):
"""Serializer for AdvertisementPageTypeMobileView.""" """Serializer for AdvertisementPageTypeMobileView."""
page = PageBaseSerializer(source='mobile_page', read_only=True) pages = PageBaseSerializer(many=True, source='mobile_pages', read_only=True)
class Meta(AdvertisementBaseSerializer.Meta): class Meta(AdvertisementSerializer.Meta):
"""Meta class.""" """Meta class."""
fields = AdvertisementBaseSerializer.Meta.fields + [ fields = AdvertisementSerializer.Meta.fields + [
'page', 'pages',
] ]

View File

@ -1,15 +1,15 @@
"""Serializers for web app advertisements""" """Serializers for web app advertisements"""
from advertisement.serializers import AdvertisementBaseSerializer from advertisement.serializers import AdvertisementSerializer
from main.serializers import PageBaseSerializer from main.serializers import PageBaseSerializer
class AdvertisementPageTypeWebListSerializer(AdvertisementBaseSerializer): class AdvertisementPageTypeWebListSerializer(AdvertisementSerializer):
"""Serializer for AdvertisementPageTypeWebView.""" """Serializer for AdvertisementPageTypeWebView."""
page = PageBaseSerializer(source='web_page', read_only=True) pages = PageBaseSerializer(many=True, source='web_pages', read_only=True)
class Meta(AdvertisementBaseSerializer.Meta): class Meta(AdvertisementSerializer.Meta):
"""Meta class.""" """Meta class."""
fields = AdvertisementBaseSerializer.Meta.fields + [ fields = AdvertisementSerializer.Meta.fields + [
'page', 'pages',
] ]

View File

@ -9,10 +9,10 @@ app_name = 'advertisements'
urlpatterns = [ urlpatterns = [
path('', views.AdvertisementListCreateView.as_view(), name='list-create'), path('', views.AdvertisementListCreateView.as_view(), name='list-create'),
path('<int:pk>/', views.AdvertisementRUDView.as_view(), name='rud'), path('<int:pk>/', views.AdvertisementRUDView.as_view(), name='rud'),
path('<int:pk>/pages/', views.AdvertisementPageListCreateView.as_view(), path('<int:pk>/pages/', views.AdvertisementPageCreateView.as_view(),
name='page-list-create'), name='ad-page-create'),
path('<int:ad_pk>/pages/<int:page_pk>/', views.AdvertisementPageRUDView.as_view(), path('<int:ad_pk>/pages/<int:page_pk>/', views.AdvertisementPageUDView.as_view(),
name='page-rud') name='ad-page-update-destroy')
] ]
urlpatterns += common_urlpatterns urlpatterns += common_urlpatterns

View File

@ -1,19 +1,19 @@
"""Back office views for app advertisement""" """Back office views for app advertisement"""
from rest_framework import generics from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework import permissions from rest_framework import permissions
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from main.serializers import PageExtendedSerializer
from advertisement.models import Advertisement from advertisement.models import Advertisement
from rest_framework.response import Response
from rest_framework import status
from advertisement.serializers import (AdvertisementBaseSerializer, from advertisement.serializers import (AdvertisementBaseSerializer,
AdvertisementPageBaseSerializer, AdvertisementDetailSerializer)
AdvertisementPageListCreateSerializer)
class AdvertisementBackOfficeViewMixin(generics.GenericAPIView): class AdvertisementBackOfficeViewMixin(generics.GenericAPIView):
"""Base back office advertisement view.""" """Base back office advertisement view."""
pagination_class = None
permission_classes = (permissions.IsAuthenticated, ) permission_classes = (permissions.IsAuthenticated, )
def get_queryset(self): def get_queryset(self):
@ -31,14 +31,14 @@ class AdvertisementRUDView(AdvertisementBackOfficeViewMixin,
generics.RetrieveUpdateDestroyAPIView): generics.RetrieveUpdateDestroyAPIView):
"""Retrieve|Update|Destroy advertisement page view.""" """Retrieve|Update|Destroy advertisement page view."""
serializer_class = AdvertisementBaseSerializer serializer_class = AdvertisementDetailSerializer
class AdvertisementPageListCreateView(AdvertisementBackOfficeViewMixin, class AdvertisementPageCreateView(AdvertisementBackOfficeViewMixin,
generics.ListCreateAPIView): generics.CreateAPIView):
"""Retrieve|Update|Destroy advertisement page view.""" """Create advertisement page view."""
serializer_class = AdvertisementPageListCreateSerializer serializer_class = PageExtendedSerializer
def get_object(self): def get_object(self):
"""Returns the object the view is displaying.""" """Returns the object the view is displaying."""
@ -56,12 +56,19 @@ class AdvertisementPageListCreateView(AdvertisementBackOfficeViewMixin,
"""Overridden get_queryset method.""" """Overridden get_queryset method."""
return self.get_object().pages.all() return self.get_object().pages.all()
def create(self, request, *args, **kwargs):
"""Overridden create method."""
request.data.update({'advertisement': self.get_object().pk})
super().create(request, *args, **kwargs)
return Response(status=status.HTTP_200_OK)
class AdvertisementPageRUDView(AdvertisementBackOfficeViewMixin,
generics.RetrieveUpdateDestroyAPIView):
"""Create|Retrieve|Update|Destroy advertisement page view."""
serializer_class = AdvertisementPageBaseSerializer class AdvertisementPageUDView(AdvertisementBackOfficeViewMixin,
generics.UpdateAPIView,
generics.DestroyAPIView):
"""Update|Destroy advertisement page view."""
serializer_class = PageExtendedSerializer
def get_object(self): def get_object(self):
"""Returns the object the view is displaying.""" """Returns the object the view is displaying."""

View File

@ -3,8 +3,7 @@ from rest_framework import generics
from rest_framework import permissions from rest_framework import permissions
from advertisement.models import Advertisement from advertisement.models import Advertisement
from advertisement.serializers import AdvertisementBaseSerializer, \ from advertisement.serializers import AdvertisementBaseSerializer
AdvertisementPageTypeCommonListSerializer
class AdvertisementBaseView(generics.GenericAPIView): class AdvertisementBaseView(generics.GenericAPIView):
@ -16,8 +15,7 @@ class AdvertisementBaseView(generics.GenericAPIView):
def get_queryset(self): def get_queryset(self):
"""Overridden get queryset method.""" """Overridden get queryset method."""
return Advertisement.objects.with_base_related() \ return Advertisement.objects.with_base_related()
.by_locale(self.request.locale)
class AdvertisementPageTypeListView(AdvertisementBaseView, generics.ListAPIView): class AdvertisementPageTypeListView(AdvertisementBaseView, generics.ListAPIView):

View File

@ -1,8 +1,9 @@
from django.conf import settings from django.conf import settings
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.files.base import ContentFile
from rest_framework import serializers from rest_framework import serializers
from sorl.thumbnail.parsers import parse_crop from sorl.thumbnail import get_thumbnail
from sorl.thumbnail.parsers import ThumbnailParseError from sorl.thumbnail.parsers import parse_crop, ThumbnailParseError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import models from . import models
@ -88,15 +89,23 @@ class CropImageSerializer(ImageSerializer):
quality = validated_data.pop('quality') quality = validated_data.pop('quality')
crop = validated_data.pop('crop') crop = validated_data.pop('crop')
crop_params = {
'geometry': f'{width}x{height}',
'quality': quality,
'crop': crop,
}
cropped_image = self._image.get_cropped_image(**crop_params)
image = self._image image = self._image
image.pk = None
crop_params['geometry_string'] = crop_params.pop('geometry')
resized = get_thumbnail(self._image.image, **crop_params)
image.image.save(resized.name, ContentFile(resized.read()), True)
image.save()
if image and width and height: if image and width and height:
setattr(image, setattr(image,
'cropped_image', 'cropped_image',
image.get_cropped_image( cropped_image)
geometry=f'{width}x{height}',
quality=quality,
crop=crop))
return image return image
@property @property

View File

@ -10,6 +10,7 @@ urlpatterns = [
path('addresses/<int:pk>/', views.AddressRUDView.as_view(), name='address-RUD'), path('addresses/<int:pk>/', views.AddressRUDView.as_view(), name='address-RUD'),
path('cities/', views.CityListCreateView.as_view(), name='city-list-create'), path('cities/', views.CityListCreateView.as_view(), name='city-list-create'),
path('cities/all/', views.CityListSearchView.as_view(), name='city-list-create'),
path('cities/<int:pk>/', views.CityRUDView.as_view(), name='city-retrieve'), path('cities/<int:pk>/', views.CityRUDView.as_view(), name='city-retrieve'),
path('cities/<int:pk>/gallery/', views.CityGalleryListView.as_view(), path('cities/<int:pk>/gallery/', views.CityGalleryListView.as_view(),
name='gallery-list'), name='gallery-list'),

View File

@ -37,6 +37,15 @@ class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView):
filter_class = filters.CityBackFilter filter_class = filters.CityBackFilter
class CityListSearchView(common.CityViewMixin, generics.ListCreateAPIView):
"""Create view for model City."""
serializer_class = serializers.CitySerializer
permission_classes = [IsAuthenticatedOrReadOnly|IsCountryAdmin]
queryset = models.City.objects.all()
filter_class = filters.CityBackFilter
pagination_class = None
class CityRUDView(common.CityViewMixin, generics.RetrieveUpdateDestroyAPIView): class CityRUDView(common.CityViewMixin, generics.RetrieveUpdateDestroyAPIView):
"""RUD view for model City.""" """RUD view for model City."""
serializer_class = serializers.CitySerializer serializer_class = serializers.CitySerializer

View File

@ -51,3 +51,6 @@ class PageTypeAdmin(admin.ModelAdmin):
@admin.register(models.Page) @admin.register(models.Page)
class PageAdmin(admin.ModelAdmin): class PageAdmin(admin.ModelAdmin):
"""Page admin.""" """Page admin."""
list_display = ('id', '__str__', 'advertisement')
list_filter = ('advertisement__url', 'source')
date_hierarchy = 'created'

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.7 on 2019-12-11 06:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('advertisement', '0008_auto_20191116_1135'),
('main', '0040_footer'),
]
operations = [
migrations.AlterUniqueTogether(
name='page',
unique_together={('advertisement', 'source')},
),
]

View File

@ -305,7 +305,7 @@ class PageQuerySet(models.QuerySet):
def by_platform(self, platform: int): def by_platform(self, platform: int):
"""Filter by platform.""" """Filter by platform."""
return self.filter(source=platform) return self.filter(source__in=[Page.ALL, platform])
class Page(URLImageMixin, PlatformMixin, ProjectBaseMixin): class Page(URLImageMixin, PlatformMixin, ProjectBaseMixin):
@ -325,6 +325,7 @@ class Page(URLImageMixin, PlatformMixin, ProjectBaseMixin):
"""Meta class.""" """Meta class."""
verbose_name = _('page') verbose_name = _('page')
verbose_name_plural = _('pages') verbose_name_plural = _('pages')
unique_together = ('advertisement', 'source')
def __str__(self): def __str__(self):
"""Overridden dunder method.""" """Overridden dunder method."""

View File

@ -152,8 +152,6 @@ class SiteShortSerializer(serializers.ModelSerializer):
] ]
class AwardBaseSerializer(serializers.ModelSerializer): class AwardBaseSerializer(serializers.ModelSerializer):
"""Award base serializer.""" """Award base serializer."""
@ -234,10 +232,26 @@ class PageBaseSerializer(serializers.ModelSerializer):
'advertisement', 'advertisement',
] ]
extra_kwargs = { extra_kwargs = {
'establishment': {'write_only': True} 'advertisement': {'write_only': True},
'image_url': {'required': True},
'width': {'required': True},
'height': {'required': True},
} }
class PageExtendedSerializer(PageBaseSerializer):
"""Extended serializer for model Page."""
source_display = serializers.CharField(read_only=True,
source='get_source_display')
class Meta(PageBaseSerializer.Meta):
"""Meta class."""
fields = PageBaseSerializer.Meta.fields + [
'source',
'source_display',
]
class PageTypeBaseSerializer(serializers.ModelSerializer): class PageTypeBaseSerializer(serializers.ModelSerializer):
"""Serializer fro model PageType.""" """Serializer fro model PageType."""

View File

@ -20,6 +20,8 @@ urlpatterns = [
name='site-feature-rud'), name='site-feature-rud'),
path('footer/', views.FooterBackView.as_view(), name='footer-list-create'), path('footer/', views.FooterBackView.as_view(), name='footer-list-create'),
path('footer/<int:pk>/', views.FooterRUDBackView.as_view(), name='footer-rud'), path('footer/<int:pk>/', views.FooterRUDBackView.as_view(), name='footer-rud'),
path('page-types/', views.PageTypeListCreateView.as_view(),
name='page-types-list-create')
] ]

View File

@ -4,7 +4,7 @@ from rest_framework import generics, permissions
from main import serializers from main import serializers
from main.filters import AwardFilter from main.filters import AwardFilter
from main.models import Award, Footer from main.models import Award, Footer, PageType
from main.views import SiteSettingsView, SiteListView from main.views import SiteSettingsView, SiteListView
@ -81,3 +81,11 @@ class FooterRUDBackView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
serializer_class = serializers.FooterBackSerializer serializer_class = serializers.FooterBackSerializer
queryset = Footer.objects.all() queryset = Footer.objects.all()
class PageTypeListCreateView(generics.ListCreateAPIView):
"""PageType back office view."""
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
pagination_class = None
serializer_class = serializers.PageTypeBaseSerializer
queryset = PageType.objects.all()

View File

@ -70,9 +70,6 @@ class CarouselListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
country_code = self.request.country_code country_code = self.request.country_code
if hasattr(settings, 'CAROUSEL_ITEMS') and country_code in settings.INTERNATIONAL_COUNTRY_CODES:
qs = models.Carousel.objects.filter(id__in=settings.CAROUSEL_ITEMS)
return qs
qs = models.Carousel.objects.is_parsed().active() qs = models.Carousel.objects.is_parsed().active()
if country_code: if country_code:
qs = qs.by_country_code(country_code) qs = qs.by_country_code(country_code)

View File

@ -177,13 +177,23 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
'backoffice_title': {'allow_null': False}, 'backoffice_title': {'allow_null': False},
} }
def validate(self, attrs): def create(self, validated_data):
slugs = attrs.get('slugs', {}) slugs = validated_data.get('slugs')
if models.News.objects.filter( if slugs:
slugs__values__contains=list(slugs.values()) if models.News.objects.filter(
).exclude(id=attrs.get('id', 0)).exists(): slugs__values__contains=list(slugs.values())
raise serializers.ValidationError({'slugs': _('News with this slug already exists.')}) ).exists():
return attrs raise serializers.ValidationError({'slugs': _('News with this slug already exists.')})
return super().create(validated_data)
def update(self, instance, validated_data):
slugs = validated_data.get('slugs')
if slugs:
if models.News.objects.filter(
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)
class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
@ -233,6 +243,15 @@ class NewsBackOfficeGallerySerializer(serializers.ModelSerializer):
"""Get url kwargs from request.""" """Get url kwargs from request."""
return self.context.get('request').parser_context.get('kwargs') return self.context.get('request').parser_context.get('kwargs')
def create(self, validated_data):
news_pk = self.get_request_kwargs().get('pk')
image_id = self.get_request_kwargs().get('image_id')
news_gallery_model = models.NewsGallery.objects.filter(image_id=image_id, news_id=news_pk).first()
if news_gallery_model:
news_gallery_model.update(**validated_data)
return news_gallery_model
return super().create(validated_data)
def validate(self, attrs): def validate(self, attrs):
"""Override validate method.""" """Override validate method."""
news_pk = self.get_request_kwargs().get('pk') news_pk = self.get_request_kwargs().get('pk')

View File

@ -2,11 +2,14 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework.fields import SerializerMethodField from rest_framework.fields import SerializerMethodField
from establishment.models import (Establishment, EstablishmentType) from establishment.models import Establishment
from news.models import News, NewsType from establishment.models import EstablishmentType
from news.models import News
from news.models import NewsType
from tag import models from tag import models
from utils.exceptions import (ObjectAlreadyAdded, BindingObjectNotFound, from utils.exceptions import BindingObjectNotFound
RemovedBindingObjectNotFound) from utils.exceptions import ObjectAlreadyAdded
from utils.exceptions import RemovedBindingObjectNotFound
from utils.serializers import TranslatedField from utils.serializers import TranslatedField
@ -95,6 +98,72 @@ class TagCategoryBaseSerializer(serializers.ModelSerializer):
return TagBaseSerializer(instance=tags, many=True, read_only=True).data return TagBaseSerializer(instance=tags, many=True, read_only=True).data
class FiltersTagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory."""
label_translated = TranslatedField()
filters = SerializerMethodField()
param_name = SerializerMethodField()
type = SerializerMethodField()
class Meta:
"""Meta class."""
model = models.TagCategory
fields = (
'id',
'label_translated',
'index_name',
'param_name',
'type',
'filters',
)
def get_type(self, obj):
return obj in ['open_now', ]
def get_param_name(self, obj):
if obj == 'service':
return 'tags_id__in'
elif obj == 'pop':
return 'tags_id__in'
elif obj == 'open_now':
return 'open_now'
elif obj == 'wine_region':
return 'wine_region_id__in'
return '%s__in' % obj.index_name
def get_fields(self, *args, **kwargs):
fields = super(FiltersTagCategoryBaseSerializer, self).get_fields()
if self.get_type(self):
fields.pop('filters', None)
else:
fields.pop('type', None)
return fields
def get_filters(self, obj):
query_params = dict(self.context['request'].query_params)
params = {}
if 'establishment_type' in query_params:
params = {
'establishments__isnull': False,
}
elif 'product_type' in query_params:
params = {
'products__isnull': False,
}
tags = obj.tags.filter(**params).distinct()
return TagBaseSerializer(instance=tags, many=True, read_only=True).data
class TagCategoryShortSerializer(serializers.ModelSerializer): class TagCategoryShortSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory.""" """Serializer for model TagCategory."""

View File

@ -7,6 +7,7 @@ app_name = 'tag'
router = SimpleRouter() router = SimpleRouter()
router.register(r'categories', views.TagCategoryViewSet) router.register(r'categories', views.TagCategoryViewSet)
router.register(r'filters', views.FiltersTagCategoryViewSet)
router.register(r'chosen_tags', views.ChosenTagsView) router.register(r'chosen_tags', views.ChosenTagsView)
urlpatterns = [ urlpatterns = [

View File

@ -1,11 +1,17 @@
"""Tag views.""" """Tag views."""
from django.conf import settings from django.conf import settings
from rest_framework import generics
from rest_framework import mixins
from rest_framework import permissions from rest_framework import permissions
from rest_framework import viewsets, mixins, status, generics from rest_framework import status
from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from tag import filters, models, serializers from location.models import WineRegion
from tag import filters
from tag import models
from tag import serializers
class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet): class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet):
@ -36,7 +42,8 @@ class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet):
serializer = self.get_serializer(queryset, many=True) serializer = self.get_serializer(queryset, many=True)
result_list = serializer.data result_list = serializer.data
if request.query_params.get('type') and (settings.ESTABLISHMENT_CHOSEN_TAGS or settings.NEWS_CHOSEN_TAGS): if request.query_params.get('type') and (settings.ESTABLISHMENT_CHOSEN_TAGS or settings.NEWS_CHOSEN_TAGS):
ordered_list = settings.ESTABLISHMENT_CHOSEN_TAGS if request.query_params.get('type') == 'establishment' else settings.NEWS_CHOSEN_TAGS ordered_list = settings.ESTABLISHMENT_CHOSEN_TAGS if request.query_params.get(
'type') == 'establishment' else settings.NEWS_CHOSEN_TAGS
result_list = sorted(result_list, key=lambda x: ordered_list.index(x['index_name'])) result_list = sorted(result_list, key=lambda x: ordered_list.index(x['index_name']))
return Response(result_list) return Response(result_list)
@ -53,6 +60,139 @@ class TagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = serializers.TagCategoryBaseSerializer serializer_class = serializers.TagCategoryBaseSerializer
# User`s views & viewsets
class FiltersTagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
"""ViewSet for TagCategory model."""
filterset_class = filters.TagCategoryFilterSet
pagination_class = None
permission_classes = (permissions.AllowAny,)
queryset = models.TagCategory.objects.with_tags().with_base_related(). \
distinct()
serializer_class = serializers.FiltersTagCategoryBaseSerializer
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
result_list = serializer.data
query_params = request.query_params
params_type = query_params.get('type')
if query_params.get('establishment_type'):
params_type = query_params.get('establishment_type')
elif query_params.get('product_type'):
params_type = query_params.get('product_type')
week_days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
flags = ('toque_number', 'wine_region', 'works_noon', 'works_evening', 'works_now', 'works_at_weekday')
filter_flags = {flag_name: False for flag_name in flags}
additional_flags = []
if params_type == 'restaurant':
additional_flags += ['toque_number', 'works_noon', 'works_evening', 'works_now']
elif params_type == 'winery':
additional_flags += ['wine_region']
elif params_type == 'artisan':
additional_flags += ['works_now', 'works_at_weekday']
for flag_name in additional_flags:
filter_flags[flag_name] = True
if filter_flags['toque_number']:
toques = {
"index_name": "toque_number",
"label_translated": "Toques",
"param_name": "toque_number__in",
"filters": [{
"id": toque_id,
"index_name": "toque_%d" % toque_id,
"label_translated": "Toque %d" % toque_id
} for toque_id in range(6)]
}
result_list.append(toques)
if filter_flags['wine_region']:
wine_region_id = query_params.get('wine_region_id__in')
if str(wine_region_id).isdigit():
queryset = WineRegion.objects.filter(id=int(wine_region_id))
else:
queryset = WineRegion.objects.all()
wine_regions = {
"index_name": "wine_region",
"label_translated": "Wine region",
"param_name": "wine_region_id__in",
"filters": [{
"id": obj.id,
"index_name": obj.name.lower().replace(' ', '_'),
"label_translated": obj.name
} for obj in queryset]
}
result_list.append(wine_regions)
if filter_flags['works_noon']:
works_noon = {
"index_name": "works_noon",
"label_translated": "Open noon",
"param_name": "works_noon__in",
"filters": [{
"id": weekday,
"index_name": week_days[weekday].lower(),
"label_translated": week_days[weekday]
} for weekday in range(7)]
}
result_list.append(works_noon)
if filter_flags['works_evening']:
works_evening = {
"index_name": "works_evening",
"label_translated": "Open evening",
"param_name": "works_evening__in",
"filters": [{
"id": weekday,
"index_name": week_days[weekday].lower(),
"label_translated": week_days[weekday]
} for weekday in range(7)]
}
result_list.append(works_evening)
if filter_flags['works_now']:
works_now = {
"index_name": "open_now",
"label_translated": "Open now",
"param_name": "open_now",
"type": True
}
result_list.append(works_now)
if filter_flags['works_at_weekday']:
works_at_weekday = {
"index_name": "works_at_weekday",
"label_translated": "Works at weekday",
"param_name": "works_at_weekday__in",
"filters": [{
"id": weekday,
"index_name": week_days[weekday].lower(),
"label_translated": week_days[weekday]
} for weekday in range(7)]
}
result_list.append(works_at_weekday)
if 'tags_id__in' in query_params:
# filtering by params_type and tags id
# todo: result_list.append( filtering_data )
pass
return Response(result_list)
# BackOffice user`s views & viewsets # BackOffice user`s views & viewsets
class BindObjectMixin: class BindObjectMixin:
"""Bind object mixin.""" """Bind object mixin."""

View File

@ -516,9 +516,6 @@ PHONENUMBER_DEFAULT_REGION = "FR"
FALLBACK_LOCALE = 'en-GB' FALLBACK_LOCALE = 'en-GB'
# TMP TODO remove it later
# Временный хардкод для демонстрации > 15 ноября, потом удалить!
CAROUSEL_ITEMS = [465]
ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', 'business', 'bar_cocktail', 'brunch', 'pop'] ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', 'business', 'bar_cocktail', 'brunch', 'pop']
NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership'] NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership']
INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next'] INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next']