From 14ae75f088a05f033b5179940b6639dfc5f2b727 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 26 Sep 2019 18:38:57 +0300 Subject: [PATCH 001/121] gm-148: refactored news serializer --- apps/establishment/models.py | 4 +- apps/news/models.py | 17 +++++--- apps/news/serializers.py | 78 +++++++++++++++++++++++++++++++++++- apps/utils/methods.py | 11 +++++ apps/utils/pagination.py | 2 +- project/settings/base.py | 42 +++++++------------ 6 files changed, 116 insertions(+), 38 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 9334e4b7..365f9767 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -149,7 +149,7 @@ class EstablishmentQuerySet(models.QuerySet): .filter(image_url__isnull=False, public_mark__gte=10) .has_published_reviews() .annotate_distance(point=establishment.location) - .order_by('distance')[:settings.LIMITING_QUERY_NUMBER] + .order_by('distance')[:settings.LIMITING_QUERY_OBJECTS] .values('id') ) return self.filter(id__in=subquery_filter_by_distance) \ @@ -168,7 +168,7 @@ class EstablishmentQuerySet(models.QuerySet): self.filter(image_url__isnull=False, public_mark__gte=10) .has_published_reviews() .annotate_distance(point=point) - .order_by('distance')[:settings.LIMITING_QUERY_NUMBER] + .order_by('distance')[:settings.LIMITING_QUERY_OBJECTS] .values('id') ) return self.filter(id__in=subquery_filter_by_distance) \ diff --git a/apps/news/models.py b/apps/news/models.py index 140c89c9..7a057d3f 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -1,9 +1,10 @@ """News app models.""" -from django.db import models from django.contrib.contenttypes import fields as generic +from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework.reverse import reverse + from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin @@ -64,7 +65,7 @@ class News(BaseAttributes, TranslatedFieldsMixin): end = models.DateTimeField(blank=True, null=True, default=None, verbose_name=_('End')) slug = models.SlugField(unique=True, max_length=50, null=True, - verbose_name=_('News slug'), editable=True,) + verbose_name=_('News slug'), editable=True,) playlist = models.IntegerField(_('playlist')) is_publish = models.BooleanField(default=False, verbose_name=_('Publish status')) @@ -74,10 +75,14 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys - image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Image URL path')) - preview_image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Preview image URL path')) + # image_url = models.URLField(blank=True, null=True, default=None, + # verbose_name=_('Image URL path')) + # preview_image_url = models.URLField(blank=True, null=True, default=None, + # verbose_name=_('Preview image URL path')) + image = models.ForeignKey('gallery.Image', + blank=True, null=True, + default=None, verbose_name=_('Image'), + on_delete=models.SET_NULL) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), on_delete=models.SET_NULL) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index dbcd0f62..b4b7f8fa 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -1,12 +1,81 @@ """News app common serializers.""" from rest_framework import serializers + +from gallery.models import Image from location import models as location_models from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer from news import models +from utils.methods import image_url_valid from utils.serializers import TranslatedField +class NewsPromoImageSerializer(serializers.Serializer): + + horizontal_web_image = serializers.SerializerMethodField() + horizontal_mobile_image = serializers.SerializerMethodField() + + def get_horizontal_web_image(self, obj): + return obj['news_promo_horizontal_web'].url + + def get_horizontal_mobile_image(self, obj): + return obj['news_promo_horizontal_mobile'].url + + +class NewsTileImageSerializer(serializers.Serializer): + + horizontal_web_image = serializers.SerializerMethodField() + horizontal_mobile_image = serializers.SerializerMethodField() + vertical_web_image = serializers.SerializerMethodField() + + def get_horizontal_web_image(self, obj): + return obj['news_tile_horizontal_web'].url + + def get_horizontal_mobile_image(self, obj): + return obj['news_tile_horizontal_mobile'].url + + def get_vertical_web_image(self, obj): + return obj['news_tile_vertical_web'].url + + +class NewsHighlightImageSerializer(serializers.Serializer): + + vertical_web_image = serializers.SerializerMethodField() + + def get_vertical_web_image(self, obj): + return obj['news_highlight_vertical'].url + + +class NewsEditorImageSerializer(serializers.Serializer): + + web_image = serializers.SerializerMethodField() + mobile_image = serializers.SerializerMethodField() + + def get_web_image(self, obj): + return obj['news_editor_web'].url + + def get_mobile_image(self, obj): + return obj['news_editor_mobile'].url + + +class NewsGallerySerializer(serializers.ModelSerializer): + + promo_images = NewsPromoImageSerializer(source='image') + tile_images = NewsTileImageSerializer(source='image') + highlight_images = NewsHighlightImageSerializer(source='image') + editor_images = NewsEditorImageSerializer(source='image') + + class Meta: + """Meta class.""" + model = Image + fields = [ + 'promo_images', + 'tile_images', + 'highlight_images', + 'editor_images', + ] + + class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" @@ -20,6 +89,8 @@ class NewsTypeSerializer(serializers.ModelSerializer): class NewsBaseSerializer(serializers.ModelSerializer): """Base serializer for News model.""" + gallery = serializers.SerializerMethodField() + # read only fields title_translated = TranslatedField() subtitle_translated = TranslatedField() @@ -39,13 +110,16 @@ class NewsBaseSerializer(serializers.ModelSerializer): 'title_translated', 'subtitle_translated', 'is_highlighted', - 'image_url', - 'preview_image_url', 'news_type', 'tags', 'slug', + 'gallery', ) + def get_gallery(self, obj): + if image_url_valid(url=obj.image.image.url): + return NewsGallerySerializer(obj.image).data + class NewsDetailSerializer(NewsBaseSerializer): """News detail serializer.""" diff --git a/apps/utils/methods.py b/apps/utils/methods.py index acad6502..5c4c399a 100644 --- a/apps/utils/methods.py +++ b/apps/utils/methods.py @@ -3,10 +3,12 @@ import random import re import string +import requests from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.http.request import HttpRequest from django.utils.timezone import datetime +from rest_framework import status from rest_framework.request import Request @@ -86,3 +88,12 @@ def get_contenttype(app_label: str, model: str): qs = ContentType.objects.filter(app_label=app_label, model=model) if qs.exists(): return qs.first() + + +def image_url_valid(url: str): + # In case if image storage is not on CDN + if not url.startswith('http') and url.startswith('/media/'): + url = f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{url}' + response = requests.request('head', url) + if response.status_code == status.HTTP_200_OK: + return True diff --git a/apps/utils/pagination.py b/apps/utils/pagination.py index 2c9e92e5..ac83f4f2 100644 --- a/apps/utils/pagination.py +++ b/apps/utils/pagination.py @@ -52,4 +52,4 @@ class EstablishmentPortionPagination(ProjectMobilePagination): """ Pagination for app establishments with limit page size equal to 12 """ - page_size = settings.LIMITING_OUTPUT_OBJECTS + page_size = settings.QUERY_OUTPUT_OBJECTS diff --git a/project/settings/base.py b/project/settings/base.py index cfea18a5..f7ae44cd 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -316,7 +316,7 @@ CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_TIMEZONE = TIME_ZONE -# Django FCM (Firebase push notificatoins) +# Django FCM (Firebase push notifications) FCM_DJANGO_SETTINGS = { 'FCM_SERVER_KEY': ( "AAAAJcC4Vbc:APA91bGovq7233-RHu2MbZTsuMU4jNf3obOue8s" @@ -327,30 +327,18 @@ FCM_DJANGO_SETTINGS = { # Thumbnail settings THUMBNAIL_ALIASES = { - 'news_preview': { - 'web': {'size': (300, 260), } - }, - 'news_promo_horizontal': { - 'web': {'size': (1900, 600), }, - 'mobile': {'size': (375, 260), }, - }, - 'news_tile_horizontal': { - 'web': {'size': (300, 275), }, - 'mobile': {'size': (343, 180), }, - }, - 'news_tile_vertical': { - 'web': {'size': (300, 380), }, - }, - 'news_highlight_vertical': { - 'web': {'size': (460, 630), }, - }, - 'news_editor': { - 'web': {'size': (940, 430), }, # при загрузке через контент эдитор - 'mobile': {'size': (343, 260), }, # через контент эдитор в мобильном браузерe - }, - 'avatar_comments': { - 'web': {'size': (116, 116), }, - }, + '': { + 'news_preview_web': {'size': (300, 260), }, + 'news_promo_horizontal_web': {'size': (1900, 600), }, + 'news_promo_horizontal_mobile': {'size': (375, 260), }, + 'news_tile_horizontal_web': {'size': (300, 275), }, + 'news_tile_horizontal_mobile': {'size': (343, 180), }, + 'news_tile_vertical_web': {'size': (300, 380), }, + 'news_highlight_vertical': {'size': (460, 630), }, + 'news_editor_web': {'size': (940, 430), }, # при загрузке через контент эдитор + 'news_editor_mobile': {'size': (343, 260), }, # через контент эдитор в мобильном браузерe + 'avatar_comments_web': {'size': (116, 116), }, + } } # Password reset @@ -431,9 +419,9 @@ SITE_NAME = 'Gault & Millau' # Used in annotations for establishments. DEFAULT_ESTABLISHMENT_PUBLIC_MARK = 10 # Limit output objects (see in pagination classes). -LIMITING_OUTPUT_OBJECTS = 12 +QUERY_OUTPUT_OBJECTS = 12 # Need to restrict objects to sort (3 times more then expected). -LIMITING_QUERY_NUMBER = LIMITING_OUTPUT_OBJECTS * 3 +LIMITING_QUERY_OBJECTS = QUERY_OUTPUT_OBJECTS * 3 # GEO # A Spatial Reference System Identifier From 7f79ce89455a321c88284757ddd88a6944a4d7ad Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 27 Sep 2019 17:48:27 +0300 Subject: [PATCH 002/121] gm-148: in progress --- apps/gallery/models.py | 29 ++++++++++++-- apps/gallery/serializers.py | 9 ++++- apps/news/models.py | 39 +++++++++++++++---- apps/news/serializers.py | 75 ------------------------------------- apps/utils/methods.py | 14 ++++++- project/settings/base.py | 8 +++- 6 files changed, 84 insertions(+), 90 deletions(-) diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 022a24e0..7678c29a 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -1,15 +1,38 @@ +from django.db import models from django.utils.translation import gettext_lazy as _ from easy_thumbnails.fields import ThumbnailerImageField from utils.methods import image_path -from utils.models import ProjectBaseMixin, ImageMixin +from utils.models import ProjectBaseMixin, ImageMixin, PlatformMixin -class Image(ProjectBaseMixin, ImageMixin): +class ImageQuerySet(models.QuerySet): + """QuerySet for model Image.""" + + +class Image(ProjectBaseMixin, ImageMixin, PlatformMixin): """Image model.""" + HORIZONTAL = 0 + VERTICAL = 1 + + ORIENTATIONS = ( + (HORIZONTAL, _('Horizontal')), + (VERTICAL, _('Vertical')), + ) image = ThumbnailerImageField(upload_to=image_path, - verbose_name=_('Image file')) + verbose_name=_('image file')) + parent = models.ForeignKey('self', + blank=True, null=True, default=None, + related_name='parent_image', + on_delete=models.SET_DEFAULT, + verbose_name=_('parent image')) + orientation = models.PositiveSmallIntegerField(choices=ORIENTATIONS, + blank=True, null=True, default=None, + verbose_name=_('image orientation')) + title = models.CharField(_('title'), max_length=255, default='') + + objects = ImageQuerySet.as_manager() class Meta: """Meta class.""" diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index a7e3f0e1..f78e4b63 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -8,10 +8,13 @@ class ImageSerializer(serializers.ModelSerializer): # REQUEST file = serializers.ImageField(source='image', write_only=True) + title = serializers.CharField() # RESPONSE url = serializers.ImageField(source='image', read_only=True) + orientation_display = serializers.CharField(source='get_orientation_display', + read_only=True) class Meta: """Meta class""" @@ -19,6 +22,10 @@ class ImageSerializer(serializers.ModelSerializer): fields = ( 'id', 'file', - 'url' + 'url', + 'parent', + 'orientation', + 'orientation_display', + 'title', ) diff --git a/apps/news/models.py b/apps/news/models.py index 7a057d3f..a442b621 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -75,14 +75,6 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys - # image_url = models.URLField(blank=True, null=True, default=None, - # verbose_name=_('Image URL path')) - # preview_image_url = models.URLField(blank=True, null=True, default=None, - # verbose_name=_('Preview image URL path')) - image = models.ForeignKey('gallery.Image', - blank=True, null=True, - default=None, verbose_name=_('Image'), - on_delete=models.SET_NULL) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), on_delete=models.SET_NULL) @@ -105,3 +97,34 @@ class News(BaseAttributes, TranslatedFieldsMixin): @property def web_url(self): return reverse('web:news:rud', kwargs={'slug': self.slug}) + + +class NewsGalleryQuerySet(models.QuerySet): + """QuerySet for model News""" + + def originals(self): + """Return QuerySet with originals images.""" + return self.filter(gallery__parent__isnull=True) + + def crops(self): + """Return QuerySet with cropped images.""" + return self.filter(gallery__parent__isnull=False) + + +class NewsGallery(models.Model): + + news = models.ForeignKey(News, null=True, + related_name='news_gallery', + on_delete=models.SET_NULL, + verbose_name=_('news')) + gallery = models.ForeignKey('gallery.Image', null=True, + related_name='news_gallery', + on_delete=models.SET_NULL, + verbose_name=_('gallery')) + + objects = NewsGalleryQuerySet.as_manager() + + class Meta: + """NewsGallery meta class.""" + verbose_name = _('news gallery') + verbose_name_plural = _('news galleries') diff --git a/apps/news/serializers.py b/apps/news/serializers.py index b4b7f8fa..b7b11a06 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -1,81 +1,13 @@ """News app common serializers.""" from rest_framework import serializers -from gallery.models import Image from location import models as location_models from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer from news import models -from utils.methods import image_url_valid from utils.serializers import TranslatedField -class NewsPromoImageSerializer(serializers.Serializer): - - horizontal_web_image = serializers.SerializerMethodField() - horizontal_mobile_image = serializers.SerializerMethodField() - - def get_horizontal_web_image(self, obj): - return obj['news_promo_horizontal_web'].url - - def get_horizontal_mobile_image(self, obj): - return obj['news_promo_horizontal_mobile'].url - - -class NewsTileImageSerializer(serializers.Serializer): - - horizontal_web_image = serializers.SerializerMethodField() - horizontal_mobile_image = serializers.SerializerMethodField() - vertical_web_image = serializers.SerializerMethodField() - - def get_horizontal_web_image(self, obj): - return obj['news_tile_horizontal_web'].url - - def get_horizontal_mobile_image(self, obj): - return obj['news_tile_horizontal_mobile'].url - - def get_vertical_web_image(self, obj): - return obj['news_tile_vertical_web'].url - - -class NewsHighlightImageSerializer(serializers.Serializer): - - vertical_web_image = serializers.SerializerMethodField() - - def get_vertical_web_image(self, obj): - return obj['news_highlight_vertical'].url - - -class NewsEditorImageSerializer(serializers.Serializer): - - web_image = serializers.SerializerMethodField() - mobile_image = serializers.SerializerMethodField() - - def get_web_image(self, obj): - return obj['news_editor_web'].url - - def get_mobile_image(self, obj): - return obj['news_editor_mobile'].url - - -class NewsGallerySerializer(serializers.ModelSerializer): - - promo_images = NewsPromoImageSerializer(source='image') - tile_images = NewsTileImageSerializer(source='image') - highlight_images = NewsHighlightImageSerializer(source='image') - editor_images = NewsEditorImageSerializer(source='image') - - class Meta: - """Meta class.""" - model = Image - fields = [ - 'promo_images', - 'tile_images', - 'highlight_images', - 'editor_images', - ] - - class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" @@ -89,8 +21,6 @@ class NewsTypeSerializer(serializers.ModelSerializer): class NewsBaseSerializer(serializers.ModelSerializer): """Base serializer for News model.""" - gallery = serializers.SerializerMethodField() - # read only fields title_translated = TranslatedField() subtitle_translated = TranslatedField() @@ -113,13 +43,8 @@ class NewsBaseSerializer(serializers.ModelSerializer): 'news_type', 'tags', 'slug', - 'gallery', ) - def get_gallery(self, obj): - if image_url_valid(url=obj.image.image.url): - return NewsGallerySerializer(obj.image).data - class NewsDetailSerializer(NewsBaseSerializer): """News detail serializer.""" diff --git a/apps/utils/methods.py b/apps/utils/methods.py index 5c4c399a..25027ae9 100644 --- a/apps/utils/methods.py +++ b/apps/utils/methods.py @@ -92,8 +92,20 @@ def get_contenttype(app_label: str, model: str): def image_url_valid(url: str): # In case if image storage is not on CDN - if not url.startswith('http') and url.startswith('/media/'): + if url.startswith('/media/'): url = f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{url}' response = requests.request('head', url) if response.status_code == status.HTTP_200_OK: return True + + +def absolute_url_decorator(func): + def get_absolute_image_url(self, obj): + """Get absolute image url""" + url_path = func(self, obj) + if url_path: + if url_path.startswith('/media/'): + return f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{url_path}/' + else: + return url_path + return get_absolute_image_url diff --git a/project/settings/base.py b/project/settings/base.py index f7ae44cd..0ae088ae 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -328,19 +328,23 @@ FCM_DJANGO_SETTINGS = { # Thumbnail settings THUMBNAIL_ALIASES = { '': { - 'news_preview_web': {'size': (300, 260), }, + 'news_preview': {'size': (300, 260), }, 'news_promo_horizontal_web': {'size': (1900, 600), }, 'news_promo_horizontal_mobile': {'size': (375, 260), }, 'news_tile_horizontal_web': {'size': (300, 275), }, 'news_tile_horizontal_mobile': {'size': (343, 180), }, 'news_tile_vertical_web': {'size': (300, 380), }, - 'news_highlight_vertical': {'size': (460, 630), }, + 'news_highlight_vertical_web': {'size': (460, 630), }, 'news_editor_web': {'size': (940, 430), }, # при загрузке через контент эдитор 'news_editor_mobile': {'size': (343, 260), }, # через контент эдитор в мобильном браузерe 'avatar_comments_web': {'size': (116, 116), }, } } +THUMBNAIL_DEFAULT_OPTIONS = { + 'crop': 'smart', +} + # Password reset RESETTING_TOKEN_EXPIRATION = 24 # hours From 501b8833edcc612003be3269d1715c0ea8527b40 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 30 Sep 2019 15:40:28 +0300 Subject: [PATCH 003/121] gm-148: finish --- .../migrations/0002_auto_20190930_0714.py | 41 +++++++++++++++++ apps/gallery/serializers.py | 2 + apps/gallery/urls.py | 2 +- apps/gallery/views.py | 13 +++++- apps/news/admin.py | 6 +++ .../migrations/0014_auto_20190926_1156.py | 21 +++++++++ apps/news/migrations/0015_newsgallery.py | 27 +++++++++++ apps/news/migrations/0016_news_gallery.py | 19 ++++++++ apps/news/models.py | 12 ++--- apps/news/serializers.py | 45 +++++++++++++++++++ apps/news/urls/back.py | 7 ++- apps/news/views.py | 43 ++++++++++++++++++ 12 files changed, 229 insertions(+), 9 deletions(-) create mode 100644 apps/gallery/migrations/0002_auto_20190930_0714.py create mode 100644 apps/news/migrations/0014_auto_20190926_1156.py create mode 100644 apps/news/migrations/0015_newsgallery.py create mode 100644 apps/news/migrations/0016_news_gallery.py diff --git a/apps/gallery/migrations/0002_auto_20190930_0714.py b/apps/gallery/migrations/0002_auto_20190930_0714.py new file mode 100644 index 00000000..8423910f --- /dev/null +++ b/apps/gallery/migrations/0002_auto_20190930_0714.py @@ -0,0 +1,41 @@ +# Generated by Django 2.2.4 on 2019-09-30 07:14 + +from django.db import migrations, models +import django.db.models.deletion +import easy_thumbnails.fields +import utils.methods + + +class Migration(migrations.Migration): + + dependencies = [ + ('gallery', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='image', + name='orientation', + field=models.PositiveSmallIntegerField(blank=True, choices=[(0, 'Horizontal'), (1, 'Vertical')], default=None, null=True, verbose_name='image orientation'), + ), + migrations.AddField( + model_name='image', + name='parent', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='parent_image', to='gallery.Image', verbose_name='parent image'), + ), + migrations.AddField( + model_name='image', + name='source', + field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web'), (2, 'All')], default=0, verbose_name='Source'), + ), + migrations.AddField( + model_name='image', + name='title', + field=models.CharField(default='', max_length=255, verbose_name='title'), + ), + migrations.AlterField( + model_name='image', + name='image', + field=easy_thumbnails.fields.ThumbnailerImageField(upload_to=utils.methods.image_path, verbose_name='image file'), + ), + ] diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index f78e4b63..c428b8f2 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -9,6 +9,8 @@ class ImageSerializer(serializers.ModelSerializer): file = serializers.ImageField(source='image', write_only=True) title = serializers.CharField() + orientation = serializers.ChoiceField(choices=models.Image.ORIENTATIONS, + write_only=True) # RESPONSE url = serializers.ImageField(source='image', diff --git a/apps/gallery/urls.py b/apps/gallery/urls.py index 53d1c097..2faa95e4 100644 --- a/apps/gallery/urls.py +++ b/apps/gallery/urls.py @@ -6,5 +6,5 @@ from . import views app_name = 'gallery' urlpatterns = [ - path('upload/', views.ImageUploadView.as_view(), name='upload-image') + path('upload/', views.ImageBaseView.as_view(), name='upload-image'), ] diff --git a/apps/gallery/views.py b/apps/gallery/views.py index 8a9195c3..b35df131 100644 --- a/apps/gallery/views.py +++ b/apps/gallery/views.py @@ -3,8 +3,17 @@ from rest_framework import generics from . import models, serializers -class ImageUploadView(generics.CreateAPIView): - """Upload image to gallery""" +class ImageBaseView(generics.CreateAPIView): + """Upload image to gallery.""" model = models.Image queryset = models.Image.objects.all() serializer_class = serializers.ImageSerializer + + +class NewsImageListView(ImageBaseView, generics.ListAPIView): + """Return list of uploaded images for news object.""" + + def get_queryset(self): + """Override get_queryset method.""" + qs = super(NewsImageListView, self).get_queryset() + return qs.filter(news_gallery__news=self.kwargs.get('news_id')) diff --git a/apps/news/admin.py b/apps/news/admin.py index 7cbfb049..0bb4c8cc 100644 --- a/apps/news/admin.py +++ b/apps/news/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin + from news import models @@ -12,3 +13,8 @@ class NewsTypeAdmin(admin.ModelAdmin): @admin.register(models.News) class NewsAdmin(admin.ModelAdmin): """News admin.""" + + +@admin.register(models.NewsGallery) +class NewsGalleryAdmin(admin.ModelAdmin): + """News gallery admin.""" diff --git a/apps/news/migrations/0014_auto_20190926_1156.py b/apps/news/migrations/0014_auto_20190926_1156.py new file mode 100644 index 00000000..d16f0ad3 --- /dev/null +++ b/apps/news/migrations/0014_auto_20190926_1156.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.4 on 2019-09-26 11:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0013_auto_20190924_0806'), + ] + + operations = [ + migrations.RemoveField( + model_name='news', + name='image_url', + ), + migrations.RemoveField( + model_name='news', + name='preview_image_url', + ), + ] diff --git a/apps/news/migrations/0015_newsgallery.py b/apps/news/migrations/0015_newsgallery.py new file mode 100644 index 00000000..a8422dab --- /dev/null +++ b/apps/news/migrations/0015_newsgallery.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.4 on 2019-09-30 08:57 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('gallery', '0002_auto_20190930_0714'), + ('news', '0014_auto_20190926_1156'), + ] + + operations = [ + migrations.CreateModel( + name='NewsGallery', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='news_gallery', to='gallery.Image', verbose_name='gallery')), + ('news', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='news_gallery', to='news.News', verbose_name='news')), + ], + options={ + 'verbose_name': 'news gallery', + 'verbose_name_plural': 'news galleries', + }, + ), + ] diff --git a/apps/news/migrations/0016_news_gallery.py b/apps/news/migrations/0016_news_gallery.py new file mode 100644 index 00000000..7917cf26 --- /dev/null +++ b/apps/news/migrations/0016_news_gallery.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.4 on 2019-09-30 12:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gallery', '0002_auto_20190930_0714'), + ('news', '0015_newsgallery'), + ] + + operations = [ + migrations.AddField( + model_name='news', + name='gallery', + field=models.ManyToManyField(through='news.NewsGallery', to='gallery.Image'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index a442b621..4b9176cd 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -83,6 +83,8 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('country')) tags = generic.GenericRelation(to='main.MetaDataContent') + gallery = models.ManyToManyField('gallery.Image', through='news.NewsGallery') + objects = NewsQuerySet.as_manager() class Meta: @@ -103,7 +105,7 @@ class NewsGalleryQuerySet(models.QuerySet): """QuerySet for model News""" def originals(self): - """Return QuerySet with originals images.""" + """Return QuerySet with original images.""" return self.filter(gallery__parent__isnull=True) def crops(self): @@ -117,10 +119,10 @@ class NewsGallery(models.Model): related_name='news_gallery', on_delete=models.SET_NULL, verbose_name=_('news')) - gallery = models.ForeignKey('gallery.Image', null=True, - related_name='news_gallery', - on_delete=models.SET_NULL, - verbose_name=_('gallery')) + image = models.ForeignKey('gallery.Image', null=True, + related_name='news_gallery', + on_delete=models.SET_NULL, + verbose_name=_('gallery')) objects = NewsGalleryQuerySet.as_manager() diff --git a/apps/news/serializers.py b/apps/news/serializers.py index b7b11a06..8901b834 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -1,6 +1,9 @@ """News app common serializers.""" +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from gallery.models import Image +from gallery.serializers import ImageSerializer from location import models as location_models from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer @@ -28,6 +31,7 @@ class NewsBaseSerializer(serializers.ModelSerializer): # related fields news_type = NewsTypeSerializer(read_only=True) tags = MetaDataContentSerializer(read_only=True, many=True) + gallery = ImageSerializer(read_only=True, many=True) slug = serializers.SlugField(allow_blank=False, required=True, max_length=50) @@ -43,6 +47,7 @@ class NewsBaseSerializer(serializers.ModelSerializer): 'news_type', 'tags', 'slug', + 'gallery', ) @@ -100,3 +105,43 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, 'country_id', ) + +class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): + """Serializer class for model NewsGallery.""" + image = ImageSerializer(read_only=True) + + class Meta: + """Meta class""" + model = models.NewsGallery + fields = [ + 'id', + 'image', + ] + + def get_request_kwargs(self): + """Get url kwargs from request.""" + return self.context.get('request').parser_context.get('kwargs') + + def validate(self, attrs): + """Override validate method.""" + news_pk = self.get_request_kwargs().get('pk') + image_id = self.get_request_kwargs().get('image_id') + + news_qs = models.News.objects.filter(pk=news_pk) + image_qs = Image.objects.filter(id=image_id) + + if not news_qs.exists(): + raise serializers.ValidationError({'detail': _('News not found')}) + if not image_qs.exists(): + raise serializers.ValidationError({'detail': _('Image not found')}) + + news = news_qs.first() + image = image_qs.first() + + if news.news_gallery.filter(image=image).exists(): + raise serializers.ValidationError({'detail': _('Image is already added')}) + + attrs['news'] = news + attrs['image'] = image + + return attrs diff --git a/apps/news/urls/back.py b/apps/news/urls/back.py index 8522592e..4ab11727 100644 --- a/apps/news/urls/back.py +++ b/apps/news/urls/back.py @@ -1,5 +1,6 @@ """News app urlpatterns for backoffice""" from django.urls import path + from news import views app_name = 'news' @@ -7,5 +8,9 @@ app_name = 'news' urlpatterns = [ path('', views.NewsBackOfficeLCView.as_view(), name='list-create'), path('/', views.NewsBackOfficeRUDView.as_view(), - name='retrieve-update-destroy'), + name='gallery-retrieve-update-destroy'), + path('/gallery/', views.NewsBackOfficeGalleryListView.as_view(), + name='gallery-list'), + path('/gallery//', views.NewsBackOfficeGalleryCreateDestroyView.as_view(), + name='gallery-create-destroy'), ] \ No newline at end of file diff --git a/apps/news/views.py b/apps/news/views.py index 74abe33f..6231fd3b 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,5 +1,8 @@ """News app views.""" +from django.shortcuts import get_object_or_404 from rest_framework import generics, permissions + +from gallery.serializers import ImageSerializer from news import filters, models, serializers @@ -59,6 +62,46 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, return super().get_serializer_class() +class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, + generics.CreateAPIView, + generics.DestroyAPIView): + """Resource for a create gallery for news for back-office users.""" + serializer_class = serializers.NewsBackOfficeGallerySerializer + + def get_object(self): + """ + Returns the object the view is displaying. + """ + news_qs = self.filter_queryset(self.get_queryset()) + + news = get_object_or_404(news_qs, pk=self.kwargs['pk']) + gallery = get_object_or_404(news.news_gallery, image_id=self.kwargs['image_id']) + + # May raise a permission denied + self.check_object_permissions(self.request, gallery) + + return gallery + + +class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView): + """Resource for returning gallery for news for back-office users.""" + serializer_class = ImageSerializer + + def get_object(self): + """Override get_object method.""" + qs = super(NewsBackOfficeGalleryListView, self).get_queryset() + news = get_object_or_404(qs, pk=self.kwargs['pk']) + + # May raise a permission denied + self.check_object_permissions(self.request, news) + + return news + + def get_queryset(self): + """Override get_queryset method.""" + return self.get_object().gallery.all() + + class NewsBackOfficeRUDView(NewsBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView): """Resource for detailed information about news for back-office users.""" From d9635a85999b48d444b9178600fa5e315399aedd Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 2 Oct 2019 10:16:32 +0300 Subject: [PATCH 004/121] gm-148: refactored --- .../migrations/0009_auto_20191002_0648.py | 18 ++++++ apps/account/models.py | 2 +- apps/authorization/serializers/common.py | 12 ++-- .../migrations/0003_auto_20191001_0647.py | 20 +++++++ apps/gallery/models.py | 10 ++-- apps/gallery/serializers.py | 6 +- .../migrations/0020_merge_20190930_1251.py | 14 +++++ apps/news/serializers.py | 27 +++++++-- apps/news/views.py | 11 +++- apps/utils/methods.py | 24 +++++--- apps/utils/models.py | 41 +++++++++++++- project/settings/amazon_s3.py | 19 +++++++ project/settings/base.py | 56 +++++++++++++------ project/settings/development.py | 3 +- project/settings/local.py | 8 ++- project/settings/production.py | 1 + project/settings/stage.py | 1 + project/storage_backends.py | 12 ++++ project/urls/__init__.py | 2 +- requirements/base.txt | 8 ++- 20 files changed, 241 insertions(+), 54 deletions(-) create mode 100644 apps/account/migrations/0009_auto_20191002_0648.py create mode 100644 apps/gallery/migrations/0003_auto_20191001_0647.py create mode 100644 apps/news/migrations/0020_merge_20190930_1251.py create mode 100644 project/settings/amazon_s3.py create mode 100644 project/storage_backends.py diff --git a/apps/account/migrations/0009_auto_20191002_0648.py b/apps/account/migrations/0009_auto_20191002_0648.py new file mode 100644 index 00000000..d9734907 --- /dev/null +++ b/apps/account/migrations/0009_auto_20191002_0648.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-02 06:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0008_auto_20190912_1325'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(default=None, max_length=254, null=True, unique=True, verbose_name='email address'), + ), + ] diff --git a/apps/account/models.py b/apps/account/models.py index 81ade4fc..4ba03521 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -58,7 +58,7 @@ class User(AbstractUser): blank=True, null=True, default=None) cropped_image_url = models.URLField(verbose_name=_('Cropped image URL path'), blank=True, null=True, default=None) - email = models.EmailField(_('email address'), blank=True, + email = models.EmailField(_('email address'), unique=True, null=True, default=None) email_confirmed = models.BooleanField(_('email status'), default=False) newsletter = models.NullBooleanField(default=True) diff --git a/apps/authorization/serializers/common.py b/apps/authorization/serializers/common.py index 5d8bb3a8..6ee108dd 100644 --- a/apps/authorization/serializers/common.py +++ b/apps/authorization/serializers/common.py @@ -18,12 +18,6 @@ from utils.tokens import GMRefreshToken # Serializers class SignupSerializer(serializers.ModelSerializer): """Signup serializer serializer mixin""" - # REQUEST - username = serializers.CharField(write_only=True) - password = serializers.CharField(write_only=True) - email = serializers.EmailField(write_only=True) - newsletter = serializers.BooleanField(write_only=True) - class Meta: model = account_models.User fields = ( @@ -32,6 +26,12 @@ class SignupSerializer(serializers.ModelSerializer): 'email', 'newsletter' ) + extra_kwargs = { + 'username': {'write_only': True}, + 'password': {'write_only': True}, + 'email': {'write_only': True}, + 'newsletter': {'write_only': True} + } def validate_username(self, value): """Custom username validation""" diff --git a/apps/gallery/migrations/0003_auto_20191001_0647.py b/apps/gallery/migrations/0003_auto_20191001_0647.py new file mode 100644 index 00000000..4defed2b --- /dev/null +++ b/apps/gallery/migrations/0003_auto_20191001_0647.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.4 on 2019-10-01 06:47 + +from django.db import migrations +import sorl.thumbnail.fields +import utils.methods + + +class Migration(migrations.Migration): + + dependencies = [ + ('gallery', '0002_auto_20190930_0714'), + ] + + operations = [ + migrations.AlterField( + model_name='image', + name='image', + field=sorl.thumbnail.fields.ImageField(upload_to=utils.methods.image_path, verbose_name='image file'), + ), + ] diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 7678c29a..687b42fc 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -1,16 +1,16 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from easy_thumbnails.fields import ThumbnailerImageField +from sorl.thumbnail.fields import ImageField as SORLImageField from utils.methods import image_path -from utils.models import ProjectBaseMixin, ImageMixin, PlatformMixin +from utils.models import ProjectBaseMixin, SORLImageMixin, PlatformMixin class ImageQuerySet(models.QuerySet): """QuerySet for model Image.""" -class Image(ProjectBaseMixin, ImageMixin, PlatformMixin): +class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): """Image model.""" HORIZONTAL = 0 VERTICAL = 1 @@ -20,8 +20,8 @@ class Image(ProjectBaseMixin, ImageMixin, PlatformMixin): (VERTICAL, _('Vertical')), ) - image = ThumbnailerImageField(upload_to=image_path, - verbose_name=_('image file')) + image = SORLImageField(upload_to=image_path, + verbose_name=_('image file')) parent = models.ForeignKey('self', blank=True, null=True, default=None, related_name='parent_image', diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index c428b8f2..f575001a 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -8,7 +8,6 @@ class ImageSerializer(serializers.ModelSerializer): # REQUEST file = serializers.ImageField(source='image', write_only=True) - title = serializers.CharField() orientation = serializers.ChoiceField(choices=models.Image.ORIENTATIONS, write_only=True) @@ -21,7 +20,7 @@ class ImageSerializer(serializers.ModelSerializer): class Meta: """Meta class""" model = models.Image - fields = ( + fields = [ 'id', 'file', 'url', @@ -29,5 +28,4 @@ class ImageSerializer(serializers.ModelSerializer): 'orientation', 'orientation_display', 'title', - ) - + ] diff --git a/apps/news/migrations/0020_merge_20190930_1251.py b/apps/news/migrations/0020_merge_20190930_1251.py new file mode 100644 index 00000000..deb45e63 --- /dev/null +++ b/apps/news/migrations/0020_merge_20190930_1251.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-09-30 12:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0019_news_author'), + ('news', '0016_news_gallery'), + ] + + operations = [ + ] diff --git a/apps/news/serializers.py b/apps/news/serializers.py index daee42b2..fad2f355 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -2,6 +2,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from account.serializers.common import UserSerializer from gallery.models import Image from gallery.serializers import ImageSerializer from location import models as location_models @@ -9,7 +10,26 @@ from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer from news import models from utils.serializers import TranslatedField -from account.serializers.common import UserSerializer + + +class NewsImageSerializer(ImageSerializer): + """News images""" + promo_web_url = serializers.SerializerMethodField() + promo_mobile_url = serializers.SerializerMethodField() + + class Meta: + model = Image + fields = ImageSerializer.Meta.fields + [ + 'promo_web_url', + 'promo_mobile_url' + ] + + def get_promo_web_url(self, obj): + return obj.get_image_url(thumbnail_key='news_promo_horizontal_web') + + def get_promo_mobile_url(self, obj): + return obj.get_image_url(thumbnail_key='news_promo_horizontal_mobile') + class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" @@ -31,7 +51,7 @@ class NewsBaseSerializer(serializers.ModelSerializer): # related fields news_type = NewsTypeSerializer(read_only=True) tags = MetaDataContentSerializer(read_only=True, many=True) - gallery = ImageSerializer(read_only=True, many=True) + gallery = NewsImageSerializer(read_only=True, many=True) class Meta: """Meta class.""" @@ -115,14 +135,11 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): """Serializer class for model NewsGallery.""" - image = ImageSerializer(read_only=True) - class Meta: """Meta class""" model = models.NewsGallery fields = [ 'id', - 'image', ] def get_request_kwargs(self): diff --git a/apps/news/views.py b/apps/news/views.py index 6231fd3b..3eb7f9ab 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,8 +1,8 @@ """News app views.""" from django.shortcuts import get_object_or_404 -from rest_framework import generics, permissions +from rest_framework import generics, permissions, status +from rest_framework.response import Response -from gallery.serializers import ImageSerializer from news import filters, models, serializers @@ -82,10 +82,15 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, return gallery + def create(self, request, *args, **kwargs): + """Override create method""" + super().create(request, *args, **kwargs) + return Response(status=status.HTTP_201_CREATED) + class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView): """Resource for returning gallery for news for back-office users.""" - serializer_class = ImageSerializer + serializer_class = serializers.NewsImageSerializer def get_object(self): """Override get_object method.""" diff --git a/apps/utils/methods.py b/apps/utils/methods.py index 25027ae9..bea8fec7 100644 --- a/apps/utils/methods.py +++ b/apps/utils/methods.py @@ -1,4 +1,5 @@ """Utils app method.""" +import logging import random import re import string @@ -10,6 +11,9 @@ from django.http.request import HttpRequest from django.utils.timezone import datetime from rest_framework import status from rest_framework.request import Request +from os.path import exists + +logger = logging.getLogger(__name__) def generate_code(digits=6, string_output=True): @@ -91,12 +95,18 @@ def get_contenttype(app_label: str, model: str): def image_url_valid(url: str): - # In case if image storage is not on CDN - if url.startswith('/media/'): - url = f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{url}' - response = requests.request('head', url) - if response.status_code == status.HTTP_200_OK: - return True + """ + Check if requested URL is valid. + :param url: string + :return: boolean + """ + try: + assert url.startswith('http') + response = requests.request('head', url) + except Exception as e: + logger.info(f'ConnectionError: {e}') + else: + return response.status_code == status.HTTP_200_OK def absolute_url_decorator(func): @@ -105,7 +115,7 @@ def absolute_url_decorator(func): url_path = func(self, obj) if url_path: if url_path.startswith('/media/'): - return f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{url_path}/' + return f'{settings.MEDIA_URL}{url_path}/' else: return url_path return get_absolute_image_url diff --git a/apps/utils/models.py b/apps/utils/models.py index 4e6df35e..299013b7 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -1,4 +1,5 @@ """Utils app models.""" +import logging from os.path import exists from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.gis.db import models @@ -10,8 +11,12 @@ from django.utils.translation import ugettext_lazy as _, get_language from easy_thumbnails.fields import ThumbnailerImageField from utils.methods import image_path, svg_image_path from utils.validators import svg_image_validator -from django.db.models.fields import Field -from django.core import exceptions +from sorl.thumbnail.fields import ImageField as SORLImageField +from sorl.thumbnail import get_thumbnail +from django.conf import settings +from utils.methods import image_url_valid + +logger = logging.getLogger(__name__) class ProjectBaseMixin(models.Model): @@ -177,6 +182,38 @@ class ImageMixin(models.Model): image_tag.allow_tags = True +class SORLImageMixin(models.Model): + """Abstract model for SORL ImageField""" + + image = SORLImageField(upload_to=image_path, + blank=True, null=True, default=None, + verbose_name=_('Image')) + + class Meta: + """Meta class.""" + abstract = True + + def get_image(self, thumbnail_key=None): + """Get thumbnail image file.""" + if thumbnail_key in settings.SORL_THUMBNAIL_ALIASES: + return get_thumbnail(file_=self.image, + **settings.SORL_THUMBNAIL_ALIASES[thumbnail_key]) + + def get_image_url(self, thumbnail_key=None): + """Get image thumbnail url.""" + return self.get_image(thumbnail_key).url + + def image_tag(self): + """Admin preview tag.""" + if self.image: + return mark_safe('' % self.image.url) + else: + return None + + image_tag.short_description = _('Image') + image_tag.allow_tags = True + + class SVGImageMixin(models.Model): """SVG image model.""" diff --git a/project/settings/amazon_s3.py b/project/settings/amazon_s3.py new file mode 100644 index 00000000..73b15e28 --- /dev/null +++ b/project/settings/amazon_s3.py @@ -0,0 +1,19 @@ +"""Settings for Amazon S3""" +import os +from .base import MEDIA_LOCATION + +# AMAZON S3 +AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY') +AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME') +AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com' +AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'} + +# Static settings +# PUBLIC_STATIC_LOCATION = 'static' +# STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{PUBLIC_STATIC_LOCATION}/' +# STATICFILES_STORAGE = 'project.storage_backends.PublicStaticStorage' + +# Public media settings +MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIA_LOCATION}/' +DEFAULT_FILE_STORAGE = 'project.storage_backends.PublicMediaStorage' diff --git a/project/settings/base.py b/project/settings/base.py index 0ae088ae..2916cff0 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -91,6 +91,8 @@ EXTERNAL_APPS = [ 'rest_framework_simplejwt.token_blacklist', 'solo', 'phonenumber_field', + 'storages', + 'sorl.thumbnail', ] @@ -190,19 +192,6 @@ LOCALE_PATHS = ( os.path.abspath(os.path.join(BASE_DIR, 'locale')), ) -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.2/howto/static-files/ - -STATIC_ROOT = os.path.join(PUBLIC_ROOT, 'static') -STATIC_URL = '/static/' - -MEDIA_ROOT = os.path.join(PUBLIC_ROOT, 'media') -MEDIA_URL = '/media/' - -STATICFILES_DIRS = ( - os.path.join(PROJECT_ROOT, 'static'), -) - AVAILABLE_VERSIONS = { # 'future': '1.0.1', 'current': '1.0.0', @@ -262,6 +251,7 @@ SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = { 'fields': 'id, name, email', } + # SMS Settings SMS_EXPIRATION = 5 SMS_SEND_DELAY = 30 @@ -272,12 +262,14 @@ SMS_CODE_LENGTH = 6 SEND_SMS = True SMS_CODE_SHOW = False + # SMSC Settings SMS_SERVICE = 'http://smsc.ru/sys/send.php' SMS_LOGIN = 'GM2019' SMS_PASSWORD = '}#6%Qe7CYG7n' SMS_SENDER = 'GM' + # EMAIL EMAIL_USE_TLS = True EMAIL_HOST = 'smtp.gmail.com' @@ -285,6 +277,7 @@ EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com' EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt' EMAIL_PORT = 587 + # Django Rest Swagger SWAGGER_SETTINGS = { # "DEFAULT_GENERATOR_CLASS": "rest_framework.schemas.generators.BaseSchemaGenerator", @@ -307,6 +300,7 @@ REDOC_SETTINGS = { 'LAZY_RENDERING': False, } + # CELERY BROKER_URL = 'amqp://rabbitmq:5672' CELERY_RESULT_BACKEND = BROKER_URL @@ -316,6 +310,7 @@ CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_TIMEZONE = TIME_ZONE + # Django FCM (Firebase push notifications) FCM_DJANGO_SETTINGS = { 'FCM_SERVER_KEY': ( @@ -325,6 +320,7 @@ FCM_DJANGO_SETTINGS = { ), } + # Thumbnail settings THUMBNAIL_ALIASES = { '': { @@ -345,11 +341,23 @@ THUMBNAIL_DEFAULT_OPTIONS = { 'crop': 'smart', } -# Password reset -RESETTING_TOKEN_EXPIRATION = 24 # hours +# SORL +THUMBNAIL_QUALITY = 85 +THUMBNAIL_DEBUG = False +SORL_THUMBNAIL_ALIASES = { + 'news_preview': {'geometry_string': '100x100', 'crop': 'center'}, + 'news_promo_horizontal_web': {'geometry_string': '1900x600', 'crop': 'center'}, + 'news_promo_horizontal_mobile': {'geometry_string': '375x260', 'crop': 'center'}, + 'news_tile_horizontal_web': {'geometry_string': '300x275', 'crop': 'center'}, + 'news_tile_horizontal_mobile': {'geometry_string': '343x180', 'crop': 'center'}, + 'news_tile_vertical_web': {'geometry_string': '300x380', 'crop': 'center'}, + 'news_highlight_vertical_web': {'geometry_string': '460x630', 'crop': 'center'}, + 'news_editor_web': {'geometry_string': '940x430', 'crop': 'center'}, + 'news_editor_mobile': {'geometry_string': '343x260', 'crop': 'center'}, # при загрузке через контент эдитор + 'avatar_comments_web': {'geometry_string': '116x116', 'crop': 'center'}, # через контент эдитор в мобильном браузерe +} -GEOIP_PATH = os.path.join(PROJECT_ROOT, 'geoip_db') # JWT SIMPLE_JWT = { @@ -409,17 +417,20 @@ DATA_UPLOAD_MAX_MEMORY_SIZE = 5242880 # 5MB FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880 # 5MB FILE_UPLOAD_PERMISSIONS = 0o644 + # SOLO SETTINGS # todo: make a separate service (redis?) and update solo_cache SOLO_CACHE = 'default' SOLO_CACHE_PREFIX = 'solo' SOLO_CACHE_TIMEOUT = 300 + # REDIRECT URL SITE_REDIRECT_URL_UNSUBSCRIBE = '/unsubscribe/' SITE_NAME = 'Gault & Millau' + # Used in annotations for establishments. DEFAULT_ESTABLISHMENT_PUBLIC_MARK = 10 # Limit output objects (see in pagination classes). @@ -427,6 +438,19 @@ QUERY_OUTPUT_OBJECTS = 12 # Need to restrict objects to sort (3 times more then expected). LIMITING_QUERY_OBJECTS = QUERY_OUTPUT_OBJECTS * 3 + # GEO # A Spatial Reference System Identifier GEO_DEFAULT_SRID = 4326 +GEOIP_PATH = os.path.join(PROJECT_ROOT, 'geoip_db') + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ +STATIC_ROOT = os.path.join(PUBLIC_ROOT, 'static') +STATIC_URL = '/static/' + +STATICFILES_DIRS = ( + os.path.join(PROJECT_ROOT, 'static'), +) + +MEDIA_LOCATION = 'media' diff --git a/project/settings/development.py b/project/settings/development.py index 43a60935..29564575 100644 --- a/project/settings/development.py +++ b/project/settings/development.py @@ -1,9 +1,10 @@ """Development settings.""" from .base import * +from .amazon_s3 import * import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration -ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126'] +ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0'] SEND_SMS = False SMS_CODE_SHOW = True diff --git a/project/settings/local.py b/project/settings/local.py index 503ad191..a7cb7c0f 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -17,6 +17,8 @@ BROKER_URL = 'amqp://rabbitmq:5672' CELERY_RESULT_BACKEND = BROKER_URL CELERY_BROKER_URL = BROKER_URL +MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) +MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' # LOGGING LOGGING = { @@ -62,8 +64,10 @@ ELASTICSEARCH_DSL = { } } - ELASTICSEARCH_INDEX_NAMES = { # 'search_indexes.documents.news': 'local_news', 'search_indexes.documents.establishment': 'local_establishment', -} \ No newline at end of file +} + +# SORL thumbnails +THUMBNAIL_DEBUG = True diff --git a/project/settings/production.py b/project/settings/production.py index e491a1fb..f2855592 100644 --- a/project/settings/production.py +++ b/project/settings/production.py @@ -1,2 +1,3 @@ """Production settings.""" from .base import * +from .amazon_s3 import * diff --git a/project/settings/stage.py b/project/settings/stage.py index c0d6fdb1..e1443ab1 100644 --- a/project/settings/stage.py +++ b/project/settings/stage.py @@ -1,5 +1,6 @@ """Stage settings.""" from .base import * +from .amazon_s3 import * ALLOWED_HOSTS = ['gm-stage.id-east.ru', '95.213.204.126'] diff --git a/project/storage_backends.py b/project/storage_backends.py new file mode 100644 index 00000000..633337e8 --- /dev/null +++ b/project/storage_backends.py @@ -0,0 +1,12 @@ +"""Extend storage backend for adding custom parameters""" +from storages.backends.s3boto3 import S3Boto3Storage + + +class PublicMediaStorage(S3Boto3Storage): + location = 'media' + file_overwrite = False + + +class PublicStaticStorage(S3Boto3Storage): + location = 'static' + file_overwrite = False diff --git a/project/urls/__init__.py b/project/urls/__init__.py index f1a89695..ca76ff43 100644 --- a/project/urls/__init__.py +++ b/project/urls/__init__.py @@ -64,7 +64,7 @@ urlpatterns = [ ] urlpatterns = urlpatterns + \ - static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + static(settings.MEDIA_LOCATION, document_root=settings.MEDIA_ROOT) if settings.DEBUG: urlpatterns.extend(urlpatterns_doc) diff --git a/requirements/base.txt b/requirements/base.txt index 25749c4b..3d8955cd 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -32,4 +32,10 @@ djangorestframework-simplejwt==4.3.0 django-elasticsearch-dsl>=7.0.0,<8.0.0 django-elasticsearch-dsl-drf==0.20.2 -sentry-sdk==0.11.2 \ No newline at end of file +sentry-sdk==0.11.2 + +# AMAZON S3 +boto3==1.9.238 +django-storages==1.7.2 + +sorl-thumbnail==12.5.0 \ No newline at end of file From 3234a71cde753ca623461073613843342b7384d4 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 2 Oct 2019 10:34:17 +0300 Subject: [PATCH 005/121] gm-148: modified settings, added missing migration --- .../migrations/0003_auto_20191002_0729.py | 17 +++++++++++++++++ project/settings/base.py | 2 ++ project/settings/local.py | 8 +++++++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 apps/timetable/migrations/0003_auto_20191002_0729.py diff --git a/apps/timetable/migrations/0003_auto_20191002_0729.py b/apps/timetable/migrations/0003_auto_20191002_0729.py new file mode 100644 index 00000000..16196f74 --- /dev/null +++ b/apps/timetable/migrations/0003_auto_20191002_0729.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.4 on 2019-10-02 07:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('timetable', '0002_auto_20190919_1124'), + ] + + operations = [ + migrations.AlterModelOptions( + name='timetable', + options={'ordering': ['weekday'], 'verbose_name': 'Timetable', 'verbose_name_plural': 'Timetables'}, + ), + ] diff --git a/project/settings/base.py b/project/settings/base.py index 2916cff0..827b78c6 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -453,4 +453,6 @@ STATICFILES_DIRS = ( os.path.join(PROJECT_ROOT, 'static'), ) + +# MEDIA MEDIA_LOCATION = 'media' diff --git a/project/settings/local.py b/project/settings/local.py index f67b8f7e..d842894c 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -4,22 +4,28 @@ import sys ALLOWED_HOSTS = ['*', ] + SEND_SMS = False SMS_CODE_SHOW = True USE_CELERY = False + SCHEMA_URI = 'http' DEFAULT_SUBDOMAIN = 'www' SITE_DOMAIN_URI = 'testserver.com:8000' DOMAIN_URI = '0.0.0.0:8000' + # CELERY BROKER_URL = 'amqp://rabbitmq:5672' CELERY_RESULT_BACKEND = BROKER_URL CELERY_BROKER_URL = BROKER_URL -MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) + +# MEDIA MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' +MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) + # LOGGING LOGGING = { From 402bd32eda8e1399ad065c2f210a76fe83733932 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 2 Oct 2019 14:10:23 +0300 Subject: [PATCH 006/121] gm-148: modified news gallery --- apps/gallery/models.py | 17 +++++++++++ apps/gallery/serializers.py | 5 ++-- apps/gallery/tasks.py | 27 +++++++++++++++++ apps/news/models.py | 13 ++++----- apps/news/serializers.py | 52 +++++++++++++++++++++++++-------- apps/news/views.py | 15 +++++++++- project/settings/development.py | 2 +- 7 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 apps/gallery/tasks.py diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 687b42fc..81bfbb40 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -3,12 +3,18 @@ from django.utils.translation import gettext_lazy as _ from sorl.thumbnail.fields import ImageField as SORLImageField from utils.methods import image_path +from django.conf import settings +from . import tasks from utils.models import ProjectBaseMixin, SORLImageMixin, PlatformMixin class ImageQuerySet(models.QuerySet): """QuerySet for model Image.""" + def originals(self): + """Return QuerySet with original images.""" + return self.filter(parent__isnull=True) + class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): """Image model.""" @@ -42,3 +48,14 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): def __str__(self): """String representation""" return str(self.id) + + def delete_from_remote_storage(self, delete_original: bool = True): + """Delete from remote storage""" + if settings.USE_CELERY: + tasks.delete_image_from_remote_storage.delay(self.id, delete_original) + else: + tasks.delete_image_from_remote_storage(self.id, delete_original) + + @property + def childs(self): + return self.parent_image.filter(parent=self) diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index f575001a..08ca4a0f 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -8,8 +8,6 @@ class ImageSerializer(serializers.ModelSerializer): # REQUEST file = serializers.ImageField(source='image', write_only=True) - orientation = serializers.ChoiceField(choices=models.Image.ORIENTATIONS, - write_only=True) # RESPONSE url = serializers.ImageField(source='image', @@ -29,3 +27,6 @@ class ImageSerializer(serializers.ModelSerializer): 'orientation_display', 'title', ] + extra_kwargs = { + 'orientation': {'write_only': True} + } diff --git a/apps/gallery/tasks.py b/apps/gallery/tasks.py new file mode 100644 index 00000000..8f3f5453 --- /dev/null +++ b/apps/gallery/tasks.py @@ -0,0 +1,27 @@ +"""Gallery app celery tasks.""" +import logging + +from celery import shared_task +from sorl.thumbnail import delete + +from . import models + +logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO) +logger = logging.getLogger(__name__) + + +@shared_task +def delete_image_from_remote_storage(image_id, delete_original=True): + """Delete an image from remote storage.""" + image_qs = models.Image.objects.filter(id=image_id) + if image_qs.exists(): + try: + image = image_qs.first() + # Delete from remote storage + delete(file_=image.image.file, delete_file=delete_original) + # Delete an instance of image + image.delete() + except: + logger.error(f'METHOD_NAME: delete_image_from_remote_storage\n' + f'DETAIL: Exception occurred while deleting an image ' + f'from remote storage: image_id - {image_id}') diff --git a/apps/news/models.py b/apps/news/models.py index ed40e09b..c128b8ed 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -4,6 +4,7 @@ from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework.reverse import reverse +from sorl.thumbnail import delete from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin from random import sample as random_sample @@ -157,18 +158,14 @@ class News(BaseAttributes, TranslatedFieldsMixin): def web_url(self): return reverse('web:news:rud', kwargs={'slug': self.slug}) + @property + def original_images(self): + return self.gallery.originals() + class NewsGalleryQuerySet(models.QuerySet): """QuerySet for model News""" - def originals(self): - """Return QuerySet with original images.""" - return self.filter(gallery__parent__isnull=True) - - def crops(self): - """Return QuerySet with cropped images.""" - return self.filter(gallery__parent__isnull=False) - class NewsGallery(models.Model): diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 8bed7692..e4b8c1c7 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -12,23 +12,51 @@ from news import models from utils.serializers import TranslatedField, ProjectModelSerializer -class NewsImageSerializer(ImageSerializer): - """News images""" - promo_web_url = serializers.SerializerMethodField() - promo_mobile_url = serializers.SerializerMethodField() +class NewsCropImageSerializer(ImageSerializer): + """Serializer for returning crop images of news image.""" + orientation_display = serializers.CharField(source='get_orientation_display', + read_only=True) + web_url = serializers.SerializerMethodField() + mobile_url = serializers.SerializerMethodField() class Meta: model = Image - fields = ImageSerializer.Meta.fields + [ - 'promo_web_url', - 'promo_mobile_url' + fields = [ + 'id', + 'title', + 'orientation_display', + 'web_url', + 'mobile_url', ] + extra_kwargs = { + 'orientation': {'write_only': True} + } - def get_promo_web_url(self, obj): - return obj.get_image_url(thumbnail_key='news_promo_horizontal_web') + def get_web_url(self, obj): + """Return URL of cropped image by thumbnail.""" + return obj.get_image_url('news_promo_horizontal_web') - def get_promo_mobile_url(self, obj): - return obj.get_image_url(thumbnail_key='news_promo_horizontal_mobile') + def get_mobile_url(self, obj): + """Return URL of cropped image by thumbnail.""" + return obj.get_image_url('news_promo_horizontal_mobile') + + +class NewsImageSerializer(ImageSerializer): + """News images""" + url = serializers.URLField(source='image.url', read_only=True) + crops = NewsCropImageSerializer(source='childs', allow_null=True, many=True) + + class Meta: + model = Image + fields = [ + 'id', + 'title', + 'url', + 'crops', + ] + extra_kwargs = { + 'orientation': {'write_only': True} + } class NewsTypeSerializer(serializers.ModelSerializer): @@ -51,7 +79,7 @@ class NewsBaseSerializer(ProjectModelSerializer): # related fields news_type = NewsTypeSerializer(read_only=True) tags = MetaDataContentSerializer(read_only=True, many=True) - gallery = NewsImageSerializer(read_only=True, many=True) + gallery = NewsImageSerializer(source='original_images', read_only=True, many=True) class Meta: """Meta class.""" diff --git a/apps/news/views.py b/apps/news/views.py index ca2c01cd..ba998c11 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -4,6 +4,7 @@ from rest_framework import generics, permissions, status from rest_framework.response import Response from news import filters, models, serializers +from gallery.serializers import ImageSerializer class NewsMixinView: @@ -90,10 +91,22 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, super().create(request, *args, **kwargs) return Response(status=status.HTTP_201_CREATED) + def destroy(self, request, *args, **kwargs): + """Override destroy method.""" + gallery_obj = self.get_object() + image_obj = gallery_obj.image + + # Delete an instances of NewsGallery model + gallery_obj.delete() + # Delete an instance of Image model + image_obj.delete_from_remote_storage() + + return Response(status=status.HTTP_204_NO_CONTENT) + class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView): """Resource for returning gallery for news for back-office users.""" - serializer_class = serializers.NewsImageSerializer + serializer_class = ImageSerializer def get_object(self): """Override get_object method.""" diff --git a/project/settings/development.py b/project/settings/development.py index 29564575..376fd9cb 100644 --- a/project/settings/development.py +++ b/project/settings/development.py @@ -8,7 +8,7 @@ ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0'] SEND_SMS = False SMS_CODE_SHOW = True -USE_CELERY = False +USE_CELERY = True SCHEMA_URI = 'http' DEFAULT_SUBDOMAIN = 'www' From e66b2f7f9f850beeca4caa104e9b266c127e53d1 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 2 Oct 2019 16:13:21 +0300 Subject: [PATCH 007/121] refactored --- apps/gallery/models.py | 4 +- .../migrations/0021_merge_20191002_1300.py | 14 +++ apps/news/models.py | 34 +++++-- apps/news/serializers.py | 92 ++++++++++++++++++- apps/news/views.py | 4 +- 5 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 apps/news/migrations/0021_merge_20191002_1300.py diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 81bfbb40..33d2debc 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -11,7 +11,7 @@ from utils.models import ProjectBaseMixin, SORLImageMixin, PlatformMixin class ImageQuerySet(models.QuerySet): """QuerySet for model Image.""" - def originals(self): + def original_images(self): """Return QuerySet with original images.""" return self.filter(parent__isnull=True) @@ -57,5 +57,5 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): tasks.delete_image_from_remote_storage(self.id, delete_original) @property - def childs(self): + def children(self): return self.parent_image.filter(parent=self) diff --git a/apps/news/migrations/0021_merge_20191002_1300.py b/apps/news/migrations/0021_merge_20191002_1300.py new file mode 100644 index 00000000..ab0acf69 --- /dev/null +++ b/apps/news/migrations/0021_merge_20191002_1300.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-02 13:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0020_remove_news_author'), + ('news', '0020_merge_20190930_1251'), + ] + + operations = [ + ] diff --git a/apps/news/models.py b/apps/news/models.py index efa0b566..9022a6a2 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -119,10 +119,6 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys - image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Image URL path')) - preview_image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Preview image URL path')) template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), @@ -132,6 +128,8 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('country')) tags = generic.GenericRelation(to='main.MetaDataContent') + gallery = models.ManyToManyField('gallery.Image', through='news.NewsGallery') + objects = NewsQuerySet.as_manager() class Meta: @@ -152,10 +150,28 @@ class News(BaseAttributes, TranslatedFieldsMixin): return reverse('web:news:rud', kwargs={'slug': self.slug}) @property - def should_read(self): - return self.__class__.objects.should_read(self)[:3] + def original_images(self): + return self.gallery.original_images() - @property - def same_theme(self): - return self.__class__.objects.same_theme(self)[:3] +class NewsGalleryQuerySet(models.QuerySet): + """QuerySet for model News""" + + +class NewsGallery(models.Model): + + news = models.ForeignKey(News, null=True, + related_name='news_gallery', + on_delete=models.SET_NULL, + verbose_name=_('news')) + image = models.ForeignKey('gallery.Image', null=True, + related_name='news_gallery', + on_delete=models.SET_NULL, + verbose_name=_('gallery')) + + objects = NewsGalleryQuerySet.as_manager() + + class Meta: + """NewsGallery meta class.""" + verbose_name = _('news gallery') + verbose_name_plural = _('news galleries') diff --git a/apps/news/serializers.py b/apps/news/serializers.py index c473be1d..08dfce5e 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -1,6 +1,10 @@ """News app common serializers.""" +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers + from account.serializers.common import UserBaseSerializer +from gallery.models import Image +from gallery.serializers import ImageSerializer from location import models as location_models from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer @@ -8,6 +12,53 @@ from news import models from utils.serializers import TranslatedField, ProjectModelSerializer +class NewsCropImageSerializer(ImageSerializer): + """Serializer for returning crop images of news image.""" + orientation_display = serializers.CharField(source='get_orientation_display', + read_only=True) + web_url = serializers.SerializerMethodField() + mobile_url = serializers.SerializerMethodField() + + class Meta: + model = Image + fields = [ + 'id', + 'title', + 'orientation_display', + 'web_url', + 'mobile_url', + ] + extra_kwargs = { + 'orientation': {'write_only': True} + } + + def get_web_url(self, obj): + """Return URL of cropped image by thumbnail.""" + return obj.get_image_url('news_promo_horizontal_web') + + def get_mobile_url(self, obj): + """Return URL of cropped image by thumbnail.""" + return obj.get_image_url('news_promo_horizontal_mobile') + + +class NewsImageSerializer(ImageSerializer): + """News images""" + url = serializers.URLField(source='image.url', read_only=True) + crops = NewsCropImageSerializer(source='children', allow_null=True, many=True) + + class Meta: + model = Image + fields = [ + 'id', + 'title', + 'url', + 'crops', + ] + extra_kwargs = { + 'orientation': {'write_only': True} + } + + class NewsTypeSerializer(serializers.ModelSerializer): """News type serializer.""" @@ -28,6 +79,7 @@ class NewsBaseSerializer(ProjectModelSerializer): # related fields news_type = NewsTypeSerializer(read_only=True) tags = MetaDataContentSerializer(read_only=True, many=True) + gallery = NewsImageSerializer(source='original_images', read_only=True, many=True) class Meta: """Meta class.""" @@ -38,11 +90,10 @@ class NewsBaseSerializer(ProjectModelSerializer): 'title_translated', 'subtitle_translated', 'is_highlighted', - 'image_url', - 'preview_image_url', 'news_type', 'tags', 'slug', + 'gallery', ) @@ -123,3 +174,40 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, 'template_display', ) + +class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): + """Serializer class for model NewsGallery.""" + class Meta: + """Meta class""" + model = models.NewsGallery + fields = [ + 'id', + ] + + def get_request_kwargs(self): + """Get url kwargs from request.""" + return self.context.get('request').parser_context.get('kwargs') + + def validate(self, attrs): + """Override validate method.""" + news_pk = self.get_request_kwargs().get('pk') + image_id = self.get_request_kwargs().get('image_id') + + news_qs = models.News.objects.filter(pk=news_pk) + image_qs = Image.objects.filter(id=image_id) + + if not news_qs.exists(): + raise serializers.ValidationError({'detail': _('News not found')}) + if not image_qs.exists(): + raise serializers.ValidationError({'detail': _('Image not found')}) + + news = news_qs.first() + image = image_qs.first() + + if news.news_gallery.filter(image=image).exists(): + raise serializers.ValidationError({'detail': _('Image is already added')}) + + attrs['news'] = news + attrs['image'] = image + + return attrs diff --git a/apps/news/views.py b/apps/news/views.py index d398f8f8..f9d94742 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -114,7 +114,7 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIView): """Resource for returning gallery for news for back-office users.""" - serializer_class = ImageSerializer + serializer_class = serializers.NewsImageSerializer def get_object(self): """Override get_object method.""" @@ -128,7 +128,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIVie def get_queryset(self): """Override get_queryset method.""" - return self.get_object().gallery.all() + return self.get_object().gallery.original_images() class NewsBackOfficeRUDView(NewsBackOfficeMixinView, From 296a09165d4a78c4dfb13c726a5fd94b58029fe2 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 2 Oct 2019 18:09:29 +0300 Subject: [PATCH 008/121] added migration --- .../migrations/0003_auto_20191001_0647.py | 20 -------------- .../migrations/0003_auto_20191002_1456.py | 26 +++++++++++++++++++ apps/gallery/models.py | 6 +---- 3 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 apps/gallery/migrations/0003_auto_20191001_0647.py create mode 100644 apps/gallery/migrations/0003_auto_20191002_1456.py diff --git a/apps/gallery/migrations/0003_auto_20191001_0647.py b/apps/gallery/migrations/0003_auto_20191001_0647.py deleted file mode 100644 index 4defed2b..00000000 --- a/apps/gallery/migrations/0003_auto_20191001_0647.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-01 06:47 - -from django.db import migrations -import sorl.thumbnail.fields -import utils.methods - - -class Migration(migrations.Migration): - - dependencies = [ - ('gallery', '0002_auto_20190930_0714'), - ] - - operations = [ - migrations.AlterField( - model_name='image', - name='image', - field=sorl.thumbnail.fields.ImageField(upload_to=utils.methods.image_path, verbose_name='image file'), - ), - ] diff --git a/apps/gallery/migrations/0003_auto_20191002_1456.py b/apps/gallery/migrations/0003_auto_20191002_1456.py new file mode 100644 index 00000000..4ef3d1d0 --- /dev/null +++ b/apps/gallery/migrations/0003_auto_20191002_1456.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.4 on 2019-10-02 14:56 + +from django.db import migrations, models +import django.db.models.deletion +import sorl.thumbnail.fields +import utils.methods + + +class Migration(migrations.Migration): + + dependencies = [ + ('gallery', '0002_auto_20190930_0714'), + ] + + operations = [ + migrations.AlterField( + model_name='image', + name='image', + field=sorl.thumbnail.fields.ImageField(upload_to=utils.methods.image_path, verbose_name='image file'), + ), + migrations.AlterField( + model_name='image', + name='parent', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='children', to='gallery.Image', verbose_name='parent image'), + ), + ] diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 33d2debc..3f60e9af 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -30,7 +30,7 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): verbose_name=_('image file')) parent = models.ForeignKey('self', blank=True, null=True, default=None, - related_name='parent_image', + related_name='children', on_delete=models.SET_DEFAULT, verbose_name=_('parent image')) orientation = models.PositiveSmallIntegerField(choices=ORIENTATIONS, @@ -55,7 +55,3 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): tasks.delete_image_from_remote_storage.delay(self.id, delete_original) else: tasks.delete_image_from_remote_storage(self.id, delete_original) - - @property - def children(self): - return self.parent_image.filter(parent=self) From 4890e00b95a460ca958335bfc388b63e98ed41e7 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 11:50:29 +0300 Subject: [PATCH 009/121] gm-148: refactored --- apps/account/tasks.py | 6 +- apps/authorization/tasks.py | 5 +- ...002_1456.py => 0003_auto_20191003_1228.py} | 14 ++- apps/gallery/models.py | 35 ++++---- apps/gallery/serializers.py | 1 - apps/gallery/tasks.py | 12 +-- apps/gallery/urls.py | 3 +- apps/gallery/views.py | 31 ++++--- apps/news/models.py | 10 +-- apps/news/serializers.py | 88 ++++++++++++------- apps/news/views.py | 17 ++-- apps/utils/models.py | 24 ++--- apps/utils/querysets.py | 8 +- project/settings/local.py | 5 +- 14 files changed, 146 insertions(+), 113 deletions(-) rename apps/gallery/migrations/{0003_auto_20191002_1456.py => 0003_auto_20191003_1228.py} (57%) diff --git a/apps/account/tasks.py b/apps/account/tasks.py index 03a231b3..13c6f594 100644 --- a/apps/account/tasks.py +++ b/apps/account/tasks.py @@ -18,7 +18,7 @@ def send_reset_password_email(user_id, country_code): user.send_email(subject=_('Password resetting'), message=user.reset_password_template(country_code)) except: - logger.error(f'METHOD_NAME: {send_reset_password_email.__name__}\n' + logger.error(f'TASK_NAME: {send_reset_password_email.__name__}\n' f'DETAIL: Exception occurred for reset password: ' f'{user_id}') @@ -31,7 +31,7 @@ def confirm_new_email_address(user_id, country_code): user.send_email(subject=_('Validate new email address'), message=user.confirm_email_template(country_code)) except: - logger.error(f'METHOD_NAME: {confirm_new_email_address.__name__}\n' + logger.error(f'TASK_NAME: {confirm_new_email_address.__name__}\n' f'DETAIL: Exception occurred for user: {user_id}') @@ -43,5 +43,5 @@ def change_email_address(user_id, country_code): user.send_email(subject=_('Validate new email address'), message=user.change_email_template(country_code)) except: - logger.error(f'METHOD_NAME: {change_email_address.__name__}\n' + logger.error(f'TASK_NAME: {change_email_address.__name__}\n' f'DETAIL: Exception occurred for user: {user_id}') diff --git a/apps/authorization/tasks.py b/apps/authorization/tasks.py index 9947c2a3..4df94bdc 100644 --- a/apps/authorization/tasks.py +++ b/apps/authorization/tasks.py @@ -1,7 +1,8 @@ """Authorization app celery tasks.""" import logging -from django.utils.translation import gettext_lazy as _ + from celery import shared_task +from django.utils.translation import gettext_lazy as _ from account import models as account_models @@ -17,5 +18,5 @@ def send_confirm_email(user_id, country_code): obj.send_email(subject=_('Email confirmation'), message=obj.confirm_email_template(country_code)) except: - logger.error(f'METHOD_NAME: {send_confirm_email.__name__}\n' + logger.error(f'TASK_NAME: {send_confirm_email.__name__}\n' f'DETAIL: Exception occurred for user: {user_id}') diff --git a/apps/gallery/migrations/0003_auto_20191002_1456.py b/apps/gallery/migrations/0003_auto_20191003_1228.py similarity index 57% rename from apps/gallery/migrations/0003_auto_20191002_1456.py rename to apps/gallery/migrations/0003_auto_20191003_1228.py index 4ef3d1d0..4d054a29 100644 --- a/apps/gallery/migrations/0003_auto_20191002_1456.py +++ b/apps/gallery/migrations/0003_auto_20191003_1228.py @@ -1,7 +1,6 @@ -# Generated by Django 2.2.4 on 2019-10-02 14:56 +# Generated by Django 2.2.4 on 2019-10-03 12:28 -from django.db import migrations, models -import django.db.models.deletion +from django.db import migrations import sorl.thumbnail.fields import utils.methods @@ -13,14 +12,13 @@ class Migration(migrations.Migration): ] operations = [ + migrations.RemoveField( + model_name='image', + name='parent', + ), migrations.AlterField( model_name='image', name='image', field=sorl.thumbnail.fields.ImageField(upload_to=utils.methods.image_path, verbose_name='image file'), ), - migrations.AlterField( - model_name='image', - name='parent', - field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='children', to='gallery.Image', verbose_name='parent image'), - ), ] diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 3f60e9af..1388f910 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -1,20 +1,15 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from sorl.thumbnail import delete from sorl.thumbnail.fields import ImageField as SORLImageField from utils.methods import image_path -from django.conf import settings -from . import tasks from utils.models import ProjectBaseMixin, SORLImageMixin, PlatformMixin class ImageQuerySet(models.QuerySet): """QuerySet for model Image.""" - def original_images(self): - """Return QuerySet with original images.""" - return self.filter(parent__isnull=True) - class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): """Image model.""" @@ -28,11 +23,6 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): image = SORLImageField(upload_to=image_path, verbose_name=_('image file')) - parent = models.ForeignKey('self', - blank=True, null=True, default=None, - related_name='children', - on_delete=models.SET_DEFAULT, - verbose_name=_('parent image')) orientation = models.PositiveSmallIntegerField(choices=ORIENTATIONS, blank=True, null=True, default=None, verbose_name=_('image orientation')) @@ -49,9 +39,20 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): """String representation""" return str(self.id) - def delete_from_remote_storage(self, delete_original: bool = True): - """Delete from remote storage""" - if settings.USE_CELERY: - tasks.delete_image_from_remote_storage.delay(self.id, delete_original) - else: - tasks.delete_image_from_remote_storage(self.id, delete_original) + def delete_image(self, completely: bool = True): + """ + Deletes an instance and crops of instance from media storage. + :param completely: if set to False then removed only crop neither original image. + """ + try: + # Delete from remote storage + delete(file_=self.image.file, delete_file=completely) + except FileNotFoundError: + pass + finally: + if completely: + # Delete an instance of image + super().delete() + + + diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index 08ca4a0f..e817cbd8 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -22,7 +22,6 @@ class ImageSerializer(serializers.ModelSerializer): 'id', 'file', 'url', - 'parent', 'orientation', 'orientation_display', 'title', diff --git a/apps/gallery/tasks.py b/apps/gallery/tasks.py index 8f3f5453..1a64d297 100644 --- a/apps/gallery/tasks.py +++ b/apps/gallery/tasks.py @@ -2,7 +2,6 @@ import logging from celery import shared_task -from sorl.thumbnail import delete from . import models @@ -11,17 +10,14 @@ logger = logging.getLogger(__name__) @shared_task -def delete_image_from_remote_storage(image_id, delete_original=True): +def delete_image(image_id: int, completely: bool = True): """Delete an image from remote storage.""" image_qs = models.Image.objects.filter(id=image_id) if image_qs.exists(): try: image = image_qs.first() - # Delete from remote storage - delete(file_=image.image.file, delete_file=delete_original) - # Delete an instance of image - image.delete() + image.delete_image(completely=completely) except: - logger.error(f'METHOD_NAME: delete_image_from_remote_storage\n' + logger.error(f'TASK_NAME: delete_image\n' f'DETAIL: Exception occurred while deleting an image ' - f'from remote storage: image_id - {image_id}') + f'and related crops from remote storage: image_id - {image_id}') diff --git a/apps/gallery/urls.py b/apps/gallery/urls.py index 2faa95e4..8258092c 100644 --- a/apps/gallery/urls.py +++ b/apps/gallery/urls.py @@ -6,5 +6,6 @@ from . import views app_name = 'gallery' urlpatterns = [ - path('upload/', views.ImageBaseView.as_view(), name='upload-image'), + path('', views.ImageListCreateView.as_view(), name='list-create-image'), + path('/', views.ImageRetrieveDestroyView.as_view(), name='retrieve-destroy-image'), ] diff --git a/apps/gallery/views.py b/apps/gallery/views.py index b35df131..2b155035 100644 --- a/apps/gallery/views.py +++ b/apps/gallery/views.py @@ -1,19 +1,30 @@ -from rest_framework import generics +from django.conf import settings +from django.db.transaction import on_commit +from rest_framework import generics, status +from rest_framework.response import Response -from . import models, serializers +from . import tasks, models, serializers -class ImageBaseView(generics.CreateAPIView): - """Upload image to gallery.""" +class ImageBaseView(generics.GenericAPIView): + """Base Image view.""" model = models.Image queryset = models.Image.objects.all() serializer_class = serializers.ImageSerializer -class NewsImageListView(ImageBaseView, generics.ListAPIView): - """Return list of uploaded images for news object.""" +class ImageListCreateView(ImageBaseView, generics.ListCreateAPIView): + """List/Create Image view.""" - def get_queryset(self): - """Override get_queryset method.""" - qs = super(NewsImageListView, self).get_queryset() - return qs.filter(news_gallery__news=self.kwargs.get('news_id')) + +class ImageRetrieveDestroyView(ImageBaseView, generics.RetrieveDestroyAPIView): + """Destroy view for model Image""" + + def delete(self, request, *args, **kwargs): + """Override destroy view""" + instance = self.get_object() + if settings.USE_CELERY: + on_commit(lambda: tasks.delete_image.delay(image_id=instance.id)) + else: + on_commit(lambda: tasks.delete_image(image_id=instance.id)) + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apps/news/models.py b/apps/news/models.py index 9022a6a2..2e79994a 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -1,11 +1,11 @@ """News app models.""" -from django.db import models from django.contrib.contenttypes import fields as generic +from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework.reverse import reverse + from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin -from random import sample as random_sample class NewsType(models.Model): @@ -149,10 +149,6 @@ class News(BaseAttributes, TranslatedFieldsMixin): def web_url(self): return reverse('web:news:rud', kwargs={'slug': self.slug}) - @property - def original_images(self): - return self.gallery.original_images() - class NewsGalleryQuerySet(models.QuerySet): """QuerySet for model News""" @@ -169,8 +165,6 @@ class NewsGallery(models.Model): on_delete=models.SET_NULL, verbose_name=_('gallery')) - objects = NewsGalleryQuerySet.as_manager() - class Meta: """NewsGallery meta class.""" verbose_name = _('news gallery') diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 08dfce5e..0ea5606f 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -4,7 +4,6 @@ from rest_framework import serializers from account.serializers.common import UserBaseSerializer from gallery.models import Image -from gallery.serializers import ImageSerializer from location import models as location_models from location.serializers import CountrySimpleSerializer from main.serializers import MetaDataContentSerializer @@ -12,12 +11,61 @@ from news import models from utils.serializers import TranslatedField, ProjectModelSerializer -class NewsCropImageSerializer(ImageSerializer): +class CropImageSerializer(serializers.Serializer): + """Serializer for crop images for News object.""" + preview_url = serializers.SerializerMethodField() + promo_horizontal_web_url = serializers.SerializerMethodField() + promo_horizontal_mobile_url = serializers.SerializerMethodField() + tile_horizontal_web_url = serializers.SerializerMethodField() + tile_horizontal_mobile_url = serializers.SerializerMethodField() + tile_vertical_web_url = serializers.SerializerMethodField() + highlight_vertical_web_url = serializers.SerializerMethodField() + editor_web_url = serializers.SerializerMethodField() + editor_mobile_url = serializers.SerializerMethodField() + + def get_preview_url(self, obj): + """Get crop preview.""" + return obj.instance.get_image_url('news_preview') + + def get_promo_horizontal_web_url(self, obj): + """Get crop promo_horizontal_web.""" + return obj.instance.get_image_url('news_promo_horizontal_web') + + def get_promo_horizontal_mobile_url(self, obj): + """Get crop promo_horizontal_mobile.""" + return obj.instance.get_image_url('news_promo_horizontal_mobile') + + def get_tile_horizontal_web_url(self, obj): + """Get crop tile_horizontal_web.""" + return obj.instance.get_image_url('news_tile_horizontal_web') + + def get_tile_horizontal_mobile_url(self, obj): + """Get crop tile_horizontal_mobile.""" + return obj.instance.get_image_url('news_tile_horizontal_mobile') + + def get_tile_vertical_web_url(self, obj): + """Get crop tile_vertical_web.""" + return obj.instance.get_image_url('news_tile_vertical_web') + + def get_highlight_vertical_web_url(self, obj): + """Get crop highlight_vertical_web.""" + return obj.instance.get_image_url('news_highlight_vertical_web') + + def get_editor_web_url(self, obj): + """Get crop editor_web.""" + return obj.instance.get_image_url('news_editor_web') + + def get_editor_mobile_url(self, obj): + """Get crop editor_mobile.""" + return obj.instance.get_image_url('news_editor_mobile') + + +class NewsImageSerializer(serializers.ModelSerializer): """Serializer for returning crop images of news image.""" orientation_display = serializers.CharField(source='get_orientation_display', read_only=True) - web_url = serializers.SerializerMethodField() - mobile_url = serializers.SerializerMethodField() + original_url = serializers.URLField(source='image.url') + auto_crop_images = CropImageSerializer(source='image', allow_null=True) class Meta: model = Image @@ -25,34 +73,8 @@ class NewsCropImageSerializer(ImageSerializer): 'id', 'title', 'orientation_display', - 'web_url', - 'mobile_url', - ] - extra_kwargs = { - 'orientation': {'write_only': True} - } - - def get_web_url(self, obj): - """Return URL of cropped image by thumbnail.""" - return obj.get_image_url('news_promo_horizontal_web') - - def get_mobile_url(self, obj): - """Return URL of cropped image by thumbnail.""" - return obj.get_image_url('news_promo_horizontal_mobile') - - -class NewsImageSerializer(ImageSerializer): - """News images""" - url = serializers.URLField(source='image.url', read_only=True) - crops = NewsCropImageSerializer(source='children', allow_null=True, many=True) - - class Meta: - model = Image - fields = [ - 'id', - 'title', - 'url', - 'crops', + 'original_url', + 'auto_crop_images', ] extra_kwargs = { 'orientation': {'write_only': True} @@ -79,7 +101,7 @@ class NewsBaseSerializer(ProjectModelSerializer): # related fields news_type = NewsTypeSerializer(read_only=True) tags = MetaDataContentSerializer(read_only=True, many=True) - gallery = NewsImageSerializer(source='original_images', read_only=True, many=True) + gallery = NewsImageSerializer(read_only=True, many=True) class Meta: """Meta class.""" diff --git a/apps/news/views.py b/apps/news/views.py index f9d94742..9fee70e5 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,10 +1,12 @@ """News app views.""" +from django.conf import settings +from django.db.transaction import on_commit from django.shortcuts import get_object_or_404 from rest_framework import generics, permissions, status from rest_framework.response import Response +from gallery.tasks import delete_image from news import filters, models, serializers -from gallery.serializers import ImageSerializer class NewsMixinView: @@ -102,13 +104,14 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, def destroy(self, request, *args, **kwargs): """Override destroy method.""" gallery_obj = self.get_object() - image_obj = gallery_obj.image - + if settings.USE_CELERY: + on_commit(lambda: delete_image.delay(image_id=gallery_obj.image.id, + completely=False)) + else: + on_commit(lambda: delete_image(image_id=gallery_obj.image.id, + completely=False)) # Delete an instances of NewsGallery model gallery_obj.delete() - # Delete an instance of Image model - image_obj.delete_from_remote_storage() - return Response(status=status.HTTP_204_NO_CONTENT) @@ -128,7 +131,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, generics.ListAPIVie def get_queryset(self): """Override get_queryset method.""" - return self.get_object().gallery.original_images() + return self.get_object().gallery.all() class NewsBackOfficeRUDView(NewsBackOfficeMixinView, diff --git a/apps/utils/models.py b/apps/utils/models.py index 299013b7..5ff52e22 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -1,6 +1,8 @@ """Utils app models.""" import logging from os.path import exists + +from django.conf import settings from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.gis.db import models from django.contrib.postgres.fields import JSONField @@ -9,12 +11,11 @@ from django.utils import timezone from django.utils.html import mark_safe from django.utils.translation import ugettext_lazy as _, get_language from easy_thumbnails.fields import ThumbnailerImageField +from sorl.thumbnail import get_thumbnail +from sorl.thumbnail.fields import ImageField as SORLImageField + from utils.methods import image_path, svg_image_path from utils.validators import svg_image_validator -from sorl.thumbnail.fields import ImageField as SORLImageField -from sorl.thumbnail import get_thumbnail -from django.conf import settings -from utils.methods import image_url_valid logger = logging.getLogger(__name__) @@ -123,7 +124,7 @@ class OAuthProjectMixin: def get_source(self): """Method to get of platform""" - return NotImplemented + return NotImplementedError class BaseAttributes(ProjectBaseMixin): @@ -193,15 +194,18 @@ class SORLImageMixin(models.Model): """Meta class.""" abstract = True - def get_image(self, thumbnail_key=None): + def get_image(self, thumbnail_key: str): """Get thumbnail image file.""" if thumbnail_key in settings.SORL_THUMBNAIL_ALIASES: - return get_thumbnail(file_=self.image, - **settings.SORL_THUMBNAIL_ALIASES[thumbnail_key]) + return get_thumbnail( + file_=self.image, + **settings.SORL_THUMBNAIL_ALIASES[thumbnail_key]) - def get_image_url(self, thumbnail_key=None): + def get_image_url(self, thumbnail_key: str): """Get image thumbnail url.""" - return self.get_image(thumbnail_key).url + crop_image = self.get_image(thumbnail_key) + if hasattr(crop_image, 'url'): + return self.get_image(thumbnail_key).url def image_tag(self): """Admin preview tag.""" diff --git a/apps/utils/querysets.py b/apps/utils/querysets.py index bf2816f2..45798fbb 100644 --- a/apps/utils/querysets.py +++ b/apps/utils/querysets.py @@ -1,8 +1,10 @@ """Utils QuerySet Mixins""" -from django.db import models -from django.db.models import Q, Sum, F from functools import reduce from operator import add + +from django.db import models +from django.db.models import Q, F + from utils.methods import get_contenttype @@ -50,7 +52,7 @@ class RelatedObjectsCountMixin(models.QuerySet): def filter_all_related_gt(self, count): """Queryset filter by all related objects count""" - exp =reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()]) + exp = reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()]) return self._annotate_related_objects_count()\ .annotate(all_related_count=exp)\ .filter(all_related_count__gt=count) diff --git a/project/settings/local.py b/project/settings/local.py index d842894c..15fbbb80 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -1,6 +1,7 @@ """Local settings.""" from .base import * import sys +from .amazon_s3 import * ALLOWED_HOSTS = ['*', ] @@ -23,8 +24,8 @@ CELERY_BROKER_URL = BROKER_URL # MEDIA -MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' -MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) +# MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' +# MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) # LOGGING From b2f900d15dcc728f766fde2f2c2528db6ff33a53 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 13:25:42 +0300 Subject: [PATCH 010/121] gm-148: refactored --- apps/gallery/admin.py | 7 ++++++- apps/gallery/models.py | 2 +- apps/main/models.py | 1 - apps/utils/models.py | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/gallery/admin.py b/apps/gallery/admin.py index fc20b0ee..f5aa0194 100644 --- a/apps/gallery/admin.py +++ b/apps/gallery/admin.py @@ -5,4 +5,9 @@ from gallery.models import Image @admin.register(Image) class ImageModelAdmin(admin.ModelAdmin): - """Image model admin""" + """Image model admin.""" + list_display = ['id', 'title', 'image_tag', 'orientation_display'] + + def orientation_display(self, obj): + """Get image orientation name.""" + return obj.get_orientation_display() if obj.orientation else None diff --git a/apps/gallery/models.py b/apps/gallery/models.py index 1388f910..bf3870cb 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -37,7 +37,7 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): def __str__(self): """String representation""" - return str(self.id) + return f'{self.title}' def delete_image(self, completely: bool = True): """ diff --git a/apps/main/models.py b/apps/main/models.py index fa6cf7d1..df04fd8a 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -353,7 +353,6 @@ class Carousel(models.Model): return self.content_object.establishment_type.name_translated - class Page(models.Model): """Page model.""" diff --git a/apps/utils/models.py b/apps/utils/models.py index 5ff52e22..fbfd186d 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -210,7 +210,7 @@ class SORLImageMixin(models.Model): def image_tag(self): """Admin preview tag.""" if self.image: - return mark_safe('' % self.image.url) + return mark_safe(f'') else: return None From 91790bbc396d33384ed0dc5c0cbc39a1af215ef5 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 14:30:34 +0300 Subject: [PATCH 011/121] refactored --- apps/gallery/admin.py | 2 +- apps/search_indexes/documents/news.py | 2 -- apps/utils/models.py | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/gallery/admin.py b/apps/gallery/admin.py index f5aa0194..e325a3ed 100644 --- a/apps/gallery/admin.py +++ b/apps/gallery/admin.py @@ -6,7 +6,7 @@ from gallery.models import Image @admin.register(Image) class ImageModelAdmin(admin.ModelAdmin): """Image model admin.""" - list_display = ['id', 'title', 'image_tag', 'orientation_display'] + list_display = ['id', 'title', 'orientation_display', 'image_tag', ] def orientation_display(self, obj): """Get image orientation name.""" diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index 6e0974d8..872d955c 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -45,8 +45,6 @@ class NewsDocument(Document): 'slug', 'state', 'is_highlighted', - 'image_url', - 'preview_image_url', 'template', ) related_models = [models.NewsType] diff --git a/apps/utils/models.py b/apps/utils/models.py index fbfd186d..fb1de17c 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -210,7 +210,7 @@ class SORLImageMixin(models.Model): def image_tag(self): """Admin preview tag.""" if self.image: - return mark_safe(f'') + return mark_safe(f'') else: return None From ee93ad4a44d2bd3f13c72f97106d72c1618cb656 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 14:31:24 +0300 Subject: [PATCH 012/121] fix settings --- project/settings/local.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/project/settings/local.py b/project/settings/local.py index 15fbbb80..8275a1a0 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -1,7 +1,7 @@ """Local settings.""" from .base import * import sys -from .amazon_s3 import * +# from .amazon_s3 import * ALLOWED_HOSTS = ['*', ] @@ -24,8 +24,8 @@ CELERY_BROKER_URL = BROKER_URL # MEDIA -# MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' -# MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) +MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' +MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) # LOGGING From 9a36b52eecc79faf95b148e7fe7a24f685cfb890 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 15:26:29 +0300 Subject: [PATCH 013/121] fix migrations --- .../migrations/0014_auto_20190926_1156.py | 21 ------------------- apps/news/migrations/0015_newsgallery.py | 1 - apps/news/models.py | 4 ++++ .../migrations/0002_auto_20191004_1217.py | 17 +++++++++++++++ 4 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 apps/news/migrations/0014_auto_20190926_1156.py create mode 100644 apps/rating/migrations/0002_auto_20191004_1217.py diff --git a/apps/news/migrations/0014_auto_20190926_1156.py b/apps/news/migrations/0014_auto_20190926_1156.py deleted file mode 100644 index d16f0ad3..00000000 --- a/apps/news/migrations/0014_auto_20190926_1156.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-26 11:56 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('news', '0013_auto_20190924_0806'), - ] - - operations = [ - migrations.RemoveField( - model_name='news', - name='image_url', - ), - migrations.RemoveField( - model_name='news', - name='preview_image_url', - ), - ] diff --git a/apps/news/migrations/0015_newsgallery.py b/apps/news/migrations/0015_newsgallery.py index a8422dab..8f81b4f8 100644 --- a/apps/news/migrations/0015_newsgallery.py +++ b/apps/news/migrations/0015_newsgallery.py @@ -8,7 +8,6 @@ class Migration(migrations.Migration): dependencies = [ ('gallery', '0002_auto_20190930_0714'), - ('news', '0014_auto_20190926_1156'), ] operations = [ diff --git a/apps/news/models.py b/apps/news/models.py index a87a34b4..4081a3a2 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -122,6 +122,10 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys + image_url = models.URLField(blank=True, null=True, default=None, + verbose_name=_('Image URL path')) + preview_image_url = models.URLField(blank=True, null=True, default=None, + verbose_name=_('Preview image URL path')) template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), diff --git a/apps/rating/migrations/0002_auto_20191004_1217.py b/apps/rating/migrations/0002_auto_20191004_1217.py new file mode 100644 index 00000000..de37a06e --- /dev/null +++ b/apps/rating/migrations/0002_auto_20191004_1217.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.4 on 2019-10-04 12:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('rating', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='rating', + options={}, + ), + ] From d60d16603168de2166699b124de75ba205792a19 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 17:01:40 +0300 Subject: [PATCH 014/121] added merge migration --- apps/rating/migrations/0003_merge_20191004_1401.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/rating/migrations/0003_merge_20191004_1401.py diff --git a/apps/rating/migrations/0003_merge_20191004_1401.py b/apps/rating/migrations/0003_merge_20191004_1401.py new file mode 100644 index 00000000..f04cc893 --- /dev/null +++ b/apps/rating/migrations/0003_merge_20191004_1401.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-04 14:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('rating', '0002_auto_20191004_1217'), + ('rating', '0002_auto_20191004_0928'), + ] + + operations = [ + ] From 90ab1a2ab98b8b92922b7279450d447c83def1e6 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 4 Oct 2019 18:02:51 +0300 Subject: [PATCH 015/121] Revert "fix migrations" This reverts commit 9a36b52e --- .../migrations/0014_auto_20190926_1156.py | 21 +++++++++++++++++++ apps/news/migrations/0015_newsgallery.py | 1 + apps/news/models.py | 4 ---- .../migrations/0002_auto_20191004_1217.py | 17 --------------- 4 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 apps/news/migrations/0014_auto_20190926_1156.py delete mode 100644 apps/rating/migrations/0002_auto_20191004_1217.py diff --git a/apps/news/migrations/0014_auto_20190926_1156.py b/apps/news/migrations/0014_auto_20190926_1156.py new file mode 100644 index 00000000..d16f0ad3 --- /dev/null +++ b/apps/news/migrations/0014_auto_20190926_1156.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.4 on 2019-09-26 11:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0013_auto_20190924_0806'), + ] + + operations = [ + migrations.RemoveField( + model_name='news', + name='image_url', + ), + migrations.RemoveField( + model_name='news', + name='preview_image_url', + ), + ] diff --git a/apps/news/migrations/0015_newsgallery.py b/apps/news/migrations/0015_newsgallery.py index 8f81b4f8..a8422dab 100644 --- a/apps/news/migrations/0015_newsgallery.py +++ b/apps/news/migrations/0015_newsgallery.py @@ -8,6 +8,7 @@ class Migration(migrations.Migration): dependencies = [ ('gallery', '0002_auto_20190930_0714'), + ('news', '0014_auto_20190926_1156'), ] operations = [ diff --git a/apps/news/models.py b/apps/news/models.py index 4081a3a2..a87a34b4 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -122,10 +122,6 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys - image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Image URL path')) - preview_image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Preview image URL path')) template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), diff --git a/apps/rating/migrations/0002_auto_20191004_1217.py b/apps/rating/migrations/0002_auto_20191004_1217.py deleted file mode 100644 index de37a06e..00000000 --- a/apps/rating/migrations/0002_auto_20191004_1217.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-04 12:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('rating', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='rating', - options={}, - ), - ] From 15ef2295caeebc9ebde351110a232cc8198c1b4f Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 7 Oct 2019 09:55:24 +0300 Subject: [PATCH 016/121] migration fix --- .../migrations/0014_auto_20190926_1156.py | 21 ------------------- apps/news/migrations/0015_newsgallery.py | 1 - apps/news/models.py | 4 ++++ .../migrations/0003_merge_20191004_1401.py | 14 ------------- 4 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 apps/news/migrations/0014_auto_20190926_1156.py delete mode 100644 apps/rating/migrations/0003_merge_20191004_1401.py diff --git a/apps/news/migrations/0014_auto_20190926_1156.py b/apps/news/migrations/0014_auto_20190926_1156.py deleted file mode 100644 index d16f0ad3..00000000 --- a/apps/news/migrations/0014_auto_20190926_1156.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-26 11:56 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('news', '0013_auto_20190924_0806'), - ] - - operations = [ - migrations.RemoveField( - model_name='news', - name='image_url', - ), - migrations.RemoveField( - model_name='news', - name='preview_image_url', - ), - ] diff --git a/apps/news/migrations/0015_newsgallery.py b/apps/news/migrations/0015_newsgallery.py index a8422dab..8f81b4f8 100644 --- a/apps/news/migrations/0015_newsgallery.py +++ b/apps/news/migrations/0015_newsgallery.py @@ -8,7 +8,6 @@ class Migration(migrations.Migration): dependencies = [ ('gallery', '0002_auto_20190930_0714'), - ('news', '0014_auto_20190926_1156'), ] operations = [ diff --git a/apps/news/models.py b/apps/news/models.py index a87a34b4..4081a3a2 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -122,6 +122,10 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Is highlighted')) # TODO: metadata_keys - описание ключей для динамического построения полей метаданных # TODO: metadata_values - Описание значений для динамических полей из MetadataKeys + image_url = models.URLField(blank=True, null=True, default=None, + verbose_name=_('Image URL path')) + preview_image_url = models.URLField(blank=True, null=True, default=None, + verbose_name=_('Preview image URL path')) template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), diff --git a/apps/rating/migrations/0003_merge_20191004_1401.py b/apps/rating/migrations/0003_merge_20191004_1401.py deleted file mode 100644 index f04cc893..00000000 --- a/apps/rating/migrations/0003_merge_20191004_1401.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-04 14:01 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('rating', '0002_auto_20191004_1217'), - ('rating', '0002_auto_20191004_0928'), - ] - - operations = [ - ] From 8ac5a1970f50be997e747df3e948cf18859cf808 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 7 Oct 2019 10:51:08 +0300 Subject: [PATCH 017/121] fixed carousel --- apps/establishment/models.py | 12 ++++++------ apps/news/serializers.py | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index fff6f45f..14debdc4 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1,6 +1,7 @@ """Establishment models.""" from functools import reduce +import elasticsearch_dsl from django.conf import settings from django.contrib.contenttypes import fields as generic from django.contrib.gis.db.models.functions import Distance @@ -11,12 +12,11 @@ from django.db import models from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from elasticsearch_dsl import Q from phonenumber_field.modelfields import PhoneNumberField from collection.models import Collection -from main.models import Award, MetaDataContent from location.models import Address +from main.models import Award, MetaDataContent from review.models import Review from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, TranslatedFieldsMixin, BaseAttributes) @@ -103,8 +103,8 @@ class EstablishmentQuerySet(models.QuerySet): """Search text via ElasticSearch.""" from search_indexes.documents import EstablishmentDocument search = EstablishmentDocument.search().filter( - Q('match', name=value) | - Q('match', **{f'description.{locale}': value}) + elasticsearch_dsl.Q('match', name=value) | + elasticsearch_dsl.Q('match', **{f'description.{locale}': value}) ).execute() ids = [result.meta.id for result in search] return self.filter(id__in=ids) @@ -375,8 +375,8 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): @property def the_most_recent_award(self): - return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)).latest( - field_name='vintage_year') + return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)) \ + .latest(field_name='vintage_year') class Position(BaseAttributes, TranslatedFieldsMixin): diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 0ea5606f..6c4db75d 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -116,6 +116,8 @@ class NewsBaseSerializer(ProjectModelSerializer): 'tags', 'slug', 'gallery', + 'image_url', + 'preview_image_url', ) From 93894db7aaedff07e4b60bba57948526201998db Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 9 Oct 2019 15:20:51 +0300 Subject: [PATCH 018/121] remove duplicate migration --- .../migrations/0003_auto_20191002_0729.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 apps/timetable/migrations/0003_auto_20191002_0729.py diff --git a/apps/timetable/migrations/0003_auto_20191002_0729.py b/apps/timetable/migrations/0003_auto_20191002_0729.py deleted file mode 100644 index 16196f74..00000000 --- a/apps/timetable/migrations/0003_auto_20191002_0729.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-02 07:29 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('timetable', '0002_auto_20190919_1124'), - ] - - operations = [ - migrations.AlterModelOptions( - name='timetable', - options={'ordering': ['weekday'], 'verbose_name': 'Timetable', 'verbose_name_plural': 'Timetables'}, - ), - ] From 05d3e3ff60573f8ca5412ec6afdc3494b19ee575 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 9 Oct 2019 15:33:05 +0300 Subject: [PATCH 019/121] fix migrations --- .../migrations/0003_auto_20191002_0729.py | 17 ----------------- .../migrations/0003_auto_20191003_0943.py | 17 ----------------- 2 files changed, 34 deletions(-) delete mode 100644 apps/timetable/migrations/0003_auto_20191002_0729.py delete mode 100644 apps/timetable/migrations/0003_auto_20191003_0943.py diff --git a/apps/timetable/migrations/0003_auto_20191002_0729.py b/apps/timetable/migrations/0003_auto_20191002_0729.py deleted file mode 100644 index 16196f74..00000000 --- a/apps/timetable/migrations/0003_auto_20191002_0729.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-02 07:29 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('timetable', '0002_auto_20190919_1124'), - ] - - operations = [ - migrations.AlterModelOptions( - name='timetable', - options={'ordering': ['weekday'], 'verbose_name': 'Timetable', 'verbose_name_plural': 'Timetables'}, - ), - ] diff --git a/apps/timetable/migrations/0003_auto_20191003_0943.py b/apps/timetable/migrations/0003_auto_20191003_0943.py deleted file mode 100644 index 4c5a22b4..00000000 --- a/apps/timetable/migrations/0003_auto_20191003_0943.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-03 09:43 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('timetable', '0002_auto_20190919_1124'), - ] - - operations = [ - migrations.AlterModelOptions( - name='timetable', - options={'ordering': ['weekday'], 'verbose_name': 'Timetable', 'verbose_name_plural': 'Timetables'}, - ), - ] From 3f9dea30ffed35244f1370529da184ea46d9f1c0 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 9 Oct 2019 15:54:04 +0300 Subject: [PATCH 020/121] Revert "remove duplicate migration" This reverts commit 93894db7 --- .../migrations/0003_auto_20191002_0729.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 apps/timetable/migrations/0003_auto_20191002_0729.py diff --git a/apps/timetable/migrations/0003_auto_20191002_0729.py b/apps/timetable/migrations/0003_auto_20191002_0729.py new file mode 100644 index 00000000..16196f74 --- /dev/null +++ b/apps/timetable/migrations/0003_auto_20191002_0729.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.4 on 2019-10-02 07:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('timetable', '0002_auto_20190919_1124'), + ] + + operations = [ + migrations.AlterModelOptions( + name='timetable', + options={'ordering': ['weekday'], 'verbose_name': 'Timetable', 'verbose_name_plural': 'Timetables'}, + ), + ] From 091491e19f3a9ae21a4228c3b25989e0ff895e20 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 9 Oct 2019 15:57:35 +0300 Subject: [PATCH 021/121] small fix --- .../{0003_auto_20191002_0729.py => 0003_auto_20191003_0943.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename apps/timetable/migrations/{0003_auto_20191002_0729.py => 0003_auto_20191003_0943.py} (88%) diff --git a/apps/timetable/migrations/0003_auto_20191002_0729.py b/apps/timetable/migrations/0003_auto_20191003_0943.py similarity index 88% rename from apps/timetable/migrations/0003_auto_20191002_0729.py rename to apps/timetable/migrations/0003_auto_20191003_0943.py index 16196f74..4c5a22b4 100644 --- a/apps/timetable/migrations/0003_auto_20191002_0729.py +++ b/apps/timetable/migrations/0003_auto_20191003_0943.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.4 on 2019-10-02 07:29 +# Generated by Django 2.2.4 on 2019-10-03 09:43 from django.db import migrations From 2d375a51cff58a133764d065923bdc208ec3314c Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 9 Oct 2019 16:00:06 +0300 Subject: [PATCH 022/121] change Celery state for dev settings --- project/settings/development.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/settings/development.py b/project/settings/development.py index 59691818..6c726e25 100644 --- a/project/settings/development.py +++ b/project/settings/development.py @@ -8,7 +8,7 @@ ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0'] SEND_SMS = False SMS_CODE_SHOW = True -USE_CELERY = True +USE_CELERY = False SCHEMA_URI = 'http' DEFAULT_SUBDOMAIN = 'www' From 868a3c1b783a30d74ee89c6c936e91d7e046ae38 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 11 Oct 2019 16:36:34 +0300 Subject: [PATCH 023/121] added merge migration --- .../account/migrations/0011_merge_20191011_1336.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/account/migrations/0011_merge_20191011_1336.py diff --git a/apps/account/migrations/0011_merge_20191011_1336.py b/apps/account/migrations/0011_merge_20191011_1336.py new file mode 100644 index 00000000..6a031068 --- /dev/null +++ b/apps/account/migrations/0011_merge_20191011_1336.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-11 13:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0009_auto_20191002_0648'), + ('account', '0010_user_password_confirmed'), + ] + + operations = [ + ] From 5562a9e7c981aa825aed03c0e1e58f3bb6e7fc32 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Tue, 15 Oct 2019 12:16:23 +0300 Subject: [PATCH 024/121] Fixed establishments/slug/, added filtering by country code --- apps/establishment/views/web.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 8f5d2a26..0d2249b1 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -45,7 +45,9 @@ class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView serializer_class = serializers.EstablishmentDetailSerializer def get_queryset(self): - return super().get_queryset().with_extended_related() + return super().get_queryset() \ + .by_country_code(self.request.country_code) \ + .with_extended_related() class EstablishmentRecentReviewListView(EstablishmentListView): From f86b7d489fd75f7dce0cea6f67c91056aa5e52b7 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Tue, 15 Oct 2019 12:23:13 +0300 Subject: [PATCH 025/121] fix merge --- .../account/migrations/0012_merge_20191015_0912.py | 14 ++++++++++++++ apps/news/migrations/0022_merge_20191015_0912.py | 14 ++++++++++++++ apps/news/views.py | 2 -- project/urls/back.py | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 apps/account/migrations/0012_merge_20191015_0912.py create mode 100644 apps/news/migrations/0022_merge_20191015_0912.py diff --git a/apps/account/migrations/0012_merge_20191015_0912.py b/apps/account/migrations/0012_merge_20191015_0912.py new file mode 100644 index 00000000..558940ce --- /dev/null +++ b/apps/account/migrations/0012_merge_20191015_0912.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-15 09:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0011_merge_20191014_1258'), + ('account', '0011_merge_20191011_1336'), + ] + + operations = [ + ] diff --git a/apps/news/migrations/0022_merge_20191015_0912.py b/apps/news/migrations/0022_merge_20191015_0912.py new file mode 100644 index 00000000..08b5a613 --- /dev/null +++ b/apps/news/migrations/0022_merge_20191015_0912.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-15 09:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0021_auto_20191009_1408'), + ('news', '0021_merge_20191002_1300'), + ] + + operations = [ + ] diff --git a/apps/news/views.py b/apps/news/views.py index 601654b6..5e85b111 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,6 +1,4 @@ """News app views.""" -from django.shortcuts import get_object_or_404 -from rest_framework import generics, permissions from django.conf import settings from django.db.transaction import on_commit from django.shortcuts import get_object_or_404 diff --git a/project/urls/back.py b/project/urls/back.py index 7cfcd038..eb049b9c 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -7,7 +7,7 @@ urlpatterns = [ path('establishments/', include('establishment.urls.back')), path('location/', include('location.urls.back')), path('news/', include('news.urls.back')), - path('tags/', include(('tag.urls', 'tag'), namespace='tag')) + path('tags/', include(('tag.urls', 'tag'), namespace='tag')), path('account/', include('account.urls.back')), path('comment/', include('comment.urls.back')), ] From 9c09f000fb894566f02460300751d40b8baeea36 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Tue, 15 Oct 2019 16:32:47 +0300 Subject: [PATCH 026/121] Password changed notification email --- apps/account/models.py | 9 ++++ apps/account/serializers/common.py | 8 ++++ apps/account/serializers/web.py | 11 ++++- apps/account/tasks.py | 45 +++++++++---------- project/settings/base.py | 3 +- .../account/password_change_email.html | 7 +++ 6 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 project/templates/account/password_change_email.html diff --git a/apps/account/models.py b/apps/account/models.py index 5052969e..aa49dc3b 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -202,6 +202,15 @@ class User(AbstractUser): template_name=settings.RESETTING_TOKEN_TEMPLATE, context=context) + def notify_password_changed_template(self, country_code): + """Get notification email template""" + context = {'contry_code': country_code} + context.update(self.base_template) + return render_to_string( + template_name=settings.NOTIFICATION_PASSWORD_TEMPLATE, + context=context, + ) + def confirm_email_template(self, country_code): """Get confirm email template""" context = {'token': self.confirm_email_token, diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index ad232eae..d68cfe56 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -127,6 +127,14 @@ class ChangePasswordSerializer(serializers.ModelSerializer): except serializers.ValidationError as e: raise serializers.ValidationError({'detail': e.detail}) else: + if settings.USE_CELERY: + tasks.send_password_changed_email( + user_id=self.instance.id, + country_code=self.context.get('request').country_code) + else: + tasks.send_password_changed_email( + user_id=self.instance.id, + country_code=self.context.get('request').country_code) return attrs def update(self, instance, validated_data): diff --git a/apps/account/serializers/web.py b/apps/account/serializers/web.py index 8be73afa..2f04b31f 100644 --- a/apps/account/serializers/web.py +++ b/apps/account/serializers/web.py @@ -1,8 +1,9 @@ """Serializers for account web""" +from django.conf import settings from django.contrib.auth import password_validation as password_validators from rest_framework import serializers -from account import models +from account import models, tasks from utils import exceptions as utils_exceptions from utils.methods import username_validator @@ -68,4 +69,12 @@ class PasswordResetConfirmSerializer(serializers.ModelSerializer): # Update user password from instance instance.set_password(validated_data.get('password')) instance.save() + if settings.USE_CELERY: + tasks.send_password_changed_email( + user_id=instance.id, + country_code=self.context.get('request').country_code) + else: + tasks.send_password_changed_email( + user_id=instance.id, + country_code=self.context.get('request').country_code) return instance diff --git a/apps/account/tasks.py b/apps/account/tasks.py index 03a231b3..d9fa7bb7 100644 --- a/apps/account/tasks.py +++ b/apps/account/tasks.py @@ -1,47 +1,46 @@ """Account app celery tasks.""" +import inspect import logging from celery import shared_task from django.utils.translation import gettext_lazy as _ -from . import models +from account.models import User logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO) logger = logging.getLogger(__name__) +def send_email(user_id: int, subject: str, message_prop: str, country_code: str): + try: + user = User.objects.get(id=user_id) + user.send_email(subject=_(subject), + message=getattr(user, message_prop, lambda _: '')(country_code)) + except: + cur_frame = inspect.currentframe() + cal_frame = inspect.getouterframes(cur_frame, 2) + logger.error(f'METHOD_NAME: {cal_frame[1][3]}\n' + f'DETAIL: Exception occurred for user: {user_id}') + @shared_task def send_reset_password_email(user_id, country_code): """Send email to user for reset password.""" - try: - user = models.User.objects.get(id=user_id) - user.send_email(subject=_('Password resetting'), - message=user.reset_password_template(country_code)) - except: - logger.error(f'METHOD_NAME: {send_reset_password_email.__name__}\n' - f'DETAIL: Exception occurred for reset password: ' - f'{user_id}') + send_email(user_id, 'Password_resetting', 'reset_password_template', country_code) @shared_task def confirm_new_email_address(user_id, country_code): """Send email to user new email.""" - try: - user = models.User.objects.get(id=user_id) - user.send_email(subject=_('Validate new email address'), - message=user.confirm_email_template(country_code)) - except: - logger.error(f'METHOD_NAME: {confirm_new_email_address.__name__}\n' - f'DETAIL: Exception occurred for user: {user_id}') + send_email(user_id, 'Confirm new email address', 'confirm_email_template', country_code) @shared_task def change_email_address(user_id, country_code): """Send email to user new email.""" - try: - user = models.User.objects.get(id=user_id) - user.send_email(subject=_('Validate new email address'), - message=user.change_email_template(country_code)) - except: - logger.error(f'METHOD_NAME: {change_email_address.__name__}\n' - f'DETAIL: Exception occurred for user: {user_id}') + send_email(user_id, 'Validate new email address', 'change_email_template', country_code) + + +@shared_task +def send_password_changed_email(user_id, country_code): + """Send email which notifies user that his password had changed""" + send_email(user_id, 'Notify password changed', 'notify_password_changed_template', country_code) diff --git a/project/settings/base.py b/project/settings/base.py index 06f83811..8bb470a6 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -406,7 +406,8 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1 RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html' CHANGE_EMAIL_TEMPLATE = 'account/change_email.html' CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html' -NEWS_EMAIL_TEMPLATE = "news/news_email.html" +NEWS_EMAIL_TEMPLATE = 'news/news_email.html' +NOTIFICATION_PASSWORD_TEMPLATE = 'account/password_change_email.html' # COOKIES diff --git a/project/templates/account/password_change_email.html b/project/templates/account/password_change_email.html new file mode 100644 index 00000000..30dd2aac --- /dev/null +++ b/project/templates/account/password_change_email.html @@ -0,0 +1,7 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}You're receiving this email because your account's password address at {{ site_name }}.{% endblocktrans %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endautoescape %} \ No newline at end of file From e0bce99ca952c542a8bf314f71cd4b1dac3332ca Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Tue, 15 Oct 2019 21:40:55 +0300 Subject: [PATCH 027/121] Fix issue with copies --- apps/tag/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tag/models.py b/apps/tag/models.py index 4fa04f8e..b811796c 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -47,7 +47,7 @@ class TagCategoryQuerySet(models.QuerySet): def with_tags(self, switcher=True): """Filter by existing tags.""" - return self.filter(tags__isnull=not switcher) + return self.exclude(tags__isnull=switcher) class TagCategory(TranslatedFieldsMixin, models.Model): From 8dad3df4bf4a64332e262338689496517ae3e17e Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 028/121] Added route for mobile tags --- apps/tag/urls/mobile.py | 7 +++++++ project/urls/mobile.py | 1 + 2 files changed, 8 insertions(+) create mode 100644 apps/tag/urls/mobile.py diff --git a/apps/tag/urls/mobile.py b/apps/tag/urls/mobile.py new file mode 100644 index 00000000..561697d2 --- /dev/null +++ b/apps/tag/urls/mobile.py @@ -0,0 +1,7 @@ +from tag.urls.web import urlpatterns as common_urlpatterns + +urlpatterns = [ + +] + +urlpatterns.extend(common_urlpatterns) diff --git a/project/urls/mobile.py b/project/urls/mobile.py index 0bcbd31c..dd09dfb7 100644 --- a/project/urls/mobile.py +++ b/project/urls/mobile.py @@ -4,6 +4,7 @@ app_name = 'mobile' urlpatterns = [ path('establishments/', include('establishment.urls.mobile')), + path('tags/', include('tag.urls.mobile')), # path('account/', include('account.urls.web')), # path('advertisement/', include('advertisement.urls.web')), # path('collection/', include('collection.urls.web')), From 12a339cd1b16b0423328e407bb263d6105ede005 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:36:17 +0300 Subject: [PATCH 029/121] Show timetables list --- apps/timetable/serialziers.py | 6 ++++++ apps/timetable/urls/__init__.py | 0 apps/timetable/{urls.py => urls/common.py} | 0 apps/timetable/urls/mobile.py | 6 ++++++ apps/timetable/urls/web.py | 6 ++++++ apps/timetable/views.py | 3 ++- project/urls/mobile.py | 1 + project/urls/web.py | 1 + 8 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 apps/timetable/urls/__init__.py rename apps/timetable/{urls.py => urls/common.py} (100%) create mode 100644 apps/timetable/urls/mobile.py create mode 100644 apps/timetable/urls/web.py diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 1339cc8e..06483d90 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -77,3 +77,9 @@ class ScheduleCreateSerializer(ScheduleRUDSerializer): schedule_qs.delete() establishment.schedule.add(instance) return instance + +class TimetableSerializer(serializers.ModelSerializer): + + class Meta: + model = Timetable + fields = '__all__' diff --git a/apps/timetable/urls/__init__.py b/apps/timetable/urls/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/timetable/urls.py b/apps/timetable/urls/common.py similarity index 100% rename from apps/timetable/urls.py rename to apps/timetable/urls/common.py diff --git a/apps/timetable/urls/mobile.py b/apps/timetable/urls/mobile.py new file mode 100644 index 00000000..1c6419a3 --- /dev/null +++ b/apps/timetable/urls/mobile.py @@ -0,0 +1,6 @@ +from timetable.urls.common import urlpatterns as common_urlpatterns + + +urlpatterns = [] + +urlpatterns.extend(common_urlpatterns) \ No newline at end of file diff --git a/apps/timetable/urls/web.py b/apps/timetable/urls/web.py new file mode 100644 index 00000000..1c6419a3 --- /dev/null +++ b/apps/timetable/urls/web.py @@ -0,0 +1,6 @@ +from timetable.urls.common import urlpatterns as common_urlpatterns + + +urlpatterns = [] + +urlpatterns.extend(common_urlpatterns) \ No newline at end of file diff --git a/apps/timetable/views.py b/apps/timetable/views.py index d8a13f71..226f6c5f 100644 --- a/apps/timetable/views.py +++ b/apps/timetable/views.py @@ -1,4 +1,4 @@ -from rest_framework import generics +from rest_framework import generics, permissions from timetable import serialziers, models @@ -6,3 +6,4 @@ class TimetableListView(generics.ListAPIView): """Method to get timetables""" serializer_class = serialziers.TimetableSerializer queryset = models.Timetable.objects.all() + permission_classes = (permissions.AllowAny, ) diff --git a/project/urls/mobile.py b/project/urls/mobile.py index dd09dfb7..5831a96a 100644 --- a/project/urls/mobile.py +++ b/project/urls/mobile.py @@ -5,6 +5,7 @@ app_name = 'mobile' urlpatterns = [ path('establishments/', include('establishment.urls.mobile')), path('tags/', include('tag.urls.mobile')), + path('timetables/', include('timetable.urls.mobile')), # path('account/', include('account.urls.web')), # path('advertisement/', include('advertisement.urls.web')), # path('collection/', include('collection.urls.web')), diff --git a/project/urls/web.py b/project/urls/web.py index 5bd67207..7fb7efec 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -34,4 +34,5 @@ urlpatterns = [ path('translation/', include('translation.urls')), path('comments/', include('comment.urls.web')), path('favorites/', include('favorites.urls')), + path('timetables/', include('timetable.urls.web')), ] From c2c9f33b571b937559db5b1ca70d59151f9522fb Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:44:55 +0300 Subject: [PATCH 030/121] Show timetables list (only necessary fields) --- apps/timetable/serialziers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 06483d90..ecd4127e 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -79,7 +79,13 @@ class ScheduleCreateSerializer(ScheduleRUDSerializer): return instance class TimetableSerializer(serializers.ModelSerializer): + """Serailzier for Timetable model.""" + weekday_display = serializers.CharField(source='get_weekday_display', + read_only=True) class Meta: model = Timetable - fields = '__all__' + fields = ( + 'id', + 'weekday_display', + ) From 6d7341377bb191fbce2479285253f02d765b5444 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:46:36 +0300 Subject: [PATCH 031/121] Show timetables list (remove pagination) --- apps/timetable/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/timetable/views.py b/apps/timetable/views.py index 226f6c5f..5129f068 100644 --- a/apps/timetable/views.py +++ b/apps/timetable/views.py @@ -6,4 +6,5 @@ class TimetableListView(generics.ListAPIView): """Method to get timetables""" serializer_class = serialziers.TimetableSerializer queryset = models.Timetable.objects.all() + pagination_class = None permission_classes = (permissions.AllowAny, ) From ed03f0f40f253ca6814866184b865d23a73c6e88 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 19:05:16 +0300 Subject: [PATCH 032/121] Search establishment by working time --- apps/establishment/models.py | 10 ++++++++++ apps/search_indexes/documents/establishment.py | 6 ++++++ apps/search_indexes/views.py | 6 ++++++ apps/timetable/models.py | 11 +++++++++++ apps/timetable/serialziers.py | 1 + 5 files changed, 34 insertions(+) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 439e99ac..f2297c6d 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -368,6 +368,16 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): def best_price_carte(self): return 200 + @property + def works_noon(self): + """ Used for indexing working by day """ + return [ret.weekday for ret in self.schedule.all() if ret.works_at_noon] + + @property + def works_afternoon(self): + """ Used for indexing working by day """ + return [ret.weekday for ret in self.schedule.all() if ret.works_at_afternoon] + @property def tags_indexing(self): return [{'id': tag.metadata.id, diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index c30d4c58..f80e78a3 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -32,6 +32,12 @@ class EstablishmentDocument(Document): }), }, multi=True) + works_afternoon = fields.ListField(fields.IntegerField( + attr='works_afternoon' + )) + works_noon = fields.ListField(fields.IntegerField( + attr='works_noon' + )) tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='id'), diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 50c32fc7..ed83cf5d 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -121,6 +121,12 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): 'establishment_subtypes': { 'field': 'establishment_subtypes.id' }, + 'works_noon': { + 'field': 'works_noon' + }, + 'works_afternoon': { + 'field': 'works_afternoon' + }, } geo_spatial_filter_fields = { diff --git a/apps/timetable/models.py b/apps/timetable/models.py index 53670d02..caa6e6ac 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 datetime import time from utils.models import ProjectBaseMixin @@ -14,6 +15,8 @@ class Timetable(ProjectBaseMixin): SATURDAY = 5 SUNDAY = 6 + NOON = time(17, 0) + WEEKDAYS_CHOICES = ( (MONDAY, _('Monday')), (TUESDAY, _('Tuesday')), @@ -32,6 +35,14 @@ class Timetable(ProjectBaseMixin): opening_at = models.TimeField(verbose_name=_('Opening time'), null=True) closed_at = models.TimeField(verbose_name=_('Closed time'), null=True) + @property + def works_at_noon(self): + return bool(self.closed_at and self.closed_at <= self.NOON) + + @property + def works_at_afternoon(self): + return bool(self.closed_at and self.closed_at > self.NOON) + class Meta: """Meta class.""" verbose_name = _('Timetable') diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index ecd4127e..76a37257 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -88,4 +88,5 @@ class TimetableSerializer(serializers.ModelSerializer): fields = ( 'id', 'weekday_display', + 'works_at_noon', ) From 2f656c459de73f387dfe0ef06bca67088fcb9dfd Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 19:09:38 +0300 Subject: [PATCH 033/121] Search establishment by working time (added fields to serializer) --- apps/search_indexes/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index 18a1e240..be2d0e8b 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -84,6 +84,8 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'preview_image', 'address', 'tags', + 'works_noon', + 'works_afternoon', # 'collections', # 'establishment_type', # 'establishment_subtypes', From 9fc4a8703cbe046eefea0e4c13a618fcf1e053f9 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 19:13:33 +0300 Subject: [PATCH 034/121] Search establishment by working time (added lookup) --- apps/search_indexes/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index ed83cf5d..9d8d680b 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -122,10 +122,16 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): 'field': 'establishment_subtypes.id' }, 'works_noon': { - 'field': 'works_noon' + 'field': 'works_noon', + 'lookups': [ + constants.LOOKUP_QUERY_IN, + ], }, 'works_afternoon': { - 'field': 'works_afternoon' + 'field': 'works_afternoon', + 'lookups': [ + constants.LOOKUP_QUERY_IN, + ], }, } From 40f1cc1e222d15092f4a5c61d82a5045f554743c Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 20:56:56 +0300 Subject: [PATCH 035/121] Add lookup for categories --- apps/search_indexes/views.py | 3 +++ apps/timetable/urls/common.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 9d8d680b..cc2b8042 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -111,6 +111,9 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): }, 'tags_category_id': { 'field': 'tags.category.id', + 'lookups': [ + constants.LOOKUP_QUERY_IN, + ], }, 'collection_type': { 'field': 'collections.collection_type' diff --git a/apps/timetable/urls/common.py b/apps/timetable/urls/common.py index ff4f4f00..95593b20 100644 --- a/apps/timetable/urls/common.py +++ b/apps/timetable/urls/common.py @@ -6,5 +6,5 @@ from timetable import views app_name = 'timetable' urlpatterns = [ - path('', views.TimetableListView.as_view(), name='list') + # path('', views.TimetableListView.as_view(), name='list') ] \ No newline at end of file From 6a85e52abcb7368658f83298c4111d738ea67fd0 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 12:30:40 +0300 Subject: [PATCH 036/121] Rename variable --- apps/establishment/models.py | 2 +- apps/search_indexes/documents/establishment.py | 4 ++-- apps/search_indexes/serializers.py | 2 +- apps/search_indexes/views.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index f2297c6d..5d50284c 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -374,7 +374,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): return [ret.weekday for ret in self.schedule.all() if ret.works_at_noon] @property - def works_afternoon(self): + def works_evening(self): """ Used for indexing working by day """ return [ret.weekday for ret in self.schedule.all() if ret.works_at_afternoon] diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index f80e78a3..88aaaaa5 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -32,8 +32,8 @@ class EstablishmentDocument(Document): }), }, multi=True) - works_afternoon = fields.ListField(fields.IntegerField( - attr='works_afternoon' + works_evening = fields.ListField(fields.IntegerField( + attr='works_evening' )) works_noon = fields.ListField(fields.IntegerField( attr='works_noon' diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index be2d0e8b..a933fd9b 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -85,7 +85,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'address', 'tags', 'works_noon', - 'works_afternoon', + 'works_evening', # 'collections', # 'establishment_type', # 'establishment_subtypes', diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index cc2b8042..0a3afe8e 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -130,8 +130,8 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): constants.LOOKUP_QUERY_IN, ], }, - 'works_afternoon': { - 'field': 'works_afternoon', + 'works_evening': { + 'field': 'works_evening', 'lookups': [ constants.LOOKUP_QUERY_IN, ], From 0c2b657244244611d3ba4972807712b0d274742a Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 17 Oct 2019 13:47:59 +0300 Subject: [PATCH 037/121] small fix --- project/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/settings/base.py b/project/settings/base.py index e779f4ed..82d6e016 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -211,8 +211,8 @@ REST_FRAMEWORK = { 'COERCE_DECIMAL_TO_STRING': False, 'DEFAULT_AUTHENTICATION_CLASSES': ( # JWT + 'utils.authentication.GMJWTAuthentication', 'rest_framework.authentication.SessionAuthentication', - # 'utils.authentication.GMJWTAuthentication', ), 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning', 'DEFAULT_VERSION': (AVAILABLE_VERSIONS['current'],), From 79b4b59478136359945bc3a513708011d303514c Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 17 Oct 2019 13:49:29 +0300 Subject: [PATCH 038/121] enabling celery for development and stage settings --- project/settings/development.py | 2 +- project/settings/stage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/settings/development.py b/project/settings/development.py index 6c726e25..59691818 100644 --- a/project/settings/development.py +++ b/project/settings/development.py @@ -8,7 +8,7 @@ ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0'] SEND_SMS = False SMS_CODE_SHOW = True -USE_CELERY = False +USE_CELERY = True SCHEMA_URI = 'http' DEFAULT_SUBDOMAIN = 'www' diff --git a/project/settings/stage.py b/project/settings/stage.py index e1443ab1..49a7ae0f 100644 --- a/project/settings/stage.py +++ b/project/settings/stage.py @@ -6,7 +6,7 @@ ALLOWED_HOSTS = ['gm-stage.id-east.ru', '95.213.204.126'] SEND_SMS = False SMS_CODE_SHOW = True -USE_CELERY = False +USE_CELERY = True SCHEMA_URI = 'https' DEFAULT_SUBDOMAIN = 'www' From c8c66e7d1b6af924167d11d7402c33cc95acdd7b Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 14:32:01 +0300 Subject: [PATCH 039/121] add schedule to search results --- apps/search_indexes/documents/establishment.py | 1 + apps/search_indexes/serializers.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 88aaaaa5..f3f9505c 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -94,6 +94,7 @@ class EstablishmentDocument(Document): 'toque_number', 'public_mark', 'slug', + 'schedule', ) def get_queryset(self): diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index a933fd9b..d4b834ca 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -4,6 +4,7 @@ from django_elasticsearch_dsl_drf.serializers import DocumentSerializer from news.serializers import NewsTypeSerializer from search_indexes.documents import EstablishmentDocument, NewsDocument from search_indexes.utils import get_translated_value +from timetable.serialziers import ScheduleRUDSerializer class TagsDocumentSerializer(serializers.Serializer): @@ -68,6 +69,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): address = AddressDocumentSerializer() tags = TagsDocumentSerializer(many=True) + schedule = ScheduleRUDSerializer(many=True, allow_null=True) class Meta: """Meta class.""" @@ -84,6 +86,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'preview_image', 'address', 'tags', + 'schedule', 'works_noon', 'works_evening', # 'collections', From acea297c946e6942ef926dfe4d58aa9c6785b375 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 14:33:20 +0300 Subject: [PATCH 040/121] Revert "add schedule to search results" This reverts commit c8c66e7 --- apps/search_indexes/documents/establishment.py | 1 - apps/search_indexes/serializers.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index f3f9505c..88aaaaa5 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -94,7 +94,6 @@ class EstablishmentDocument(Document): 'toque_number', 'public_mark', 'slug', - 'schedule', ) def get_queryset(self): diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index d4b834ca..a933fd9b 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -4,7 +4,6 @@ from django_elasticsearch_dsl_drf.serializers import DocumentSerializer from news.serializers import NewsTypeSerializer from search_indexes.documents import EstablishmentDocument, NewsDocument from search_indexes.utils import get_translated_value -from timetable.serialziers import ScheduleRUDSerializer class TagsDocumentSerializer(serializers.Serializer): @@ -69,7 +68,6 @@ class EstablishmentDocumentSerializer(DocumentSerializer): address = AddressDocumentSerializer() tags = TagsDocumentSerializer(many=True) - schedule = ScheduleRUDSerializer(many=True, allow_null=True) class Meta: """Meta class.""" @@ -86,7 +84,6 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'preview_image', 'address', 'tags', - 'schedule', 'works_noon', 'works_evening', # 'collections', From c59811e0379462ae2406f9a780c0227579f093a7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 14:34:49 +0300 Subject: [PATCH 041/121] Revert "Revert "add schedule to search results"" This reverts commit acea297 --- apps/search_indexes/documents/establishment.py | 1 + apps/search_indexes/serializers.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 88aaaaa5..f3f9505c 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -94,6 +94,7 @@ class EstablishmentDocument(Document): 'toque_number', 'public_mark', 'slug', + 'schedule', ) def get_queryset(self): diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index a933fd9b..d4b834ca 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -4,6 +4,7 @@ from django_elasticsearch_dsl_drf.serializers import DocumentSerializer from news.serializers import NewsTypeSerializer from search_indexes.documents import EstablishmentDocument, NewsDocument from search_indexes.utils import get_translated_value +from timetable.serialziers import ScheduleRUDSerializer class TagsDocumentSerializer(serializers.Serializer): @@ -68,6 +69,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): address = AddressDocumentSerializer() tags = TagsDocumentSerializer(many=True) + schedule = ScheduleRUDSerializer(many=True, allow_null=True) class Meta: """Meta class.""" @@ -84,6 +86,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'preview_image', 'address', 'tags', + 'schedule', 'works_noon', 'works_evening', # 'collections', From 97fc8dce155168929182a32739209927a9804211 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 14:45:33 +0300 Subject: [PATCH 042/121] add schedule to search results --- apps/search_indexes/documents/establishment.py | 14 +++++++++++++- apps/search_indexes/serializers.py | 18 ++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index f3f9505c..f79b6427 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -45,6 +45,19 @@ class EstablishmentDocument(Document): properties=OBJECT_FIELD_PROPERTIES), }, multi=True) + schedule = fields.ListField(fields.ObjectField( + properties={ + 'id': fields.IntegerField(attr='id'), + 'weekday': fields.IntegerField(attr='weekday'), + 'weekday_display': fields.KeywordField(attr='weekday_display'), + 'opening_at': fields.KeywordField(attr='opening_at'), + 'closed_at': fields.KeywordField(attr='closed_at'), + 'lunch_start': fields.KeywordField(attr='lunch_start'), + 'lunch_end': fields.KeywordField(attr='lunch_end'), + 'dinner_end': fields.KeywordField(attr='dinner_end'), + 'dinner_start': fields.KeywordField(attr='dinner_start'), + } + )) address = fields.ObjectField( properties={ 'id': fields.IntegerField(), @@ -94,7 +107,6 @@ class EstablishmentDocument(Document): 'toque_number', 'public_mark', 'slug', - 'schedule', ) def get_queryset(self): diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index d4b834ca..8194504d 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -4,7 +4,6 @@ from django_elasticsearch_dsl_drf.serializers import DocumentSerializer from news.serializers import NewsTypeSerializer from search_indexes.documents import EstablishmentDocument, NewsDocument from search_indexes.utils import get_translated_value -from timetable.serialziers import ScheduleRUDSerializer class TagsDocumentSerializer(serializers.Serializer): @@ -31,6 +30,21 @@ class AddressDocumentSerializer(serializers.Serializer): geo_lat = serializers.FloatField(allow_null=True, source='coordinates.lat') +class ScheduleDocumentSerializer(serializers.Serializer): + """Schedule serializer for ES Document""" + + id = serializers.IntegerField() + weekday = serializers.IntegerField() + weekday_display = serializers.CharField() + opening_at = serializers.CharField() + closed_at = serializers.CharField() + lunch_start = serializers.CharField() + lunch_end = serializers.CharField() + dinner_end = serializers.CharField() + dinner_start = serializers.CharField() + + + class NewsDocumentSerializer(DocumentSerializer): """News document serializer.""" @@ -69,7 +83,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): address = AddressDocumentSerializer() tags = TagsDocumentSerializer(many=True) - schedule = ScheduleRUDSerializer(many=True, allow_null=True) + schedule = ScheduleDocumentSerializer(many=True, allow_null=True) class Meta: """Meta class.""" From 2c5c067b53c4afca3dfde66d7257325cb89888c8 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 14:56:31 +0300 Subject: [PATCH 043/121] try to remove weekday_display --- apps/search_indexes/documents/establishment.py | 2 +- apps/search_indexes/serializers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index f79b6427..69314f6c 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -49,7 +49,7 @@ class EstablishmentDocument(Document): properties={ 'id': fields.IntegerField(attr='id'), 'weekday': fields.IntegerField(attr='weekday'), - 'weekday_display': fields.KeywordField(attr='weekday_display'), + # 'weekday_display': fields.KeywordField(attr='weekday_display'), 'opening_at': fields.KeywordField(attr='opening_at'), 'closed_at': fields.KeywordField(attr='closed_at'), 'lunch_start': fields.KeywordField(attr='lunch_start'), diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index 8194504d..d29c2b6b 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -35,7 +35,7 @@ class ScheduleDocumentSerializer(serializers.Serializer): id = serializers.IntegerField() weekday = serializers.IntegerField() - weekday_display = serializers.CharField() + # weekday_display = serializers.CharField() opening_at = serializers.CharField() closed_at = serializers.CharField() lunch_start = serializers.CharField() From 1998e3dc570ffbe111cd76eb8be3bb51fc7325b7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 14:59:35 +0300 Subject: [PATCH 044/121] Change establishment schedule ES fields type --- apps/search_indexes/documents/establishment.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 69314f6c..1f147baa 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -49,13 +49,13 @@ class EstablishmentDocument(Document): properties={ 'id': fields.IntegerField(attr='id'), 'weekday': fields.IntegerField(attr='weekday'), - # 'weekday_display': fields.KeywordField(attr='weekday_display'), - 'opening_at': fields.KeywordField(attr='opening_at'), - 'closed_at': fields.KeywordField(attr='closed_at'), - 'lunch_start': fields.KeywordField(attr='lunch_start'), - 'lunch_end': fields.KeywordField(attr='lunch_end'), - 'dinner_end': fields.KeywordField(attr='dinner_end'), - 'dinner_start': fields.KeywordField(attr='dinner_start'), + 'weekday_display': fields.KeywordField(attr='get_weekday_display'), + 'opening_at': fields.DateField(attr='opening_at'), + 'closed_at': fields.DateField(attr='closed_at'), + 'lunch_start': fields.DateField(attr='lunch_start'), + 'lunch_end': fields.DateField(attr='lunch_end'), + 'dinner_end': fields.DateField(attr='dinner_end'), + 'dinner_start': fields.DateField(attr='dinner_start'), } )) address = fields.ObjectField( From d2e34e5d49a668b6e1886afc834bafed2f98f623 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 15:10:26 +0300 Subject: [PATCH 045/121] Timetable search results --- apps/search_indexes/documents/establishment.py | 7 +------ apps/search_indexes/serializers.py | 8 +------- apps/timetable/models.py | 4 ++++ 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 1f147baa..4306eed4 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -50,12 +50,7 @@ class EstablishmentDocument(Document): 'id': fields.IntegerField(attr='id'), 'weekday': fields.IntegerField(attr='weekday'), 'weekday_display': fields.KeywordField(attr='get_weekday_display'), - 'opening_at': fields.DateField(attr='opening_at'), - 'closed_at': fields.DateField(attr='closed_at'), - 'lunch_start': fields.DateField(attr='lunch_start'), - 'lunch_end': fields.DateField(attr='lunch_end'), - 'dinner_end': fields.DateField(attr='dinner_end'), - 'dinner_start': fields.DateField(attr='dinner_start'), + 'closed_at': fields.KeywordField(attr='closed_at_str'), } )) address = fields.ObjectField( diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index d29c2b6b..651205d7 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -35,14 +35,8 @@ class ScheduleDocumentSerializer(serializers.Serializer): id = serializers.IntegerField() weekday = serializers.IntegerField() - # weekday_display = serializers.CharField() - opening_at = serializers.CharField() + weekday_display = serializers.CharField() closed_at = serializers.CharField() - lunch_start = serializers.CharField() - lunch_end = serializers.CharField() - dinner_end = serializers.CharField() - dinner_start = serializers.CharField() - class NewsDocumentSerializer(DocumentSerializer): diff --git a/apps/timetable/models.py b/apps/timetable/models.py index caa6e6ac..35469c32 100644 --- a/apps/timetable/models.py +++ b/apps/timetable/models.py @@ -35,6 +35,10 @@ class Timetable(ProjectBaseMixin): opening_at = models.TimeField(verbose_name=_('Opening time'), null=True) closed_at = models.TimeField(verbose_name=_('Closed time'), null=True) + @property + def closed_at_str(self): + return str(self.closed_at) if self.closed_at else None + @property def works_at_noon(self): return bool(self.closed_at and self.closed_at <= self.NOON) From cce0913245cf559e1625903001d49b6e5ef04660 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 17 Oct 2019 18:37:48 +0300 Subject: [PATCH 046/121] change AWS_S3_CUSTOM_DOMAIN --- celerybeat-schedule | Bin 12845 -> 12845 bytes project/settings/amazon_s3.py | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/celerybeat-schedule b/celerybeat-schedule index 3efe528a0f0c9bb6e563876fdc4c1a02541e62f7..e1a56a152b845b38540fb26180e00295cb3cb2b1 100644 GIT binary patch delta 101 zcmZ3RvNmOb0we2WMg?(6DFz7OHGt6Pp!~?miVEtRBNTS9@Y+D7xPekan`Kqyxn<+y k%TkMqGxPJ};~SVGIDir+dd7MNMaf2m40@X*4X!W(0FSs74gdfE delta 76 zcmZ3RvNmOb0wc?0Mg?)s*9;I~Guco Date: Thu, 17 Oct 2019 23:25:39 +0300 Subject: [PATCH 047/121] add preview image url --- apps/search_indexes/documents/news.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index cd6fc089..dee4a1c9 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -24,6 +24,7 @@ class NewsDocument(Document): country = fields.ObjectField(properties={'id': fields.IntegerField(), 'code': fields.KeywordField()}) web_url = fields.KeywordField(attr='web_url') + preview_image_url = fields.TextField(attr='preview_image_field') tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='id'), From 4b5b34de205cb1ac5a57c0217cc20d5bfb23df61 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 17 Oct 2019 23:28:13 +0300 Subject: [PATCH 048/121] add preview image url (fix typo) --- apps/search_indexes/documents/news.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index dee4a1c9..21c59e68 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -24,7 +24,7 @@ class NewsDocument(Document): country = fields.ObjectField(properties={'id': fields.IntegerField(), 'code': fields.KeywordField()}) web_url = fields.KeywordField(attr='web_url') - preview_image_url = fields.TextField(attr='preview_image_field') + preview_image_url = fields.TextField(attr='preview_image_url') tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='id'), From b77c654c6ca71e215fcf9362c773fc3229794ea8 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 18 Oct 2019 02:38:59 +0300 Subject: [PATCH 049/121] Open now filter --- apps/establishment/models.py | 13 +++++++++++++ apps/location/models.py | 9 ++++++++- apps/search_indexes/documents/establishment.py | 1 + apps/search_indexes/serializers.py | 1 + apps/search_indexes/views.py | 9 ++++++++- project/settings/base.py | 1 + requirements/base.txt | 1 + 7 files changed, 33 insertions(+), 2 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 5d50284c..3f61dbe9 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1,4 +1,5 @@ """Establishment models.""" +from datetime import datetime from functools import reduce import elasticsearch_dsl @@ -13,6 +14,7 @@ from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField +from pytz import timezone as py_tz from collection.models import Collection from location.models import Address @@ -378,6 +380,17 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): """ Used for indexing working by day """ return [ret.weekday for ret in self.schedule.all() if ret.works_at_afternoon] + # @property + def works_now(self): + """ Is establishment working now """ + now_at_est_tz = datetime.now(tz=py_tz(self.address.tz_name)) + current_week = now_at_est_tz.weekday() + schedule_for_today = self.schedule.filter(weekday=current_week).first() + if schedule_for_today is None: + return False + time_at_est_tz = now_at_est_tz.time() + return schedule_for_today.closed_at > time_at_est_tz > schedule_for_today.opening_at + @property def tags_indexing(self): return [{'id': tag.metadata.id, diff --git a/apps/location/models.py b/apps/location/models.py index 2298c28e..e05f5b26 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -5,8 +5,10 @@ from django.db.models.signals import post_save from django.db.transaction import on_commit from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ -from utils.models import ProjectBaseMixin, SVGImageMixin, TranslatedFieldsMixin, TJSONField + +from timezonefinder import TimezoneFinder from translation.models import Language +from utils.models import ProjectBaseMixin, SVGImageMixin, TranslatedFieldsMixin, TJSONField class Country(TranslatedFieldsMixin, SVGImageMixin, ProjectBaseMixin): @@ -99,6 +101,11 @@ class Address(models.Model): def get_street_name(self): return self.street_name_1 or self.street_name_2 + @property + def tz_name(self): + tf = TimezoneFinder(in_memory=True) + return tf.certain_timezone_at(lng=self.latitude, lat=self.longitude) + @property def latitude(self): return self.coordinates.y if self.coordinates else float(0) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 4306eed4..dabc98bb 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -38,6 +38,7 @@ class EstablishmentDocument(Document): works_noon = fields.ListField(fields.IntegerField( attr='works_noon' )) + works_now = fields.BooleanField() tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='id'), diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index 651205d7..535974a9 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -97,6 +97,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer): 'schedule', 'works_noon', 'works_evening', + 'works_now', # 'collections', # 'establishment_type', # 'establishment_subtypes', diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 0a3afe8e..913c2d95 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -133,9 +133,16 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): 'works_evening': { 'field': 'works_evening', 'lookups': [ - constants.LOOKUP_QUERY_IN, + constants.LOOKUP_FILTER_TERM, ], }, + 'works_now': { + 'field': 'works_now', + 'lookups': [ + constants.LOOKUP_FILTER_EXISTS, + constants.LOOKUP_QUERY_IN, + ] + }, } geo_spatial_filter_fields = { diff --git a/project/settings/base.py b/project/settings/base.py index a64bedb9..95babea4 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -96,6 +96,7 @@ EXTERNAL_APPS = [ 'phonenumber_field', 'storages', 'sorl.thumbnail', + 'timezonefinder' ] diff --git a/requirements/base.txt b/requirements/base.txt index 856c70f3..dbb3b20e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -9,6 +9,7 @@ fcm-django django-easy-select2 bootstrap-admin drf-yasg==1.16.0 +timezonefinder PySocks!=1.5.7,>=1.5.6; djangorestframework==3.9.4 From 3bc9dd73476ae121c55455f37b0a9a383def1c99 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 18 Oct 2019 02:56:49 +0300 Subject: [PATCH 050/121] Open now filter (typo fix) --- apps/search_indexes/documents/establishment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index dabc98bb..5d858321 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -38,7 +38,7 @@ class EstablishmentDocument(Document): works_noon = fields.ListField(fields.IntegerField( attr='works_noon' )) - works_now = fields.BooleanField() + works_now = fields.BooleanField(attr='works_now') tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='id'), From f7facf826df3f01cceaed754f9486a0e17d96581 Mon Sep 17 00:00:00 2001 From: Semyon Date: Fri, 18 Oct 2019 18:37:24 +0300 Subject: [PATCH 051/121] Implemented chosen tags by adding priority field: chosen tags should have non-null priority field, other tags should have null priority field. --- apps/tag/migrations/0003_tag_priority.py | 18 ++++++++++++++++++ apps/tag/models.py | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 apps/tag/migrations/0003_tag_priority.py diff --git a/apps/tag/migrations/0003_tag_priority.py b/apps/tag/migrations/0003_tag_priority.py new file mode 100644 index 00000000..5e93bcaa --- /dev/null +++ b/apps/tag/migrations/0003_tag_priority.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-18 15:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0002_auto_20191009_1408'), + ] + + operations = [ + migrations.AddField( + model_name='tag', + name='priority', + field=models.IntegerField(default=None, null=True, unique=True), + ), + ] diff --git a/apps/tag/models.py b/apps/tag/models.py index b811796c..0e5f9859 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -14,6 +14,8 @@ class Tag(TranslatedFieldsMixin, models.Model): category = models.ForeignKey('TagCategory', on_delete=models.PROTECT, null=True, related_name='tags', verbose_name=_('Category')) + # chosen tags should have non-null priority,other tags priority should be null + priority = models.IntegerField(unique=True, null=True, default=None) class Meta: """Meta class.""" From 71ec593ecf26db24722a06f865727bd2e1d20c76 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 21 Oct 2019 14:09:08 +0300 Subject: [PATCH 052/121] News email new layout --- project/templates/news/news_email.html | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/project/templates/news/news_email.html b/project/templates/news/news_email.html index d14bd898..6fe14060 100644 --- a/project/templates/news/news_email.html +++ b/project/templates/news/news_email.html @@ -12,7 +12,6 @@ -
@@ -25,7 +24,7 @@
-
+
{{ title }}
{% if not image_url is None %} @@ -33,11 +32,13 @@
{% endif %} -
+
{{ description | safe }}
- +
+ Go to news +
- +
- + \ No newline at end of file From cc5249674d4a3c0696f258aad5e3b07651b54c47 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 21 Oct 2019 14:39:23 +0300 Subject: [PATCH 053/121] merge migrations --- apps/tag/migrations/0004_merge_20191021_1138.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/tag/migrations/0004_merge_20191021_1138.py diff --git a/apps/tag/migrations/0004_merge_20191021_1138.py b/apps/tag/migrations/0004_merge_20191021_1138.py new file mode 100644 index 00000000..6298ce4f --- /dev/null +++ b/apps/tag/migrations/0004_merge_20191021_1138.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-21 11:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0003_auto_20191018_0758'), + ('tag', '0003_tag_priority'), + ] + + operations = [ + ] From f96a1d4aa24309b97b6be5d67f711ded310fc663 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Mon, 21 Oct 2019 16:42:08 +0300 Subject: [PATCH 054/121] Revert "Implemented chosen tags by adding priority field: chosen tags should have non-null priority field, other tags should have null priority field." This reverts commit f7facf826df3f01cceaed754f9486a0e17d96581. --- apps/tag/migrations/0003_tag_priority.py | 18 ------------------ apps/tag/models.py | 2 -- 2 files changed, 20 deletions(-) delete mode 100644 apps/tag/migrations/0003_tag_priority.py diff --git a/apps/tag/migrations/0003_tag_priority.py b/apps/tag/migrations/0003_tag_priority.py deleted file mode 100644 index 5e93bcaa..00000000 --- a/apps/tag/migrations/0003_tag_priority.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-18 15:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('tag', '0002_auto_20191009_1408'), - ] - - operations = [ - migrations.AddField( - model_name='tag', - name='priority', - field=models.IntegerField(default=None, null=True, unique=True), - ), - ] diff --git a/apps/tag/models.py b/apps/tag/models.py index ff4afee1..6e83ca12 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -14,8 +14,6 @@ class Tag(TranslatedFieldsMixin, models.Model): category = models.ForeignKey('TagCategory', on_delete=models.CASCADE, null=True, related_name='tags', verbose_name=_('Category')) - # chosen tags should have non-null priority,other tags priority should be null - priority = models.IntegerField(unique=True, null=True, default=None) class Meta: """Meta class.""" From 55263e4059b7e9126179d77210867a3f32dcf1c9 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Mon, 21 Oct 2019 16:51:01 +0300 Subject: [PATCH 055/121] remove merge migration --- apps/tag/migrations/0004_merge_20191021_1138.py | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 apps/tag/migrations/0004_merge_20191021_1138.py diff --git a/apps/tag/migrations/0004_merge_20191021_1138.py b/apps/tag/migrations/0004_merge_20191021_1138.py deleted file mode 100644 index 6298ce4f..00000000 --- a/apps/tag/migrations/0004_merge_20191021_1138.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-21 11:38 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('tag', '0003_auto_20191018_0758'), - ('tag', '0003_tag_priority'), - ] - - operations = [ - ] From 386e7d1bf88f67ef15661681aa97c44a1f59d9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 22 Oct 2019 15:39:51 +0300 Subject: [PATCH 056/121] First CI --- compose_ci.yml | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ fabfile.py | 75 ++++++++++++++++++++++++++++++++++++ gitlab-ci.yml | 60 +++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 compose_ci.yml create mode 100644 fabfile.py create mode 100644 gitlab-ci.yml diff --git a/compose_ci.yml b/compose_ci.yml new file mode 100644 index 00000000..3c41f200 --- /dev/null +++ b/compose_ci.yml @@ -0,0 +1,101 @@ +version: '3.5' +services: + # PostgreSQL database + db: + build: + context: ./_dockerfiles/db + dockerfile: Dockerfile + hostname: db + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=postgres + ports: + - "5436:5432" + volumes: + - gm-db:/var/lib/postgresql/data/ + + elasticsearch: + image: elasticsearch:7.3.1 + volumes: + - gm-esdata:/usr/share/elasticsearch/data + hostname: elasticsearch + ports: + - 9200:9200 + - 9300:9300 + environment: + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - discovery.type=single-node + - xpack.security.enabled=false + + # Redis + redis: + image: redis:2.8.23 + ports: + - "6379:6379" + + # Celery + worker: + build: . + command: ./run_celery.sh + environment: + - SETTINGS_CONFIGURATION=local + - DB_NAME=postgres + - DB_USERNAME=postgres + - DB_HOSTNAME=db + - DB_PORT=5432 + - DB_PASSWORD=postgres + volumes: + - .:/code + links: + - db + - redis + + worker_beat: + build: . + command: ./run_celery_beat.sh + environment: + - SETTINGS_CONFIGURATION=local + - DB_NAME=postgres + - DB_USERNAME=postgres + - DB_HOSTNAME=db + - DB_PORT=5432 + - DB_PASSWORD=postgres + volumes: + - .:/code + links: + - db + - redis + + + # App: G&M + gm_app: + build: . + command: python manage.py runserver 0.0.0.0:8000 + environment: + - SETTINGS_CONFIGURATION=local + - DB_HOSTNAME=db + - DB_PORT=5432 + - DB_NAME=postgres + - DB_USERNAME=postgres + - DB_PASSWORD=postgres + depends_on: + - db + - redis + - worker + - worker_beat + - elasticsearch + volumes: + - .:/code + - gm-media:/media-data + ports: + - "8000:8000" + +volumes: + gm-db: + name: gm-db + + gm-media: + name: gm-media + + gm-esdata: \ No newline at end of file diff --git a/fabfile.py b/fabfile.py new file mode 100644 index 00000000..643080be --- /dev/null +++ b/fabfile.py @@ -0,0 +1,75 @@ +import os # NOQA +from fabric.api import * # NOQA + +user = 'gm' + +env.root = '~/' +env.src = '~/project' + +env.default_branch = 'feature/develop_ci' +env.tmpdir = '~/tmp' + + +env.roledefs = { + 'develop': { + 'branch': env.default_branch, + 'hosts': ['%s@rock.spider.ru:31' % user, ] + }, + 'staging': { + 'hosts': ['%s@5.200.53.99' % user, ] + }, + 'production': { + 'branch': 'master', + 'hosts': ['%s@87.226.166.80' % user, ] + } +} + + +def fetch(branch=None): + with cd(env.src): + role = env.roles[0] + run('git pull origin {}'.format(env.roledefs[role]['branch'])) + run('git submodule update') + + +def migrate(): + with cd(env.src): + run('./manage.py migrate') + + +def install_requirements(): + with cd(env.src): + run('pip install -r requirements/base.txt') + + +def touch(): + with cd(env.src): + run('touch ~/%s.touch' % user) + + +def kill_celery(): + """Kill celery workers for $user.""" + with cd(env.src): + run('ps -u %s -o pid,fname | grep celery | (while read a b; do kill -9 $a; done;)' % user) + + +def collectstatic(): + with cd(env.src): + run('./manage.py collectstatic --noinput') + + +def deploy(branch=None): + fetch() + install_requirements() + migrate() + collectstatic() + touch() + kill_celery() + + +def rev(): + """Show head commit.""" + with hide('running', 'stdout'): + with cd(env.src): + commit = run('git rev-parse HEAD') + return local('git show -q %s' % commit) diff --git a/gitlab-ci.yml b/gitlab-ci.yml new file mode 100644 index 00000000..0de48055 --- /dev/null +++ b/gitlab-ci.yml @@ -0,0 +1,60 @@ +image: docker:latest + +stages: + - hello + +#stages: +# - build +# - test +# - deploy +# - clean +# + +before_script: + - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make + - pip install docker-compose + + +hello: + - echo 'Test GitLab CI' + +#clean: +# stage: clean +# script: +# - docker-compose -f compose-ci.yml stop +# - docker-compose -f compose-ci.yml rm --force gm_app +# when: always +# +# +#buid: +# stage: build +# script: +# - docker-compose -f compose-ci.yml build gm_app +# when: always +# +# +#test: +# stage: test +# script: +# - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput +# when: always +# +# +#deploy-develop: +# stage: deploy +# only: +# - feature/develop_ci +# script: +# - fab --roles=develop deploy +# environment: +# name: Develop +# +# +##deploy-staging: +## stage: deploy +## only: +## - master +## script: +## - fab --roles=staging deploy +## environment: +## name: Staging From 580081f76d6a107300d8ff26c4646988b4ade911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 22 Oct 2019 15:41:10 +0300 Subject: [PATCH 057/121] 1 --- gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gitlab-ci.yml b/gitlab-ci.yml index 0de48055..aaa3fb35 100644 --- a/gitlab-ci.yml +++ b/gitlab-ci.yml @@ -16,7 +16,10 @@ before_script: hello: - - echo 'Test GitLab CI' + script: + - echo 'Test GitLab CI' + only: + - feature/develop_ci #clean: # stage: clean From 307abddbbf12cc383ee0d59774114a232d8a94ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 22 Oct 2019 15:43:26 +0300 Subject: [PATCH 058/121] 1 --- gitlab-ci.yml => .gitlab-ci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gitlab-ci.yml => .gitlab-ci.yml (100%) diff --git a/gitlab-ci.yml b/.gitlab-ci.yml similarity index 100% rename from gitlab-ci.yml rename to .gitlab-ci.yml From ba58b74fdff200711fb106830b9cd5905652e0ff Mon Sep 17 00:00:00 2001 From: "v.gladkikh" Date: Tue, 22 Oct 2019 12:44:48 +0000 Subject: [PATCH 059/121] Update .gitlab-ci.yml --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aaa3fb35..162d90bd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,8 +18,8 @@ before_script: hello: script: - echo 'Test GitLab CI' - only: - - feature/develop_ci + only: + - feature/develop_ci #clean: # stage: clean From e70efe36094ba63329c3f0efa9ca429a6e8b5dc6 Mon Sep 17 00:00:00 2001 From: "v.gladkikh" Date: Tue, 22 Oct 2019 12:45:53 +0000 Subject: [PATCH 060/121] Update .gitlab-ci.yml --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 162d90bd..9995a2e4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,7 @@ before_script: hello: + stage: hello script: - echo 'Test GitLab CI' only: From e1e3799c5f68887dd3eddacc240c523e0beb1d04 Mon Sep 17 00:00:00 2001 From: "v.gladkikh" Date: Tue, 22 Oct 2019 12:49:47 +0000 Subject: [PATCH 061/121] Update .gitlab-ci.yml --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9995a2e4..7bcfe10c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,9 +10,9 @@ stages: # - clean # -before_script: - - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - - pip install docker-compose +# before_script: +# - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make +# - pip install docker-compose hello: From 4a0a305e2cbedb234646b9ea7a805a1f7349a28e Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 062/121] tag category indexing name --- .../0005_tagcategory_name_indexing.py | 18 ++++++++++++++++++ apps/tag/models.py | 2 ++ apps/tag/serializers.py | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 apps/tag/migrations/0005_tagcategory_name_indexing.py diff --git a/apps/tag/migrations/0005_tagcategory_name_indexing.py b/apps/tag/migrations/0005_tagcategory_name_indexing.py new file mode 100644 index 00000000..839b5747 --- /dev/null +++ b/apps/tag/migrations/0005_tagcategory_name_indexing.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-22 15:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0004_tag_priority'), + ] + + operations = [ + migrations.AddField( + model_name='tagcategory', + name='name_indexing', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='indexing name'), + ), + ] diff --git a/apps/tag/models.py b/apps/tag/models.py index 44eacddc..dbacbd2c 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -69,6 +69,8 @@ class TagCategory(TranslatedFieldsMixin, models.Model): on_delete=models.SET_NULL, null=True, default=None) public = models.BooleanField(default=False) + name_indexing = models.CharField(max_length=255, blank=True, null=True, + verbose_name=_('indexing name')) objects = TagCategoryQuerySet.as_manager() diff --git a/apps/tag/serializers.py b/apps/tag/serializers.py index 6ee55c84..445042c7 100644 --- a/apps/tag/serializers.py +++ b/apps/tag/serializers.py @@ -49,7 +49,8 @@ class TagCategoryBaseSerializer(serializers.ModelSerializer): fields = ( 'id', 'label_translated', - 'tags' + 'name_indexing', + 'tags', ) From 778162922e16049bae9ab56158f8c277332efef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:39:10 +0300 Subject: [PATCH 063/121] Test runner --- .gitignore | 4 +++- .gitlab-ci.yml | 57 +++++++++++++++++++------------------------- celerybeat-schedule | Bin 12845 -> 0 bytes 3 files changed, 27 insertions(+), 34 deletions(-) delete mode 100644 celerybeat-schedule diff --git a/.gitignore b/.gitignore index a32ff3df..5d44eda8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ logs/ /geoip_db/ # dev -./docker-compose.override.yml \ No newline at end of file +./docker-compose.override.yml + +celerybeat-schedule diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aaa3fb35..b43dc9dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,48 +1,39 @@ image: docker:latest stages: - - hello - -#stages: -# - build -# - test + - build + - test # - deploy -# - clean -# + - clean + before_script: - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - pip install docker-compose -hello: +clean: + stage: clean script: - - echo 'Test GitLab CI' - only: - - feature/develop_ci + - docker-compose -f compose-ci.yml stop + - docker-compose -f compose-ci.yml rm --force gm_app + when: always + + +buid: + stage: build + script: + - docker-compose -f compose-ci.yml build gm_app + when: always + + +test: + stage: test + script: + - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput + when: always + -#clean: -# stage: clean -# script: -# - docker-compose -f compose-ci.yml stop -# - docker-compose -f compose-ci.yml rm --force gm_app -# when: always -# -# -#buid: -# stage: build -# script: -# - docker-compose -f compose-ci.yml build gm_app -# when: always -# -# -#test: -# stage: test -# script: -# - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput -# when: always -# -# #deploy-develop: # stage: deploy # only: diff --git a/celerybeat-schedule b/celerybeat-schedule deleted file mode 100644 index 3efe528a0f0c9bb6e563876fdc4c1a02541e62f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12845 zcmeI#O=}ZD7zgl4H!sbkY1*c~KvBH46}rS41i^z)JQYQ@6$66HW;3wCxXqKDhzSI8 zD*6@t0OC>n0($l0#iJ+jD0uZE=uBo(69^u1C@TLfyU)z-W@h)dGY`8TUfeIU6~;!Z z3OeR(e5vD2yxPdjEOT_n(NO&36UWZAq>0KOKKK!`TSH0S=P_ zro8d+&Bd+^{hf*YjQ7dkPRO6e0uJ*z1kb;fMJzka&aa$mMt>8+b3V_vuMf}5K4Y8q zdwD@Mqd%GdHc$P`n<5!H17faz0q!KtquBR{PA#J@x9VPXE9!{s51l)%PQW4#>MhY2 zI?vq6|JKf`vqRAmot|3{!bc)*_+d)~akovWaC|@nk{^VTXmnfRmg>33X<;i$MX9h6 zNFke#1wSc}v&VB^a_0&cuCu!jC>^$Yvd{f{hkKkd+a6_)Dd*ARJ{z~5*oG4xc8+2h z)H&t1YAJPH72=~K{oMk_QYagRb&t*@?k}_khc*BJ From 9383c806b0b6467f48665d1a08a71e99baa9ecf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:45:29 +0300 Subject: [PATCH 064/121] Fix --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9c5694f4..7414f21f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,9 +7,9 @@ stages: - clean -# before_script: -# - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make -# - pip install docker-compose + before_script: + - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make + - pip install docker-compose clean: stage: clean From 19facc3b94c47aefa7f8b4ea9abab4a5192a2a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:46:56 +0300 Subject: [PATCH 065/121] Fix --- .gitlab-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7414f21f..a364ad5a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,8 +6,7 @@ stages: # - deploy - clean - - before_script: +before_script: - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - pip install docker-compose From f11a194eb2f9db7101834f1fc48e0ed0c7a3d9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:49:41 +0300 Subject: [PATCH 066/121] Fix --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a364ad5a..b4d0e418 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,8 @@ stages: - clean before_script: - - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make + - apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make +# - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - pip install docker-compose clean: From fea2764bde617193a3dce57d7af4f99a22d2afe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:51:09 +0300 Subject: [PATCH 067/121] Fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b4d0e418..a04a6a03 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ stages: - clean before_script: - - apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make + - sudo apt install -y python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make # - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - pip install docker-compose From 0961d3ee7360061b50bfaa26f2043d3cf4fe29c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:57:08 +0300 Subject: [PATCH 068/121] Fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a04a6a03..1e64cf22 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ stages: - clean before_script: - - sudo apt install -y python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make + - sudo apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make # - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - pip install docker-compose From 89c949c12c2200d4a1e66d0b423cfa0ebcd8620d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 10:59:04 +0300 Subject: [PATCH 069/121] Fix --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e64cf22..d36fdf49 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,10 +6,10 @@ stages: # - deploy - clean -before_script: - - sudo apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make -# - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make - - pip install docker-compose +#before_script: +# - sudo apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make +## - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make +# - pip install docker-compose clean: stage: clean From 78209a11015a9bacacc8d8e98e57fdfae77b3f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:02:43 +0300 Subject: [PATCH 070/121] pwd --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d36fdf49..f730977d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,7 @@ stages: clean: stage: clean script: + - pwd - docker-compose -f compose-ci.yml stop - docker-compose -f compose-ci.yml rm --force gm_app when: always From dd7a578a12c497d983e37e7409f93a2166afbca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:04:52 +0300 Subject: [PATCH 071/121] pwd --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f730977d..e3163c20 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,6 @@ stages: clean: stage: clean script: - - pwd - docker-compose -f compose-ci.yml stop - docker-compose -f compose-ci.yml rm --force gm_app when: always @@ -23,6 +22,7 @@ clean: buid: stage: build script: + - pwd - docker-compose -f compose-ci.yml build gm_app when: always From cddab519e4dba53ba38954e850230b71ea2b927f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:07:01 +0300 Subject: [PATCH 072/121] pwd --- .gitlab-ci.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e3163c20..88ec7752 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,21 +2,21 @@ image: docker:latest stages: - build - - test +# - test # - deploy - - clean +# - clean #before_script: # - sudo apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make ## - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make # - pip install docker-compose -clean: - stage: clean - script: - - docker-compose -f compose-ci.yml stop - - docker-compose -f compose-ci.yml rm --force gm_app - when: always +#clean: +# stage: clean +# script: +# - docker-compose -f compose-ci.yml stop +# - docker-compose -f compose-ci.yml rm --force gm_app +# when: always buid: @@ -27,11 +27,11 @@ buid: when: always -test: - stage: test - script: - - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput - when: always +#test: +# stage: test +# script: +# - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput +# when: always #clean: From 5c7af176fdaced52c170911c661afe43656a7bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:09:11 +0300 Subject: [PATCH 073/121] pwd --- .gitlab-ci.yml | 26 +++++++++++++------------- compose_ci.yml => compose-ci.yml | 0 2 files changed, 13 insertions(+), 13 deletions(-) rename compose_ci.yml => compose-ci.yml (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88ec7752..e3163c20 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,21 +2,21 @@ image: docker:latest stages: - build -# - test + - test # - deploy -# - clean + - clean #before_script: # - sudo apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make ## - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make # - pip install docker-compose -#clean: -# stage: clean -# script: -# - docker-compose -f compose-ci.yml stop -# - docker-compose -f compose-ci.yml rm --force gm_app -# when: always +clean: + stage: clean + script: + - docker-compose -f compose-ci.yml stop + - docker-compose -f compose-ci.yml rm --force gm_app + when: always buid: @@ -27,11 +27,11 @@ buid: when: always -#test: -# stage: test -# script: -# - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput -# when: always +test: + stage: test + script: + - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput + when: always #clean: diff --git a/compose_ci.yml b/compose-ci.yml similarity index 100% rename from compose_ci.yml rename to compose-ci.yml From 1755c192b6f71c0a8eee14a9e915a14d76db7f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:10:15 +0300 Subject: [PATCH 074/121] pwd --- compose-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose-ci.yml b/compose-ci.yml index 3c41f200..3920500f 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -1,4 +1,4 @@ -version: '3.5' +version: '2.0' services: # PostgreSQL database db: From fd65c986604c4ba024539ffaf1cb64cabbfa3abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:12:53 +0300 Subject: [PATCH 075/121] pwd --- compose-ci.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compose-ci.yml b/compose-ci.yml index 3920500f..fdab41b2 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -1,6 +1,5 @@ -version: '2.0' +version: '2' services: - # PostgreSQL database db: build: context: ./_dockerfiles/db @@ -91,11 +90,11 @@ services: ports: - "8000:8000" -volumes: - gm-db: - name: gm-db - - gm-media: - name: gm-media - - gm-esdata: \ No newline at end of file +#volumes: +# gm-db: +# name: gm-db +# +# gm-media: +# name: gm-media +# +# gm-esdata: \ No newline at end of file From d6a9fa6ac0864bcc0b627b5bbba96c15ab226d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:18:26 +0300 Subject: [PATCH 076/121] pwd --- compose-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compose-ci.yml b/compose-ci.yml index fdab41b2..83b20a44 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -90,11 +90,11 @@ services: ports: - "8000:8000" -#volumes: -# gm-db: +volumes: + gm-db: # name: gm-db # -# gm-media: + gm-media: # name: gm-media # -# gm-esdata: \ No newline at end of file + gm-esdata: \ No newline at end of file From 7ed853e7a4b6559358d39fde780c5acd07f4cab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:44:59 +0300 Subject: [PATCH 077/121] Fix fab --- .gitlab-ci.yml | 24 ------------------------ fabfile.py | 21 +++++++++++---------- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e3163c20..7668f645 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,6 @@ clean: buid: stage: build script: - - pwd - docker-compose -f compose-ci.yml build gm_app when: always @@ -34,29 +33,6 @@ test: when: always -#clean: -# stage: clean -# script: -# - docker-compose -f compose-ci.yml stop -# - docker-compose -f compose-ci.yml rm --force gm_app -# when: always -# -# -#buid: -# stage: build -# script: -# - docker-compose -f compose-ci.yml build gm_app -# when: always -# -# -#test: -# stage: test -# script: -# - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput -# when: always -# -# - #deploy-develop: # stage: deploy # only: diff --git a/fabfile.py b/fabfile.py index 643080be..90052b4c 100644 --- a/fabfile.py +++ b/fabfile.py @@ -1,18 +1,12 @@ -import os # NOQA +import os # NOQA from fabric.api import * # NOQA + user = 'gm' -env.root = '~/' -env.src = '~/project' - -env.default_branch = 'feature/develop_ci' -env.tmpdir = '~/tmp' - - env.roledefs = { 'develop': { - 'branch': env.default_branch, + 'branch': 'develop', 'hosts': ['%s@rock.spider.ru:31' % user, ] }, 'staging': { @@ -25,6 +19,13 @@ env.roledefs = { } +env.root = '~/' +env.src = '~/project' + +env.default_branch = 'develop' +env.tmpdir = '~/tmp' + + def fetch(branch=None): with cd(env.src): role = env.roles[0] @@ -72,4 +73,4 @@ def rev(): with hide('running', 'stdout'): with cd(env.src): commit = run('git rev-parse HEAD') - return local('git show -q %s' % commit) + return local('git show -q %s' % commit) \ No newline at end of file From 1b218a5c0754893c7d4343722e6f1ad841e72bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 11:51:25 +0300 Subject: [PATCH 078/121] Fix fab --- .gitlab-ci.yml | 15 ++++++++++----- fabfile.py | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7668f645..76a23eaf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,13 +1,18 @@ image: docker:latest stages: - - build - - test -# - deploy - - clean + - fabtest +# - build +# - test +## - deploy +# - clean + +fabtest: + stage: fabtest + script: + - fab test #before_script: -# - sudo apt install python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make ## - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make # - pip install docker-compose diff --git a/fabfile.py b/fabfile.py index 90052b4c..c06e42d0 100644 --- a/fabfile.py +++ b/fabfile.py @@ -2,6 +2,9 @@ import os # NOQA from fabric.api import * # NOQA +def test(): + print('Fab test') + user = 'gm' env.roledefs = { From 4d1d16637e6329bd0fe5e87928ae4032ed7249cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 12:01:46 +0300 Subject: [PATCH 079/121] add deploy --- .gitlab-ci.yml | 50 ++++++++++++++++++++++---------------------------- fabfile.py | 4 +--- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 76a23eaf..78bcc748 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,10 @@ image: docker:latest stages: - - fabtest -# - build -# - test -## - deploy -# - clean - -fabtest: - stage: fabtest - script: - - fab test + - build + - test + - deploy + - clean #before_script: ## - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make @@ -38,21 +32,21 @@ test: when: always -#deploy-develop: -# stage: deploy -# only: -# - feature/develop_ci -# script: -# - fab --roles=develop deploy -# environment: -# name: Develop -# -# -##deploy-staging: -## stage: deploy -## only: -## - master -## script: -## - fab --roles=staging deploy -## environment: -## name: Staging +deploy-develop: + stage: deploy + only: + - feature/develop_ci + script: + - fab --roles=develop deploy + environment: + name: Develop + + +deploy-staging: + stage: deploy + only: + - master + script: + - fab --roles=staging deploy + environment: + name: Staging diff --git a/fabfile.py b/fabfile.py index c06e42d0..d49b2e03 100644 --- a/fabfile.py +++ b/fabfile.py @@ -2,10 +2,8 @@ import os # NOQA from fabric.api import * # NOQA -def test(): - print('Fab test') -user = 'gm' +user = 'gitlab-runner' env.roledefs = { 'develop': { From ed551ea1ab216e0bea679073b018dc29c05a3747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 12:14:21 +0300 Subject: [PATCH 080/121] add deploy --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 78bcc748..a4d335b5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ buid: test: stage: test script: - - docker-compose -f compose-ci.yml run agro python manage.py test -v 3 --noinput + - docker-compose -f compose-ci.yml run gm_app python manage.py test -v 3 --noinput when: always From c1b51745186625cdf1a48a5e94b7cf6a76e0b775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 12:15:00 +0300 Subject: [PATCH 081/121] add deploy --- fabfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fabfile.py b/fabfile.py index d49b2e03..6f300c37 100644 --- a/fabfile.py +++ b/fabfile.py @@ -31,7 +31,6 @@ def fetch(branch=None): with cd(env.src): role = env.roles[0] run('git pull origin {}'.format(env.roledefs[role]['branch'])) - run('git submodule update') def migrate(): From 083c8a7eaa258a753397ddc5cd476e7d4aeb8a94 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 13:00:39 +0300 Subject: [PATCH 082/121] same queryset for Establishment list and detail views --- apps/establishment/views/web.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 1a0d5f58..0699d9d0 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -19,9 +19,12 @@ class EstablishmentMixinView: def get_queryset(self): """Overridden method 'get_queryset'.""" - return models.Establishment.objects.published() \ - .with_base_related() \ - .annotate_in_favorites(self.request.user) + qs = models.Establishment.objects.published() \ + .with_base_related() \ + .annotate_in_favorites(self.request.user) + if self.request.country_code: + qs = qs.by_country_code(self.request.country_code) + return qs class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): @@ -30,13 +33,6 @@ class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView): filter_class = filters.EstablishmentFilter serializer_class = serializers.EstablishmentBaseSerializer - def get_queryset(self): - """Overridden method 'get_queryset'.""" - qs = super(EstablishmentListView, self).get_queryset() - if self.request.country_code: - qs = qs.by_country_code(self.request.country_code) - return qs - class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView): """Resource for getting a establishment.""" @@ -45,9 +41,7 @@ class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView serializer_class = serializers.EstablishmentDetailSerializer def get_queryset(self): - return super().get_queryset() \ - .by_country_code(self.request.country_code) \ - .with_extended_related() + return super().get_queryset().with_extended_related() class EstablishmentRecentReviewListView(EstablishmentListView): From f46d4bd9f1173331d312f52bdddb7670bc657b5c Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 083/121] Review fix --- apps/tag/migrations/0005_tagcategory_name_indexing.py | 2 +- apps/tag/models.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/tag/migrations/0005_tagcategory_name_indexing.py b/apps/tag/migrations/0005_tagcategory_name_indexing.py index 839b5747..7fcb7f0f 100644 --- a/apps/tag/migrations/0005_tagcategory_name_indexing.py +++ b/apps/tag/migrations/0005_tagcategory_name_indexing.py @@ -12,7 +12,7 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='tagcategory', - name='name_indexing', + name='index_name', field=models.CharField(blank=True, max_length=255, null=True, verbose_name='indexing name'), ), ] diff --git a/apps/tag/models.py b/apps/tag/models.py index dbacbd2c..6e7ec65e 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -69,8 +69,8 @@ class TagCategory(TranslatedFieldsMixin, models.Model): on_delete=models.SET_NULL, null=True, default=None) public = models.BooleanField(default=False) - name_indexing = models.CharField(max_length=255, blank=True, null=True, - verbose_name=_('indexing name')) + index_name = models.CharField(max_length=255, blank=True, null=True, + verbose_name=_('indexing name'), unique=True) objects = TagCategoryQuerySet.as_manager() From 239ebc1dd6e7fa6d9cfa1f7c75efb218ada6533a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 14:11:02 +0300 Subject: [PATCH 084/121] add deploy --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a4d335b5..a617263b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,6 +28,8 @@ buid: test: stage: test script: + - docker-compose -f compose-ci.yml run db up -d + - docker-compose -f compose-ci.yml run db down - docker-compose -f compose-ci.yml run gm_app python manage.py test -v 3 --noinput when: always From 05cccf85eab1e3d0702fabf06b0b86f90cc9e7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 14:22:03 +0300 Subject: [PATCH 085/121] add deploy --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a617263b..36bfb0f9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,6 +34,7 @@ test: when: always + deploy-develop: stage: deploy only: From 81bd7b491230855d42551c7d7e15322445f8ba49 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Wed, 23 Oct 2019 11:58:33 +0000 Subject: [PATCH 086/121] Feature/currency sign --- .../migrations/0022_auto_20191023_1113.py | 37 +++++++++++++++++++ apps/main/models.py | 29 +++++++++------ apps/main/serializers.py | 25 +++++++------ 3 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 apps/main/migrations/0022_auto_20191023_1113.py diff --git a/apps/main/migrations/0022_auto_20191023_1113.py b/apps/main/migrations/0022_auto_20191023_1113.py new file mode 100644 index 00000000..0a190b23 --- /dev/null +++ b/apps/main/migrations/0022_auto_20191023_1113.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.4 on 2019-10-23 11:13 + +from django.db import migrations, models +import django.db.models.deletion +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0021_auto_20191023_0924'), + ] + + operations = [ + migrations.AddField( + model_name='currency', + name='sign', + field=models.CharField(default='?', max_length=1, verbose_name='sign'), + preserve_default=False, + ), + migrations.AddField( + model_name='currency', + name='slug', + field=models.SlugField(default='?', max_length=255, unique=True), + preserve_default=False, + ), + migrations.AddField( + model_name='sitesettings', + name='currency', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='main.Currency'), + ), + migrations.AlterField( + model_name='currency', + name='name', + field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='name'), + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index e5d947fd..bbdd6386 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -106,6 +106,22 @@ from utils.querysets import ContentTypeQuerySetMixin # +class Currency(models.Model, TranslatedFieldsMixin): + """Currency model.""" + name = TJSONField( + _('name'), null=True, blank=True, + default=None, help_text='{"en-GB":"some text"}') + sign = models.CharField(_('sign'), max_length=1) + slug = models.SlugField(max_length=255, unique=True) + + class Meta: + verbose_name = _('currency') + verbose_name_plural = _('currencies') + + def __str__(self): + return f'{self.name}' + + class SiteSettingsQuerySet(models.QuerySet): """Extended queryset for SiteSettings model.""" @@ -135,6 +151,7 @@ class SiteSettings(ProjectBaseMixin): verbose_name=_('Config')) ad_config = models.TextField(blank=True, null=True, default=None, verbose_name=_('AD config')) + currency = models.ForeignKey(Currency, on_delete=models.PROTECT, null=True, default=None) objects = SiteSettingsQuerySet.as_manager() @@ -257,18 +274,6 @@ class AwardType(models.Model): return self.name -class Currency(models.Model): - """Currency model.""" - name = models.CharField(_('name'), max_length=50) - - class Meta: - verbose_name = _('currency') - verbose_name_plural = _('currencies') - - def __str__(self): - return f'{self.name}' - - class CarouselQuerySet(models.QuerySet): """Carousel QuerySet.""" diff --git a/apps/main/serializers.py b/apps/main/serializers.py index e2523fa9..0e65741e 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -40,11 +40,24 @@ class SiteFeatureSerializer(serializers.ModelSerializer): ) +class CurrencySerializer(serializers.ModelSerializer): + """Currency serializer""" + + class Meta: + model = models.Currency + fields = [ + 'id', + 'name_translated', + 'sign' + ] + + class SiteSettingsSerializer(serializers.ModelSerializer): """Site settings serializer.""" published_features = SiteFeatureSerializer(source='published_sitefeatures', many=True, allow_null=True) + currency = CurrencySerializer() # todo: remove this country_code = serializers.CharField(source='subdomain', read_only=True) @@ -63,6 +76,7 @@ class SiteSettingsSerializer(serializers.ModelSerializer): 'config', 'ad_config', 'published_features', + 'currency' ) @@ -114,17 +128,6 @@ class AwardSerializer(AwardBaseSerializer): fields = AwardBaseSerializer.Meta.fields + ['award_type', ] -class CurrencySerializer(serializers.ModelSerializer): - """Currency serializer""" - - class Meta: - model = models.Currency - fields = [ - 'id', - 'name' - ] - - class CarouselListSerializer(serializers.ModelSerializer): """Serializer for retrieving list of carousel items.""" model_name = serializers.CharField() From 46fdf3e55d94a2efff19734656d9cc8210882517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 15:15:03 +0300 Subject: [PATCH 087/121] delete celery, redis --- compose-ci.yml | 63 +------------------------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) diff --git a/compose-ci.yml b/compose-ci.yml index 83b20a44..b412e7a5 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -14,59 +14,6 @@ services: volumes: - gm-db:/var/lib/postgresql/data/ - elasticsearch: - image: elasticsearch:7.3.1 - volumes: - - gm-esdata:/usr/share/elasticsearch/data - hostname: elasticsearch - ports: - - 9200:9200 - - 9300:9300 - environment: - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - - discovery.type=single-node - - xpack.security.enabled=false - - # Redis - redis: - image: redis:2.8.23 - ports: - - "6379:6379" - - # Celery - worker: - build: . - command: ./run_celery.sh - environment: - - SETTINGS_CONFIGURATION=local - - DB_NAME=postgres - - DB_USERNAME=postgres - - DB_HOSTNAME=db - - DB_PORT=5432 - - DB_PASSWORD=postgres - volumes: - - .:/code - links: - - db - - redis - - worker_beat: - build: . - command: ./run_celery_beat.sh - environment: - - SETTINGS_CONFIGURATION=local - - DB_NAME=postgres - - DB_USERNAME=postgres - - DB_HOSTNAME=db - - DB_PORT=5432 - - DB_PASSWORD=postgres - volumes: - - .:/code - links: - - db - - redis - - # App: G&M gm_app: build: . @@ -80,10 +27,6 @@ services: - DB_PASSWORD=postgres depends_on: - db - - redis - - worker - - worker_beat - - elasticsearch volumes: - .:/code - gm-media:/media-data @@ -92,9 +35,5 @@ services: volumes: gm-db: -# name: gm-db -# + gm-media: -# name: gm-media -# - gm-esdata: \ No newline at end of file From 6a9f11e98960892d3e9a0d1eb6792a0760b3452f Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 23 Oct 2019 15:31:04 +0300 Subject: [PATCH 088/121] merge from develop --- .../migrations/0014_merge_20191023_0959.py | 14 ++++++++ .../migrations/0023_merge_20191023_1000.py | 14 ++++++++ .../migrations/0024_newsgallery_is_main.py | 18 ++++++++++ apps/news/models.py | 16 ++++++--- apps/news/serializers.py | 33 +++++++++++++++++-- apps/news/views.py | 2 +- .../migrations/0003_merge_20191004_1401.py | 14 -------- .../migrations/0003_auto_20191002_0729.py | 17 ---------- project/settings/base.py | 15 ++++++++- project/settings/local.py | 8 ++--- 10 files changed, 108 insertions(+), 43 deletions(-) create mode 100644 apps/account/migrations/0014_merge_20191023_0959.py create mode 100644 apps/news/migrations/0023_merge_20191023_1000.py create mode 100644 apps/news/migrations/0024_newsgallery_is_main.py delete mode 100644 apps/rating/migrations/0003_merge_20191004_1401.py delete mode 100644 apps/timetable/migrations/0003_auto_20191002_0729.py diff --git a/apps/account/migrations/0014_merge_20191023_0959.py b/apps/account/migrations/0014_merge_20191023_0959.py new file mode 100644 index 00000000..07ab850e --- /dev/null +++ b/apps/account/migrations/0014_merge_20191023_0959.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-23 09:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0009_auto_20191002_0648'), + ('account', '0013_auto_20191016_0810'), + ] + + operations = [ + ] diff --git a/apps/news/migrations/0023_merge_20191023_1000.py b/apps/news/migrations/0023_merge_20191023_1000.py new file mode 100644 index 00000000..75dfd1b2 --- /dev/null +++ b/apps/news/migrations/0023_merge_20191023_1000.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-23 10:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0021_merge_20191002_1300'), + ('news', '0022_auto_20191021_1306'), + ] + + operations = [ + ] diff --git a/apps/news/migrations/0024_newsgallery_is_main.py b/apps/news/migrations/0024_newsgallery_is_main.py new file mode 100644 index 00000000..aa7fffd9 --- /dev/null +++ b/apps/news/migrations/0024_newsgallery_is_main.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.4 on 2019-10-23 10:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0023_merge_20191023_1000'), + ] + + operations = [ + migrations.AddField( + model_name='newsgallery', + name='is_main', + field=models.BooleanField(default=False, verbose_name='Is the main image'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 643a0143..246aadf0 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -146,10 +146,6 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('State')) is_highlighted = models.BooleanField(default=False, verbose_name=_('Is highlighted')) - image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Image URL path')) - preview_image_url = models.URLField(blank=True, null=True, default=None, - verbose_name=_('Preview image URL path')) template = models.PositiveIntegerField(choices=TEMPLATE_CHOICES, default=NEWSPAPER) address = models.ForeignKey('location.Address', blank=True, null=True, default=None, verbose_name=_('address'), @@ -197,10 +193,18 @@ class News(BaseAttributes, TranslatedFieldsMixin): def same_theme(self): return self.__class__.objects.same_theme(self)[:3] + @property + def main_image(self): + return self.news_gallery.main_images().first().image + class NewsGalleryQuerySet(models.QuerySet): """QuerySet for model News""" + def main_images(self): + """Return objects with flag is_main is True""" + return self.filter(is_main=True) + class NewsGallery(models.Model): @@ -212,6 +216,10 @@ class NewsGallery(models.Model): related_name='news_gallery', on_delete=models.SET_NULL, verbose_name=_('gallery')) + is_main = models.BooleanField(default=False, + verbose_name=_('Is the main image')) + + objects = NewsGalleryQuerySet.as_manager() class Meta: """NewsGallery meta class.""" diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 658e4203..12c76e67 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -134,7 +134,6 @@ class NewsBaseSerializer(ProjectModelSerializer): # related fields news_type = NewsTypeSerializer(read_only=True) - gallery = NewsImageSerializer(read_only=True, many=True) tags = TagBaseSerializer(read_only=True, many=True) class Meta: @@ -149,7 +148,34 @@ class NewsBaseSerializer(ProjectModelSerializer): 'news_type', 'tags', 'slug', - 'gallery', + ) + + +class NewsListSerializer(NewsBaseSerializer): + """List serializer for News model.""" + + # read only fields + title_translated = TranslatedField() + subtitle_translated = TranslatedField() + + # related fields + news_type = NewsTypeSerializer(read_only=True) + tags = TagBaseSerializer(read_only=True, many=True) + image = NewsImageSerializer(source='main_image', allow_null=True) + + class Meta: + """Meta class.""" + + model = models.News + fields = ( + 'id', + 'title_translated', + 'subtitle_translated', + 'is_highlighted', + 'news_type', + 'tags', + 'slug', + 'image', ) @@ -161,6 +187,7 @@ class NewsDetailSerializer(NewsBaseSerializer): author = UserBaseSerializer(source='created_by', read_only=True) state_display = serializers.CharField(source='get_state_display', read_only=True) + gallery = NewsImageSerializer(read_only=True, many=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" @@ -175,6 +202,7 @@ class NewsDetailSerializer(NewsBaseSerializer): 'state_display', 'author', 'country', + 'gallery', ) @@ -242,6 +270,7 @@ class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): model = models.NewsGallery fields = [ 'id', + 'is_main', ] def get_request_kwargs(self): diff --git a/apps/news/views.py b/apps/news/views.py index ba5719b0..c2a71eca 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -30,7 +30,7 @@ class NewsMixinView: class NewsListView(NewsMixinView, generics.ListAPIView): """News list view.""" - + serializer_class = serializers.NewsListSerializer filter_class = filters.NewsListFilterSet diff --git a/apps/rating/migrations/0003_merge_20191004_1401.py b/apps/rating/migrations/0003_merge_20191004_1401.py deleted file mode 100644 index f04cc893..00000000 --- a/apps/rating/migrations/0003_merge_20191004_1401.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-04 14:01 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('rating', '0002_auto_20191004_1217'), - ('rating', '0002_auto_20191004_0928'), - ] - - operations = [ - ] diff --git a/apps/timetable/migrations/0003_auto_20191002_0729.py b/apps/timetable/migrations/0003_auto_20191002_0729.py deleted file mode 100644 index 16196f74..00000000 --- a/apps/timetable/migrations/0003_auto_20191002_0729.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-02 07:29 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('timetable', '0002_auto_20190919_1124'), - ] - - operations = [ - migrations.AlterModelOptions( - name='timetable', - options={'ordering': ['weekday'], 'verbose_name': 'Timetable', 'verbose_name_plural': 'Timetables'}, - ), - ] diff --git a/project/settings/base.py b/project/settings/base.py index 0e078f9f..cd0a63b0 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -369,7 +369,6 @@ SORL_THUMBNAIL_ALIASES = { 'avatar_comments_web': {'geometry_string': '116x116', 'crop': 'center'}, # через контент эдитор в мобильном браузерe } -GEOIP_PATH = os.path.join(PROJECT_ROOT, 'geoip_db') # JWT SIMPLE_JWT = { @@ -455,3 +454,17 @@ LIMITING_QUERY_OBJECTS = QUERY_OUTPUT_OBJECTS * 3 # GEO # A Spatial Reference System Identifier GEO_DEFAULT_SRID = 4326 +GEOIP_PATH = os.path.join(PROJECT_ROOT, 'geoip_db') + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ +STATIC_ROOT = os.path.join(PUBLIC_ROOT, 'static') +STATIC_URL = '/static/' + +STATICFILES_DIRS = ( + os.path.join(PROJECT_ROOT, 'static'), +) + + +# MEDIA +MEDIA_LOCATION = 'media' \ No newline at end of file diff --git a/project/settings/local.py b/project/settings/local.py index 4e219bfb..e87b99f6 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -31,6 +31,10 @@ MEDIA_URL = f'{SCHEMA_URI}://{DOMAIN_URI}/{MEDIA_LOCATION}/' MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) +# SORL thumbnails +THUMBNAIL_DEBUG = True + + # LOGGING LOGGING = { 'version': 1, @@ -84,7 +88,3 @@ ELASTICSEARCH_INDEX_NAMES = { TESTING = sys.argv[1:2] == ['test'] if TESTING: ELASTICSEARCH_INDEX_NAMES = {} - - -# SORL thumbnails -THUMBNAIL_DEBUG = True From 9588c7cd881a61d92f85f7035d2ad31c09b669f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 15:35:23 +0300 Subject: [PATCH 089/121] delete celery, redis --- compose-ci.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compose-ci.yml b/compose-ci.yml index b412e7a5..f5697f6b 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -11,8 +11,8 @@ services: - POSTGRES_DB=postgres ports: - "5436:5432" - volumes: - - gm-db:/var/lib/postgresql/data/ +# volumes: +# - gm-db:/var/lib/postgresql/data/ # App: G&M gm_app: @@ -27,13 +27,13 @@ services: - DB_PASSWORD=postgres depends_on: - db - volumes: - - .:/code - - gm-media:/media-data +# volumes: +# - .:/code +# - gm-media:/media-data ports: - "8000:8000" -volumes: - gm-db: - - gm-media: +#volumes: +# gm-db: +# +# gm-media: From 7613f67f912571a2d7e54df950107ad38557148d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 15:44:58 +0300 Subject: [PATCH 090/121] delete celery, redis --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 36bfb0f9..758a52f9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ test: script: - docker-compose -f compose-ci.yml run db up -d - docker-compose -f compose-ci.yml run db down - - docker-compose -f compose-ci.yml run gm_app python manage.py test -v 3 --noinput + - docker-compose -f compose-ci.yml run gm_app python -B manage.py test -v 3 --noinput when: always From 052ff07ac7b8b9a825e53ce6e88f3c462a221a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 15:47:51 +0300 Subject: [PATCH 091/121] delete celery, redis --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 758a52f9..3c60aac4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: docker:latest +#image: docker:latest stages: - build From 5668da8e79e5da76ba1d10848997ea43b725389c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 16:03:50 +0300 Subject: [PATCH 092/121] delete celery, redis --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3c60aac4..758a52f9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -#image: docker:latest +image: docker:latest stages: - build From 649fcc67935e42ec2c991585d76c21be7906d86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 16:25:57 +0300 Subject: [PATCH 093/121] delete celery, redis --- compose-ci.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/compose-ci.yml b/compose-ci.yml index f5697f6b..a322ed56 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -11,8 +11,6 @@ services: - POSTGRES_DB=postgres ports: - "5436:5432" -# volumes: -# - gm-db:/var/lib/postgresql/data/ # App: G&M gm_app: @@ -27,13 +25,5 @@ services: - DB_PASSWORD=postgres depends_on: - db -# volumes: -# - .:/code -# - gm-media:/media-data ports: - "8000:8000" - -#volumes: -# gm-db: -# -# gm-media: From d8c99062ca480fe2040ed97025fb0bd09efd1041 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 16:28:42 +0300 Subject: [PATCH 094/121] fix singals --- apps/search_indexes/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index 77660a2c..3da50fb8 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -38,7 +38,7 @@ def update_document(sender, **kwargs): for establishment in establishments: registry.update(establishment) if model_name == 'establishmentsubtype': - if instance(instance, establishment_models.EstablishmentSubType): + if isinstance(instance, establishment_models.EstablishmentSubType): establishments = Establishment.objects.filter( establishment_subtypes=instance) for establishment in establishments: From bd3a809d8517b22f2427a76e49d155e30277c5fe Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 23 Oct 2019 16:47:57 +0300 Subject: [PATCH 095/121] fix migrations --- .../0039_establishmentsubtype_index_name.py | 2 +- .../migrations/0022_auto_20191023_1113.py | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/establishment/migrations/0039_establishmentsubtype_index_name.py b/apps/establishment/migrations/0039_establishmentsubtype_index_name.py index a29b1ae0..cf35010a 100644 --- a/apps/establishment/migrations/0039_establishmentsubtype_index_name.py +++ b/apps/establishment/migrations/0039_establishmentsubtype_index_name.py @@ -8,7 +8,7 @@ def fill_establishment_subtype(apps, schema_editor): # version than this migration expects. We use the historical version. EstablishmentSubType = apps.get_model('establishment', 'EstablishmentSubType') for n, et in enumerate(EstablishmentSubType.objects.all()): - et.index_name = f'Type {n}' + et.index_name = 'Type %s' % n et.save() diff --git a/apps/main/migrations/0022_auto_20191023_1113.py b/apps/main/migrations/0022_auto_20191023_1113.py index 0a190b23..6ed60051 100644 --- a/apps/main/migrations/0022_auto_20191023_1113.py +++ b/apps/main/migrations/0022_auto_20191023_1113.py @@ -5,6 +5,16 @@ import django.db.models.deletion import utils.models +def fill_currency_name(apps, schema_editor): + # We can't import the Person model directly as it may be a newer + # version than this migration expects. We use the historical version. + Currency = apps.get_model('main', 'Currency') + for currency in Currency.objects.all(): + currency.name_json = {'en-GB': currency.name} + currency.save() + + + class Migration(migrations.Migration): dependencies = [ @@ -29,9 +39,19 @@ class Migration(migrations.Migration): name='currency', field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='main.Currency'), ), - migrations.AlterField( + migrations.AddField( model_name='currency', - name='name', + name='name_json', field=utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='name'), ), + migrations.RunPython(fill_currency_name, migrations.RunPython.noop), + migrations.RemoveField( + model_name='currency', + name='name', + ), + migrations.RenameField( + model_name='currency', + old_name='name_json', + new_name='name', + ), ] From 1f44f21ede8e8f0a2394519a7b854dd864eb736f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 16:52:12 +0300 Subject: [PATCH 096/121] delete celery, redis --- .gitlab-ci.yml | 31 ++++++++++--------------------- fabfile.py | 11 ++--------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 758a52f9..6d852c07 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ image: docker:latest stages: - build - test - - deploy +# - deploy - clean #before_script: @@ -28,28 +28,17 @@ buid: test: stage: test script: - - docker-compose -f compose-ci.yml run db up -d - - docker-compose -f compose-ci.yml run db down + - docker-compose -f compose-ci.yml run db - docker-compose -f compose-ci.yml run gm_app python -B manage.py test -v 3 --noinput when: always -deploy-develop: - stage: deploy - only: - - feature/develop_ci - script: - - fab --roles=develop deploy - environment: - name: Develop - - -deploy-staging: - stage: deploy - only: - - master - script: - - fab --roles=staging deploy - environment: - name: Staging +#deploy-develop: +# stage: deploy +# only: +# - feature/develop_ci +# script: +# - fab --roles=develop deploy +# environment: +# name: Develop diff --git a/fabfile.py b/fabfile.py index 6f300c37..9ad7f871 100644 --- a/fabfile.py +++ b/fabfile.py @@ -3,19 +3,12 @@ from fabric.api import * # NOQA -user = 'gitlab-runner' +user = 'gm' env.roledefs = { 'develop': { 'branch': 'develop', - 'hosts': ['%s@rock.spider.ru:31' % user, ] - }, - 'staging': { - 'hosts': ['%s@5.200.53.99' % user, ] - }, - 'production': { - 'branch': 'master', - 'hosts': ['%s@87.226.166.80' % user, ] + 'hosts': ['%s@95.213.204.126' % user, ] } } From d1e40101b44f57f6953f508ada5bdd39558df0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 16:59:11 +0300 Subject: [PATCH 097/121] delete celery, redis --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d852c07..75e0f8d4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ buid: test: stage: test script: - - docker-compose -f compose-ci.yml run db + - docker-compose -f compose-ci.yml up db - docker-compose -f compose-ci.yml run gm_app python -B manage.py test -v 3 --noinput when: always From b1169675a8c9915d7a2aadfb621780e7ca8e93fa Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Wed, 23 Oct 2019 17:03:24 +0300 Subject: [PATCH 098/121] fix CurrencySerializer --- apps/main/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 0e65741e..8975af14 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from advertisement.serializers.web import AdvertisementSerializer from location.serializers import CountrySerializer from main import models -from utils.serializers import TranslatedField +from utils.serializers import ProjectModelSerializer class FeatureSerializer(serializers.ModelSerializer): @@ -40,7 +40,7 @@ class SiteFeatureSerializer(serializers.ModelSerializer): ) -class CurrencySerializer(serializers.ModelSerializer): +class CurrencySerializer(ProjectModelSerializer): """Currency serializer""" class Meta: From 7e93aafc3f8d8d7eb77fb6a3a1363567a1246b25 Mon Sep 17 00:00:00 2001 From: "a.feteleu" Date: Wed, 23 Oct 2019 17:05:37 +0300 Subject: [PATCH 099/121] added merge migrations --- .../migrations/0015_merge_20191023_1317.py | 14 ++++++++++++++ apps/news/migrations/0025_merge_20191023_1317.py | 15 +++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 apps/account/migrations/0015_merge_20191023_1317.py create mode 100644 apps/news/migrations/0025_merge_20191023_1317.py diff --git a/apps/account/migrations/0015_merge_20191023_1317.py b/apps/account/migrations/0015_merge_20191023_1317.py new file mode 100644 index 00000000..f014afb2 --- /dev/null +++ b/apps/account/migrations/0015_merge_20191023_1317.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-23 13:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0014_merge_20191023_0959'), + ('account', '0012_merge_20191015_0912'), + ] + + operations = [ + ] diff --git a/apps/news/migrations/0025_merge_20191023_1317.py b/apps/news/migrations/0025_merge_20191023_1317.py new file mode 100644 index 00000000..96cb3980 --- /dev/null +++ b/apps/news/migrations/0025_merge_20191023_1317.py @@ -0,0 +1,15 @@ +# Generated by Django 2.2.4 on 2019-10-23 13:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0024_newsgallery_is_main'), + ('news', '0023_auto_20191023_0903'), + ('news', '0022_merge_20191015_0912'), + ] + + operations = [ + ] From 125c438f199359a556f726dba903aad053b3f789 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 100/121] Review fix #2 --- apps/tag/migrations/0005_tagcategory_name_indexing.py | 2 +- apps/tag/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/tag/migrations/0005_tagcategory_name_indexing.py b/apps/tag/migrations/0005_tagcategory_name_indexing.py index 7fcb7f0f..2547481a 100644 --- a/apps/tag/migrations/0005_tagcategory_name_indexing.py +++ b/apps/tag/migrations/0005_tagcategory_name_indexing.py @@ -13,6 +13,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='tagcategory', name='index_name', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='indexing name'), + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='indexing name', unique=True), ), ] diff --git a/apps/tag/models.py b/apps/tag/models.py index 6e7ec65e..1ee594ad 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -70,7 +70,7 @@ class TagCategory(TranslatedFieldsMixin, models.Model): default=None) public = models.BooleanField(default=False) index_name = models.CharField(max_length=255, blank=True, null=True, - verbose_name=_('indexing name'), unique=True) + verbose_name=_('indexing name'), unique=True) objects = TagCategoryQuerySet.as_manager() From 40e64eafe71e146e6ff646eddc37895cec817030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 17:08:44 +0300 Subject: [PATCH 101/121] delete celery, redis --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 75e0f8d4..fe878f7f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,8 +28,8 @@ buid: test: stage: test script: - - docker-compose -f compose-ci.yml up db - - docker-compose -f compose-ci.yml run gm_app python -B manage.py test -v 3 --noinput +# - docker-compose -f compose-ci.yml build + - docker-compose -f compose-ci.yml run gm_app python manage.py test -v 3 --noinput when: always From 1092ec18847d15ac50227819a7077a8448638d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 17:24:21 +0300 Subject: [PATCH 102/121] delete celery, redis --- compose-ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compose-ci.yml b/compose-ci.yml index a322ed56..c2a154cd 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -12,6 +12,19 @@ services: ports: - "5436:5432" + elasticsearch: + image: elasticsearch:7.3.1 + volumes: + - gm-esdata:/usr/share/elasticsearch/data + hostname: elasticsearch + ports: + - 9200:9200 + - 9300:9300 + environment: + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - discovery.type=single-node + - xpack.security.enabled=false + # App: G&M gm_app: build: . @@ -25,5 +38,6 @@ services: - DB_PASSWORD=postgres depends_on: - db + - elasticsearch ports: - "8000:8000" From 2e99431bdd1d0a5eff78b561bd39dc31e94037ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 17:25:28 +0300 Subject: [PATCH 103/121] elastic --- compose-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/compose-ci.yml b/compose-ci.yml index c2a154cd..53ede064 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -14,8 +14,6 @@ services: elasticsearch: image: elasticsearch:7.3.1 - volumes: - - gm-esdata:/usr/share/elasticsearch/data hostname: elasticsearch ports: - 9200:9200 From 40357c20b6403e0efed74b0ccd329372101f08e4 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 104/121] Changes from gm-148 --- apps/tag/migrations/0004_merge_20191021_1138.py | 14 ++++++++++++++ apps/timetable/serialziers.py | 1 + project/templates/news/news_email.html | 12 ++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 apps/tag/migrations/0004_merge_20191021_1138.py diff --git a/apps/tag/migrations/0004_merge_20191021_1138.py b/apps/tag/migrations/0004_merge_20191021_1138.py new file mode 100644 index 00000000..6298ce4f --- /dev/null +++ b/apps/tag/migrations/0004_merge_20191021_1138.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.4 on 2019-10-21 11:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0003_auto_20191018_0758'), + ('tag', '0003_tag_priority'), + ] + + operations = [ + ] diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 76a37257..babe33c1 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -78,6 +78,7 @@ class ScheduleCreateSerializer(ScheduleRUDSerializer): establishment.schedule.add(instance) return instance + class TimetableSerializer(serializers.ModelSerializer): """Serailzier for Timetable model.""" weekday_display = serializers.CharField(source='get_weekday_display', diff --git a/project/templates/news/news_email.html b/project/templates/news/news_email.html index 6fe14060..0227b9de 100644 --- a/project/templates/news/news_email.html +++ b/project/templates/news/news_email.html @@ -7,7 +7,7 @@ {{ title }} - +
@@ -24,19 +24,19 @@ -
+
{{ title }}
{% if not image_url is None %}
- +
{% endif %} -
+
{{ description | safe }}
- -
+ +
Go to news
From 47da9929cb5ef6c12881ac81fa9ef5a4d5ab0a2e Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 105/121] Some fixes --- apps/search_indexes/documents/news.py | 1 - apps/tag/migrations/0004_merge_20191021_1138.py | 14 -------------- 2 files changed, 15 deletions(-) delete mode 100644 apps/tag/migrations/0004_merge_20191021_1138.py diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index 21c59e68..cd6fc089 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -24,7 +24,6 @@ class NewsDocument(Document): country = fields.ObjectField(properties={'id': fields.IntegerField(), 'code': fields.KeywordField()}) web_url = fields.KeywordField(attr='web_url') - preview_image_url = fields.TextField(attr='preview_image_url') tags = fields.ObjectField( properties={ 'id': fields.IntegerField(attr='id'), diff --git a/apps/tag/migrations/0004_merge_20191021_1138.py b/apps/tag/migrations/0004_merge_20191021_1138.py deleted file mode 100644 index 6298ce4f..00000000 --- a/apps/tag/migrations/0004_merge_20191021_1138.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.4 on 2019-10-21 11:38 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('tag', '0003_auto_20191018_0758'), - ('tag', '0003_tag_priority'), - ] - - operations = [ - ] From d5609e4cb89f724e60aae4b98d485de4e9004fc2 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 106/121] Works now filter first part --- .../migrations/0040_establishment_tz.py | 33 +++++++++++++++++++ apps/establishment/models.py | 9 ++--- apps/establishment/serializers/back.py | 1 + apps/location/models.py | 6 ---- apps/search_indexes/views.py | 5 ++- project/settings/base.py | 1 + requirements/base.txt | 1 + 7 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 apps/establishment/migrations/0040_establishment_tz.py diff --git a/apps/establishment/migrations/0040_establishment_tz.py b/apps/establishment/migrations/0040_establishment_tz.py new file mode 100644 index 00000000..d5a383d8 --- /dev/null +++ b/apps/establishment/migrations/0040_establishment_tz.py @@ -0,0 +1,33 @@ +# Generated by Django 2.2.4 on 2019-10-21 17:33 + +import timezone_field.fields +from django.db import migrations +from django.conf import settings +from timezonefinder import TimezoneFinder +from establishment.models import Establishment + + +def fill_timezones(apps, schema_editor): + tf = TimezoneFinder(in_memory=True) + for establishment in Establishment.objects.prefetch_related('address').all(): + if establishment.address and establishment.address.latitude and establishment.address.longitude: + establishment.tz = tf.certain_timezone_at(lng=establishment.address.longitude, + lat=establishment.address.latitude) + establishment.save() + + +class Migration(migrations.Migration): + + + dependencies = [ + ('establishment', '0039_establishmentsubtype_index_name'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='tz', + field=timezone_field.fields.TimeZoneField(default=settings.TIME_ZONE), + ), + migrations.RunPython(fill_timezones, migrations.RunPython.noop), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index c01d8613..7de88d91 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -14,7 +14,6 @@ from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField -from pytz import timezone as py_tz from collection.models import Collection from location.models import Address @@ -22,6 +21,7 @@ from main.models import Award from review.models import Review from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, TranslatedFieldsMixin, BaseAttributes) +from timezone_field import TimeZoneField # todo: establishment type&subtypes check @@ -354,6 +354,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): blank=True, null=True, default=None) slug = models.SlugField(unique=True, max_length=255, null=True, verbose_name=_('Establishment slug')) + tz = TimeZoneField(default=settings.TIME_ZONE) awards = generic.GenericRelation(to='main.Award', related_query_name='establishment') tags = models.ManyToManyField('tag.Tag', related_name='establishments', @@ -429,13 +430,13 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): """ Used for indexing working by day """ return [ret.weekday for ret in self.schedule.all() if ret.works_at_afternoon] - # @property + @property def works_now(self): """ Is establishment working now """ - now_at_est_tz = datetime.now(tz=py_tz(self.address.tz_name)) + now_at_est_tz = datetime.now(tz=self.tz) current_week = now_at_est_tz.weekday() schedule_for_today = self.schedule.filter(weekday=current_week).first() - if schedule_for_today is None: + if schedule_for_today is None or schedule_for_today.closed_at is None or schedule_for_today.opening_at is None: return False time_at_est_tz = now_at_est_tz.time() return schedule_for_today.closed_at > time_at_est_tz > schedule_for_today.opening_at diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 59725710..714dd62d 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -41,6 +41,7 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer): 'is_publish', 'guestonline_id', 'lastable_id', + 'tz', ] diff --git a/apps/location/models.py b/apps/location/models.py index 3591d4df..da645de6 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -6,7 +6,6 @@ from django.db.transaction import on_commit from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ -from timezonefinder import TimezoneFinder from translation.models import Language from utils.models import ProjectBaseMixin, SVGImageMixin, TranslatedFieldsMixin, TJSONField @@ -114,11 +113,6 @@ class Address(models.Model): def get_street_name(self): return self.street_name_1 or self.street_name_2 - @property - def tz_name(self): - tf = TimezoneFinder(in_memory=True) - return tf.certain_timezone_at(lng=self.latitude, lat=self.longitude) - @property def latitude(self): return self.coordinates.y if self.coordinates else float(0) diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py index 913c2d95..25205bac 100644 --- a/apps/search_indexes/views.py +++ b/apps/search_indexes/views.py @@ -133,14 +133,13 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet): 'works_evening': { 'field': 'works_evening', 'lookups': [ - constants.LOOKUP_FILTER_TERM, + constants.LOOKUP_QUERY_IN, ], }, 'works_now': { 'field': 'works_now', 'lookups': [ - constants.LOOKUP_FILTER_EXISTS, - constants.LOOKUP_QUERY_IN, + constants.LOOKUP_FILTER_TERM, ] }, } diff --git a/project/settings/base.py b/project/settings/base.py index bec8fc2b..02df804e 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -95,6 +95,7 @@ EXTERNAL_APPS = [ 'rest_framework_simplejwt.token_blacklist', 'solo', 'phonenumber_field', + 'timezone_field', 'storages', 'sorl.thumbnail', 'timezonefinder' diff --git a/requirements/base.txt b/requirements/base.txt index dbb3b20e..c95055de 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -18,6 +18,7 @@ django-filter==2.1.0 djangorestframework-xml geoip2==2.9.0 django-phonenumber-field[phonenumbers]==2.1.0 +django-timezone-field==3.1 # auth socials django-rest-framework-social-oauth2==1.1.0 From ce5987de9528da4d891a9c4870b7c61eb632a551 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 107/121] Attach establishment timestamps as command --- apps/establishment/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../commands/attach_establishments_tz.py | 23 +++++++++++++++++++ .../migrations/0040_establishment_tz.py | 12 ---------- 4 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 apps/establishment/management/__init__.py create mode 100644 apps/establishment/management/commands/__init__.py create mode 100644 apps/establishment/management/commands/attach_establishments_tz.py diff --git a/apps/establishment/management/__init__.py b/apps/establishment/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/establishment/management/commands/__init__.py b/apps/establishment/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/establishment/management/commands/attach_establishments_tz.py b/apps/establishment/management/commands/attach_establishments_tz.py new file mode 100644 index 00000000..58be04cb --- /dev/null +++ b/apps/establishment/management/commands/attach_establishments_tz.py @@ -0,0 +1,23 @@ +from django.core.management.base import BaseCommand +from pytz import timezone as py_tz +from timezonefinder import TimezoneFinder +from establishment.models import Establishment + + +class Command(BaseCommand): + help = 'Attach correct timestamps according to coordinates to existing establishments' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.tf = TimezoneFinder(in_memory=True) + + def handle(self, *args, **options): + for establishment in Establishment.objects.prefetch_related('address').all(): + if establishment.address and establishment.address.latitude and establishment.address.longitude: + establishment.tz = py_tz(self.tf.certain_timezone_at(lng=establishment.address.longitude, + lat=establishment.address.latitude)) + establishment.save() + self.stdout.write(self.style.SUCCESS(f'Attached timezone {establishment.tz} to {establishment}')) + else: + self.stdout.write(self.style.WARNING(f'Establishment {establishment} has no coordinates' + f'passing...')) diff --git a/apps/establishment/migrations/0040_establishment_tz.py b/apps/establishment/migrations/0040_establishment_tz.py index d5a383d8..75f54c06 100644 --- a/apps/establishment/migrations/0040_establishment_tz.py +++ b/apps/establishment/migrations/0040_establishment_tz.py @@ -3,17 +3,6 @@ import timezone_field.fields from django.db import migrations from django.conf import settings -from timezonefinder import TimezoneFinder -from establishment.models import Establishment - - -def fill_timezones(apps, schema_editor): - tf = TimezoneFinder(in_memory=True) - for establishment in Establishment.objects.prefetch_related('address').all(): - if establishment.address and establishment.address.latitude and establishment.address.longitude: - establishment.tz = tf.certain_timezone_at(lng=establishment.address.longitude, - lat=establishment.address.latitude) - establishment.save() class Migration(migrations.Migration): @@ -29,5 +18,4 @@ class Migration(migrations.Migration): name='tz', field=timezone_field.fields.TimeZoneField(default=settings.TIME_ZONE), ), - migrations.RunPython(fill_timezones, migrations.RunPython.noop), ] From 711d536c98cf8a96473681101b3f80c44ab3b99d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 108/121] Update migration --- .../{0040_establishment_tz.py => 0041_establishment_tz.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename apps/establishment/migrations/{0040_establishment_tz.py => 0041_establishment_tz.py} (86%) diff --git a/apps/establishment/migrations/0040_establishment_tz.py b/apps/establishment/migrations/0041_establishment_tz.py similarity index 86% rename from apps/establishment/migrations/0040_establishment_tz.py rename to apps/establishment/migrations/0041_establishment_tz.py index 75f54c06..a06bbad9 100644 --- a/apps/establishment/migrations/0040_establishment_tz.py +++ b/apps/establishment/migrations/0041_establishment_tz.py @@ -9,7 +9,7 @@ class Migration(migrations.Migration): dependencies = [ - ('establishment', '0039_establishmentsubtype_index_name'), + ('establishment', '0040_employee_tags'), ] operations = [ From 07ac06f405f0a4473cd2a00b5d15e9d88dff60ea Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 109/121] Update migration --- .../{0041_establishment_tz.py => 0042_establishment_tz.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename apps/establishment/migrations/{0041_establishment_tz.py => 0042_establishment_tz.py} (88%) diff --git a/apps/establishment/migrations/0041_establishment_tz.py b/apps/establishment/migrations/0042_establishment_tz.py similarity index 88% rename from apps/establishment/migrations/0041_establishment_tz.py rename to apps/establishment/migrations/0042_establishment_tz.py index a06bbad9..e804242f 100644 --- a/apps/establishment/migrations/0041_establishment_tz.py +++ b/apps/establishment/migrations/0042_establishment_tz.py @@ -9,7 +9,7 @@ class Migration(migrations.Migration): dependencies = [ - ('establishment', '0040_employee_tags'), + ('establishment', '0041_auto_20191023_0920'), ] operations = [ From 5a0ea251caa0ea69f7e5971f48bd5eae7adc0d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 17:35:33 +0300 Subject: [PATCH 110/121] elastic --- compose-ci.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/compose-ci.yml b/compose-ci.yml index 53ede064..66c767c4 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -23,6 +23,41 @@ services: - discovery.type=single-node - xpack.security.enabled=false + # Redis + redis: + image: redis:2.8.23 + ports: + - "6379:6379" + + # Celery + worker: + build: . + command: ./run_celery.sh + environment: + - SETTINGS_CONFIGURATION=local + - DB_NAME=postgres + - DB_USERNAME=postgres + - DB_HOSTNAME=db + - DB_PORT=5432 + - DB_PASSWORD=postgres + links: + - db + - redis + + worker_beat: + build: . + command: ./run_celery_beat.sh + environment: + - SETTINGS_CONFIGURATION=local + - DB_NAME=postgres + - DB_USERNAME=postgres + - DB_HOSTNAME=db + - DB_PORT=5432 + - DB_PASSWORD=postgres + links: + - db + - redis + # App: G&M gm_app: build: . From 18062e60a560c56976ab5cf16315b499b5979c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 23 Oct 2019 17:36:13 +0300 Subject: [PATCH 111/121] elastic --- compose-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compose-ci.yml b/compose-ci.yml index 66c767c4..94079822 100644 --- a/compose-ci.yml +++ b/compose-ci.yml @@ -71,6 +71,9 @@ services: - DB_PASSWORD=postgres depends_on: - db + - redis + - worker + - worker_beat - elasticsearch ports: - "8000:8000" From 1e3c80ad0cda529a5f5c0575224308f03833c38e Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 23 Oct 2019 17:40:42 +0300 Subject: [PATCH 112/121] small fix --- apps/gallery/models.py | 2 +- apps/main/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/gallery/models.py b/apps/gallery/models.py index bf3870cb..baed48fc 100644 --- a/apps/gallery/models.py +++ b/apps/gallery/models.py @@ -37,7 +37,7 @@ class Image(ProjectBaseMixin, SORLImageMixin, PlatformMixin): def __str__(self): """String representation""" - return f'{self.title}' + return f'{self.id}' def delete_image(self, completely: bool = True): """ diff --git a/apps/main/models.py b/apps/main/models.py index 3a54bedf..6af4e4d8 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -106,7 +106,7 @@ from utils.querysets import ContentTypeQuerySetMixin # -class Currency(models.Model, TranslatedFieldsMixin): +class Currency(TranslatedFieldsMixin, models.Model): """Currency model.""" name = TJSONField( _('name'), null=True, blank=True, From a8001c44b13de1f8b466cc74ab8b88b6378618e7 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Wed, 23 Oct 2019 17:50:08 +0300 Subject: [PATCH 113/121] added validation rule to NewsBackOfficeGallerySerializer --- apps/news/serializers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 417db72e..6814965a 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -282,6 +282,7 @@ class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): """Override validate method.""" news_pk = self.get_request_kwargs().get('pk') image_id = self.get_request_kwargs().get('image_id') + is_main = attrs.get('is_main') news_qs = models.News.objects.filter(pk=news_pk) image_qs = Image.objects.filter(id=image_id) @@ -297,6 +298,9 @@ class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): if news.news_gallery.filter(image=image).exists(): raise serializers.ValidationError({'detail': _('Image is already added')}) + if is_main and news.news_gallery.main_images().exists(): + raise serializers.ValidationError({'detail': _('Main image is already added')}) + attrs['news'] = news attrs['image'] = image From b2341194bc23684f70b402d2479ed1e181394d79 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 16 Oct 2019 14:11:07 +0300 Subject: [PATCH 114/121] fix serializer --- apps/tag/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tag/serializers.py b/apps/tag/serializers.py index 445042c7..790c8926 100644 --- a/apps/tag/serializers.py +++ b/apps/tag/serializers.py @@ -49,7 +49,7 @@ class TagCategoryBaseSerializer(serializers.ModelSerializer): fields = ( 'id', 'label_translated', - 'name_indexing', + 'index_name', 'tags', ) From f45f69e0e561828c4af907cc72e524356b9defbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Thu, 24 Oct 2019 09:47:29 +0300 Subject: [PATCH 115/121] add deploy --- .gitlab-ci.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fe878f7f..94dda56d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,12 +3,9 @@ image: docker:latest stages: - build - test -# - deploy + - deploy - clean -#before_script: -## - apk add --update python python-dev py-pip gcc libc-dev libffi-dev openssl-dev make -# - pip install docker-compose clean: stage: clean @@ -28,17 +25,16 @@ buid: test: stage: test script: -# - docker-compose -f compose-ci.yml build - docker-compose -f compose-ci.yml run gm_app python manage.py test -v 3 --noinput when: always -#deploy-develop: -# stage: deploy -# only: -# - feature/develop_ci -# script: -# - fab --roles=develop deploy -# environment: -# name: Develop +deploy-develop: + stage: deploy + only: + - develop + script: + - fab --roles=develop deploy + environment: + name: Develop \ No newline at end of file From 890d1004e14231914522b8d3c917d98e155b077c Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Thu, 24 Oct 2019 07:44:47 +0000 Subject: [PATCH 116/121] Added view for chosen tags --- apps/tag/models.py | 10 ++++++++++ apps/tag/urls/web.py | 1 + apps/tag/views.py | 13 ++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/tag/models.py b/apps/tag/models.py index f1fd4c37..26079849 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -5,6 +5,14 @@ from configuration.models import TranslationSettings from utils.models import TJSONField, TranslatedFieldsMixin +class TagQuerySet(models.QuerySet): + def filter_chosen(self): + return self.exclude(priority__isnull=True) + + def order_by_priority(self): + return self.order_by('priority') + + class Tag(TranslatedFieldsMixin, models.Model): """Tag model.""" @@ -16,6 +24,8 @@ class Tag(TranslatedFieldsMixin, models.Model): verbose_name=_('Category')) priority = models.IntegerField(unique=True, null=True, default=None) + objects = TagQuerySet.as_manager() + class Meta: """Meta class.""" diff --git a/apps/tag/urls/web.py b/apps/tag/urls/web.py index c99253eb..ec63931e 100644 --- a/apps/tag/urls/web.py +++ b/apps/tag/urls/web.py @@ -7,6 +7,7 @@ app_name = 'tag' router = SimpleRouter() router.register(r'categories', views.TagCategoryViewSet) +router.register(r'chosen_tags', views.ChosenTagsView, basename='Tag') urlpatterns = [ diff --git a/apps/tag/views.py b/apps/tag/views.py index 2a0ff0f5..6d98f39f 100644 --- a/apps/tag/views.py +++ b/apps/tag/views.py @@ -1,11 +1,22 @@ """Tag views.""" -from rest_framework import viewsets, mixins, status +from rest_framework import viewsets, mixins, status, generics from rest_framework.decorators import action from rest_framework.response import Response from tag import filters, models, serializers from rest_framework import permissions +class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet): + pagination_class = None + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.TagBaseSerializer + + def get_queryset(self): + return models.Tag.objects\ + .filter_chosen() \ + .order_by_priority() + + # User`s views & viewsets class TagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): """ViewSet for TagCategory model.""" From 2756529c7f95cd3df1f22be07a4d49415c201d0b Mon Sep 17 00:00:00 2001 From: "a.feteleu" Date: Thu, 24 Oct 2019 11:34:00 +0300 Subject: [PATCH 117/121] fix migrations in userroles --- .../migrations/0016_auto_20191024_0830.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 apps/account/migrations/0016_auto_20191024_0830.py diff --git a/apps/account/migrations/0016_auto_20191024_0830.py b/apps/account/migrations/0016_auto_20191024_0830.py new file mode 100644 index 00000000..63373499 --- /dev/null +++ b/apps/account/migrations/0016_auto_20191024_0830.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.4 on 2019-10-24 08:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0015_merge_20191023_1317'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='role', + field=models.PositiveIntegerField(choices=[(1, 'Standard user'), (2, 'Comments moderator'), (3, 'Country admin'), (4, 'Content page manager'), (5, 'Establishment manager'), (6, 'Reviewer manager'), (7, 'Restaurant reviewer')], verbose_name='Role'), + ), + migrations.AlterField( + model_name='userrole', + name='establishment', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='establishment.Establishment', verbose_name='Establishment'), + ), + ] From 0edc2fae38aa3060c38c03ec55454f23d924296a Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 24 Oct 2019 12:03:41 +0300 Subject: [PATCH 118/121] Clean news serializer --- apps/news/serializers.py | 24 ++---------------------- apps/news/views.py | 4 +--- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 6814965a..c23ad2c3 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -129,11 +129,6 @@ class NewsTypeSerializer(serializers.ModelSerializer): class NewsBaseSerializer(ProjectModelSerializer): """Base serializer for News model.""" - # read only fields - title_translated = TranslatedField() - subtitle_translated = TranslatedField() - - # related fields news_type = NewsTypeSerializer(read_only=True) tags = TagBaseSerializer(read_only=True, many=True) @@ -155,27 +150,12 @@ class NewsBaseSerializer(ProjectModelSerializer): class NewsListSerializer(NewsBaseSerializer): """List serializer for News model.""" - # read only fields - title_translated = TranslatedField() - subtitle_translated = TranslatedField() - - # related fields - news_type = NewsTypeSerializer(read_only=True) - tags = TagBaseSerializer(read_only=True, many=True) image = NewsImageSerializer(source='main_image', allow_null=True) - class Meta: + class Meta(NewsBaseSerializer.Meta): """Meta class.""" - model = models.News - fields = ( - 'id', - 'title_translated', - 'subtitle_translated', - 'is_highlighted', - 'news_type', - 'tags', - 'slug', + fields = NewsBaseSerializer.Meta.fields + ( 'image', ) diff --git a/apps/news/views.py b/apps/news/views.py index c2a71eca..e0034afa 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,6 +1,4 @@ """News app views.""" -from django.shortcuts import get_object_or_404 -from rest_framework import generics, permissions from django.conf import settings from django.db.transaction import on_commit from django.shortcuts import get_object_or_404 @@ -17,7 +15,6 @@ class NewsMixinView: """News mixin.""" permission_classes = (permissions.AllowAny, ) - serializer_class = serializers.NewsBaseSerializer def get_queryset(self, *args, **kwargs): """Override get_queryset method.""" @@ -30,6 +27,7 @@ class NewsMixinView: class NewsListView(NewsMixinView, generics.ListAPIView): """News list view.""" + serializer_class = serializers.NewsListSerializer filter_class = filters.NewsListFilterSet From 6c4f1a2e40d37613583369e21f4817a9b73baa0d Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 24 Oct 2019 12:08:44 +0300 Subject: [PATCH 119/121] Fix news serializer --- apps/news/serializers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index c23ad2c3..cd7afff4 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -129,6 +129,8 @@ class NewsTypeSerializer(serializers.ModelSerializer): class NewsBaseSerializer(ProjectModelSerializer): """Base serializer for News model.""" + title_translated = TranslatedField() + subtitle_translated = TranslatedField() news_type = NewsTypeSerializer(read_only=True) tags = TagBaseSerializer(read_only=True, many=True) @@ -246,8 +248,10 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): """Serializer class for model NewsGallery.""" + class Meta: """Meta class""" + model = models.NewsGallery fields = [ 'id', From 7fa52d770e43f91dd74a09e674832a2c87007235 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 24 Oct 2019 12:17:58 +0300 Subject: [PATCH 120/121] added missing migration for app news --- .../migrations/0026_auto_20191024_0913.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 apps/news/migrations/0026_auto_20191024_0913.py diff --git a/apps/news/migrations/0026_auto_20191024_0913.py b/apps/news/migrations/0026_auto_20191024_0913.py new file mode 100644 index 00000000..bcad2ce2 --- /dev/null +++ b/apps/news/migrations/0026_auto_20191024_0913.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.4 on 2019-10-24 09:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0025_merge_20191023_1317'), + ] + + operations = [ + migrations.RemoveField( + model_name='news', + name='image_url', + ), + migrations.RemoveField( + model_name='news', + name='preview_image_url', + ), + ] From 636bc43a35ff59d38c098eee80e0a3ac3c3b8840 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 24 Oct 2019 12:38:44 +0300 Subject: [PATCH 121/121] remove playlist from news model --- apps/favorites/tests.py | 2 +- .../migrations/0027_remove_news_playlist.py | 17 +++++++++++++++++ apps/news/models.py | 1 - apps/news/serializers.py | 1 - apps/news/tests.py | 3 +-- apps/search_indexes/documents/news.py | 1 - apps/utils/tests/tests_translated.py | 1 - 7 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 apps/news/migrations/0027_remove_news_playlist.py diff --git a/apps/favorites/tests.py b/apps/favorites/tests.py index 99b01444..dd5c6c4f 100644 --- a/apps/favorites/tests.py +++ b/apps/favorites/tests.py @@ -26,7 +26,7 @@ class BaseTestCase(APITestCase): self.test_news = News.objects.create(created_by=self.user, modified_by=self.user, title={"en-GB": "Test news"}, news_type=self.test_news_type, description={"en-GB": "Description test news"}, - playlist=1, start="2020-12-03 12:00:00", end="2020-12-13 12:00:00", + start="2020-12-03 12:00:00", end="2020-12-13 12:00:00", state=News.PUBLISHED, slug='test-news') self.test_content_type = ContentType.objects.get(app_label="news", model="news") diff --git a/apps/news/migrations/0027_remove_news_playlist.py b/apps/news/migrations/0027_remove_news_playlist.py new file mode 100644 index 00000000..3741fca1 --- /dev/null +++ b/apps/news/migrations/0027_remove_news_playlist.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.4 on 2019-10-24 09:37 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0026_auto_20191024_0913'), + ] + + operations = [ + migrations.RemoveField( + model_name='news', + name='playlist', + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 1abd10e1..a2130fac 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -141,7 +141,6 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('End')) slug = models.SlugField(unique=True, max_length=255, verbose_name=_('News slug')) - playlist = models.IntegerField(_('playlist')) state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, verbose_name=_('State')) is_highlighted = models.BooleanField(default=False, diff --git a/apps/news/serializers.py b/apps/news/serializers.py index cd7afff4..31c8fc91 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -179,7 +179,6 @@ class NewsDetailSerializer(NewsBaseSerializer): 'description_translated', 'start', 'end', - 'playlist', 'is_publish', 'state', 'state_display', diff --git a/apps/news/tests.py b/apps/news/tests.py index b4e2b296..f5340d15 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -50,7 +50,7 @@ class BaseTestCase(APITestCase): title={"en-GB": "Test news"}, news_type=self.test_news_type, description={"en-GB": "Description test news"}, - playlist=1, start=datetime.now() + timedelta(hours=-2), + start=datetime.now() + timedelta(hours=-2), end=datetime.now() + timedelta(hours=2), state=News.PUBLISHED, slug='test-news-slug', country=self.country_ru) @@ -85,7 +85,6 @@ class NewsTestCase(BaseTestCase): 'description': {"en-GB": "Description test news!"}, 'slug': self.test_news.slug, 'start': self.test_news.start, - 'playlist': self.test_news.playlist, 'news_type_id':self.test_news.news_type_id, 'country_id': self.country_ru.id } diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index cd6fc089..699a1cca 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -37,7 +37,6 @@ class NewsDocument(Document): model = models.News fields = ( 'id', - 'playlist', 'start', 'end', 'slug', diff --git a/apps/utils/tests/tests_translated.py b/apps/utils/tests/tests_translated.py index c6a990c0..7a304095 100644 --- a/apps/utils/tests/tests_translated.py +++ b/apps/utils/tests/tests_translated.py @@ -47,7 +47,6 @@ class TranslateFieldTests(BaseTestCase): "ru-RU": "Тестовая новость" }, description={"en-GB": "Test description"}, - playlist=1, start=datetime.now(pytz.utc) + timedelta(hours=-13), end=datetime.now(pytz.utc) + timedelta(hours=13), news_type=self.news_type,