Merge branch 'develop' into feature/add_tag_product

This commit is contained in:
Виктор Гладких 2019-11-12 17:37:51 +03:00
commit 097bab4bb1
38 changed files with 672 additions and 90 deletions

View File

@ -6,4 +6,4 @@ from . import models
@admin.register(models.Comment) @admin.register(models.Comment)
class CommentModelAdmin(admin.ModelAdmin): class CommentModelAdmin(admin.ModelAdmin):
"""Model admin for model Comment""" """Model admin for model Comment"""
raw_id_fields = ('user', 'country') raw_id_fields = ('user',)

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.7 on 2019-11-12 13:17
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('comment', '0004_comment_old_id'),
]
operations = [
migrations.RemoveField(
model_name='comment',
name='country',
),
]

View File

@ -4,10 +4,9 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from account.models import User from account.models import User
from translation.models import Language
from utils.models import ProjectBaseMixin from utils.models import ProjectBaseMixin
from utils.querysets import ContentTypeQuerySetMixin from utils.querysets import ContentTypeQuerySetMixin
from translation.models import Language
from location.models import Country
class CommentQuerySet(ContentTypeQuerySetMixin): class CommentQuerySet(ContentTypeQuerySetMixin):
@ -34,7 +33,6 @@ class Comment(ProjectBaseMixin):
text = models.TextField(verbose_name=_('Comment text')) text = models.TextField(verbose_name=_('Comment text'))
mark = models.PositiveIntegerField(blank=True, null=True, default=None, verbose_name=_('Mark')) mark = models.PositiveIntegerField(blank=True, null=True, default=None, verbose_name=_('Mark'))
user = models.ForeignKey('account.User', related_name='comments', on_delete=models.CASCADE, verbose_name=_('User')) user = models.ForeignKey('account.User', related_name='comments', on_delete=models.CASCADE, verbose_name=_('User'))
country = models.ForeignKey(Country, verbose_name=_('Country'), on_delete=models.SET_NULL, null=True)
old_id = models.IntegerField(null=True, blank=True, default=None) old_id = models.IntegerField(null=True, blank=True, default=None)
content_type = models.ForeignKey(generic.ContentType, on_delete=models.CASCADE) content_type = models.ForeignKey(generic.ContentType, on_delete=models.CASCADE)

View File

@ -1,8 +1,7 @@
from pprint import pprint from pprint import pprint
from django.db.models import Q from account.models import User
from establishment.models import Establishment
from account.transfer_data import STOP_LIST
from transfer.models import Comments from transfer.models import Comments
from transfer.serializers.comments import CommentSerializer from transfer.serializers.comments import CommentSerializer
@ -10,19 +9,15 @@ from transfer.serializers.comments import CommentSerializer
def transfer_comments(): def transfer_comments():
# В queryset исключены объекты по условию в связанные моделях # В queryset исключены объекты по условию в связанные моделях
# см. transfer_establishment() и transfer_user() # см. transfer_establishment() и transfer_user()
queryset = Comments.objects.exclude( establishments = Establishment.objects.all().values_list('old_id', flat=True)
Q(establishment__type='Wineyard') | users = User.objects.all().values_list('old_id', flat=True)
Q(establishment__location__timezone__isnull=True) | queryset = Comments.objects.filter(
Q(account__confirmed_at__isnull=True) | establishment_id__in=list(establishments),
Q(account__email__in=STOP_LIST) account_id__in=list(users),
).filter(
account__isnull=False,
mark__isnull=False
).only( ).only(
'id', 'id',
'comment', 'comment',
'mark', 'mark',
'locale',
'establishment_id', 'establishment_id',
'account_id', 'account_id',
) )

View File

@ -0,0 +1,14 @@
from django.core.management.base import BaseCommand
from timetable.models import Timetable
class Command(BaseCommand):
help = '''Add closed_at, opening_at Timetable fields'''
def handle(self, *args, **options):
for tt in Timetable.objects.all():
end = tt.dinner_end or tt.lunch_end
start = tt.lunch_start or tt.dinner_start
if end or start:
tt.closed_at = end
tt.opening_at = start
tt.save()

View File

@ -0,0 +1,33 @@
# Generated by Django 2.2.7 on 2019-11-12 10:07
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('establishment', '0058_merge_20191106_0921'),
]
operations = [
migrations.CreateModel(
name='EstablishmentNote',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('old_id', models.PositiveIntegerField(blank=True, null=True)),
('text', models.TextField(verbose_name='text')),
('establishment', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='establishment_notes', to='establishment.Establishment', verbose_name='establishment')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='establishment_notes', to=settings.AUTH_USER_MODEL, verbose_name='author')),
],
options={
'verbose_name': 'product notes',
'verbose_name_plural': 'product note',
},
),
]

View File

