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

This commit is contained in:
alex 2020-02-07 15:03:07 +03:00
commit e0cbf1115b
6 changed files with 122 additions and 20 deletions

View File

@ -0,0 +1,23 @@
# Generated by Django 2.2.7 on 2020-02-07 11:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('establishment', '0098_auto_20200204_1205'),
]
operations = [
migrations.AddField(
model_name='establishment',
name='last_update_by_gm',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='establishment',
name='last_update_by_manager',
field=models.DateTimeField(null=True),
),
]

View File

@ -33,7 +33,7 @@ from utils.models import (
BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin, BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin,
IntermediateGalleryModelMixin, ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, IntermediateGalleryModelMixin, ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
TypeDefaultImageMixin, URLImageMixin, default_menu_bool_array, PhoneModelMixin, TypeDefaultImageMixin, URLImageMixin, default_menu_bool_array, PhoneModelMixin,
AwardsModelMixin) AwardsModelMixin, CarouselMixin, UpdateByMixin)
# todo: establishment type&subtypes check # todo: establishment type&subtypes check
@ -137,6 +137,14 @@ class EstablishmentQuerySet(models.QuerySet):
"""Return qs with related reviews.""" """Return qs with related reviews."""
return self.prefetch_related('reviews') return self.prefetch_related('reviews')
def with_reviews_sorted(self):
return self.prefetch_related(
Prefetch(
'reviews',
queryset=Review.objects.published().order_by('-published_at'),
)
)
def with_currency_related(self): def with_currency_related(self):
"""Return qs with related """ """Return qs with related """
return self.prefetch_related('currency') return self.prefetch_related('currency')
@ -547,8 +555,15 @@ class EstablishmentQuerySet(models.QuerySet):
return self.prefetch_related('menu_set', 'menu_set__plates', 'back_office_wine') return self.prefetch_related('menu_set', 'menu_set__plates', 'back_office_wine')
class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, class Establishment(GalleryMixin,
TranslatedFieldsMixin, HasTagsMixin, FavoritesMixin, AwardsModelMixin): ProjectBaseMixin,
URLImageMixin,
TranslatedFieldsMixin,
HasTagsMixin,
FavoritesMixin,
AwardsModelMixin,
CarouselMixin,
UpdateByMixin):
"""Establishment model.""" """Establishment model."""
ABANDONED = 0 ABANDONED = 0
@ -717,9 +732,13 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
raise ValidationError('Establishment type of subtype does not match') raise ValidationError('Establishment type of subtype does not match')
self.establishment_subtypes.add(establishment_subtype) self.establishment_subtypes.add(establishment_subtype)
@property
def last_review(self):
return self.reviews.by_status(Review.READY).last()
@property @property
def vintage_year(self): def vintage_year(self):
last_review = self.reviews.by_status(Review.READY).last() last_review = self.last_review
if last_review: if last_review:
return last_review.vintage return last_review.vintage

View File

