Merge branch 'develop' into feature/fix-country-region-city-transfer
This commit is contained in:
commit
6ef0e7adfe
|
|
@ -9,11 +9,17 @@ from utils.views import BindObjectMixin
|
|||
class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
||||
"""ViewSet for Collection model."""
|
||||
|
||||
pagination_class = None
|
||||
# pagination_class = None
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
queryset = models.Collection.objects.all()
|
||||
serializer_class = serializers.CollectionBackOfficeSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
"""Overridden method 'get_queryset'."""
|
||||
qs = models.Collection.objects.all().order_by('-created')
|
||||
if self.request.country_code:
|
||||
qs = qs.by_country_code(self.request.country_code)
|
||||
return qs
|
||||
|
||||
|
||||
class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
|
|
|
|||
|
|
@ -213,7 +213,13 @@ class EstablishmentQuerySet(models.QuerySet):
|
|||
))
|
||||
|
||||
def similar_base(self, establishment):
|
||||
|
||||
"""
|
||||
Return filtered QuerySet by base filters.
|
||||
Filters including:
|
||||
1 Filter by type (and subtype) establishment.
|
||||
2 Filter by published Review.
|
||||
3 With annotated distance.
|
||||
"""
|
||||
filters = {
|
||||
'reviews__status': Review.READY,
|
||||
'establishment_type': establishment.establishment_type,
|
||||
|
|
@ -224,27 +230,64 @@ class EstablishmentQuerySet(models.QuerySet):
|
|||
.filter(**filters) \
|
||||
.annotate_distance(point=establishment.location)
|
||||
|
||||
def similar_base_subquery(self, establishment, filters: dict) -> Subquery:
|
||||
"""
|
||||
Return filtered Subquery object by filters.
|
||||
Filters including:
|
||||
1 Filter by transmitted filters.
|
||||
2 With ordering by distance.
|
||||
"""
|
||||
return Subquery(
|
||||
self.similar_base(establishment)
|
||||
.filter(**filters)
|
||||
.order_by('distance')[:settings.LIMITING_QUERY_OBJECTS]
|
||||
.values('id')
|
||||
)
|
||||
|
||||
def similar_restaurants(self, slug):
|
||||
"""
|
||||
Return QuerySet with objects that similar to Restaurant.
|
||||
:param restaurant_slug: str Establishment slug
|
||||
:param slug: str restaurant slug
|
||||
"""
|
||||
restaurant_qs = self.filter(slug=slug,
|
||||
public_mark__isnull=False)
|
||||
restaurant_qs = self.filter(slug=slug)
|
||||
if restaurant_qs.exists():
|
||||
establishment = restaurant_qs.first()
|
||||
subquery_filter_by_distance = Subquery(
|
||||
self.similar_base(establishment)
|
||||
.filter(public_mark__gte=10,
|
||||
establishment_gallery__is_main=True)
|
||||
.order_by('distance')[:settings.LIMITING_QUERY_OBJECTS]
|
||||
.values('id')
|
||||
restaurant = restaurant_qs.first()
|
||||
ids_by_subquery = self.similar_base_subquery(
|
||||
establishment=restaurant,
|
||||
filters={
|
||||
'public_mark__gte': 10,
|
||||
'establishment_gallery__is_main': True,
|
||||
}
|
||||
)
|
||||
return self.filter(id__in=subquery_filter_by_distance) \
|
||||
return self.filter(id__in=ids_by_subquery) \
|
||||
.annotate_intermediate_public_mark() \
|
||||
.annotate_mark_similarity(mark=establishment.public_mark) \
|
||||
.annotate_mark_similarity(mark=restaurant.public_mark) \
|
||||
.order_by('mark_similarity') \
|
||||
.distinct('mark_similarity', 'id')
|
||||
else:
|
||||
return self.none()
|
||||
|
||||
def similar_artisans(self, slug):
|
||||
"""
|
||||
Return QuerySet with objects that similar to Artisan.
|
||||
:param slug: str artisan slug
|
||||
"""
|
||||
artisan_qs = self.filter(slug=slug)
|
||||
if artisan_qs.exists():
|
||||
artisan = artisan_qs.first()
|
||||
ids_by_subquery = self.similar_base_subquery(
|
||||
establishment=artisan,
|
||||
filters={
|
||||
'public_mark__gte': 10,
|
||||
}
|
||||
)
|
||||
return self.filter(id__in=ids_by_subquery) \
|
||||
.annotate_intermediate_public_mark() \
|
||||
.annotate_mark_similarity(mark=artisan.public_mark) \
|
||||
.order_by('mark_similarity') \
|
||||
.distinct('mark_similarity', 'id')
|
||||
else:
|
||||
return self.none()
|
||||
|
||||
def by_wine_region(self, wine_region):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ urlpatterns = [
|
|||
path('slug/<slug:slug>/similar/', views.RestaurantSimilarListView.as_view(),
|
||||
name='similar-restaurants'),
|
||||
path('slug/<slug:slug>/similar/wineries/', views.WinerySimilarListView.as_view(),
|
||||
name='similar-restaurants'),
|
||||
name='similar-wineries'),
|
||||
path('slug/<slug:slug>/similar/artisans/', views.ArtisanSimilarListView.as_view(),
|
||||
name='similar-artisans'),
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Establishment app views."""
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import generics, permissions, status
|
||||
|
||||
from establishment import filters, models, serializers
|
||||
|
|
@ -41,7 +42,7 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
|
|||
"""Establishment schedule RUD view"""
|
||||
lookup_field = 'slug'
|
||||
serializer_class = ScheduleRUDSerializer
|
||||
permission_classes = [IsWineryReviewer |IsEstablishmentManager]
|
||||
permission_classes = [IsWineryReviewer | IsEstablishmentManager]
|
||||
|
||||
def get_object(self):
|
||||
"""
|
||||
|
|
@ -75,6 +76,11 @@ class MenuListCreateView(generics.ListCreateAPIView):
|
|||
serializer_class = serializers.MenuSerializers
|
||||
queryset = models.Menu.objects.all()
|
||||
permission_classes = [IsWineryReviewer | IsEstablishmentManager]
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filterset_fields = (
|
||||
'establishment',
|
||||
'establishment__slug',
|
||||
)
|
||||
|
||||
|
||||
class MenuRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
|
@ -161,7 +167,7 @@ class EmailRUDView(generics.RetrieveUpdateDestroyAPIView):
|
|||
|
||||
class EmployeeListCreateView(generics.ListCreateAPIView):
|
||||
"""Emplyoee list create view."""
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
filter_class = filters.EmployeeBackFilter
|
||||
serializer_class = serializers.EmployeeBackSerializers
|
||||
queryset = models.Employee.objects.all()
|
||||
|
|
@ -170,7 +176,7 @@ class EmployeeListCreateView(generics.ListCreateAPIView):
|
|||
|
||||
class EstablishmentEmployeeListView(generics.ListCreateAPIView):
|
||||
"""Establishment emplyoees list view."""
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
serializer_class = serializers.EstablishmentEmployeeBackSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
@ -352,8 +358,8 @@ class EstablishmentEmployeeCreateView(generics.CreateAPIView):
|
|||
class EstablishmentEmployeeDeleteView(generics.DestroyAPIView):
|
||||
|
||||
def _get_object_to_delete(self, establishment_id, employee_id):
|
||||
result_qs = models.EstablishmentEmployee\
|
||||
.objects\
|
||||
result_qs = models.EstablishmentEmployee \
|
||||
.objects \
|
||||
.filter(establishment_id=establishment_id, employee_id=employee_id)
|
||||
if not result_qs.exists():
|
||||
raise Http404
|
||||
|
|
@ -371,6 +377,6 @@ class EstablishmentPositionListView(generics.ListAPIView):
|
|||
"""Establishment positions list view."""
|
||||
|
||||
pagination_class = None
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
queryset = models.Position.objects.all()
|
||||
serializer_class = serializers.PositionBackSerializer
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class RestaurantSimilarListView(EstablishmentSimilarList):
|
|||
"""Resource for getting a list of similar restaurants."""
|
||||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method"""
|
||||
"""Overridden get_queryset method"""
|
||||
return EstablishmentMixinView.get_queryset(self) \
|
||||
.similar_restaurants(slug=self.kwargs.get('slug'))
|
||||
|
||||
|
|
@ -96,11 +96,20 @@ class WinerySimilarListView(EstablishmentSimilarList):
|
|||
"""Resource for getting a list of similar wineries."""
|
||||
|
||||
def get_queryset(self):
|
||||
"""Override get_queryset method"""
|
||||
"""Overridden get_queryset method"""
|
||||
return EstablishmentMixinView.get_queryset(self) \
|
||||
.similar_wineries(slug=self.kwargs.get('slug'))
|
||||
|
||||
|
||||
class ArtisanSimilarListView(EstablishmentSimilarList):
|
||||
"""Resource for getting a list of similar artisans."""
|
||||
|
||||
def get_queryset(self):
|
||||
"""Overridden get_queryset method"""
|
||||
return EstablishmentMixinView.get_queryset(self) \
|
||||
.similar_artisans(slug=self.kwargs.get('slug'))
|
||||
|
||||
|
||||
class EstablishmentTypeListView(generics.ListAPIView):
|
||||
"""Resource for getting a list of establishment types."""
|
||||
|
||||
|
|
|
|||
38
apps/main/management/commands/add_panels.py
Normal file
38
apps/main/management/commands/add_panels.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
from tqdm import tqdm
|
||||
|
||||
from account.models import User
|
||||
from main.models import Panel, SiteSettings
|
||||
from transfer.models import Panels
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '''Add panels from legacy DB.'''
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
objects = []
|
||||
deleted = 0
|
||||
panels_list = Panels.objects.filter(name__isnull=False)
|
||||
# remove existing panel
|
||||
exist_panel = Panel.objects.filter(old_id__isnull=False)
|
||||
if exist_panel.exists():
|
||||
deleted = exist_panel.count()
|
||||
exist_panel.delete()
|
||||
|
||||
for old_panel in tqdm(panels_list, desc='Add panels'):
|
||||
site = SiteSettings.objects.filter(old_id=old_panel.site_id).first()
|
||||
user = User.objects.filter(old_id=old_panel.site_id).first()
|
||||
if site:
|
||||
new_panel = Panel(
|
||||
old_id=old_panel.id,
|
||||
user=user,
|
||||
site=site,
|
||||
name=old_panel.name,
|
||||
display=old_panel.display,
|
||||
description=old_panel.description,
|
||||
query=old_panel.query,
|
||||
)
|
||||
objects.append(new_panel)
|
||||
Panel.objects.bulk_create(objects)
|
||||
self.stdout.write(
|
||||
self.style.WARNING(f'Created {len(objects)}/Deleted {deleted} footer objects.'))
|
||||
36
apps/main/migrations/0042_panel.py
Normal file
36
apps/main/migrations/0042_panel.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-12 12:00
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('main', '0041_auto_20191211_0631'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Panel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
|
||||
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||
('display', models.CharField(blank=True, choices=[('table', 'table'), ('table', 'mailing')], default=None, max_length=255, null=True, verbose_name='display')),
|
||||
('description', models.CharField(blank=True, default=None, max_length=255, null=True, verbose_name='description')),
|
||||
('query', models.TextField(blank=True, default=None, null=True, verbose_name='query')),
|
||||
('old_id', models.IntegerField(blank=True, default=None, null=True, verbose_name='old id')),
|
||||
('site', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='main.SiteSettings', verbose_name='site')),
|
||||
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'panel',
|
||||
'verbose_name_plural': 'panels',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -361,3 +361,46 @@ class Footer(ProjectBaseMixin):
|
|||
)
|
||||
about_us = models.TextField(_('about_us'))
|
||||
copyright = models.TextField(_('copyright'))
|
||||
|
||||
|
||||
class PanelQuerySet(models.QuerySet):
|
||||
"""Panels QuerySet."""
|
||||
|
||||
|
||||
class Panel(ProjectBaseMixin):
|
||||
"""Custom panel model with stored SQL query."""
|
||||
TABLE = 'table'
|
||||
MAILING = 'table'
|
||||
|
||||
DISPLAY_CHOICES = (
|
||||
(TABLE, _('table')),
|
||||
(MAILING, _('mailing'))
|
||||
)
|
||||
name = models.CharField(_('name'), max_length=255)
|
||||
display = models.CharField(
|
||||
_('display'), max_length=255, choices=DISPLAY_CHOICES,
|
||||
blank=True, null=True, default=None
|
||||
)
|
||||
description = models.CharField(
|
||||
_('description'), max_length=255, blank=True, null=True, default=None)
|
||||
query = models.TextField(_('query'), blank=True, null=True, default=None)
|
||||
user = models.ForeignKey(
|
||||
'account.User', verbose_name=_('user'), null=True,
|
||||
on_delete=models.SET_NULL)
|
||||
site = models.ForeignKey(
|
||||
'main.SiteSettings', verbose_name=_('site'), null=True,
|
||||
on_delete=models.SET_NULL)
|
||||
old_id = models.IntegerField(
|
||||
_('old id'), null=True, blank=True, default=None)
|
||||
|
||||
objects = PanelQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('panel')
|
||||
verbose_name_plural = _('panels')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def execute_query(self):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ from rest_framework import serializers
|
|||
from location.serializers import CountrySerializer
|
||||
from main import models
|
||||
from utils.serializers import ProjectModelSerializer, TranslatedField, RecursiveFieldSerializer
|
||||
from account.serializers.back import BackUserSerializer
|
||||
from account.models import User
|
||||
|
||||
|
||||
class FeatureSerializer(serializers.ModelSerializer):
|
||||
|
|
@ -265,8 +267,32 @@ class PageTypeBaseSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class ContentTypeBackSerializer(serializers.ModelSerializer):
|
||||
"""Serializer fro model ContentType."""
|
||||
"""Serializer for model ContentType."""
|
||||
|
||||
class Meta:
|
||||
model = ContentType
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class PanelSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for Custom panel."""
|
||||
user_id = serializers.PrimaryKeyRelatedField(
|
||||
queryset=User.objects.all(),
|
||||
source='user',
|
||||
write_only=True
|
||||
)
|
||||
user = BackUserSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.Panel
|
||||
fields = [
|
||||
'id',
|
||||
'name',
|
||||
'display',
|
||||
'description',
|
||||
'query',
|
||||
'created',
|
||||
'modified',
|
||||
'user',
|
||||
'user_id'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ urlpatterns = [
|
|||
path('footer/', views.FooterBackView.as_view(), name='footer-list-create'),
|
||||
path('footer/<int:pk>/', views.FooterRUDBackView.as_view(), name='footer-rud'),
|
||||
path('page-types/', views.PageTypeListCreateView.as_view(),
|
||||
name='page-types-list-create')
|
||||
name='page-types-list-create'),
|
||||
path('panels/', views.PanelsListCreateView.as_view(), name='panels'),
|
||||
path('panels/<int:pk>/', views.PanelsListCreateView.as_view(), name='panels-rud'),
|
||||
# path('panels/<int:pk>/execute/', views.PanelsView.as_view(), name='panels-execute')
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from rest_framework import generics, permissions
|
|||
|
||||
from main import serializers
|
||||
from main.filters import AwardFilter
|
||||
from main.models import Award, Footer, PageType
|
||||
from main.models import Award, Footer, PageType, Panel
|
||||
from main.views import SiteSettingsView, SiteListView
|
||||
|
||||
|
||||
|
|
@ -89,3 +89,21 @@ class PageTypeListCreateView(generics.ListCreateAPIView):
|
|||
pagination_class = None
|
||||
serializer_class = serializers.PageTypeBaseSerializer
|
||||
queryset = PageType.objects.all()
|
||||
|
||||
|
||||
class PanelsListCreateView(generics.ListCreateAPIView):
|
||||
"""Custom panels view."""
|
||||
permission_classes = (
|
||||
permissions.IsAdminUser,
|
||||
)
|
||||
serializer_class = serializers.PanelSerializer
|
||||
queryset = Panel.objects.all()
|
||||
|
||||
|
||||
class PanelsRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""Custom panels view."""
|
||||
permission_classes = (
|
||||
permissions.IsAdminUser,
|
||||
)
|
||||
serializer_class = serializers.PanelSerializer
|
||||
queryset = Panel.objects.all()
|
||||
18
apps/news/migrations/0042_news_duplication_date.py
Normal file
18
apps/news/migrations/0042_news_duplication_date.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-12 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('news', '0041_auto_20191211_1528'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='news',
|
||||
name='duplication_date',
|
||||
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Duplication datetime'),
|
||||
),
|
||||
]
|
||||
|
|
@ -211,6 +211,8 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi
|
|||
verbose_name=_('banner'))
|
||||
site = models.ForeignKey('main.SiteSettings', blank=True, null=True,
|
||||
on_delete=models.SET_NULL, verbose_name=_('site settings'))
|
||||
duplication_date = models.DateTimeField(blank=True, null=True, default=None,
|
||||
verbose_name=_('Duplication datetime'))
|
||||
objects = NewsQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
|
|
@ -220,7 +222,16 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi
|
|||
verbose_name_plural = _('news')
|
||||
|
||||
def __str__(self):
|
||||
return f'news: {self.slug}'
|
||||
return f'news: {next(iter(self.slugs.values()))}'
|
||||
|
||||
def create_duplicate(self, new_country, view_count_model):
|
||||
self.pk = None
|
||||
self.state = self.WAITING
|
||||
self.slugs = {locale: f'{slug}-{new_country.code}' for locale, slug in self.slugs.items()}
|
||||
self.country = new_country
|
||||
self.views_count = view_count_model
|
||||
self.duplication_date = timezone.now()
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def is_publish(self):
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ from tag.serializers import TagBaseSerializer
|
|||
from utils import exceptions as utils_exceptions
|
||||
from utils.serializers import (TranslatedField, ProjectModelSerializer,
|
||||
FavoritesCreateSerializer, ImageBaseSerializer, CarouselCreateSerializer)
|
||||
from rating import models as rating_models
|
||||
from django.shortcuts import get_object_or_404
|
||||
from utils.models import get_current_locale, get_default_locale
|
||||
|
||||
|
||||
class AgendaSerializer(ProjectModelSerializer):
|
||||
|
|
@ -68,6 +71,13 @@ class NewsBaseSerializer(ProjectModelSerializer):
|
|||
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."""
|
||||
|
|
@ -75,12 +85,12 @@ class NewsBaseSerializer(ProjectModelSerializer):
|
|||
model = models.News
|
||||
fields = (
|
||||
'id',
|
||||
'slug',
|
||||
'title_translated',
|
||||
'subtitle_translated',
|
||||
'is_highlighted',
|
||||
'news_type',
|
||||
'tags',
|
||||
'slugs',
|
||||
'view_counter',
|
||||
)
|
||||
|
||||
|
|
@ -171,10 +181,13 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
|
|||
'title',
|
||||
'backoffice_title',
|
||||
'subtitle',
|
||||
'slugs',
|
||||
'is_published',
|
||||
'duplication_date',
|
||||
)
|
||||
extra_kwargs = {
|
||||
'backoffice_title': {'allow_null': False},
|
||||
'duplication_date': {'read_only': True},
|
||||
}
|
||||
|
||||
def create(self, validated_data):
|
||||
|
|
@ -327,3 +340,24 @@ class NewsCarouselCreateSerializer(CarouselCreateSerializer):
|
|||
'content_object': validated_data.pop('news')
|
||||
})
|
||||
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)
|
||||
class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta):
|
||||
fields = NewsBackOfficeBaseSerializer.Meta.fields + NewsDetailSerializer.Meta.fields + (
|
||||
'template_display',
|
||||
)
|
||||
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 instance
|
||||
|
||||
|
|
|
|||
|
|
@ -14,4 +14,5 @@ urlpatterns = [
|
|||
path('<int:pk>/gallery/<int:image_id>/', views.NewsBackOfficeGalleryCreateDestroyView.as_view(),
|
||||
name='gallery-create-destroy'),
|
||||
path('<int:pk>/carousels/', views.NewsCarouselCreateDestroyView.as_view(), name='create-destroy-carousels'),
|
||||
path('<int:pk>/clone/<str:country_code>', views.NewsCloneView.as_view(), name='create-destroy-carousels'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from news import filters, models, serializers
|
|||
from rating.tasks import add_rating
|
||||
from utils.permissions import IsCountryAdmin, IsContentPageManager
|
||||
from utils.views import CreateDestroyGalleryViewMixin, FavoritesCreateDestroyMixinView, CarouselCreateDestroyMixinView
|
||||
from utils.serializers import ImageBaseSerializer
|
||||
from utils.serializers import ImageBaseSerializer, EmptySerializer
|
||||
|
||||
|
||||
class NewsMixinView:
|
||||
|
|
@ -167,3 +167,10 @@ class NewsCarouselCreateDestroyView(CarouselCreateDestroyMixinView):
|
|||
|
||||
_model = models.News
|
||||
serializer_class = serializers.NewsCarouselCreateSerializer
|
||||
|
||||
|
||||
class NewsCloneView(generics.CreateAPIView):
|
||||
"""View for creating clone News"""
|
||||
permission_classes = (permissions.AllowAny, )
|
||||
serializer_class = serializers.NewsCloneCreateSerializer
|
||||
queryset = models.News.objects.all()
|
||||
|
|
|
|||
19
apps/product/migrations/0021_auto_20191212_0926.py
Normal file
19
apps/product/migrations/0021_auto_20191212_0926.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-12 09:26
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('product', '0020_merge_20191209_0911'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='product_type',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='products', to='product.ProductType', verbose_name='Type'),
|
||||
),
|
||||
]
|
||||
|
|
@ -222,6 +222,7 @@ class NewsDocumentSerializer(InFavoritesMixin, DocumentSerializer):
|
|||
subtitle_translated = serializers.SerializerMethodField(allow_null=True)
|
||||
news_type = NewsTypeSerializer()
|
||||
tags = TagsDocumentSerializer(many=True, source='visible_tags')
|
||||
slug = serializers.SerializerMethodField(allow_null=True)
|
||||
|
||||
class Meta:
|
||||
"""Meta class."""
|
||||
|
|
@ -237,9 +238,13 @@ class NewsDocumentSerializer(InFavoritesMixin, DocumentSerializer):
|
|||
'news_type',
|
||||
'tags',
|
||||
'start',
|
||||
'slugs',
|
||||
'slug',
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_slug(obj):
|
||||
return get_translated_value(obj.slugs)
|
||||
|
||||
@staticmethod
|
||||
def get_title_translated(obj):
|
||||
return get_translated_value(obj.title)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from rest_framework import permissions
|
|||
from django_elasticsearch_dsl_drf import constants
|
||||
from django_elasticsearch_dsl_drf.filter_backends import (
|
||||
FilteringFilterBackend,
|
||||
GeoSpatialFilteringFilterBackend,
|
||||
GeoSpatialOrderingFilterBackend,
|
||||
OrderingFilterBackend,
|
||||
)
|
||||
|
|
@ -13,9 +12,24 @@ from search_indexes import serializers, filters, utils
|
|||
from search_indexes.documents import EstablishmentDocument, NewsDocument
|
||||
from search_indexes.documents.product import ProductDocument
|
||||
from utils.pagination import ESDocumentPagination
|
||||
from tag.models import TagCategory
|
||||
|
||||
|
||||
class NewsDocumentViewSet(BaseDocumentViewSet):
|
||||
class CustomBaseDocumentViewSet(BaseDocumentViewSet):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if self.filter_fields:
|
||||
for name in TagCategory.objects.all().values('index_name'):
|
||||
self.filter_fields.update({
|
||||
f'{name["index_name"]}_id': {
|
||||
'field': 'tags.id',
|
||||
'lookups': [constants.LOOKUP_QUERY_IN]
|
||||
}
|
||||
})
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class NewsDocumentViewSet(CustomBaseDocumentViewSet):
|
||||
"""News document ViewSet."""
|
||||
|
||||
document = NewsDocument
|
||||
|
|
@ -94,7 +108,7 @@ class MobileNewsDocumentViewSet(NewsDocumentViewSet):
|
|||
]
|
||||
|
||||
|
||||
class EstablishmentDocumentViewSet(BaseDocumentViewSet):
|
||||
class EstablishmentDocumentViewSet(CustomBaseDocumentViewSet):
|
||||
"""Establishment document ViewSet."""
|
||||
|
||||
document = EstablishmentDocument
|
||||
|
|
@ -319,7 +333,7 @@ class MobileEstablishmentDocumentViewSet(EstablishmentDocumentViewSet):
|
|||
]
|
||||
|
||||
|
||||
class ProductDocumentViewSet(BaseDocumentViewSet):
|
||||
class ProductDocumentViewSet(CustomBaseDocumentViewSet):
|
||||
"""Product document ViewSet."""
|
||||
|
||||
document = ProductDocument
|
||||
|
|
|
|||
|
|
@ -123,19 +123,7 @@ class FiltersTagCategoryBaseSerializer(serializers.ModelSerializer):
|
|||
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
|
||||
return f'{obj.index_name}_id__in'
|
||||
|
||||
def get_fields(self, *args, **kwargs):
|
||||
fields = super(FiltersTagCategoryBaseSerializer, self).get_fields()
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
"""Tag views."""
|
||||
from django.conf import settings
|
||||
from rest_framework import generics
|
||||
from rest_framework import mixins
|
||||
from rest_framework import permissions
|
||||
from rest_framework import status
|
||||
from rest_framework import viewsets
|
||||
from rest_framework import generics, mixins, permissions, status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from search_indexes import views as search_views
|
||||
|
||||
from location.models import WineRegion
|
||||
from tag import filters
|
||||
from tag import models
|
||||
from tag import serializers
|
||||
from product.models import ProductType
|
||||
from tag import filters, models, serializers
|
||||
|
||||
|
||||
class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet):
|
||||
|
|
@ -61,14 +59,9 @@ class TagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
|||
|
||||
|
||||
# User`s views & viewsets
|
||||
class FiltersTagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
||||
class FiltersTagCategoryViewSet(TagCategoryViewSet):
|
||||
"""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):
|
||||
|
|
@ -92,7 +85,7 @@ class FiltersTagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
|||
if params_type == 'restaurant':
|
||||
additional_flags += ['toque_number', 'works_noon', 'works_evening', 'works_now']
|
||||
|
||||
elif params_type == 'winery':
|
||||
elif params_type in ['winery', 'wine']:
|
||||
additional_flags += ['wine_region']
|
||||
|
||||
elif params_type == 'artisan':
|
||||
|
|
@ -114,7 +107,7 @@ class FiltersTagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
|||
}
|
||||
result_list.append(toques)
|
||||
|
||||
if filter_flags['wine_region']:
|
||||
if request.query_params.get('product_type') == ProductType.WINE:
|
||||
wine_region_id = query_params.get('wine_region_id__in')
|
||||
|
||||
if str(wine_region_id).isdigit():
|
||||
|
|
@ -185,12 +178,42 @@ class FiltersTagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
|
|||
}
|
||||
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
|
||||
search_view_class = self.define_search_view_by_request(request)
|
||||
facets = search_view_class.as_view({'get': 'list'})(self.mutate_request(self.request)).data['facets']
|
||||
return Response(self.remove_empty_filters(result_list, facets))
|
||||
|
||||
return Response(result_list)
|
||||
@staticmethod
|
||||
def mutate_request(request):
|
||||
"""Remove all filtering get params and remove s_ from the rest of them"""
|
||||
request.GET._mutable = True
|
||||
for name in request.query_params.copy().keys():
|
||||
value = request.query_params.pop(name)
|
||||
if name.startswith('s_'):
|
||||
request.query_params[name[2:]] = value[0]
|
||||
request.GET._mutable = False
|
||||
return request._request
|
||||
|
||||
@staticmethod
|
||||
def define_search_view_by_request(request):
|
||||
request.GET._mutable = True
|
||||
if request.query_params.get('items'):
|
||||
items = request.query_params.pop('items')[0]
|
||||
else:
|
||||
raise ValidationError({'detail': _('Missing required "items" parameter')})
|
||||
item_to_class = {
|
||||
'news': search_views.NewsDocumentViewSet,
|
||||
'establishments': search_views.EstablishmentDocumentViewSet,
|
||||
'products': search_views.ProductDocumentViewSet,
|
||||
}
|
||||
klass = item_to_class.get(items)
|
||||
if klass is None:
|
||||
raise ValidationError({'detail': _('news/establishments/products')})
|
||||
request.GET._mutable = False
|
||||
return klass
|
||||
|
||||
@staticmethod
|
||||
def remove_empty_filters(filters, facets):
|
||||
return filters
|
||||
|
||||
|
||||
# BackOffice user`s views & viewsets
|
||||
|
|
|
|||
|
|
@ -1222,3 +1222,20 @@ class Footers(MigrateMixin):
|
|||
class Meta:
|
||||
managed = False
|
||||
db_table = 'footers'
|
||||
|
||||
|
||||
class Panels(MigrateMixin):
|
||||
using = 'legacy'
|
||||
|
||||
name = models.CharField(max_length=255, blank=True, null=True)
|
||||
display = models.CharField(max_length=255, blank=True, null=True)
|
||||
description = models.CharField(max_length=255, blank=True, null=True)
|
||||
query = models.TextField(blank=True, null=True)
|
||||
created_at = models.DateTimeField(blank=True, null=True)
|
||||
updated_at = models.DateTimeField(blank=True, null=True)
|
||||
account_id = models.IntegerField(blank=True, null=True)
|
||||
site_id = models.IntegerField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'panels'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import logging
|
|||
import random
|
||||
import re
|
||||
import string
|
||||
from collections import namedtuple
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
|
@ -124,3 +125,10 @@ def absolute_url_decorator(func):
|
|||
def get_point_from_coordinates(latitude: str, longitude: str):
|
||||
if latitude and longitude:
|
||||
return Point(x=longitude, y=latitude, srid=4326)
|
||||
|
||||
|
||||
def namedtuplefetchall(cursor):
|
||||
"""Return all rows from a cursor as a namedtuple."""
|
||||
desc = cursor.description
|
||||
nt_result = namedtuple('Result', [col[0] for col in desc])
|
||||
return [nt_result(*row) for row in cursor.fetchall()]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user