@ -111,6 +111,19 @@ class EstablishmentQuerySet(models.QuerySet):
return self.select_related('address', 'establishment_type').\ return self.select_related('address', 'establishment_type').\
prefetch_related('tags') prefetch_related('tags')
def with_schedule(self):
"""Return qs with related schedule."""
return self.prefetch_related('schedule')
def with_currency_related(self):
"""Return qs with related """
return self.prefetch_related('currency')
def with_extended_address_related(self):
"""Return qs with deeply related address models."""
return self.select_related('address__city', 'address__city__region', 'address__city__region__country',
'address__city__country')
def with_extended_related(self): def with_extended_related(self):
return self.select_related('establishment_type').\ return self.select_related('establishment_type').\
prefetch_related('establishment_subtypes', 'awards', 'schedule', prefetch_related('establishment_subtypes', 'awards', 'schedule',
@ -461,6 +474,11 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
""" Used for indexing working by day """ """ Used for indexing working by day """
return [ret.weekday for ret in self.schedule.all() if ret.works_at_noon] return [ret.weekday for ret in self.schedule.all() if ret.works_at_noon]
@property
def works_at_weekday(self):
""" Used for indexing by working whole day criteria """
return [ret.weekday for ret in self.schedule.all()]
@property @property
def works_evening(self): def works_evening(self):
""" Used for indexing working by day """ """ Used for indexing working by day """
@ -520,6 +538,30 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
return self.products.wines() return self.products.wines()
class EstablishmentNoteQuerySet(models.QuerySet):
"""QuerySet for model EstablishmentNote."""
class EstablishmentNote(ProjectBaseMixin):
"""Note model for Establishment entity."""
old_id = models.PositiveIntegerField(null=True, blank=True)
text = models.TextField(verbose_name=_('text'))
establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT,
related_name='establishment_notes',
verbose_name=_('establishment'))
user = models.ForeignKey('account.User', on_delete=models.PROTECT,
null=True,
related_name='establishment_notes',
verbose_name=_('author'))
objects = EstablishmentNoteQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name_plural = _('product note')
verbose_name = _('product notes')
class Position(BaseAttributes, TranslatedFieldsMixin): class Position(BaseAttributes, TranslatedFieldsMixin):
"""Position model.""" """Position model."""

View File

@ -5,7 +5,7 @@ from rest_framework import serializers
from comment import models as comment_models from comment import models as comment_models
from comment.serializers import common as comment_serializers from comment.serializers import common as comment_serializers
from establishment import models from establishment import models
from location.serializers import AddressBaseSerializer, CitySerializer from location.serializers import AddressBaseSerializer, CitySerializer, AddressDetailSerializer
from main.serializers import AwardSerializer, CurrencySerializer from main.serializers import AwardSerializer, CurrencySerializer
from tag.serializers import TagBaseSerializer from tag.serializers import TagBaseSerializer
from timetable.serialziers import ScheduleRUDSerializer from timetable.serialziers import ScheduleRUDSerializer
@ -225,6 +225,19 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
'currency' 'currency'
] ]
class EstablishmentListRetrieveSerializer(EstablishmentBaseSerializer):
"""Establishment with city serializer."""
address = AddressDetailSerializer()
schedule = ScheduleRUDSerializer(many=True, allow_null=True)
class Meta(EstablishmentBaseSerializer.Meta):
"""Meta class."""
fields = EstablishmentBaseSerializer.Meta.fields + [
'schedule',
]
class EstablishmentGeoSerializer(EstablishmentBaseSerializer): class EstablishmentGeoSerializer(EstablishmentBaseSerializer):
"""Serializer for Geo view.""" """Serializer for Geo view."""
@ -260,6 +273,8 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
review = ReviewShortSerializer(source='last_published_review', allow_null=True) review = ReviewShortSerializer(source='last_published_review', allow_null=True)
employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees', employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees',
many=True) many=True)
address = AddressDetailSerializer(read_only=True)
menu = MenuSerializers(source='menu_set', many=True, read_only=True) menu = MenuSerializers(source='menu_set', many=True, read_only=True)
best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)

View File

@ -4,8 +4,9 @@ from django.db.models import Q, F
from establishment.models import Establishment from establishment.models import Establishment
from location.models import Address from location.models import Address
from transfer.models import Establishments, Dishes from transfer.models import Establishments, Dishes, EstablishmentNotes
from transfer.serializers.establishment import EstablishmentSerializer from transfer.serializers.establishment import EstablishmentSerializer, \
EstablishmentNoteSerializer
from transfer.serializers.plate import PlateSerializer from transfer.serializers.plate import PlateSerializer
@ -124,10 +125,26 @@ def transfer_establishment_addresses():
establishment.save() establishment.save()
def transfer_establishment_note():
errors = []
queryset = EstablishmentNotes.objects.exclude(text__exact='') \
.exclude(text__isnull=True) \
.exclude(establishment_id__isnull=True)
serialized_data = EstablishmentNoteSerializer(
data=list(queryset.values()),
many=True)
if serialized_data.is_valid():
serialized_data.save()
else:
for d in serialized_data.errors: errors.append(d) if d else None
pprint(f"transfer_establishment_note errors: {errors}")
data_types = { data_types = {
"establishment": [ "establishment": [
transfer_establishment, transfer_establishment,
], ],
"establishment_note": [transfer_establishment_note],
"location_establishment": [ "location_establishment": [
transfer_establishment_addresses transfer_establishment_addresses
], ],

View File

@ -32,7 +32,11 @@ class EstablishmentListView(EstablishmentMixinView, generics.ListAPIView):
"""Resource for getting a list of establishments.""" """Resource for getting a list of establishments."""
filter_class = filters.EstablishmentFilter filter_class = filters.EstablishmentFilter
serializer_class = serializers.EstablishmentBaseSerializer serializer_class = serializers.EstablishmentListRetrieveSerializer
def get_queryset(self):
return super().get_queryset().with_schedule()\
.with_extended_address_related().with_currency_related()
class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView): class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView):