@ -1,3 +1,4 @@
from datetime import datetime
from functools import lru_cache from functools import lru_cache
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -9,17 +10,20 @@ from rest_framework import serializers
from slugify import slugify from slugify import slugify
from account import models as account_models from account import models as account_models
from account.models import Role
from account.serializers.common import UserShortSerializer from account.serializers.common import UserShortSerializer
from collection.models import Guide from collection.models import Guide
from establishment import models, serializers as model_serializers from establishment import models, serializers as model_serializers
from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee
from establishment.serializers.common import ContactPhonesSerializer from establishment.serializers.common import ContactPhonesSerializer
from review.serializers.common import ReviewBaseSerializer
from gallery.models import Image from gallery.models import Image
from location.serializers import AddressDetailSerializer, TranslatedField, AddressBaseSerializer, \ from location.serializers import AddressDetailSerializer, TranslatedField, AddressBaseSerializer, \
AddressEstablishmentSerializer AddressEstablishmentSerializer
from main import models as main_models from main import models as main_models
from main.models import Currency from main.models import Currency
from main.serializers import AwardSerializer from main.serializers import AwardSerializer
from review.serializers import ReviewBaseSerializer, User
from tag.serializers import TagBaseSerializer from tag.serializers import TagBaseSerializer
from utils.decorators import with_base_attributes from utils.decorators import with_base_attributes
from utils.methods import string_random from utils.methods import string_random
@ -92,7 +96,7 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria
) )
subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes', subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes',
read_only=True, many=True) read_only=True, many=True)
reviews = ReviewBaseSerializer(allow_null=True, read_only=True, many=True)
restaurant_category = TagBaseSerializer(read_only=True, many=True, allow_null=True) restaurant_category = TagBaseSerializer(read_only=True, many=True, allow_null=True)
restaurant_cuisine = TagBaseSerializer(read_only=True, many=True, allow_null=True) restaurant_cuisine = TagBaseSerializer(read_only=True, many=True, allow_null=True)
artisan_category = TagBaseSerializer(read_only=True, many=True, allow_null=True) artisan_category = TagBaseSerializer(read_only=True, many=True, allow_null=True)
@ -137,6 +141,7 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria
'artisan_category', 'artisan_category',
'distillery_type', 'distillery_type',
'food_producer', 'food_producer',
'reviews',
] ]
def to_representation(self, instance): def to_representation(self, instance):
@ -211,6 +216,7 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes', subtypes = model_serializers.EstablishmentSubTypeBaseSerializer(source='establishment_subtypes',
read_only=True, many=True) read_only=True, many=True)
type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True)
phones = serializers.ListField( phones = serializers.ListField(
source='contact_phones', source='contact_phones',
allow_null=True, allow_null=True,
@ -219,8 +225,11 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
required=False, required=False,
write_only=True, write_only=True,
) )
contact_phones = ContactPhonesSerializer(source='phones', read_only=True, many=True) contact_phones = ContactPhonesSerializer(source='phones', read_only=True, many=True)
last_review = ReviewBaseSerializer(read_only=True)
class Meta(model_serializers.EstablishmentBaseSerializer.Meta): class Meta(model_serializers.EstablishmentBaseSerializer.Meta):
fields = [ fields = [
'id', 'id',
@ -249,6 +258,10 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
'tags', 'tags',
'status', 'status',
'status_display', 'status_display',
'last_review',
'must_of_the_week',
'last_update_by_gm',
'last_update_by_manager',
] ]
def to_representation(self, instance): def to_representation(self, instance):
@ -256,7 +269,7 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
data['phones'] = data.pop('contact_phones', None) data['phones'] = data.pop('contact_phones', None)
return data return data
def update(self, instance, validated_data): def update(self, instance: models.Establishment, validated_data):
phones_list = [] phones_list = []
if 'contact_phones' in validated_data: if 'contact_phones' in validated_data:
phones_list = validated_data.pop('contact_phones') phones_list = validated_data.pop('contact_phones')
@ -265,9 +278,30 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer):
if 'contact_emails' in validated_data: if 'contact_emails' in validated_data:
emails_list = validated_data.pop('contact_emails') emails_list = validated_data.pop('contact_emails')
request = self.context.get('request')
if request and hasattr(request, 'user'):
user = request.user
if isinstance(user, User):
is_by_manager = user.userrole_set.filter(
pk=user.pk,
role__in=(
Role.ESTABLISHMENT_MANAGER,
Role.ESTABLISHMENT_ADMINISTRATOR,
Role.COUNTRY_ADMIN
)
).exists()
if is_by_manager:
instance.last_update_by_manager = datetime.now()
else:
''' by gm. '''
instance.last_update_by_gm = datetime.now()
instance = super().update(instance, validated_data) instance = super().update(instance, validated_data)
phones_handler(phones_list, instance) phones_handler(phones_list, instance)
emails_handler(emails_list, instance) emails_handler(emails_list, instance)
return instance return instance

