From 39b2fc49b5c8e1c22682088fcd144563b7ae4c96 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 18 Sep 2019 11:46:53 +0300 Subject: [PATCH 01/26] ES Establishment tags. indexing & search --- apps/search_indexes/documents/establishment.py | 13 +++++++++---- apps/search_indexes/views.py | 6 ++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index b82bc579..7138cabd 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -18,8 +18,13 @@ class EstablishmentDocument(Document): properties=OBJECT_FIELD_PROPERTIES) tags = fields.ObjectField( properties={ - 'id': fields.IntegerField(attr='id'), - 'label': fields.ObjectField(attr='label') + 'id': fields.IntegerField(attr='metadata.id'), + 'label': fields.ObjectField(attr='metadata.label_indexing', + properties=OBJECT_FIELD_PROPERTIES), + 'category': fields.ObjectField(attr='metadata.category', + properties={ + 'id': fields.IntegerField(), + }) }, multi=True) address = fields.ObjectField( @@ -61,5 +66,5 @@ class EstablishmentDocument(Document): 'price_level', ) - def prepare_tags(self, instance): - return instance.tags_indexing + def get_queryset(self): + return super().get_queryset().published() diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 31d982a4..2accb3e5 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -85,5 +85,11 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): }, 'country': { 'field': 'address.city.country.code' + }, + 'tags_id': { + 'field': 'tags.id', + }, + 'tags_category_id': { + 'field': 'tags.category.id', } } From 4a3e028c3a594eec30d52e468add8e661561f722 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 18 Sep 2019 11:50:28 +0300 Subject: [PATCH 02/26] Fixed reset password --- apps/account/serializers/web.py | 7 ++----- apps/account/views/web.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/account/serializers/web.py b/apps/account/serializers/web.py index 8d6fadf7..8be73afa 100644 --- a/apps/account/serializers/web.py +++ b/apps/account/serializers/web.py @@ -35,11 +35,8 @@ class PasswordResetSerializer(serializers.Serializer): if filters: filters.update({'is_active': True}) user_qs = models.User.objects.filter(**filters) - - if not user_qs.exists(): - raise utils_exceptions.UserNotFoundError() - user = user_qs.first() - + if user_qs.exists(): + user = user_qs.first() attrs['user'] = user return attrs diff --git a/apps/account/views/web.py b/apps/account/views/web.py index e4596e9f..897c955e 100644 --- a/apps/account/views/web.py +++ b/apps/account/views/web.py @@ -22,7 +22,7 @@ class PasswordResetView(generics.GenericAPIView): """Override create method""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - if serializer.validated_data.get('user'): + if not serializer.validated_data.get('user').is_anonymous: user = serializer.validated_data.pop('user') if settings.USE_CELERY: tasks.send_reset_password_email.delay(user_id=user.id, From af0c134957e811a3dedefdd3c1392f33e8310637 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 18 Sep 2019 12:16:32 +0300 Subject: [PATCH 03/26] Fixed docs --- apps/collection/serializers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/collection/serializers/common.py b/apps/collection/serializers/common.py index 4fd692a6..87bf7802 100644 --- a/apps/collection/serializers/common.py +++ b/apps/collection/serializers/common.py @@ -48,8 +48,8 @@ class CollectionItemSerializer(serializers.ModelSerializer): fields = [ 'id', 'collection', - 'item_type', - 'item_ids' + 'content_type', + 'object_id', ] From 6e7a617a67703ed85c75ecbd4424c7538d1fee79 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 18 Sep 2019 13:14:57 +0300 Subject: [PATCH 04/26] Added field - vintage_year, absolute image_url to Carousel ListAPIView --- apps/main/models.py | 12 ++++++++++-- apps/main/serializers.py | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/main/models.py b/apps/main/models.py index d2a3dbb0..0078a5d5 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -7,6 +7,7 @@ from django.utils.translation import gettext_lazy as _ from django.contrib.contenttypes.models import ContentType from location.models import Country from main import methods +from review.models import Review from utils.models import (ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, ImageMixin) from utils.querysets import ContentTypeQuerySetMixin @@ -311,6 +312,13 @@ class Carousel(models.Model): if hasattr(self.content_object, 'awards'): return self.content_object.awards + @property + def vintage_year(self): + if hasattr(self.content_object, 'reviews'): + review_qs = self.content_object.reviews.by_status(Review.READY) + if review_qs.exists(): + return review_qs.last().vintage + @property def toque_number(self): if hasattr(self.content_object, 'toque_number'): @@ -326,8 +334,8 @@ class Carousel(models.Model): # Check if Generic obj has an image if not hasattr(self.content_object.image, 'url'): # Check if Generic obj has a FK to gallery - return self.content_object.image.image.url - return self.content_object.image.url + return self.content_object.image.image + return self.content_object.image @property def model_name(self): diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 7f94f105..590e3d9f 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -134,8 +134,9 @@ class CarouselListSerializer(serializers.ModelSerializer): name = serializers.CharField() toque_number = serializers.CharField() public_mark = serializers.CharField() - image = serializers.URLField() + image = serializers.ImageField() awards = AwardBaseSerializer(many=True) + vintage_year = serializers.IntegerField() class Meta: """Meta class.""" @@ -148,4 +149,5 @@ class CarouselListSerializer(serializers.ModelSerializer): 'toque_number', 'public_mark', 'image', + 'vintage_year', ] From 3f55e001cc1664d0699409ee38cbda36f0a3fcba Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 18 Sep 2019 15:05:28 +0300 Subject: [PATCH 05/26] ES. Establishment. filter by collection_type --- apps/search_indexes/documents/establishment.py | 4 ++++ apps/search_indexes/serializers.py | 1 + apps/search_indexes/views.py | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 7138cabd..f5305991 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -55,6 +55,10 @@ class EstablishmentDocument(Document): ), } ) + collections = fields.ObjectField(properties={ + 'id': fields.IntegerField(attr='collection.id'), + 'collection_type': fields.IntegerField(attr='collection.collection_type'), + }) class Django: diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index ed8b65eb..e81535ec 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -60,6 +60,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'description_translated', 'tags', 'address', + 'collections', ) @staticmethod diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 2accb3e5..52a99470 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -91,5 +91,8 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): }, 'tags_category_id': { 'field': 'tags.category.id', - } + }, + 'collection_type': { + 'field': 'collections.collection_type' + }, } From 28e8bac84ecc020823b643caebad3fbc0fede5b7 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Wed, 18 Sep 2019 12:51:39 +0000 Subject: [PATCH 06/26] Added priority to site features --- apps/main/migrations/0014_feature_priority.py | 18 ++++++++++++++++++ apps/main/models.py | 1 + apps/main/serializers.py | 6 +++++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 apps/main/migrations/0014_feature_priority.py diff --git a/apps/main/migrations/0014_feature_priority.py b/apps/main/migrations/0014_feature_priority.py new file mode 100644 index 00000000..d405bf0e --- /dev/null +++ b/apps/main/migrations/0014_feature_priority.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-09-12 13:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0013_auto_20190901_1032'), + ] + + operations = [ + migrations.AddField( + model_name='feature', + name='priority', + field=models.IntegerField(default=None, null=True, unique=True), + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index 0078a5d5..37e9dd0b 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -161,6 +161,7 @@ class Feature(ProjectBaseMixin): """Feature model.""" slug = models.CharField(max_length=255, unique=True) + priority = models.IntegerField(unique=True, null=True, default=None) site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature') class Meta: diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 590e3d9f..0c84da86 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -15,6 +15,7 @@ class FeatureSerializer(serializers.ModelSerializer): fields = ( 'id', 'slug', + 'priority' ) @@ -22,13 +23,16 @@ class SiteFeatureSerializer(serializers.ModelSerializer): id = serializers.IntegerField(source='feature.id') slug = serializers.CharField(source='feature.slug') + priority = serializers.IntegerField(source='feature.priority') class Meta: """Meta class.""" model = models.SiteFeature fields = ('main', 'id', - 'slug') + 'slug', + 'priority' + ) class SiteSettingsSerializer(serializers.ModelSerializer): From 4be007aade6e23778e86e3b4f216059fd1bf693e Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 18 Sep 2019 16:05:42 +0300 Subject: [PATCH 07/26] ES: geo spatial filter --- apps/search_indexes/views.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 52a99470..eefcb84c 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -1,7 +1,8 @@ """Search indexes app views.""" from rest_framework import permissions from django_elasticsearch_dsl_drf import constants -from django_elasticsearch_dsl_drf.filter_backends import FilteringFilterBackend +from django_elasticsearch_dsl_drf.filter_backends import (FilteringFilterBackend, + GeoSpatialFilteringFilterBackend) from django_elasticsearch_dsl_drf.viewsets import BaseDocumentViewSet from django_elasticsearch_dsl_drf.pagination import PageNumberPagination from search_indexes import serializers, filters @@ -47,6 +48,7 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): filter_backends = [ FilteringFilterBackend, filters.CustomSearchFilterBackend, + GeoSpatialFilteringFilterBackend, ] search_fields = ( @@ -96,3 +98,12 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): 'field': 'collections.collection_type' }, } + + geo_spatial_filter_fields = { + 'location': { + 'field': 'address.location', + 'lookups': [ + constants.LOOKUP_FILTER_GEO_BOUNDING_BOX, + ] + } + } From 8e0b19b8b04953091751ec9b5caefad148ad8ed6 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 18 Sep 2019 16:40:07 +0300 Subject: [PATCH 08/26] ES. Establishment type & subtypes --- .../search_indexes/documents/establishment.py | 23 +++++++++++++++---- apps/search_indexes/serializers.py | 2 ++ apps/search_indexes/views.py | 6 +++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index f5305991..8c96066e 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -16,6 +16,19 @@ class EstablishmentDocument(Document): description = fields.ObjectField(attr='description_indexing', properties=OBJECT_FIELD_PROPERTIES) + establishment_type = fields.ObjectField( + properties={ + 'id': fields.IntegerField(), + 'name': fields.ObjectField(attr='name_indexing', + properties=OBJECT_FIELD_PROPERTIES) + }) + establishment_subtypes = fields.ObjectField( + properties={ + 'id': fields.IntegerField(), + 'name': fields.ObjectField(attr='name_indexing', + properties=OBJECT_FIELD_PROPERTIES) + }, + multi=True) tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='metadata.id'), @@ -55,10 +68,12 @@ class EstablishmentDocument(Document): ), } ) - collections = fields.ObjectField(properties={ - 'id': fields.IntegerField(attr='collection.id'), - 'collection_type': fields.IntegerField(attr='collection.collection_type'), - }) + collections = fields.ObjectField( + properties={ + 'id': fields.IntegerField(attr='collection.id'), + 'collection_type': fields.IntegerField(attr='collection.collection_type'), + }, + multi=True) class Django: diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index e81535ec..e6950bdd 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -61,6 +61,8 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'tags', 'address', 'collections', + 'establishment_type', + 'establishment_subtypes', ) @staticmethod diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index eefcb84c..1547fd9d 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -97,6 +97,12 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): 'collection_type': { 'field': 'collections.collection_type' }, + 'establishment_type': { + 'field': 'establishment_type.id' + }, + 'establishment_subtypes': { + 'field': 'establishment_subtypes.id' + }, } geo_spatial_filter_fields = { From 06c518501df56ff96e34075887705fca97a0290e Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 18 Sep 2019 16:42:06 +0300 Subject: [PATCH 09/26] fix migrations --- apps/main/migrations/0015_merge_20190918_1341.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/main/migrations/0015_merge_20190918_1341.py diff --git a/apps/main/migrations/0015_merge_20190918_1341.py b/apps/main/migrations/0015_merge_20190918_1341.py new file mode 100644 index 00000000..1562fa1f --- /dev/null +++ b/apps/main/migrations/0015_merge_20190918_1341.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-09-18 13:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0014_feature_priority'), + ('main', '0014_carousel'), + ] + + operations = [ + ] From bb5e294d6e0a2f8f581d2ad094afde0f84523792 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Wed, 18 Sep 2019 13:52:09 +0000 Subject: [PATCH 10/26] Fixed preview image crop --- apps/establishment/serializers/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 740a5f93..ac881c20 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -160,7 +160,7 @@ class EstablishmentBaseSerializer(serializers.ModelSerializer): subtypes = EstablishmentSubTypeSerializer(many=True) address = AddressSerializer() tags = MetaDataContentSerializer(many=True) - preview_image = serializers.ImageField(source='image') + preview_image = serializers.SerializerMethodField() class Meta: """Meta class.""" From a1ea35fce170e291536ad9ecd779156c809f7c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=88=C3=90=D0=92=D0=B8=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=20=D0=93=D0=BB=D0=B0=D0=B4=D0=BA=D0=B8=D1=85=C3=90?= Date: Wed, 18 Sep 2019 18:12:49 +0300 Subject: [PATCH 11/26] CRUD logic for employee --- apps/establishment/serializers/back.py | 11 +++++++++++ apps/establishment/urls/back.py | 2 ++ apps/establishment/views/back.py | 14 +++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 1c7d9d27..ac771e8e 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -82,4 +82,15 @@ class ContactEmailBackSerializers(PlateSerializer): 'id', 'establishment', 'email' + ] + + +class EmployeeBackSerializers(serializers.ModelSerializer): + """Social network serializers.""" + class Meta: + model = models.Employee + fields = [ + 'id', + 'user', + 'name' ] \ No newline at end of file diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index bce07ee5..158fb659 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -20,4 +20,6 @@ urlpatterns = [ path('phones//', views.PhonesRUDView.as_view(), name='phones-rud'), path('emails/', views.EmailListCreateView.as_view(), name='emails'), path('emails//', views.EmailRUDView.as_view(), name='emails-rud'), + path('employees/', views.EmployeeListCreateView.as_view(), name='employees'), + path('employees//', views.EmployeeRUDView.as_view(), name='employees-rud'), ] \ No newline at end of file diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 14488993..5cd69890 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -73,4 +73,16 @@ class EmailListCreateView(generics.ListCreateAPIView): class EmailRUDView(generics.RetrieveUpdateDestroyAPIView): """Social RUD view.""" serializer_class = serializers.ContactEmailBackSerializers - queryset = models.ContactEmail.objects.all() \ No newline at end of file + queryset = models.ContactEmail.objects.all() + + +class EmployeeListCreateView(generics.ListCreateAPIView): + """Emplyoee list create view.""" + serializer_class = serializers.EmployeeBackSerializers + queryset = models.Employee.objects.all() + pagination_class = None + +class EmployeeRUDView(generics.RetrieveDestroyAPIView): + """Social RUD view.""" + serializer_class = serializers.EmployeeBackSerializers + queryset = models.Employee.objects.all() \ No newline at end of file From 540a819f945052917f982c4cf55d3cc2b332273a Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 18 Sep 2019 18:46:24 +0300 Subject: [PATCH 12/26] gm-80: in progress --- apps/establishment/admin.py | 5 -- .../0021_delete_establishmentschedule.py | 16 +++++ apps/establishment/models.py | 20 +------ apps/establishment/serializers/back.py | 7 ++- apps/establishment/serializers/common.py | 22 +------ apps/establishment/urls/back.py | 4 ++ apps/establishment/views/back.py | 3 +- apps/establishment/views/web.py | 46 +++++++++++++-- apps/main/models.py | 7 +-- apps/timetable/serialziers.py | 58 ++++++++++++++++--- 10 files changed, 127 insertions(+), 61 deletions(-) create mode 100644 apps/establishment/migrations/0021_delete_establishmentschedule.py diff --git a/apps/establishment/admin.py b/apps/establishment/admin.py index 19b34cdd..d43171bf 100644 --- a/apps/establishment/admin.py +++ b/apps/establishment/admin.py @@ -60,11 +60,6 @@ class EstablishmentAdmin(admin.ModelAdmin): ReviewInline, CommentInline] -@admin.register(models.EstablishmentSchedule) -class EstablishmentSchedule(admin.ModelAdmin): - """Establishment schedule""" - - @admin.register(models.Position) class PositionAdmin(admin.ModelAdmin): """Position admin.""" diff --git a/apps/establishment/migrations/0021_delete_establishmentschedule.py b/apps/establishment/migrations/0021_delete_establishmentschedule.py new file mode 100644 index 00000000..c475bbcb --- /dev/null +++ b/apps/establishment/migrations/0021_delete_establishmentschedule.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.4 on 2019-09-18 12:07 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0020_merge_20190917_1415'), + ] + + operations = [ + migrations.DeleteModel( + name='EstablishmentSchedule', + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 00115303..6a2c0160 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -262,6 +262,9 @@ class Establishment(ProjectBaseMixin, ImageMixin, TranslatedFieldsMixin): booking = models.URLField(blank=True, null=True, default=None, verbose_name=_('Booking URL')) is_publish = models.BooleanField(default=False, verbose_name=_('Publish status')) + schedule = models.ManyToManyField(to='timetable.Timetable', + verbose_name=_('Establishment schedule'), + related_name='schedule') awards = generic.GenericRelation(to='main.Award') tags = generic.GenericRelation(to='main.MetaDataContent') reviews = generic.GenericRelation(to='review.Review') @@ -392,23 +395,6 @@ class EstablishmentScheduleQuerySet(models.QuerySet): """QuerySet for model EstablishmentSchedule""" -class EstablishmentSchedule(BaseAttributes): - """Establishment schedule model.""" - establishment = models.OneToOneField(Establishment, - related_name='schedule', - on_delete=models.CASCADE, - verbose_name=_('Establishment')) - schedule = models.ManyToManyField(to='timetable.Timetable', - verbose_name=_('Establishment schedule')) - - objects = EstablishmentScheduleQuerySet.as_manager() - - class Meta: - """Meta class""" - verbose_name = _('Establishment schedule') - verbose_name_plural = _('Establishment schedules') - - class ContactPhone(models.Model): """Contact phone model.""" establishment = models.ForeignKey( diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 1c7d9d27..6720677d 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -1,9 +1,12 @@ +import json from rest_framework import serializers from establishment import models +from timetable.models import Timetable from establishment.serializers import ( EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer, - ContactPhonesSerializer, SocialNetworkRelatedSerializers) + ContactPhonesSerializer, SocialNetworkRelatedSerializers, EstablishmentDetailSerializer +) from main.models import Currency @@ -82,4 +85,4 @@ class ContactEmailBackSerializers(PlateSerializer): 'id', 'establishment', 'email' - ] \ No newline at end of file + ] diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 740a5f93..18d07ce5 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -9,7 +9,7 @@ from location.serializers import AddressSerializer from main.models import MetaDataContent from main.serializers import MetaDataContentSerializer, AwardSerializer, CurrencySerializer from review import models as review_models -from timetable.models import Timetable +from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions @@ -111,22 +111,6 @@ class EstablishmentSubTypeSerializer(serializers.ModelSerializer): fields = ('id', 'name_translated') -class EstablishmentScheduleSerializer(serializers.ModelSerializer): - """Serializer for Establishment model.""" - weekday = serializers.CharField(source='get_weekday_display') - - class Meta: - """Meta class.""" - model = Timetable - fields = ( - 'weekday', - 'lunch_start', - 'lunch_end', - 'dinner_start', - 'dinner_end', - ) - - class ReviewSerializer(serializers.ModelSerializer): """Serializer for model Review.""" text_translated = serializers.CharField(read_only=True) @@ -203,9 +187,7 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer): """Serializer for Establishment model.""" description_translated = serializers.CharField(allow_null=True) awards = AwardSerializer(many=True) - schedule = EstablishmentScheduleSerializer(source='schedule.schedule', - many=True, - allow_null=True) + schedule = ScheduleRUDSerializer(many=True, allow_null=True) phones = ContactPhonesSerializer(read_only=True, many=True, ) emails = ContactEmailsSerializer(read_only=True, many=True, ) review = serializers.SerializerMethodField() diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index bce07ee5..110f9d63 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -10,6 +10,10 @@ app_name = 'establishment' urlpatterns = [ path('', views.EstablishmentListCreateView.as_view(), name='list'), path('/', views.EstablishmentRetrieveView.as_view(), name='detail'), + path('/schedule//', views.EstablishmentScheduleRUDView.as_view(), + name='schedule-rud'), + path('/schedule/', views.EstablishmentScheduleCreateView.as_view(), + name='schedule-create'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 14488993..8e38325b 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -2,7 +2,8 @@ from rest_framework import generics -from establishment import models, serializers +from establishment import models +from establishment import serializers from establishment.views.common import EstablishmentMixin diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index cc0d1c56..4ca5c4fe 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -7,8 +7,9 @@ from comment import models as comment_models from establishment import filters from establishment import models, serializers from main.models import MetaDataContent -from utils.views import JWTGenericViewMixin from establishment.views import EstablishmentMixin +from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer +from timetable.models import Timetable class EstablishmentListView(EstablishmentMixin, generics.ListAPIView): @@ -34,12 +35,12 @@ class EstablishmentSimilarListView(EstablishmentListView): .order_by('-total_mark')[:13] -class EstablishmentRetrieveView(EstablishmentMixin, JWTGenericViewMixin, generics.RetrieveAPIView): +class EstablishmentRetrieveView(EstablishmentMixin, generics.RetrieveAPIView): """Resource for getting a establishment.""" serializer_class = serializers.EstablishmentDetailSerializer -class EstablishmentTypeListView(JWTGenericViewMixin, generics.ListAPIView): +class EstablishmentTypeListView(generics.ListAPIView): """Resource for getting a list of establishment types.""" permission_classes = (permissions.AllowAny,) @@ -122,7 +123,7 @@ class EstablishmentFavoritesCreateDestroyView(generics.CreateAPIView, generics.D return obj -class EstablishmentNearestRetrieveView(EstablishmentMixin, JWTGenericViewMixin, generics.ListAPIView): +class EstablishmentNearestRetrieveView(EstablishmentMixin, generics.ListAPIView): """Resource for getting list of nearest establishments.""" serializer_class = serializers.EstablishmentListSerializer filter_class = filters.EstablishmentFilter @@ -151,3 +152,40 @@ class EstablishmentTagListView(generics.ListAPIView): """Override get_queryset method""" return MetaDataContent.objects.by_content_type(app_label='establishment', model='establishment') + + +class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): + """Establishment schedule RUD view""" + serializer_class = ScheduleRUDSerializer + + def get_object(self): + """ + Returns the object the view is displaying. + """ + lookup_url_kwargs = ('pk', 'schedule_id') + + assert lookup_url_kwargs not in self.kwargs.keys(), ( + 'Expected view %s to be called with a URL keyword argument ' + 'named "%s". Fix your URL conf, or set the `.lookup_field` ' + 'attribute on the view correctly.' % + (self.__class__.__name__, lookup_url_kwargs) + ) + + establishment_pk = self.kwargs['pk'] + schedule_id = self.kwargs['schedule_id'] + + establishment = get_object_or_404(klass=models.Establishment.objects.all(), + pk=establishment_pk) + schedule = get_object_or_404(klass=establishment.schedule, + id=schedule_id) + + # May raise a permission denied + self.check_object_permissions(self.request, establishment) + self.check_object_permissions(self.request, schedule) + + return schedule + + +class EstablishmentScheduleCreateView(generics.CreateAPIView): + """Establishment schedule Create view""" + serializer_class = ScheduleCreateSerializer diff --git a/apps/main/models.py b/apps/main/models.py index 0078a5d5..bd7722fd 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -331,11 +331,10 @@ class Carousel(models.Model): @property def image(self): - # Check if Generic obj has an image - if not hasattr(self.content_object.image, 'url'): - # Check if Generic obj has a FK to gallery + if hasattr(self.content_object.image, 'url'): + return self.content_object.image + if hasattr(self.content_object.image.image, 'url'): return self.content_object.image.image - return self.content_object.image @property def model_name(self): diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index bf239858..899833fa 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -1,16 +1,58 @@ """Serializer for app timetable""" from rest_framework import serializers -from timetable import models +from timetable.models import Timetable +from establishment.models import Establishment -class TimetableSerializer(serializers.ModelSerializer): - """Serializer for model Timetable""" +class ScheduleRUDSerializer(serializers.ModelSerializer): + """Serializer for Establishment model.""" + weekday_display = serializers.CharField(source='get_weekday_display', + read_only=True) class Meta: """Meta class.""" - model = models.Timetable - fields = ( + model = Timetable + fields = [ + 'id', + 'weekday_display', + 'lunch_start', + 'lunch_end', + 'dinner_start', + 'dinner_end', + ] + + +class ScheduleCreateSerializer(ScheduleRUDSerializer): + """Serializer for Establishment model.""" + weekday = serializers.IntegerField(write_only=True) + + class Meta: + """Meta class.""" + model = Timetable + fields = ScheduleRUDSerializer.Meta.fields + [ 'weekday', - 'start', - 'end', - ) + ] + + def validate(self, attrs): + """Override validate method""" + return attrs + + def create(self, validated_data): + """Override create method""" + instance = super().create(validated_data) + weekday = validated_data.get('weekday') + establishment_pk = self.context.get('request')\ + .parser_context.get('view')\ + .kwargs.get('pk') + + establishment_qs = Establishment.objects.filter(pk=establishment_pk) + if not establishment_qs.exists(): + # todo: replace on appropriate exception + raise serializers.ValidationError() + + establishment = establishment_qs.first() + schedule_qs = establishment.schedule.filter(weekday=weekday) + if schedule_qs.exists(): + schedule_qs.delete() + establishment.schedule.add(instance) + return instance From f700491d42b36db07384c848ce77917b54689937 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Thu, 19 Sep 2019 07:02:45 +0000 Subject: [PATCH 13/26] Added employee position priority --- .../migrations/0019_position_priority.py | 18 ++++++++++++++++++ apps/establishment/models.py | 2 ++ apps/establishment/serializers/common.py | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 apps/establishment/migrations/0019_position_priority.py diff --git a/apps/establishment/migrations/0019_position_priority.py b/apps/establishment/migrations/0019_position_priority.py new file mode 100644 index 00000000..b632271d --- /dev/null +++ b/apps/establishment/migrations/0019_position_priority.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-09-13 13:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0018_socialnetwork'), + ] + + operations = [ + migrations.AddField( + model_name='position', + name='priority', + field=models.IntegerField(default=None, null=True, unique=True), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 00115303..c3a71e92 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -335,6 +335,8 @@ class Position(BaseAttributes, TranslatedFieldsMixin): name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'), help_text='{"en":"some text"}') + priority = models.IntegerField(unique=True, null=True, default=None) + class Meta: """Meta class.""" diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index ac881c20..87931485 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -146,12 +146,13 @@ class EstablishmentEmployeeSerializer(serializers.ModelSerializer): name = serializers.CharField(source='employee.name') position_translated = serializers.CharField(source='position.name_translated') awards = AwardSerializer(source='employee.awards', many=True) + priority = serializers.IntegerField(source='position.priority') class Meta: """Meta class.""" model = models.Employee - fields = ('id', 'name', 'position_translated', 'awards') + fields = ('id', 'name', 'position_translated', 'awards', 'priority') class EstablishmentBaseSerializer(serializers.ModelSerializer): From e2e7734bf2939a79f1480a2856c268e6b9b3895b Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Thu, 19 Sep 2019 07:05:32 +0000 Subject: [PATCH 14/26] Feature/advertisements update --- .../migrations/0002_auto_20190917_1307.py | 35 +++++++++++++++++++ apps/advertisement/models.py | 22 +++++------- apps/advertisement/serializers/web.py | 3 ++ apps/advertisement/urls/web.py | 4 +-- apps/advertisement/views/web.py | 8 +++-- apps/main/migrations/0015_page.py | 26 ++++++++++++++ apps/main/models.py | 17 +++++++++ apps/main/serializers.py | 22 +++++++++--- apps/utils/models.py | 2 ++ 9 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 apps/advertisement/migrations/0002_auto_20190917_1307.py create mode 100644 apps/main/migrations/0015_page.py diff --git a/apps/advertisement/migrations/0002_auto_20190917_1307.py b/apps/advertisement/migrations/0002_auto_20190917_1307.py new file mode 100644 index 00000000..cec5d157 --- /dev/null +++ b/apps/advertisement/migrations/0002_auto_20190917_1307.py @@ -0,0 +1,35 @@ +# Generated by Django 2.2.4 on 2019-09-17 13:07 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('translation', '0003_auto_20190901_1032'), + ('advertisement', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='advertisement', + name='source', + field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web')], default=0, verbose_name='Source'), + ), + migrations.AddField( + model_name='advertisement', + name='target_languages', + field=models.ManyToManyField(to='translation.Language'), + ), + migrations.AddField( + model_name='advertisement', + name='uuid', + field=models.UUIDField(default=uuid.UUID('5fcaa103-a7da-49ad-be7e-1292371f008e'), editable=False, unique=True), + ), + migrations.AlterField( + model_name='advertisement', + name='block_level', + field=models.CharField(max_length=10, verbose_name='Block level'), + ), + ] diff --git a/apps/advertisement/models.py b/apps/advertisement/models.py index 75978009..fdc25988 100644 --- a/apps/advertisement/models.py +++ b/apps/advertisement/models.py @@ -1,28 +1,22 @@ """Advertisement app models.""" +import uuid + from django.db import models from django.utils.translation import gettext_lazy as _ -from utils.models import ProjectBaseMixin, ImageMixin +from translation.models import Language +from utils.models import ProjectBaseMixin, ImageMixin, PlatformMixin -class Advertisement(ImageMixin, ProjectBaseMixin): +class Advertisement(ImageMixin, ProjectBaseMixin, PlatformMixin): """Advertisement model.""" - LEVEL_1 = 1 - LEVEL_2 = 2 - LEVEL_3 = 3 - - BLOCK_LEVEL_CHOICES = ( - (LEVEL_1, _('Ad block level 1')), - (LEVEL_2, _('Ad block level 2')), - (LEVEL_3, _('Ad block level 3')) - ) - + uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) url = models.URLField(verbose_name=_('Ad URL')) width = models.PositiveIntegerField(verbose_name=_('Block width')) height = models.PositiveIntegerField(verbose_name=_('Block height')) - block_level = models.PositiveSmallIntegerField(choices=BLOCK_LEVEL_CHOICES, - verbose_name=_('Block level')) + block_level = models.CharField(verbose_name=_('Block level'), max_length=10) + target_languages = models.ManyToManyField(Language) class Meta: verbose_name = _('Advertisement') diff --git a/apps/advertisement/serializers/web.py b/apps/advertisement/serializers/web.py index 4000b1b6..c12ab8f9 100644 --- a/apps/advertisement/serializers/web.py +++ b/apps/advertisement/serializers/web.py @@ -2,6 +2,7 @@ from rest_framework import serializers from advertisement import models +from translation.serializers import LanguageSerializer class AdvertisementSerializer(serializers.ModelSerializer): @@ -11,9 +12,11 @@ class AdvertisementSerializer(serializers.ModelSerializer): model = models.Advertisement fields = ( 'id', + 'uuid', 'url', 'image', 'width', 'height', 'block_level', + 'source' ) diff --git a/apps/advertisement/urls/web.py b/apps/advertisement/urls/web.py index f7550015..48f9d71a 100644 --- a/apps/advertisement/urls/web.py +++ b/apps/advertisement/urls/web.py @@ -3,8 +3,8 @@ from django.urls import path from advertisement.views import web as views -app_name = 'advertisement' +app_name = 'advertisements' urlpatterns = [ - path('', views.AdvertisementListView.as_view(), name='list') + path('/', views.AdvertisementListView.as_view(), name='list') ] diff --git a/apps/advertisement/views/web.py b/apps/advertisement/views/web.py index a02cff3c..1740022c 100644 --- a/apps/advertisement/views/web.py +++ b/apps/advertisement/views/web.py @@ -10,6 +10,10 @@ class AdvertisementListView(generics.ListAPIView): """List view for model Advertisement""" pagination_class = None model = models.Advertisement - permission_classes = (permissions.AllowAny, ) - queryset = models.Advertisement.objects.all() + permission_classes = (permissions.AllowAny,) serializer_class = serializers.AdvertisementSerializer + + def get_queryset(self): + return models.Advertisement.objects\ + .filter(page__page_name__contains=self.kwargs['page'])\ + .filter(target_languages__locale=self.request.locale) diff --git a/apps/main/migrations/0015_page.py b/apps/main/migrations/0015_page.py new file mode 100644 index 00000000..1ce11443 --- /dev/null +++ b/apps/main/migrations/0015_page.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.4 on 2019-09-17 13:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('advertisement', '0002_auto_20190917_1307'), + ('main', '0014_carousel'), + ] + + operations = [ + migrations.CreateModel( + name='Page', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('page_name', models.CharField(max_length=255, unique=True)), + ('advertisements', models.ManyToManyField(to='advertisement.Advertisement')), + ], + options={ + 'verbose_name': 'Page', + 'verbose_name_plural': 'Pages', + }, + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index 37e9dd0b..b2e3fd39 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -5,6 +5,8 @@ from django.contrib.postgres.fields import JSONField from django.db import models from django.utils.translation import gettext_lazy as _ from django.contrib.contenttypes.models import ContentType + +from advertisement.models import Advertisement from location.models import Country from main import methods from review.models import Review @@ -341,3 +343,18 @@ class Carousel(models.Model): @property def model_name(self): return self.content_object.__class__.__name__ + + +class Page(models.Model): + """Page model.""" + + page_name = models.CharField(max_length=255, unique=True) + advertisements = models.ManyToManyField(Advertisement) + + class Meta: + """Meta class.""" + verbose_name = _('Page') + verbose_name_plural = _('Pages') + + def __str__(self): + return f'{self.page_name}' diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 0c84da86..607e3a30 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -1,6 +1,7 @@ """Main app serializers.""" from rest_framework import serializers +from advertisement.serializers.web import AdvertisementSerializer from location.serializers import CountrySerializer from main import models @@ -20,7 +21,6 @@ class FeatureSerializer(serializers.ModelSerializer): class SiteFeatureSerializer(serializers.ModelSerializer): - id = serializers.IntegerField(source='feature.id') slug = serializers.CharField(source='feature.slug') priority = serializers.IntegerField(source='feature.priority') @@ -40,7 +40,7 @@ class SiteSettingsSerializer(serializers.ModelSerializer): published_features = SiteFeatureSerializer(source='published_sitefeatures', many=True, allow_null=True) - #todo: remove this + # todo: remove this country_code = serializers.CharField(source='subdomain', read_only=True) class Meta: @@ -62,7 +62,6 @@ class SiteSettingsSerializer(serializers.ModelSerializer): class SiteSerializer(serializers.ModelSerializer): - country = CountrySerializer() class Meta: @@ -110,7 +109,7 @@ class AwardSerializer(AwardBaseSerializer): class MetaDataContentSerializer(serializers.ModelSerializer): - id = serializers.IntegerField(source='metadata.id', read_only=True,) + id = serializers.IntegerField(source='metadata.id', read_only=True, ) label_translated = serializers.CharField( source='metadata.label_translated', read_only=True, allow_null=True) @@ -124,6 +123,7 @@ class MetaDataContentSerializer(serializers.ModelSerializer): class CurrencySerializer(serializers.ModelSerializer): """Currency serializer""" + class Meta: model = models.Currency fields = [ @@ -155,3 +155,17 @@ class CarouselListSerializer(serializers.ModelSerializer): 'image', 'vintage_year', ] + + +class PageSerializer(serializers.ModelSerializer): + page_name = serializers.CharField() + advertisements = AdvertisementSerializer(source='advertisements', many=True) + + class Meta: + """Meta class.""" + model = models.Carousel + fields = [ + 'id', + 'page_name', + 'advertisements' + ] diff --git a/apps/utils/models.py b/apps/utils/models.py index 92db5c47..60d2e9c3 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -186,10 +186,12 @@ class PlatformMixin(models.Model): MOBILE = 0 WEB = 1 + ALL = 2 SOURCES = ( (MOBILE, _('Mobile')), (WEB, _('Web')), + (ALL, _('All')) ) source = models.PositiveSmallIntegerField(choices=SOURCES, default=MOBILE, verbose_name=_('Source')) From 38ba443b4734c19302f922a464699347622349e8 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 11:26:54 +0300 Subject: [PATCH 15/26] gm-80: complete --- .../migrations/0022_establishment_schedule.py | 19 ++++++++ apps/establishment/models.py | 4 ++ .../migrations/0002_auto_20190919_0741.py | 48 +++++++++++++++++++ apps/timetable/models.py | 20 ++++++-- apps/timetable/serialziers.py | 42 ++++++++++++---- 5 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 apps/establishment/migrations/0022_establishment_schedule.py create mode 100644 apps/timetable/migrations/0002_auto_20190919_0741.py diff --git a/apps/establishment/migrations/0022_establishment_schedule.py b/apps/establishment/migrations/0022_establishment_schedule.py new file mode 100644 index 00000000..fbb23577 --- /dev/null +++ b/apps/establishment/migrations/0022_establishment_schedule.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.4 on 2019-09-18 14:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('timetable', '0001_initial'), + ('establishment', '0021_delete_establishmentschedule'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='schedule', + field=models.ManyToManyField(related_name='schedule', to='timetable.Timetable', verbose_name='Establishment schedule'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 6a2c0160..0c2a7595 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -265,6 +265,10 @@ class Establishment(ProjectBaseMixin, ImageMixin, TranslatedFieldsMixin): schedule = models.ManyToManyField(to='timetable.Timetable', verbose_name=_('Establishment schedule'), related_name='schedule') + # holidays_from = models.DateTimeField(verbose_name=_('Holidays from'), + # help_text=_('Holidays closing date from')) + # holidays_to = models.DateTimeField(verbose_name=_('Holidays to'), + # help_text=_('Holidays closing date to')) awards = generic.GenericRelation(to='main.Award') tags = generic.GenericRelation(to='main.MetaDataContent') reviews = generic.GenericRelation(to='review.Review') diff --git a/apps/timetable/migrations/0002_auto_20190919_0741.py b/apps/timetable/migrations/0002_auto_20190919_0741.py new file mode 100644 index 00000000..c508b7ba --- /dev/null +++ b/apps/timetable/migrations/0002_auto_20190919_0741.py @@ -0,0 +1,48 @@ +# Generated by Django 2.2.4 on 2019-09-19 07:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('timetable', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='timetable', + name='closed_at', + field=models.TimeField(blank=True, default=None, null=True, verbose_name='Closed time'), + ), + migrations.AddField( + model_name='timetable', + name='continuous_opening', + field=models.BooleanField(default=False, verbose_name='Continuous opening'), + ), + migrations.AddField( + model_name='timetable', + name='opening_at', + field=models.TimeField(blank=True, default=None, null=True, verbose_name='Opening time'), + ), + migrations.AlterField( + model_name='timetable', + name='dinner_end', + field=models.TimeField(blank=True, default=None, null=True, verbose_name='Dinner end time'), + ), + migrations.AlterField( + model_name='timetable', + name='dinner_start', + field=models.TimeField(blank=True, default=None, null=True, verbose_name='Dinner start time'), + ), + migrations.AlterField( + model_name='timetable', + name='lunch_end', + field=models.TimeField(blank=True, default=None, null=True, verbose_name='Lunch end time'), + ), + migrations.AlterField( + model_name='timetable', + name='lunch_start', + field=models.TimeField(blank=True, default=None, null=True, verbose_name='Lunch start time'), + ), + ] diff --git a/apps/timetable/models.py b/apps/timetable/models.py index 5f3a8762..bef7794c 100644 --- a/apps/timetable/models.py +++ b/apps/timetable/models.py @@ -23,10 +23,22 @@ class Timetable(ProjectBaseMixin): (SUNDAY, _('Sunday'))) weekday = models.PositiveSmallIntegerField(choices=WEEKDAYS_CHOICES, verbose_name=_('Week day')) - lunch_start = models.TimeField(verbose_name=_('Lunch start time')) - lunch_end = models.TimeField(verbose_name=_('Lunch end time')) - dinner_start = models.TimeField(verbose_name=_('Dinner start time')) - dinner_end = models.TimeField(verbose_name=_('Dinner end time')) + continuous_opening = models.BooleanField(verbose_name=_('Continuous opening'), default=False) + + lunch_start = models.TimeField(verbose_name=_('Lunch start time'), + blank=True, null=True, default=None) + lunch_end = models.TimeField(verbose_name=_('Lunch end time'), + blank=True, null=True, default=None) + dinner_start = models.TimeField(verbose_name=_('Dinner start time'), + blank=True, null=True, default=None) + dinner_end = models.TimeField(verbose_name=_('Dinner end time'), + blank=True, null=True, default=None) + + # If continuous opening + opening_at = models.TimeField(verbose_name=_('Opening time'), + blank=True, null=True, default=None) + closed_at = models.TimeField(verbose_name=_('Closed time'), + blank=True, null=True, default=None) class Meta: """Meta class.""" diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 899833fa..90332668 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -2,12 +2,19 @@ from rest_framework import serializers from timetable.models import Timetable from establishment.models import Establishment +from django.utils.translation import gettext_lazy as _ class ScheduleRUDSerializer(serializers.ModelSerializer): """Serializer for Establishment model.""" weekday_display = serializers.CharField(source='get_weekday_display', read_only=True) + lunch_start = serializers.TimeField(required=False, allow_null=True) + lunch_end = serializers.TimeField(required=False, allow_null=True) + dinner_start = serializers.TimeField(required=False, allow_null=True) + dinner_end = serializers.TimeField(required=False, allow_null=True) + opening_at = serializers.TimeField(required=False, allow_null=True) + closed_at = serializers.TimeField(required=False, allow_null=True) class Meta: """Meta class.""" @@ -19,6 +26,9 @@ class ScheduleRUDSerializer(serializers.ModelSerializer): 'lunch_end', 'dinner_start', 'dinner_end', + 'opening_at', + 'closed_at', + 'continuous_opening', ] @@ -35,22 +45,38 @@ class ScheduleCreateSerializer(ScheduleRUDSerializer): def validate(self, attrs): """Override validate method""" - return attrs + # Validate by continuous_opening + if attrs.get('continuous_opening'): + for attr in attrs: + if attr.startswith('lunch') or attr.startswith('dinner'): + raise serializers.ValidationError( + {'detail': _('Lunch or Dinner times should not be ' + 'in request data with continuous opening.')}) + if not ('opening_at' in attrs) and ('closed_at' in attrs): + raise serializers.ValidationError( + {'detail': _('Opening or Closed times not in request data.')}) + else: + if not (('lunch_start' in attrs) and ('lunch_end' in attrs) and + ('dinner_start' in attrs) and ('dinner_end' in attrs)): + raise serializers.ValidationError( + {'detail': _('Lunch or dinner times not in request data.')}) - def create(self, validated_data): - """Override create method""" - instance = super().create(validated_data) - weekday = validated_data.get('weekday') establishment_pk = self.context.get('request')\ .parser_context.get('view')\ .kwargs.get('pk') + # Check if exists establishment. establishment_qs = Establishment.objects.filter(pk=establishment_pk) if not establishment_qs.exists(): - # todo: replace on appropriate exception - raise serializers.ValidationError() + raise serializers.ValidationError({'detail': _('Establishment not found.')}) + attrs['establishment'] = establishment_qs.first() + return attrs - establishment = establishment_qs.first() + def create(self, validated_data): + """Override create method""" + establishment = validated_data.pop('establishment') + weekday = validated_data.get('weekday') + instance = super().create(validated_data) schedule_qs = establishment.schedule.filter(weekday=weekday) if schedule_qs.exists(): schedule_qs.delete() From 4de2ca4ef10519cd77c812f753be774d7a208c3e Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 19 Sep 2019 11:57:58 +0300 Subject: [PATCH 16/26] Recipe app&model --- apps/recipe/__init__.py | 0 apps/recipe/apps.py | 8 ++++++ apps/recipe/migrations/__init__.py | 0 apps/recipe/models.py | 44 ++++++++++++++++++++++++++++++ project/settings/base.py | 1 + 5 files changed, 53 insertions(+) create mode 100644 apps/recipe/__init__.py create mode 100644 apps/recipe/apps.py create mode 100644 apps/recipe/migrations/__init__.py create mode 100644 apps/recipe/models.py diff --git a/apps/recipe/__init__.py b/apps/recipe/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/recipe/apps.py b/apps/recipe/apps.py new file mode 100644 index 00000000..a05c714d --- /dev/null +++ b/apps/recipe/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class RecipeConfig(AppConfig): + + name = 'recipe' + verbose_name = _('RecipeConfig') diff --git a/apps/recipe/migrations/__init__.py b/apps/recipe/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/recipe/models.py b/apps/recipe/models.py new file mode 100644 index 00000000..f3e5bf2e --- /dev/null +++ b/apps/recipe/models.py @@ -0,0 +1,44 @@ +"""Recipe app models.""" +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from utils.models import (TranslatedFieldsMixin, ImageMixin, BaseAttributes, + TJSONField) + + +class Recipe(TranslatedFieldsMixin, ImageMixin, BaseAttributes): + """Recipe model.""" + + WAITING = 0 + HIDDEN = 1 + PUBLISHED = 2 + PUBLISHED_EXCLUSIVE = 3 + + STATE_CHOICES = ( + (WAITING, _('Waiting')), + (HIDDEN, _('Hidden')), + (PUBLISHED, _('Published')), + (PUBLISHED_EXCLUSIVE, _('Published exclusive')), + ) + + STR_FIELD_NAME = 'title' + + title = TJSONField(blank=True, null=True, default=None, verbose_name=_('Title'), + help_text='{"en-GB": "some text"}') + subtitle = TJSONField(blank=True, null=True, default=None, verbose_name=_('Subtitle'), + help_text='{"en-GB": "some text"}') + description = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'), + help_text='{"en-GB": "some text"}') + state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, + verbose_name=_('State')) + published_at = models.DateTimeField(verbose_name=_('Published at'), + blank=True, default=None, null=True, + help_text=_('Published at')) + published_scheduled_at = models.DateTimeField(verbose_name=_('Published scheduled at'), + blank=True, default=None, null=True, + help_text=_('Published scheduled at')) + + class Meta: + """Meta class.""" + + verbose_name = _('Recipe') + verbose_name_plural = _('Recipes') diff --git a/project/settings/base.py b/project/settings/base.py index 1a54b1b7..4aec6f4c 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -63,6 +63,7 @@ PROJECT_APPS = [ 'news.apps.NewsConfig', 'notification.apps.NotificationConfig', 'partner.apps.PartnerConfig', + 'recipe.apps.RecipeConfig', 'search_indexes.apps.SearchIndexesConfig', 'translation.apps.TranslationConfig', 'configuration.apps.ConfigurationConfig', From 140172a17d3e9ac219774fccaa032b60e83406ce Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 19 Sep 2019 12:55:58 +0300 Subject: [PATCH 17/26] merge migrations --- .../migrations/0021_merge_20190919_0955.py | 14 ++++++++++++++ apps/main/migrations/0016_merge_20190919_0954.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 apps/establishment/migrations/0021_merge_20190919_0955.py create mode 100644 apps/main/migrations/0016_merge_20190919_0954.py diff --git a/apps/establishment/migrations/0021_merge_20190919_0955.py b/apps/establishment/migrations/0021_merge_20190919_0955.py new file mode 100644 index 00000000..2f2285cb --- /dev/null +++ b/apps/establishment/migrations/0021_merge_20190919_0955.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-09-19 09:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0020_merge_20190917_1415'), + ('establishment', '0019_position_priority'), + ] + + operations = [ + ] diff --git a/apps/main/migrations/0016_merge_20190919_0954.py b/apps/main/migrations/0016_merge_20190919_0954.py new file mode 100644 index 00000000..ad288a32 --- /dev/null +++ b/apps/main/migrations/0016_merge_20190919_0954.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-09-19 09:54 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0015_page'), + ('main', '0015_merge_20190918_1341'), + ] + + operations = [ + ] From cace6a7d3bf9e58ba190001665fb420cfc842fbd Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 19 Sep 2019 14:22:40 +0300 Subject: [PATCH 18/26] Recipe list&detail api-endpoints --- apps/recipe/migrations/0001_initial.py | 44 ++++++++++++++++++++++++++ apps/recipe/models.py | 13 ++++++++ apps/recipe/serializers/__init__.py | 0 apps/recipe/serializers/common.py | 29 +++++++++++++++++ apps/recipe/urls/__init__.py | 0 apps/recipe/urls/common.py | 10 ++++++ apps/recipe/urls/web.py | 6 ++++ apps/recipe/views/__init__.py | 0 apps/recipe/views/common.py | 21 ++++++++++++ project/urls/web.py | 1 + 10 files changed, 124 insertions(+) create mode 100644 apps/recipe/migrations/0001_initial.py create mode 100644 apps/recipe/serializers/__init__.py create mode 100644 apps/recipe/serializers/common.py create mode 100644 apps/recipe/urls/__init__.py create mode 100644 apps/recipe/urls/common.py create mode 100644 apps/recipe/urls/web.py create mode 100644 apps/recipe/views/__init__.py create mode 100644 apps/recipe/views/common.py diff --git a/apps/recipe/migrations/0001_initial.py b/apps/recipe/migrations/0001_initial.py new file mode 100644 index 00000000..64e9f3ad --- /dev/null +++ b/apps/recipe/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 2.2.4 on 2019-09-19 11:07 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import easy_thumbnails.fields +import utils.methods +import utils.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Recipe', + 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')), + ('image', easy_thumbnails.fields.ThumbnailerImageField(blank=True, default=None, null=True, upload_to=utils.methods.image_path, verbose_name='Image')), + ('title', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB": "some text"}', null=True, verbose_name='Title')), + ('subtitle', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB": "some text"}', null=True, verbose_name='Subtitle')), + ('description', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB": "some text"}', null=True, verbose_name='Description')), + ('state', models.PositiveSmallIntegerField(choices=[(0, 'Waiting'), (1, 'Hidden'), (2, 'Published'), (3, 'Published exclusive')], default=0, verbose_name='State')), + ('author', models.CharField(blank=True, default=None, max_length=255, null=True, verbose_name='Author')), + ('published_at', models.DateTimeField(blank=True, default=None, help_text='Published at', null=True, verbose_name='Published at')), + ('published_scheduled_at', models.DateTimeField(blank=True, default=None, help_text='Published scheduled at', null=True, verbose_name='Published scheduled at')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recipe_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recipe_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')), + ], + options={ + 'verbose_name': 'Recipe', + 'verbose_name_plural': 'Recipes', + }, + bases=(utils.models.TranslatedFieldsMixin, models.Model), + ), + ] diff --git a/apps/recipe/models.py b/apps/recipe/models.py index f3e5bf2e..e799e588 100644 --- a/apps/recipe/models.py +++ b/apps/recipe/models.py @@ -5,6 +5,15 @@ from utils.models import (TranslatedFieldsMixin, ImageMixin, BaseAttributes, TJSONField) +class RecipeQuerySet(models.QuerySet): + """Extended queryset for Recipe model.""" + + # todo: what records are considered published? + def published(self): + return self.filter(state__in=[self.model.PUBLISHED, + self.model.PUBLISHED_EXCLUSIVE]) + + class Recipe(TranslatedFieldsMixin, ImageMixin, BaseAttributes): """Recipe model.""" @@ -30,6 +39,8 @@ class Recipe(TranslatedFieldsMixin, ImageMixin, BaseAttributes): help_text='{"en-GB": "some text"}') state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, verbose_name=_('State')) + author = models.CharField(max_length=255, blank=True, null=True, default=None, + verbose_name=_('Author')) published_at = models.DateTimeField(verbose_name=_('Published at'), blank=True, default=None, null=True, help_text=_('Published at')) @@ -37,6 +48,8 @@ class Recipe(TranslatedFieldsMixin, ImageMixin, BaseAttributes): blank=True, default=None, null=True, help_text=_('Published scheduled at')) + objects = RecipeQuerySet.as_manager() + class Meta: """Meta class.""" diff --git a/apps/recipe/serializers/__init__.py b/apps/recipe/serializers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/recipe/serializers/common.py b/apps/recipe/serializers/common.py new file mode 100644 index 00000000..acbfaad3 --- /dev/null +++ b/apps/recipe/serializers/common.py @@ -0,0 +1,29 @@ +"""Recipe app common serializers.""" +from rest_framework import serializers +from recipe import models + + +class RecipeListSerializer(serializers.ModelSerializer): + """Serializer for list of recipes.""" + + title_translated = serializers.CharField(allow_null=True, read_only=True) + subtitle_translated = serializers.CharField(allow_null=True, read_only=True) + + class Meta: + """Meta class.""" + + model = models.Recipe + fields = ('id', 'title_translated', 'subtitle_translated', 'author', + 'published_at') + read_only_fields = fields + + +class RecipeDetailSerializer(RecipeListSerializer): + """Serializer for more information about the recipe.""" + + description_translated = serializers.CharField(allow_null=True, read_only=True) + + class Meta(RecipeListSerializer.Meta): + """Meta class.""" + + fields = RecipeListSerializer.Meta.fields + ('description_translated',) diff --git a/apps/recipe/urls/__init__.py b/apps/recipe/urls/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/recipe/urls/common.py b/apps/recipe/urls/common.py new file mode 100644 index 00000000..017dca8d --- /dev/null +++ b/apps/recipe/urls/common.py @@ -0,0 +1,10 @@ +"""Recipe app common urlpatterns.""" +from django.urls import path +from recipe.views import common as views + + +app_name = 'recipe' +urlpatterns = [ + path('', views.RecipeListView.as_view(), name='list'), + path('/', views.RecipeDetailView.as_view(), name='detail'), +] diff --git a/apps/recipe/urls/web.py b/apps/recipe/urls/web.py new file mode 100644 index 00000000..f78eafb5 --- /dev/null +++ b/apps/recipe/urls/web.py @@ -0,0 +1,6 @@ +"""Recipe app web urlconf.""" +from recipe.urls.common import urlpatterns as common_urlpatterns + + +urlpatterns = [] +urlpatterns.extend(common_urlpatterns) diff --git a/apps/recipe/views/__init__.py b/apps/recipe/views/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/recipe/views/common.py b/apps/recipe/views/common.py new file mode 100644 index 00000000..c52f8a9f --- /dev/null +++ b/apps/recipe/views/common.py @@ -0,0 +1,21 @@ +"""Recipe app common views.""" +from rest_framework import generics, permissions +from recipe import models +from recipe.serializers import common as serializers + + +class RecipeListView(generics.ListAPIView): + """Resource for obtaining a list of recipes.""" + + pagination_class = None + permission_classes = (permissions.AllowAny,) + queryset = models.Recipe.objects.published() + serializer_class = serializers.RecipeListSerializer + + +class RecipeDetailView(generics.RetrieveAPIView): + """Resource for detailed recipe information.""" + + permission_classes = (permissions.AllowAny,) + queryset = models.Recipe.objects.published() + serializer_class = serializers.RecipeDetailSerializer diff --git a/project/urls/web.py b/project/urls/web.py index 9c2a932e..18bb8fd9 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -27,6 +27,7 @@ urlpatterns = [ path('partner/', include('partner.urls.web')), path('location/', include('location.urls.web')), path('main/', include('main.urls')), + path('recipes/', include('recipe.urls.web')), path('translation/', include('translation.urls')), path('comments/', include('comment.urls.web')), path('favorites/', include('favorites.urls')), From 09450a1ae5103fff07aab38b4cb1bb543bc9d307 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 14:30:32 +0300 Subject: [PATCH 19/26] gm-80: complete --- .../migrations/0002_auto_20190919_0741.py | 48 ------------ .../migrations/0002_auto_20190919_1124.py | 43 +++++++++++ apps/timetable/models.py | 21 ++---- apps/timetable/serialziers.py | 73 +++++++++---------- 4 files changed, 83 insertions(+), 102 deletions(-) delete mode 100644 apps/timetable/migrations/0002_auto_20190919_0741.py create mode 100644 apps/timetable/migrations/0002_auto_20190919_1124.py diff --git a/apps/timetable/migrations/0002_auto_20190919_0741.py b/apps/timetable/migrations/0002_auto_20190919_0741.py deleted file mode 100644 index c508b7ba..00000000 --- a/apps/timetable/migrations/0002_auto_20190919_0741.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-19 07:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('timetable', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='timetable', - name='closed_at', - field=models.TimeField(blank=True, default=None, null=True, verbose_name='Closed time'), - ), - migrations.AddField( - model_name='timetable', - name='continuous_opening', - field=models.BooleanField(default=False, verbose_name='Continuous opening'), - ), - migrations.AddField( - model_name='timetable', - name='opening_at', - field=models.TimeField(blank=True, default=None, null=True, verbose_name='Opening time'), - ), - migrations.AlterField( - model_name='timetable', - name='dinner_end', - field=models.TimeField(blank=True, default=None, null=True, verbose_name='Dinner end time'), - ), - migrations.AlterField( - model_name='timetable', - name='dinner_start', - field=models.TimeField(blank=True, default=None, null=True, verbose_name='Dinner start time'), - ), - migrations.AlterField( - model_name='timetable', - name='lunch_end', - field=models.TimeField(blank=True, default=None, null=True, verbose_name='Lunch end time'), - ), - migrations.AlterField( - model_name='timetable', - name='lunch_start', - field=models.TimeField(blank=True, default=None, null=True, verbose_name='Lunch start time'), - ), - ] diff --git a/apps/timetable/migrations/0002_auto_20190919_1124.py b/apps/timetable/migrations/0002_auto_20190919_1124.py new file mode 100644 index 00000000..1e89bbf2 --- /dev/null +++ b/apps/timetable/migrations/0002_auto_20190919_1124.py @@ -0,0 +1,43 @@ +# Generated by Django 2.2.4 on 2019-09-19 11:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('timetable', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='timetable', + name='closed_at', + field=models.TimeField(null=True, verbose_name='Closed time'), + ), + migrations.AddField( + model_name='timetable', + name='opening_at', + field=models.TimeField(null=True, verbose_name='Opening time'), + ), + migrations.AlterField( + model_name='timetable', + name='dinner_end', + field=models.TimeField(null=True, verbose_name='Dinner end time'), + ), + migrations.AlterField( + model_name='timetable', + name='dinner_start', + field=models.TimeField(null=True, verbose_name='Dinner start time'), + ), + migrations.AlterField( + model_name='timetable', + name='lunch_end', + field=models.TimeField(null=True, verbose_name='Lunch end time'), + ), + migrations.AlterField( + model_name='timetable', + name='lunch_start', + field=models.TimeField(null=True, verbose_name='Lunch start time'), + ), + ] diff --git a/apps/timetable/models.py b/apps/timetable/models.py index bef7794c..4e42094f 100644 --- a/apps/timetable/models.py +++ b/apps/timetable/models.py @@ -23,22 +23,13 @@ class Timetable(ProjectBaseMixin): (SUNDAY, _('Sunday'))) weekday = models.PositiveSmallIntegerField(choices=WEEKDAYS_CHOICES, verbose_name=_('Week day')) - continuous_opening = models.BooleanField(verbose_name=_('Continuous opening'), default=False) - lunch_start = models.TimeField(verbose_name=_('Lunch start time'), - blank=True, null=True, default=None) - lunch_end = models.TimeField(verbose_name=_('Lunch end time'), - blank=True, null=True, default=None) - dinner_start = models.TimeField(verbose_name=_('Dinner start time'), - blank=True, null=True, default=None) - dinner_end = models.TimeField(verbose_name=_('Dinner end time'), - blank=True, null=True, default=None) - - # If continuous opening - opening_at = models.TimeField(verbose_name=_('Opening time'), - blank=True, null=True, default=None) - closed_at = models.TimeField(verbose_name=_('Closed time'), - blank=True, null=True, default=None) + lunch_start = models.TimeField(verbose_name=_('Lunch start time'), null=True) + lunch_end = models.TimeField(verbose_name=_('Lunch end time'), null=True) + dinner_start = models.TimeField(verbose_name=_('Dinner start time'), null=True) + dinner_end = models.TimeField(verbose_name=_('Dinner end time'), null=True) + opening_at = models.TimeField(verbose_name=_('Opening time'), null=True) + closed_at = models.TimeField(verbose_name=_('Closed time'), null=True) class Meta: """Meta class.""" diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 90332668..1339cc8e 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -1,20 +1,25 @@ """Serializer for app timetable""" -from rest_framework import serializers -from timetable.models import Timetable -from establishment.models import Establishment from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from establishment.models import Establishment +from timetable.models import Timetable class ScheduleRUDSerializer(serializers.ModelSerializer): """Serializer for Establishment model.""" weekday_display = serializers.CharField(source='get_weekday_display', read_only=True) - lunch_start = serializers.TimeField(required=False, allow_null=True) - lunch_end = serializers.TimeField(required=False, allow_null=True) - dinner_start = serializers.TimeField(required=False, allow_null=True) - dinner_end = serializers.TimeField(required=False, allow_null=True) - opening_at = serializers.TimeField(required=False, allow_null=True) - closed_at = serializers.TimeField(required=False, allow_null=True) + + lunch_start = serializers.TimeField(required=False) + lunch_end = serializers.TimeField(required=False) + dinner_start = serializers.TimeField(required=False) + dinner_end = serializers.TimeField(required=False) + opening_at = serializers.TimeField(required=False) + closed_at = serializers.TimeField(required=False) + + NULLABLE_FIELDS = ['lunch_start', 'lunch_end', 'dinner_start', + 'dinner_end', 'opening_at', 'closed_at'] class Meta: """Meta class.""" @@ -28,9 +33,27 @@ class ScheduleRUDSerializer(serializers.ModelSerializer): 'dinner_end', 'opening_at', 'closed_at', - 'continuous_opening', ] + def validate(self, attrs): + """Override validate method""" + establishment_pk = self.context.get('request')\ + .parser_context.get('view')\ + .kwargs.get('pk') + + # Check if establishment exists. + establishment_qs = Establishment.objects.filter(pk=establishment_pk) + if not establishment_qs.exists(): + raise serializers.ValidationError({'detail': _('Establishment not found.')}) + attrs['establishment'] = establishment_qs.first() + + # If fields not in request data then put it in attrs with None value. + if not self.partial: + for field in self.NULLABLE_FIELDS: + if field not in attrs: + attrs.setdefault(field, None) + return attrs + class ScheduleCreateSerializer(ScheduleRUDSerializer): """Serializer for Establishment model.""" @@ -43,39 +66,11 @@ class ScheduleCreateSerializer(ScheduleRUDSerializer): 'weekday', ] - def validate(self, attrs): - """Override validate method""" - # Validate by continuous_opening - if attrs.get('continuous_opening'): - for attr in attrs: - if attr.startswith('lunch') or attr.startswith('dinner'): - raise serializers.ValidationError( - {'detail': _('Lunch or Dinner times should not be ' - 'in request data with continuous opening.')}) - if not ('opening_at' in attrs) and ('closed_at' in attrs): - raise serializers.ValidationError( - {'detail': _('Opening or Closed times not in request data.')}) - else: - if not (('lunch_start' in attrs) and ('lunch_end' in attrs) and - ('dinner_start' in attrs) and ('dinner_end' in attrs)): - raise serializers.ValidationError( - {'detail': _('Lunch or dinner times not in request data.')}) - - establishment_pk = self.context.get('request')\ - .parser_context.get('view')\ - .kwargs.get('pk') - - # Check if exists establishment. - establishment_qs = Establishment.objects.filter(pk=establishment_pk) - if not establishment_qs.exists(): - raise serializers.ValidationError({'detail': _('Establishment not found.')}) - attrs['establishment'] = establishment_qs.first() - return attrs - def create(self, validated_data): """Override create method""" establishment = validated_data.pop('establishment') weekday = validated_data.get('weekday') + instance = super().create(validated_data) schedule_qs = establishment.schedule.filter(weekday=weekday) if schedule_qs.exists(): From 24f364084e4bea9e12f9ef2f78b7015c88aabaa4 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 14:34:36 +0300 Subject: [PATCH 20/26] gm-80: refactored --- apps/establishment/views/web.py | 3 +-- apps/timetable/models.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 4ca5c4fe..a7a1c08b 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -6,10 +6,9 @@ from rest_framework import generics, permissions from comment import models as comment_models from establishment import filters from establishment import models, serializers -from main.models import MetaDataContent from establishment.views import EstablishmentMixin +from main.models import MetaDataContent from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer -from timetable.models import Timetable class EstablishmentListView(EstablishmentMixin, generics.ListAPIView): diff --git a/apps/timetable/models.py b/apps/timetable/models.py index 4e42094f..e1e7fae7 100644 --- a/apps/timetable/models.py +++ b/apps/timetable/models.py @@ -1,5 +1,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ + from utils.models import ProjectBaseMixin From 306a6010e810563aa7b4e069a6af4f564bf1a8f1 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 14:37:01 +0300 Subject: [PATCH 21/26] added merge migration --- .../migrations/0023_merge_20190919_1136.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/establishment/migrations/0023_merge_20190919_1136.py diff --git a/apps/establishment/migrations/0023_merge_20190919_1136.py b/apps/establishment/migrations/0023_merge_20190919_1136.py new file mode 100644 index 00000000..8634b0dc --- /dev/null +++ b/apps/establishment/migrations/0023_merge_20190919_1136.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-09-19 11:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0021_merge_20190919_0955'), + ('establishment', '0022_establishment_schedule'), + ] + + operations = [ + ] From c26ca72f72f4e8386e50aeab91c81de003141a59 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 19 Sep 2019 14:55:50 +0300 Subject: [PATCH 22/26] added in_favorites to recipe endpoints --- apps/recipe/serializers/common.py | 3 ++- apps/recipe/views/common.py | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/recipe/serializers/common.py b/apps/recipe/serializers/common.py index acbfaad3..fec5978d 100644 --- a/apps/recipe/serializers/common.py +++ b/apps/recipe/serializers/common.py @@ -8,13 +8,14 @@ class RecipeListSerializer(serializers.ModelSerializer): title_translated = serializers.CharField(allow_null=True, read_only=True) subtitle_translated = serializers.CharField(allow_null=True, read_only=True) + in_favorites = serializers.BooleanField() class Meta: """Meta class.""" model = models.Recipe fields = ('id', 'title_translated', 'subtitle_translated', 'author', - 'published_at') + 'published_at', 'in_favorites') read_only_fields = fields diff --git a/apps/recipe/views/common.py b/apps/recipe/views/common.py index c52f8a9f..f268107e 100644 --- a/apps/recipe/views/common.py +++ b/apps/recipe/views/common.py @@ -4,18 +4,25 @@ from recipe import models from recipe.serializers import common as serializers -class RecipeListView(generics.ListAPIView): - """Resource for obtaining a list of recipes.""" +class RecipeViewMixin(generics.GenericAPIView): + """Recipe view mixin.""" pagination_class = None permission_classes = (permissions.AllowAny,) - queryset = models.Recipe.objects.published() + + def get_queryset(self): + user = self.request.user + qs = models.Recipe.objects.published().annotate_in_favorites(user) + return qs + + +class RecipeListView(RecipeViewMixin, generics.ListAPIView): + """Resource for obtaining a list of recipes.""" + serializer_class = serializers.RecipeListSerializer -class RecipeDetailView(generics.RetrieveAPIView): +class RecipeDetailView(RecipeViewMixin, generics.RetrieveAPIView): """Resource for detailed recipe information.""" - permission_classes = (permissions.AllowAny,) - queryset = models.Recipe.objects.published() serializer_class = serializers.RecipeDetailSerializer From 8d94279dbc9fa0a5868d875dbd68a4da7a893409 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 16:46:25 +0300 Subject: [PATCH 23/26] fixed migration for app advertisement, added choice to PlatformMixin --- .../migrations/0002_auto_20190917_1307.py | 10 +++++--- .../migrations/0003_auto_20190919_1344.py | 18 +++++++++++++++ .../migrations/0008_auto_20190919_1344.py | 23 +++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 apps/advertisement/migrations/0003_auto_20190919_1344.py create mode 100644 apps/authorization/migrations/0008_auto_20190919_1344.py diff --git a/apps/advertisement/migrations/0002_auto_20190917_1307.py b/apps/advertisement/migrations/0002_auto_20190917_1307.py index cec5d157..178d0f3a 100644 --- a/apps/advertisement/migrations/0002_auto_20190917_1307.py +++ b/apps/advertisement/migrations/0002_auto_20190917_1307.py @@ -25,11 +25,15 @@ class Migration(migrations.Migration): migrations.AddField( model_name='advertisement', name='uuid', - field=models.UUIDField(default=uuid.UUID('5fcaa103-a7da-49ad-be7e-1292371f008e'), editable=False, unique=True), + field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True), ), - migrations.AlterField( + migrations.RemoveField( model_name='advertisement', name='block_level', - field=models.CharField(max_length=10, verbose_name='Block level'), ), + migrations.AddField( + model_name='advertisement', + name='block_level', + field=models.CharField(max_length=10, verbose_name='Block level') + ) ] diff --git a/apps/advertisement/migrations/0003_auto_20190919_1344.py b/apps/advertisement/migrations/0003_auto_20190919_1344.py new file mode 100644 index 00000000..84a73523 --- /dev/null +++ b/apps/advertisement/migrations/0003_auto_20190919_1344.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-09-19 13:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('advertisement', '0002_auto_20190917_1307'), + ] + + operations = [ + migrations.AlterField( + model_name='advertisement', + name='source', + field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web'), (2, 'All')], default=0, verbose_name='Source'), + ), + ] diff --git a/apps/authorization/migrations/0008_auto_20190919_1344.py b/apps/authorization/migrations/0008_auto_20190919_1344.py new file mode 100644 index 00000000..ef9d8f92 --- /dev/null +++ b/apps/authorization/migrations/0008_auto_20190919_1344.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.4 on 2019-09-19 13:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authorization', '0007_jwtaccesstoken_refresh_token'), + ] + + operations = [ + migrations.AlterField( + model_name='application', + name='source', + field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web'), (2, 'All')], default=0, verbose_name='Source'), + ), + migrations.AlterField( + model_name='jwtrefreshtoken', + name='source', + field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web'), (2, 'All')], default=0, verbose_name='Source'), + ), + ] From 1db37a7cee63c0433aa61553e7bd44fe3995d550 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 16:56:00 +0300 Subject: [PATCH 24/26] fixed tag list --- apps/establishment/views/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index a7a1c08b..a58d462c 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -150,7 +150,7 @@ class EstablishmentTagListView(generics.ListAPIView): def get_queryset(self): """Override get_queryset method""" return MetaDataContent.objects.by_content_type(app_label='establishment', - model='establishment') + model='establishment').distinct() class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): From 00925b7e0ae38229c205abd9ee3b7c13822861fb Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 17:02:27 +0300 Subject: [PATCH 25/26] fixed tag list --- apps/establishment/views/web.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index a58d462c..73c1621d 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -150,7 +150,8 @@ class EstablishmentTagListView(generics.ListAPIView): def get_queryset(self): """Override get_queryset method""" return MetaDataContent.objects.by_content_type(app_label='establishment', - model='establishment').distinct() + model='establishment')\ + .distinct('metadata__label') class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): From 2ce69eb00394fc015a795d2888dd31e1ebeaaeea Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 19 Sep 2019 17:10:18 +0300 Subject: [PATCH 26/26] added establishment tag identifier to TagListAPIView --- apps/establishment/serializers/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index cf076f81..956130d6 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -338,6 +338,7 @@ class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer): class EstablishmentTagListSerializer(serializers.ModelSerializer): """List establishment tag serializer.""" + id = serializers.IntegerField(source='metadata.id') label_translated = serializers.CharField( source='metadata.label_translated', read_only=True, allow_null=True) @@ -345,5 +346,6 @@ class EstablishmentTagListSerializer(serializers.ModelSerializer): """Meta class.""" model = MetaDataContent fields = [ + 'id', 'label_translated', ]