View File

@ -68,7 +68,7 @@ class CitySerializer(serializers.ModelSerializer):
queryset=models.Country.objects.all(), queryset=models.Country.objects.all(),
write_only=True write_only=True
) )
country = CountrySerializer() country = CountrySerializer(read_only=True)
class Meta: class Meta:
model = models.City model = models.City

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-11-07 14:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0033_auto_20191106_0744'),
]
operations = [
migrations.AddField(
model_name='sitefeature',
name='nested',
field=models.ManyToManyField(to='main.SiteFeature'),
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.7 on 2019-11-12 12:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0034_sitefeature_nested'),
('main', '0034_auto_20191112_0104'),
]
operations = [
]

View File

@ -151,6 +151,7 @@ class SiteFeature(ProjectBaseMixin):
feature = models.ForeignKey(Feature, on_delete=models.PROTECT) feature = models.ForeignKey(Feature, on_delete=models.PROTECT)
published = models.BooleanField(default=False, verbose_name=_('Published')) published = models.BooleanField(default=False, verbose_name=_('Published'))
main = models.BooleanField(default=False, verbose_name=_('Main')) main = models.BooleanField(default=False, verbose_name=_('Main'))
nested = models.ManyToManyField('self', symmetrical=False)
objects = SiteFeatureQuerySet.as_manager() objects = SiteFeatureQuerySet.as_manager()

View File

@ -4,7 +4,7 @@ from rest_framework import serializers
from advertisement.serializers.web import AdvertisementSerializer from advertisement.serializers.web import AdvertisementSerializer
from location.serializers import CountrySerializer from location.serializers import CountrySerializer
from main import models from main import models
from utils.serializers import ProjectModelSerializer, TranslatedField from utils.serializers import ProjectModelSerializer, TranslatedField, RecursiveFieldSerializer
class FeatureSerializer(serializers.ModelSerializer): class FeatureSerializer(serializers.ModelSerializer):
@ -27,6 +27,7 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
priority = serializers.IntegerField(source='feature.priority') priority = serializers.IntegerField(source='feature.priority')
route = serializers.CharField(source='feature.route.page_name') route = serializers.CharField(source='feature.route.page_name')
source = serializers.IntegerField(source='feature.source') source = serializers.IntegerField(source='feature.source')
nested = RecursiveFieldSerializer(many=True, allow_null=True)
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -36,7 +37,8 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
'slug', 'slug',
'priority', 'priority',
'route', 'route',
'source' 'source',
'nested',
) )

View File

@ -1,14 +1,21 @@
"""Filters from application News""" """Filters from application News"""
import django_filters from django_filters import rest_framework as filters
from django.utils.translation import gettext_lazy as _
from news import models from news import models
class NewsListFilterSet(django_filters.FilterSet): class NewsListFilterSet(filters.FilterSet):
"""FilterSet for News list""" """FilterSet for News list"""
is_highlighted = django_filters.BooleanFilter() is_highlighted = filters.BooleanFilter()
title = django_filters.CharFilter(method='by_title') title = filters.CharFilter(method='by_title')
tag_group = filters.ChoiceFilter(
choices=(
(models.News.RECIPES_TAG_VALUE, _('Recipes')),
),
method='by_tag_group'
)
class Meta: class Meta:
"""Meta class""" """Meta class"""
@ -16,8 +23,14 @@ class NewsListFilterSet(django_filters.FilterSet):
fields = ( fields = (
'title', 'title',
'is_highlighted', 'is_highlighted',
'tag_group',
) )
def by_tag_group(self, queryset, name, value):
if value == models.News.RECIPES_TAG_VALUE:
queryset = queryset.recipe_news()
return queryset
def by_title(self, queryset, name, value): def by_title(self, queryset, name, value):
"""Crappy search by title according to locale""" """Crappy search by title according to locale"""
if value: if value:

View File

@ -53,6 +53,10 @@ class NewsQuerySet(TranslationQuerysetMixin):
"""Filter collection by country code.""" """Filter collection by country code."""
return self.filter(country__code=code) return self.filter(country__code=code)
def recipe_news(self):
"""Returns news with tag 'cook' qs."""
return self.filter(tags__value=News.RECIPES_TAG_VALUE)
def published(self): def published(self):
"""Return only published news""" """Return only published news"""
now = timezone.now() now = timezone.now()
@ -127,6 +131,8 @@ class News(BaseAttributes, TranslatedFieldsMixin):
(PUBLISHED_EXCLUSIVE, _('Published exclusive')), (PUBLISHED_EXCLUSIVE, _('Published exclusive')),
) )
RECIPES_TAG_VALUE = 'cook'
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT, news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT,
verbose_name=_('news type')) verbose_name=_('news type'))

9
apps/news/urls/common.py Normal file
View File