View File

@ -65,7 +65,8 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP
.with_certain_tag_category_related('cuisine', 'restaurant_cuisine') \ .with_certain_tag_category_related('cuisine', 'restaurant_cuisine') \
.with_certain_tag_category_related('shop_category', 'artisan_category') \ .with_certain_tag_category_related('shop_category', 'artisan_category') \
.with_certain_tag_category_related('distillery_type', 'distillery_type') \ .with_certain_tag_category_related('distillery_type', 'distillery_type') \
.with_certain_tag_category_related('producer_type', 'food_producer') .with_certain_tag_category_related('producer_type', 'food_producer') \
.with_reviews_sorted()
class EmployeeEstablishmentPositionsView(generics.ListAPIView): class EmployeeEstablishmentPositionsView(generics.ListAPIView):

View File

@ -22,7 +22,7 @@ from utils.models import (
BaseAttributes, FavoritesMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin, BaseAttributes, FavoritesMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin,
ProjectBaseMixin, ProjectBaseMixin,
TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin, TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin,
) CarouselMixin)
from utils.querysets import TranslationQuerysetMixin from utils.querysets import TranslationQuerysetMixin
@ -257,8 +257,12 @@ class NewsQuerySet(TranslationQuerysetMixin):
return self.filter(site__country__code=country_code) if not user.is_superuser else self return self.filter(site__country__code=country_code) if not user.is_superuser else self
class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin, class News(GalleryMixin,
FavoritesMixin): BaseAttributes,
TranslatedFieldsMixin,
HasTagsMixin,
FavoritesMixin,
CarouselMixin):
"""News model.""" """News model."""
STR_FIELD_NAME = 'title' STR_FIELD_NAME = 'title'
@ -366,16 +370,6 @@ class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin,
self.duplication_date = timezone.now() self.duplication_date = timezone.now()
self.save() self.save()
@property
def must_of_the_week(self) -> bool:
"""Detects whether current item in carousel"""
kwargs = {
'content_type': ContentType.objects.get_for_model(self),
'object_id': self.pk,
'country': self.country,
}
return Carousel.objects.filter(**kwargs).exists()
@property @property
def publication_datetime(self): def publication_datetime(self):
"""Represents datetime object combined from `publication_date` & `publication_time` fields""" """Represents datetime object combined from `publication_date` & `publication_time` fields"""

View File

@ -4,6 +4,7 @@ from os.path import exists
from django.conf import settings from django.conf import settings
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.contrib.contenttypes.models import ContentType
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.aggregates import ArrayAgg
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
@ -517,6 +518,7 @@ def default_menu_bool_array():
class PhoneModelMixin: class PhoneModelMixin:
"""Mixin for PhoneNumberField.""" """Mixin for PhoneNumberField."""
@cached_property @cached_property
def country_calling_code(self): def country_calling_code(self):
"""Return phone code from PhonеNumberField.""" """Return phone code from PhonеNumberField."""
@ -537,3 +539,32 @@ class AwardsModelMixin:
if hasattr(self, 'awards'): if hasattr(self, 'awards'):
self.awards.remove(award) self.awards.remove(award)
class CarouselMixin:
@property
def must_of_the_week(self) -> bool:
"""Detects whether current item in carousel"""
from main.models import Carousel
if hasattr(self, 'pk') and (hasattr(self, 'country') or hasattr(self, 'country_id')):
kwargs = {
'content_type': ContentType.objects.get_for_model(self),
'object_id': self.pk,
'country': getattr(self, 'country', getattr(self, 'country_id', None)),
}
return Carousel.objects.filter(**kwargs).exists()
return False
class UpdateByMixin(models.Model):
"""Modify by mixin"""
last_update_by_manager = models.DateTimeField(null=True)
last_update_by_gm = models.DateTimeField(null=True)
class Meta:
"""Meta class."""
abstract = True