@ -0,0 +1,9 @@
from django.urls import path
from news import views
common_urlpatterns = [
path('', views.NewsListView.as_view(), name='list'),
path('types/', views.NewsTypeListView.as_view(), name='type'),
path('slug/<slug:slug>/', views.NewsDetailView.as_view(), name='rud'),
path('slug/<slug:slug>/favorites/', views.NewsFavoritesCreateDestroyView.as_view(), name='create-destroy-favorites')
]

8
apps/news/urls/mobile.py Normal file
View File

@ -0,0 +1,8 @@
"""News app urlconf."""
from news.urls.common import common_urlpatterns
app_name = 'news'
urlpatterns = []
urlpatterns.extend(common_urlpatterns)

View File

@ -1,12 +1,8 @@
"""News app urlconf.""" """News app urlconf."""
from django.urls import path from news.urls.common import common_urlpatterns
from news import views
app_name = 'news' app_name = 'news'
urlpatterns = [ urlpatterns = []
path('', views.NewsListView.as_view(), name='list'),
path('types/', views.NewsTypeListView.as_view(), name='type'), urlpatterns.extend(common_urlpatterns)
path('slug/<slug:slug>/', views.NewsDetailView.as_view(), name='rud'),
path('slug/<slug:slug>/favorites/', views.NewsFavoritesCreateDestroyView.as_view(), name='create-destroy-favorites')
]

View File

@ -0,0 +1,37 @@
# Generated by Django 2.2.7 on 2019-11-12 10:07
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('product', '0011_product_win_import_id'),
]
operations = [
migrations.RemoveField(
model_name='product',
name='win_import_id',
),
migrations.CreateModel(
name='ProductNote',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('old_id', models.PositiveIntegerField(blank=True, null=True)),
('text', models.TextField(verbose_name='text')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='product_notes', to='product.Product', verbose_name='product')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='product_notes', to=settings.AUTH_USER_MODEL, verbose_name='author')),
],
options={
'verbose_name': 'product notes',
'verbose_name_plural': 'product note',
},
),
]

View File

@ -193,9 +193,6 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
validators=[MinValueValidator(EARLIEST_VINTAGE_YEAR), validators=[MinValueValidator(EARLIEST_VINTAGE_YEAR),
MaxValueValidator(LATEST_VINTAGE_YEAR)]) MaxValueValidator(LATEST_VINTAGE_YEAR)])
gallery = models.ManyToManyField('gallery.Image', through='ProductGallery') gallery = models.ManyToManyField('gallery.Image', through='ProductGallery')
win_import_id = models.CharField(max_length=255,
blank=True, null=True, default=None,
help_text=_('attribute from legacy db'))
reviews = generic.GenericRelation(to='review.Review') reviews = generic.GenericRelation(to='review.Review')
comments = generic.GenericRelation(to='comment.Comment') comments = generic.GenericRelation(to='comment.Comment')
awards = generic.GenericRelation(to='main.Award', related_query_name='product') awards = generic.GenericRelation(to='main.Award', related_query_name='product')
@ -409,3 +406,27 @@ class ProductClassification(models.Model):
"""Meta class.""" """Meta class."""
verbose_name = _('product classification') verbose_name = _('product classification')
verbose_name_plural = _('product classifications') verbose_name_plural = _('product classifications')
class ProductNoteQuerySet(models.QuerySet):
"""QuerySet for model ProductNote."""
class ProductNote(ProjectBaseMixin):
"""Note model for Product entity."""
old_id = models.PositiveIntegerField(null=True, blank=True)
text = models.TextField(verbose_name=_('text'))
product = models.ForeignKey(Product, on_delete=models.PROTECT,
related_name='product_notes',
verbose_name=_('product'))
user = models.ForeignKey('account.User', on_delete=models.PROTECT,
null=True,
related_name='product_notes',
verbose_name=_('author'))
objects = ProductNoteQuerySet.as_manager()
class Meta:
"""Meta class."""
verbose_name_plural = _('product note')
verbose_name = _('product notes')

View File

@ -76,7 +76,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
subtypes = ProductSubTypeBaseSerializer(many=True) subtypes = ProductSubTypeBaseSerializer(many=True)
establishment = EstablishmentShortSerializer() establishment = EstablishmentShortSerializer()
tags = TagBaseSerializer(source='related_tags', many=True) tags = TagBaseSerializer(source='related_tags', many=True)
preview_image_url = serializers.ImageField(source='preview_main_image_url', allow_null=True) preview_image_url = serializers.URLField(source='preview_main_image_url', allow_null=True)
class Meta: class Meta:
"""Meta class.""" """Meta class."""

View File

@ -101,6 +101,7 @@ def transfer_wine_classifications():
def transfer_product(): def transfer_product():
errors = []
queryset = transfer_models.Products.objects.all() queryset = transfer_models.Products.objects.all()
serialized_data = product_serializers.ProductSerializer( serialized_data = product_serializers.ProductSerializer(
data=list(queryset.values()), data=list(queryset.values()),
@ -108,26 +109,25 @@ def transfer_product():
if serialized_data.is_valid(): if serialized_data.is_valid():
serialized_data.save() serialized_data.save()
else: else:
errors = []
for d in serialized_data.errors: errors.append(d) if d else None for d in serialized_data.errors: errors.append(d) if d else None
pprint(f"transfer_product errors: {errors}") pprint(f"transfer_product errors: {errors}")
def transfer_product_description(): def transfer_product_note():
pass errors = []
# queryset = transfer_models.Products.objects.all() queryset = transfer_models.ProductNotes.objects.exclude(text='')
# serialized_data = product_serializers.ProductSerializer( serialized_data = product_serializers.ProductNoteSerializer(
# data=list(queryset.values()), data=list(queryset.values()),
# many=True) many=True)
# if serialized_data.is_valid(): if serialized_data.is_valid():
# serialized_data.save() serialized_data.save()
# else: else:
# errors = [] for d in serialized_data.errors: errors.append(d) if d else None
# for d in serialized_data.errors: errors.append(d) if d else None pprint(f"transfer_product_note errors: {errors}")
# pprint(f"transfer_product errors: {errors}")
def transfer_plate(): def transfer_plate():
errors = []
queryset = transfer_models.Merchandise.objects.all() queryset = transfer_models.Merchandise.objects.all()
serialized_data = product_serializers.PlateSerializer( serialized_data = product_serializers.PlateSerializer(
data=list(queryset.values()), data=list(queryset.values()),
@ -135,12 +135,12 @@ def transfer_plate():
if serialized_data.is_valid(): if serialized_data.is_valid():
serialized_data.save() serialized_data.save()
else: else:
errors = []
for d in serialized_data.errors: errors.append(d) if d else None for d in serialized_data.errors: errors.append(d) if d else None
pprint(f"transfer_plates errors: {errors}") pprint(f"transfer_plates errors: {errors}")
def transfer_plate_image(): def transfer_plate_image():
errors = []
queryset = transfer_models.Merchandise.objects.all() queryset = transfer_models.Merchandise.objects.all()
serialized_data = product_serializers.PlateImageSerializer( serialized_data = product_serializers.PlateImageSerializer(
data=list(queryset.values()), data=list(queryset.values()),
@ -148,7 +148,6 @@ def transfer_plate_image():
if serialized_data.is_valid(): if serialized_data.is_valid():
serialized_data.save() serialized_data.save()
else: else:
errors = []
for d in serialized_data.errors: errors.append(d) if d else None for d in serialized_data.errors: errors.append(d) if d else None
pprint(f"transfer_plates_images errors: {errors}") pprint(f"transfer_plates_images errors: {errors}")
@ -166,9 +165,9 @@ data_types = {
"product": [ "product": [
transfer_product, transfer_product,
], ],
# "product_description": [ "product_note": [
# transfer_product_description, transfer_product_note,
# ], ],
"souvenir": [ "souvenir": [
transfer_plate, transfer_plate,
transfer_plate_image, transfer_plate_image,

View File

@ -37,6 +37,9 @@ class EstablishmentDocument(Document):
works_noon = fields.ListField(fields.IntegerField( works_noon = fields.ListField(fields.IntegerField(
attr='works_noon' attr='works_noon'
)) ))
works_at_weekday = fields.ListField(fields.IntegerField(
attr='works_at_weekday'
))
works_now = fields.BooleanField(attr='works_now') works_now = fields.BooleanField(attr='works_now')
tags = fields.ObjectField( tags = fields.ObjectField(
properties={ properties={
@ -65,7 +68,6 @@ class EstablishmentDocument(Document):
'number': fields.IntegerField(), 'number': fields.IntegerField(),
'postal_code': fields.KeywordField(), 'postal_code': fields.KeywordField(),
'coordinates': fields.GeoPointField(attr='location_field_indexing'), 'coordinates': fields.GeoPointField(attr='location_field_indexing'),
# todo: remove if not used
'city': fields.ObjectField( 'city': fields.ObjectField(
properties={ properties={
'id': fields.IntegerField(), 'id': fields.IntegerField(),

View File

@ -19,10 +19,19 @@ class TagsDocumentSerializer(serializers.Serializer):
return get_translated_value(obj.label) return get_translated_value(obj.label)
class CityDocumentShortSerializer(serializers.Serializer):
"""City serializer for ES Document,"""
id = serializers.IntegerField()
code = serializers.CharField(allow_null=True)
name = serializers.CharField()
class AddressDocumentSerializer(serializers.Serializer): class AddressDocumentSerializer(serializers.Serializer):
"""Address serializer for ES Document.""" """Address serializer for ES Document."""
id = serializers.IntegerField() id = serializers.IntegerField()
city = CityDocumentShortSerializer()
street_name_1 = serializers.CharField() street_name_1 = serializers.CharField()
street_name_2 = serializers.CharField() street_name_2 = serializers.CharField()
number = serializers.IntegerField() number = serializers.IntegerField()
@ -108,6 +117,7 @@ class EstablishmentDocumentSerializer(DocumentSerializer):
'schedule', 'schedule',
'works_noon', 'works_noon',
'works_evening', 'works_evening',
'works_at_weekday',
# 'works_now', # 'works_now',
# 'collections', # 'collections',
# 'establishment_type', # 'establishment_type',

View File

@ -2,13 +2,226 @@
from django_elasticsearch_dsl import fields from django_elasticsearch_dsl import fields
from utils.models import get_current_locale, get_default_locale from utils.models import get_current_locale, get_default_locale
ALL_LOCALES_LIST = [
'af-ZA',
'am-ET',
'ar-AE',
'ar-BH',
'ar-DZ',
'ar-EG',
'ar-IQ',
'ar-JO',
'ar-KW',
'ar-LB',
'ar-LY',
'ar-MA',
'arn-CL',
'ar-OM',
'ar-QA',
'ar-SA',
'ar-SY',
'ar-TN',
'ar-YE',
'as-IN',
'az-Cyrl-AZ',
'az-Latn-AZ',
'ba-RU',
'be-BY',
'bg-BG',
'bn-BD',
'bn-IN',
'bo-CN',
'br-FR',
'bs-Cyrl-BA',
'bs-Latn-BA',
'ca-ES',
'co-FR',
'cs-CZ',
'cy-GB',
'da-DK',
'de-AT',
'de-CH',
'de-DE',
'de-LI',
'de-LU',
'dsb-DE',
'dv-MV',
'el-GR',
'en-029',
'en-AU',
'en-BZ',
'en-CA',
'en-GB',
'en-IE',
'en-IN',
'en-JM',
'en-MY',
'en-NZ',
'en-PH',
'en-SG',
'en-TT',
'en-US',
'en-ZA',
'en-ZW',
'es-AR',
'es-BO',
'es-CL',
'es-CO',
'es-CR',
'es-DO',
'es-EC',
'es-ES',
'es-GT',
'es-HN',
'es-MX',
'es-NI',
'es-PA',
'es-PE',
'es-PR',
'es-PY',
'es-SV',
'es-US',
'es-UY',
'es-VE',
'et-EE',
'eu-ES',
'fa-IR',
'fi-FI',
'fil-PH',
'fo-FO',
'fr-BE',
'fr-CA',
'fr-CH',
'fr-FR',
'fr-LU',
'fr-MC',
'fy-NL',
'ga-IE',
'gd-GB',
'gl-ES',
'gsw-FR',
'gu-IN',
'ha-Latn-NG',
'he-IL',
'hi-IN',
'hr-BA',
'hr-HR',
'hsb-DE',
'hu-HU',
'hy-AM',
'id-ID',
'ig-NG',
'ii-CN',
'is-IS',
'it-CH',
'it-IT',
'iu-Cans-CA',
'iu-Latn-CA',
'ja-JP',
'ka-GE',
'kk-KZ',
'kl-GL',
'km-KH',
'kn-IN',
'kok-IN',
'ko-KR',
'ky-KG',
'lb-LU',
'lo-LA',
'lt-LT',
'lv-LV',
'mi-NZ',
'mk-MK',
'ml-IN',
'mn-MN',
'mn-Mong-CN',
'moh-CA',
'mr-IN',
'ms-BN',
'ms-MY',
'mt-MT',
'nb-NO',
'ne-NP',
'nl-BE',
'nl-NL',
'nn-NO',
'nso-ZA',
'oc-FR',
'or-IN',
'pa-IN',
'pl-PL',
'prs-AF',
'ps-AF',
'pt-BR',
'pt-PT',
'qut-GT',
'quz-BO',
'quz-EC',
'quz-PE',
'rm-CH',
'ro-RO',
'ru-RU',
'rw-RW',
'sah-RU',
'sa-IN',
'se-FI',
'se-NO',
'se-SE',
'si-LK',
'sk-SK',
'sl-SI',
'sma-NO',
'sma-SE',
'smj-NO',
'smj-SE',
'smn-FI',
'sms-FI',
'sq-AL',
'sr-Cyrl-BA',
'sr-Cyrl-CS',
'sr-Cyrl-ME',
'sr-Cyrl-RS',
'sr-Latn-BA',
'sr-Latn-CS',
'sr-Latn-ME',
'sr-Latn-RS',
'sv-FI',
'sv-SE',
'sw-KE',
'syr-SY',
'ta-IN',
'te-IN',
'tg-Cyrl-TJ',
'th-TH',
'tk-TM',
'tn-ZA',
'tr-TR',
'tt-RU',
'tzm-Latn-DZ',
'ug-CN',
'uk-UA',
'ur-PK',
'uz-Cyrl-UZ',
'uz-Latn-UZ',
'vi-VN',
'wo-SN',
'xh-ZA',
'yo-NG',
'zh-CN',
'zh-HK',
'zh-MO',
'zh-SG',
'zh-TW',
'zu-ZA',
]
# object field properties # object field properties
OBJECT_FIELD_PROPERTIES = { OBJECT_FIELD_PROPERTIES = {locale: fields.TextField() for locale in ALL_LOCALES_LIST}
OBJECT_FIELD_PROPERTIES.update({
'en-GB': fields.TextField(analyzer='english'), 'en-GB': fields.TextField(analyzer='english'),
'ru-RU': fields.TextField(analyzer='russian'), 'ru-RU': fields.TextField(analyzer='russian'),
'fr-FR': fields.TextField(analyzer='french'), 'fr-FR': fields.TextField(analyzer='french')
} })
# todo: refactor serializer # todo: refactor serializer
@ -17,10 +230,14 @@ def get_translated_value(value):
return None return None
elif not isinstance(value, dict): elif not isinstance(value, dict):
field_dict = value.to_dict() field_dict = value.to_dict()
elif isinstance(value, dict): else:
field_dict = value field_dict = value
value = field_dict.get(get_current_locale()) field_dict = {k: v for k, v in field_dict.items() if v is not None}
result = field_dict.get(get_current_locale(), None)
# fallback # fallback
if value is None: if result is None:
value = field_dict.get(get_default_locale()) result = field_dict.get(get_default_locale(), None)
return value if result is None:
values = list(field_dict.values())
result = values[0] if values else None
return result

View File

@ -156,6 +156,12 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
constants.LOOKUP_QUERY_IN, constants.LOOKUP_QUERY_IN,
], ],
}, },
'works_at_weekday': {
'field': 'works_at_weekday',
'lookups': [
constants.LOOKUP_QUERY_IN,
],
},
'works_evening': { 'works_evening': {
'field': 'works_evening', 'field': 'works_evening',
'lookups': [ 'lookups': [

View File

@ -46,7 +46,7 @@ class Command(BaseCommand):
'en-GB': key_value.key_name, 'en-GB': key_value.key_name,
'fr-FR': key_value.key_name, 'fr-FR': key_value.key_name,
'ru-RU': key_value.key_name, 'ru-RU': key_value.key_name,
}, }
tag_category.value_type = key_value.value_type tag_category.value_type = key_value.value_type
tag_category.save() tag_category.save()
est_type.tag_categories.add( est_type.tag_categories.add(

View File

@ -42,9 +42,7 @@ class TagBackOfficeSerializer(TagBaseSerializer):
class TagCategoryBaseSerializer(serializers.ModelSerializer): class TagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory.""" """Serializer for model TagCategory."""
# todo: refactor this label_translated = TranslatedField()
# label_translated = TranslatedField()
label_translated = serializers.CharField(source='index_name', read_only=True, allow_null=True)
tags = TagBaseSerializer(many=True, read_only=True) tags = TagBaseSerializer(many=True, read_only=True)
class Meta: class Meta:

View File

@ -66,6 +66,7 @@ card = {
"fields": { "fields": {
"Schedules": { "Schedules": {
# нет аналогов для weekday, opening_at, closed_at # нет аналогов для weekday, opening_at, closed_at
# upd: запустить команду add_closed_at_timetable. она заполнит opening_at, closed_at
"lunch_start": "lunch_start", "lunch_start": "lunch_start",
"lunch_end": "lunch_end", "lunch_end": "lunch_end",
"diner_start": "diner_start", "diner_start": "diner_start",

View File

@ -32,8 +32,9 @@ class Command(BaseCommand):
'inquiries', # №6 - перенос запросов оценок 'inquiries', # №6 - перенос запросов оценок
'wine_characteristics', 'wine_characteristics',
'product', 'product',
'product_description', 'product_note',
'souvenir', 'souvenir',
'establishment_note',
] ]
def handle(self, *args, **options): def handle(self, *args, **options):

View File

@ -447,6 +447,18 @@ class Establishments(MigrateMixin):
db_table = 'establishments' db_table = 'establishments'
class EstablishmentNotes(MigrateMixin):
using = 'legacy'
establishment_id = models.IntegerField(null=True, blank=True)
account_id = models.IntegerField(null=True, blank=True)
text = models.TextField(null=True)
class Meta:
managed = False
db_table = 'notes'
class Descriptions(MigrateMixin): class Descriptions(MigrateMixin):
using = 'legacy' using = 'legacy'
@ -973,7 +985,7 @@ class Products(MigrateMixin):
class ProductNotes(MigrateMixin): class ProductNotes(MigrateMixin):
using = 'legacy' using = 'legacy'
product_id = models.ForeignKey(Products, on_delete=models.DO_NOTHING) product_id = models.IntegerField(null=True, blank=True)
text = models.CharField(max_length=255) text = models.CharField(max_length=255)
win_import_id = models.CharField(max_length=255) win_import_id = models.CharField(max_length=255)

View File

@ -1,14 +1,13 @@
from rest_framework import serializers from rest_framework import serializers
from comment.models import Comment, User from comment.models import Comment, User
from establishment.models import Establishment from establishment.models import Establishment
from location.models import Country
class CommentSerializer(serializers.Serializer): class CommentSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
comment = serializers.CharField() comment = serializers.CharField()
mark = serializers.DecimalField(max_digits=4, decimal_places=2) mark = serializers.DecimalField(max_digits=4, decimal_places=2, allow_null=True)
locale = serializers.CharField()
account_id = serializers.IntegerField() account_id = serializers.IntegerField()
establishment_id = serializers.CharField() establishment_id = serializers.CharField()
@ -16,14 +15,12 @@ class CommentSerializer(serializers.Serializer):
data.update({ data.update({
'old_id': data.pop('id'), 'old_id': data.pop('id'),
'text': data.pop('comment'), 'text': data.pop('comment'),
'mark': data['mark'] * -1 if data['mark'] < 0 else data['mark'], 'mark': self.get_mark(data),
'content_object': self.get_content_object(data), 'content_object': self.get_content_object(data),
'user': self.get_account(data), 'user': self.get_account(data),
'country': self.get_country(data),
}) })
data.pop('establishment_id') data.pop('establishment_id')
data.pop('account_id') data.pop('account_id')
data.pop('locale')
return data return data
def create(self, validated_data): def create(self, validated_data):
@ -47,10 +44,7 @@ class CommentSerializer(serializers.Serializer):
return user return user
@staticmethod @staticmethod
def get_country(data): def get_mark(data):
locale = data['locale'] if not data['mark']:
country_code = locale[:locale.index("-")] if len(locale) > 2 else locale return None
country = Country.objects.filter(code=country_code).first() return data['mark'] * -1 if data['mark'] < 0 else data['mark']
if not country:
raise ValueError(f"Country not found with code {country_code}")
return country

View File

@ -1,16 +1,17 @@
from django.conf import settings from django.conf import settings
from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.db import transaction from django.db import transaction
from django.utils.text import slugify
from rest_framework import serializers from rest_framework import serializers
from establishment.models import Establishment, ContactEmail, ContactPhone, EstablishmentType, \ from account.models import User
EstablishmentSubType from establishment.models import Establishment, ContactEmail, ContactPhone, \
EstablishmentType, EstablishmentSubType, EstablishmentNote
from location.models import Address from location.models import Address
from timetable.models import Timetable from timetable.models import Timetable
from utils.legacy_parser import parse_legacy_schedule_content from utils.legacy_parser import parse_legacy_schedule_content
from utils.serializers import TimeZoneChoiceField from utils.serializers import TimeZoneChoiceField
from utils.slug_generator import generate_unique_slug from utils.slug_generator import generate_unique_slug
from django.utils.text import slugify
class EstablishmentSerializer(serializers.ModelSerializer): class EstablishmentSerializer(serializers.ModelSerializer):
@ -173,3 +174,45 @@ class EstablishmentSerializer(serializers.ModelSerializer):
index_name=slugify(subtype_name), index_name=slugify(subtype_name),
establishment_type_id=establishment_type_id) establishment_type_id=establishment_type_id)
return subtype return subtype
class EstablishmentNoteSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
establishment_id = serializers.IntegerField()
account_id = serializers.IntegerField(allow_null=True)
text = serializers.CharField(allow_blank=True, allow_null=True)
class Meta:
model = EstablishmentNote
fields = (
'id',
'establishment_id',
'account_id',
'text',
)
def validate(self, attrs):
attrs['old_id'] = attrs['id']
attrs['establishment'] = self.get_establishment(attrs.pop('establishment_id'))
attrs['user'] = self.get_user(attrs.pop('account_id'))
return attrs
def create(self, validated_data):
qs = self.Meta.model.objects.filter(**validated_data)
establishment = validated_data.get('establishment')
if not qs.exists() and establishment:
obj = super().create(validated_data)
return obj
def get_establishment(self, old_id):
if old_id:
qs = Establishment.objects.filter(old_id=old_id)
if qs.exists():
return qs.first()
def get_user(self, old_id):
qs = User.objects.exclude(old_id__isnull=True).filter(old_id=old_id)
if qs.exists():
return qs.first()

View File

@ -541,3 +541,36 @@ class PlateImageSerializer(serializers.ModelSerializer):
product_qs = models.Product.objects.filter(old_id=product_id) product_qs = models.Product.objects.filter(old_id=product_id)
if product_qs.exists(): if product_qs.exists():
return product_qs.first() return product_qs.first()
class ProductNoteSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
product_id = serializers.IntegerField()
text = serializers.CharField(allow_blank=True)
class Meta:
model = models.ProductNote
fields = (
'id',
'product_id',
'text',
)
def validate(self, attrs):
attrs['old_id'] = attrs['id']
attrs['product'] = self.get_product(attrs.pop('product_id'))
return attrs
def create(self, validated_data):
qs = self.Meta.model.objects.filter(**validated_data)
product = validated_data.get('product')
if not qs.exists() and product:
return super().create(validated_data)
def get_product(self, old_id):
if old_id:
qs = models.Product.objects.filter(old_id=old_id)
if qs.exists():
return qs.first()

View File

@ -98,3 +98,9 @@ class FavoritesCreateSerializer(serializers.ModelSerializer):
@property @property
def slug(self): def slug(self):
return self.request.parser_context.get('kwargs').get('slug') return self.request.parser_context.get('kwargs').get('slug')
class RecursiveFieldSerializer(serializers.Serializer):
def to_representation(self, value):
serializer = self.parent.parent.__class__(value, context=self.context)
return serializer.data

View File

@ -12,6 +12,6 @@ urlpatterns = [
# path('advertisement/', include('advertisement.urls.web')), # path('advertisement/', include('advertisement.urls.web')),
# path('collection/', include('collection.urls.web')), # path('collection/', include('collection.urls.web')),
# path('establishments/', include('establishment.urls.web')), # path('establishments/', include('establishment.urls.web')),
# path('news/', include('news.urls.web')), path('news/', include('news.urls.mobile')),
# path('partner/', include('partner.urls.web')), # path('partner/', include('partner.urls.web')),
] ]