Merge remote-tracking branch 'origin/develop' into fix/hide-excess-tags

This commit is contained in:
dormantman 2019-12-04 17:29:24 +03:00
commit 234f21e03b
23 changed files with 431 additions and 97 deletions

View File

@ -0,0 +1,152 @@
from account.models import OldRole, Role, User, UserRole
from main.models import SiteSettings
from django.core.management.base import BaseCommand
from django.db import connections, transaction
from django.db.models import Prefetch
from establishment.management.commands.add_position import namedtuplefetchall
from tqdm import tqdm
class Command(BaseCommand):
help = '''Add site affilations from old db to new db.
Run after migrate account models!!!'''
def map_role_sql(self):
with connections['legacy'].cursor() as cursor:
cursor.execute('''
select distinct
case when role = 'news_editor' then 'CONTENT_PAGE_MANAGER'
when role in ('reviewer', 'reviwer', 'reviewer_manager') then 'REVIEWER_MANGER'
when role = 'admin' then 'SUPERUSER'
when role ='community_manager' then 'COUNTRY_ADMIN'
when role = 'site_admin' then 'COUNTRY_ADMIN'
when role = 'wine_reviewer' then 'WINERY_REVIEWER'
when role in ('salesman', 'sales_man') then 'SALES_MAN'
when role = 'seller' then 'SELLER'
else role
end as new_role,
case when role = 'GUEST' then null else role end as role
from
(
SELECT
DISTINCT
COALESCE(role, 'GUEST') as role
FROM site_affiliations AS sa
) t
''')
return namedtuplefetchall(cursor)
def add_old_roles(self):
objects = []
OldRole.objects.all().delete()
for s in tqdm(self.map_role_sql(), desc='Add permissions old'):
objects.append(
OldRole(new_role=s.new_role, old_role=s.role)
)
OldRole.objects.bulk_create(objects)
self.stdout.write(self.style.WARNING(f'Migrated old roles.'))
def site_role_sql(self):
with connections['legacy'].cursor() as cursor:
cursor.execute('''
select site_id,
role
from
(
SELECT
DISTINCT
site_id,
COALESCE(role, 'GUEST') as role
FROM site_affiliations AS sa
) t
where t.role not in ('admin', 'GUEST')
''')
return namedtuplefetchall(cursor)
def add_site_role(self):
objects = []
for s in tqdm(self.site_role_sql(), desc='Add site role'):
old_role = OldRole.objects.get(old_role=s.role)
role_choice = getattr(Role, old_role.new_role)
sites = SiteSettings.objects.filter(old_id=s.site_id)
for site in sites:
role = Role.objects.filter(site=site, role=role_choice)
if not role.exists():
objects.append(
Role(site=site, role=role_choice)
)
Role.objects.bulk_create(objects)
self.stdout.write(self.style.WARNING(f'Added site roles.'))
def update_site_role(self):
roles = Role.objects.filter(country__isnull=True).select_related('site')\
.filter(site__id__isnull=False).select_for_update()
with transaction.atomic():
for role in tqdm(roles, desc='Update role country'):
role.country = role.site.country
role.save()
self.stdout.write(self.style.WARNING(f'Updated site roles.'))
def user_role_sql(self):
with connections['legacy'].cursor() as cursor:
cursor.execute('''
select t.*
from
(
SELECT
site_id,
account_id,
COALESCE(role, 'GUEST') as role
FROM site_affiliations AS sa
) t
join accounts a on a.id = t.account_id
where t.role not in ('admin', 'GUEST')
''')
return namedtuplefetchall(cursor)
def add_role_user(self):
for s in tqdm(self.user_role_sql(), desc='Add role to user'):
sites = SiteSettings.objects.filter(old_id=s.site_id)
old_role = OldRole.objects.get(old_role=s.role)
role_choice = getattr(Role, old_role.new_role)
roles = Role.objects.filter(site__in=[site for site in sites], role=role_choice)
users = User.objects.filter(old_id=s.account_id)
for user in users:
for role in roles:
user_role = UserRole.objects.get_or_create(user=user,
role=role)
self.stdout.write(self.style.WARNING(f'Added users roles.'))
def superuser_role_sql(self):
with connections['legacy'].cursor() as cursor:
cursor.execute('''
select t.*
from
(
SELECT
site_id,
account_id,
COALESCE(role, 'GUEST') as role
FROM site_affiliations AS sa
) t
join accounts a on a.id = t.account_id
where t.role in ('admin')
''')
return namedtuplefetchall(cursor)
def add_superuser(self):
for s in tqdm(self.superuser_role_sql(), desc='Add superuser'):
users = User.objects.filter(old_id=s.account_id).select_for_update()
with transaction.atomic():
for user in users:
user.is_superuser = True
user.save()
self.stdout.write(self.style.WARNING(f'Added superuser.'))
def handle(self, *args, **kwargs):
self.add_old_roles()
self.add_site_role()
self.update_site_role()
self.add_role_user()
self.add_superuser()

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.7 on 2019-12-03 10:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0020_role_site'),
]
operations = [
migrations.CreateModel(
name='OldRole',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('new_role', models.CharField(max_length=512, verbose_name='New role')),
('old_role', models.CharField(max_length=512, verbose_name='Old role')),
],
options={
'unique_together': {('new_role', 'old_role')},
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.7 on 2019-12-03 11:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0021_oldrole'),
]
operations = [
migrations.AlterField(
model_name='oldrole',
name='old_role',
field=models.CharField(max_length=512, null=True, verbose_name='Old role'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.2.7 on 2019-12-04 09:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0022_auto_20191203_1149'),
]
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'), (8, 'Sales man'), (9, 'Winery reviewer'), (10, 'Seller')], verbose_name='Role'),
),
migrations.AlterUniqueTogether(
name='userrole',
unique_together={('user', 'role')},
),
]

View File

@ -32,6 +32,9 @@ class Role(ProjectBaseMixin):
ESTABLISHMENT_MANAGER = 5 ESTABLISHMENT_MANAGER = 5
REVIEWER_MANGER = 6 REVIEWER_MANGER = 6
RESTAURANT_REVIEWER = 7 RESTAURANT_REVIEWER = 7
SALES_MAN = 8
WINERY_REVIEWER = 9
SELLER = 10
ROLE_CHOICES = ( ROLE_CHOICES = (
(STANDARD_USER, 'Standard user'), (STANDARD_USER, 'Standard user'),
@ -40,7 +43,10 @@ class Role(ProjectBaseMixin):
(CONTENT_PAGE_MANAGER, 'Content page manager'), (CONTENT_PAGE_MANAGER, 'Content page manager'),
(ESTABLISHMENT_MANAGER, 'Establishment manager'), (ESTABLISHMENT_MANAGER, 'Establishment manager'),
(REVIEWER_MANGER, 'Reviewer manager'), (REVIEWER_MANGER, 'Reviewer manager'),
(RESTAURANT_REVIEWER, 'Restaurant reviewer') (RESTAURANT_REVIEWER, 'Restaurant reviewer'),
(SALES_MAN, 'Sales man'),
(WINERY_REVIEWER, 'Winery reviewer'),
(SELLER, 'Seller')
) )
role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES,
null=False, blank=False) null=False, blank=False)
@ -293,3 +299,13 @@ class UserRole(ProjectBaseMixin):
role = models.ForeignKey(Role, verbose_name=_('Role'), on_delete=models.SET_NULL, null=True) role = models.ForeignKey(Role, verbose_name=_('Role'), on_delete=models.SET_NULL, null=True)
establishment = models.ForeignKey(Establishment, verbose_name=_('Establishment'), establishment = models.ForeignKey(Establishment, verbose_name=_('Establishment'),
on_delete=models.SET_NULL, null=True, blank=True) on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
unique_together = ['user', 'role']
class OldRole(models.Model):
new_role = models.CharField(verbose_name=_('New role'), max_length=512)
old_role = models.CharField(verbose_name=_('Old role'), max_length=512, null=True)
class Meta:
unique_together = ('new_role', 'old_role')

View File

@ -34,7 +34,7 @@ class CheckWhetherBookingAvailable(generics.GenericAPIView):
periods = response['periods'] periods = response['periods']
periods_by_name = {period['period']: period for period in periods if 'period' in period} periods_by_name = {period['period']: period for period in periods if 'period' in period}
if not periods_by_name: if not periods_by_name:
return None return response
period_template = iter(periods_by_name.values()).__next__().copy() period_template = iter(periods_by_name.values()).__next__().copy()
period_template.pop('total_left_seats') period_template.pop('total_left_seats')

View File

@ -17,10 +17,11 @@ class FeatureSerializer(serializers.ModelSerializer):
fields = ( fields = (
'id', 'id',
'slug', 'slug',
'priority' 'priority',
'route',
'site_settings',
) )
class CurrencySerializer(ProjectModelSerializer): class CurrencySerializer(ProjectModelSerializer):
"""Currency serializer.""" """Currency serializer."""
@ -36,20 +37,24 @@ class CurrencySerializer(ProjectModelSerializer):
class SiteFeatureSerializer(serializers.ModelSerializer): class SiteFeatureSerializer(serializers.ModelSerializer):
"""Site feature serializer.""" id = serializers.IntegerField(source='feature.id')
slug = serializers.CharField(source='feature.slug')
priority = serializers.IntegerField(source='feature.priority')
route = serializers.CharField(source='feature.route.name')
source = serializers.IntegerField(source='feature.source')
nested = RecursiveFieldSerializer(many=True, allow_null=True)
class Meta: class Meta:
"""Meta class.""" """Meta class."""
model = models.SiteFeature model = models.SiteFeature
fields = ( fields = ('main',
'id', 'id',
'site_settings', 'slug',
'feature', 'priority',
'published', 'route',
'main', 'source',
'nested' 'nested',
) )
class SiteSettingsSerializer(serializers.ModelSerializer): class SiteSettingsSerializer(serializers.ModelSerializer):
@ -95,23 +100,15 @@ class SiteSettingsBackOfficeSerializer(SiteSettingsSerializer):
] ]
class SiteSerializer(serializers.ModelSerializer): class SiteSerializer(SiteSettingsSerializer):
country = CountrySerializer() country = CountrySerializer()
class Meta: class Meta:
"""Meta class.""" """Meta class."""
model = models.SiteSettings model = models.SiteSettings
fields = [ fields = SiteSettingsSerializer.Meta.fields + [
'subdomain', 'id',
'site_url', 'country'
'country',
'default_site',
'pinterest_page_url',
'twitter_page_url',
'facebook_page_url',
'instagram_page_url',
'contact_email',
'currency'
] ]
@ -125,30 +122,6 @@ class SiteShortSerializer(serializers.ModelSerializer):
] ]
class SiteBackOfficeSerializer(SiteSerializer):
"""Serializer for back office."""
class Meta(SiteSerializer.Meta):
"""Meta class."""
fields = SiteSerializer.Meta.fields + [
'id',
]
class FeatureSerializer(serializers.ModelSerializer):
"""Feature serializer."""
class Meta:
"""Meta class."""
model = models.Feature
fields = (
'id',
'slug',
'priority',
'route',
'site_settings',
)
class AwardBaseSerializer(serializers.ModelSerializer): class AwardBaseSerializer(serializers.ModelSerializer):

View File

@ -61,9 +61,9 @@ class SiteFeatureRUDBackView(generics.RetrieveUpdateDestroyAPIView):
class SiteSettingsBackOfficeView(SiteSettingsView): class SiteSettingsBackOfficeView(SiteSettingsView):
"""Site settings View.""" """Site settings View."""
serializer_class = serializers.SiteSettingsBackOfficeSerializer serializer_class = serializers.SiteSerializer
class SiteListBackOfficeView(SiteListView): class SiteListBackOfficeView(SiteListView):
"""Site settings View.""" """Site settings View."""
serializer_class = serializers.SiteBackOfficeSerializer serializer_class = serializers.SiteSerializer

View File

@ -1,6 +1,7 @@
from search_indexes.documents.establishment import EstablishmentDocument from search_indexes.documents.establishment import EstablishmentDocument
from search_indexes.documents.news import NewsDocument from search_indexes.documents.news import NewsDocument
from search_indexes.documents.product import ProductDocument from search_indexes.documents.product import ProductDocument
from search_indexes.documents.tag_category import TagCategoryDocument
from search_indexes.tasks import es_update from search_indexes.tasks import es_update
# todo: make signal to update documents on related fields # todo: make signal to update documents on related fields
@ -8,5 +9,6 @@ __all__ = [
'EstablishmentDocument', 'EstablishmentDocument',
'NewsDocument', 'NewsDocument',
'ProductDocument', 'ProductDocument',
'TagCategoryDocument',
'es_update', 'es_update',
] ]

View File

@ -116,6 +116,7 @@ class EstablishmentDocument(Document):
'weekday': fields.IntegerField(attr='weekday'), 'weekday': fields.IntegerField(attr='weekday'),
'weekday_display': fields.KeywordField(attr='get_weekday_display'), 'weekday_display': fields.KeywordField(attr='get_weekday_display'),
'closed_at': fields.KeywordField(attr='closed_at_str'), 'closed_at': fields.KeywordField(attr='closed_at_str'),
'opening_at': fields.KeywordField(attr='opening_at_str'),
} }
)) ))
address = fields.ObjectField( address = fields.ObjectField(

View File

@ -0,0 +1,33 @@
"""Product app documents."""
from django.conf import settings
from django_elasticsearch_dsl import Document, Index, fields
from tag import models
TagCategoryIndex = Index(settings.ELASTICSEARCH_INDEX_NAMES.get(__name__, 'tag_category'))
TagCategoryIndex.settings(number_of_shards=2, number_of_replicas=2)
@TagCategoryIndex.doc_type
class TagCategoryDocument(Document):
"""TagCategory document."""
tags = fields.ListField(fields.ObjectField(
properties={
'id': fields.IntegerField(),
'value': fields.KeywordField(),
},
))
class Django:
model = models.TagCategory
fields = (
'id',
'index_name',
'public',
'value_type'
)
related_models = [models.Tag]
def get_queryset(self):
return super().get_queryset().with_base_related()

View File

@ -4,6 +4,8 @@ from django_elasticsearch_dsl_drf.filter_backends import SearchFilterBackend, \
FacetedSearchFilterBackend, GeoSpatialFilteringFilterBackend FacetedSearchFilterBackend, GeoSpatialFilteringFilterBackend
from search_indexes.utils import OBJECT_FIELD_PROPERTIES from search_indexes.utils import OBJECT_FIELD_PROPERTIES
from six import iteritems from six import iteritems
from search_indexes.documents import TagCategoryDocument
from tag.models import TagCategory
class CustomGeoSpatialFilteringFilterBackend(GeoSpatialFilteringFilterBackend): class CustomGeoSpatialFilteringFilterBackend(GeoSpatialFilteringFilterBackend):
@ -21,15 +23,9 @@ class CustomGeoSpatialFilteringFilterBackend(GeoSpatialFilteringFilterBackend):
else: else:
result_part = 180 - (180 - first[1] - diff) result_part = 180 - (180 - first[1] - diff)
elif second[1] < 0 > first[1]: elif second[1] < 0 > first[1] or second[1] > 0 < first[1]:
diff = abs(abs(second[1]) - abs(first[1])) reverse_first, reverse_second = 180 - abs(first[1]), 180 - abs(second[1])
result_part = ((reverse_first + reverse_second) / 2) * (-1 + (second[1] < 0) * 2)
if diff > 90:
reverse_first, reverse_second = 180 - abs(first[1]), 180 - abs(second[1])
result_part = (reverse_first + reverse_second) / 2
else:
result_part = (first[1] + second[1]) / 2
else: else:
result_part = (first[1] + second[1]) / 2 result_part = (first[1] + second[1]) / 2
@ -62,10 +58,30 @@ class CustomFacetedSearchFilterBackend(FacetedSearchFilterBackend):
:param view: :param view:
:return: :return:
""" """
def makefilter(cur_facet): def make_filter(cur_facet):
def myfilter(x): def _filter(x):
return cur_facet['facet']._params['field'] != next(iter(x._params)) return cur_facet['facet']._params['field'] != next(iter(x._params))
return myfilter return _filter
def make_tags_filter(cur_facet, tags_to_remove_ids):
def _filter(x):
if hasattr(x, '_params') and (x._params.get('must') or x._params.get('should')):
ret = []
for t in ['must', 'should']:
terms = x._params.get(t)
if terms:
for term in terms:
if cur_facet['facet']._params['field'] != next(iter(term._params)):
return True # different fields. preserve filter
else:
ret.append(next(iter(term._params.values())) not in tags_to_remove_ids)
return all(ret)
if cur_facet['facet']._params['field'] != next(iter(x._params)):
return True # different fields. preserve filter
else:
return next(iter(x._params.values())) not in tags_to_remove_ids
return _filter
__facets = self.construct_facets(request, view) __facets = self.construct_facets(request, view)
setattr(view.paginator, 'facets_computed', {}) setattr(view.paginator, 'facets_computed', {})
for __field, __facet in iteritems(__facets): for __field, __facet in iteritems(__facets):
@ -77,29 +93,73 @@ class CustomFacetedSearchFilterBackend(FacetedSearchFilterBackend):
'global' 'global'
).bucket(__field, agg) ).bucket(__field, agg)
else: else:
qs = queryset.__copy__() if __field != 'tag':
qs.query = queryset.query._clone() qs = queryset.__copy__()
filterer = makefilter(__facet) qs.query = queryset.query._clone()
for param_type in ['must', 'must_not', 'should']: filterer = make_filter(__facet)
if qs.query._proxied._params.get(param_type): for param_type in ['must', 'must_not', 'should']:
qs.query._proxied._params[param_type] = list( if qs.query._proxied._params.get(param_type):
filter( qs.query._proxied._params[param_type] = list(
filterer, qs.query._proxied._params[param_type] filter(
filterer, qs.query._proxied._params[param_type]
)
) )
) sh = qs.query._proxied._params.get('should')
sh = qs.query._proxied._params.get('should') if (not sh or not len(sh)) \
if (not sh or not len(sh)) \ and qs.query._proxied._params.get('minimum_should_match'):
and qs.query._proxied._params.get('minimum_should_match'): qs.query._proxied._params.pop('minimum_should_match')
qs.query._proxied._params.pop('minimum_should_match') facet_name = '_filter_' + __field
facet_name = '_filter_' + __field qs.aggs.bucket(
qs.aggs.bucket( facet_name,
facet_name, 'filter',
'filter', filter=agg_filter
filter=agg_filter ).bucket(__field, agg)
).bucket(__field, agg) view.paginator.facets_computed.update({facet_name: qs.execute().aggregations[facet_name]})
view.paginator.facets_computed.update({facet_name: qs.execute().aggregations[facet_name]}) else:
tag_facets = []
preserve_ids = []
facet_name = '_filter_' + __field
all_tag_categories = TagCategoryDocument.search() \
.filter('term', public=True) \
.filter(Q('term', value_type=TagCategory.LIST) | Q('match', index_name='wine-color'))
for category in all_tag_categories:
tags_to_remove = list(map(lambda t: str(t.id), category.tags))
qs = queryset.__copy__()
qs.query = queryset.query._clone()
filterer = make_tags_filter(__facet, tags_to_remove)
for param_type in ['must', 'should']:
if qs.query._proxied._params.get(param_type):
if qs.query._proxied._params.get(param_type):
qs.query._proxied._params[param_type] = list(
filter(
filterer, qs.query._proxied._params[param_type]
)
)
sh = qs.query._proxied._params.get('should')
if (not sh or not len(sh)) \
and qs.query._proxied._params.get('minimum_should_match'):
qs.query._proxied._params.pop('minimum_should_match')
qs.aggs.bucket(
facet_name,
'filter',
filter=agg_filter
).bucket(__field, agg)
tag_facets.append(qs.execute().aggregations[facet_name])
preserve_ids.append(list(map(int, tags_to_remove)))
view.paginator.facets_computed.update({facet_name: self.merge_buckets(tag_facets, preserve_ids)})
return queryset return queryset
@staticmethod
def merge_buckets(buckets: list, preserve_ids: list):
"""Reduces all buckets preserving class"""
result_bucket = buckets[0]
result_bucket.tag.buckets = list(filter(lambda x: x['key'] in preserve_ids[0], result_bucket.tag.buckets._l_))
for bucket, ids in list(zip(buckets, preserve_ids))[1:]:
for tag in bucket.tag.buckets._l_:
if tag['key'] in ids:
result_bucket.tag.buckets.append(tag)
return result_bucket
class CustomSearchFilterBackend(SearchFilterBackend): class CustomSearchFilterBackend(SearchFilterBackend):
"""Custom SearchFilterBackend.""" """Custom SearchFilterBackend."""

View File

@ -168,6 +168,7 @@ class ScheduleDocumentSerializer(serializers.Serializer):
weekday = serializers.IntegerField() weekday = serializers.IntegerField()
weekday_display = serializers.CharField() weekday_display = serializers.CharField()
closed_at = serializers.CharField() closed_at = serializers.CharField()
opening_at = serializers.CharField()
class InFavoritesMixin(DocumentSerializer): class InFavoritesMixin(DocumentSerializer):

View File

@ -346,7 +346,7 @@ class ProductDocumentViewSet(BaseDocumentViewSet):
faceted_search_fields = { faceted_search_fields = {
'tag': { 'tag': {
'field': 'wine_colors.id', 'field': 'tags.id',
'enabled': True, 'enabled': True,
'facet': TermsFacet, 'facet': TermsFacet,
'options': { 'options': {

View File

@ -47,7 +47,7 @@ class Tag(TranslatedFieldsMixin, models.Model):
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
old_id_meta_product = models.PositiveIntegerField(_('old id metadata product'), old_id_meta_product = models.PositiveIntegerField(_('old id metadata product'),
blank=True, null=True, default=None) blank=True, null=True, default=None)
objects = TagQuerySet.as_manager() objects = TagQuerySet.as_manager()

View File

@ -1,7 +1,8 @@
"""Tag serializers.""" """Tag serializers."""
from rest_framework import serializers from rest_framework import serializers
from establishment.models import (Establishment, EstablishmentType, from rest_framework.fields import SerializerMethodField
EstablishmentSubType)
from establishment.models import (Establishment, EstablishmentType)
from news.models import News, NewsType from news.models import News, NewsType
from tag import models from tag import models
from utils.exceptions import (ObjectAlreadyAdded, BindingObjectNotFound, from utils.exceptions import (ObjectAlreadyAdded, BindingObjectNotFound,
@ -12,6 +13,9 @@ from utils.serializers import TranslatedField
class TagBaseSerializer(serializers.ModelSerializer): class TagBaseSerializer(serializers.ModelSerializer):
"""Serializer for model Tag.""" """Serializer for model Tag."""
def get_extra_kwargs(self):
return super().get_extra_kwargs()
label_translated = TranslatedField() label_translated = TranslatedField()
index_name = serializers.CharField(source='value', read_only=True, allow_null=True) index_name = serializers.CharField(source='value', read_only=True, allow_null=True)
@ -37,6 +41,7 @@ class TagBackOfficeSerializer(TagBaseSerializer):
'category' 'category'
) )
class TagCategoryProductSerializer(serializers.ModelSerializer): class TagCategoryProductSerializer(serializers.ModelSerializer):
"""SHORT Serializer for TagCategory""" """SHORT Serializer for TagCategory"""
@ -57,7 +62,7 @@ class TagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory.""" """Serializer for model TagCategory."""
label_translated = TranslatedField() label_translated = TranslatedField()
tags = TagBaseSerializer(many=True, read_only=True) tags = SerializerMethodField()
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -70,6 +75,25 @@ class TagCategoryBaseSerializer(serializers.ModelSerializer):
'tags', 'tags',
) )
def get_tags(self, obj):
query_params = dict(self.context['request'].query_params)
if len(query_params) > 1:
return []
params = {}
if 'establishment_type' in query_params:
params = {
'establishments__isnull': False,
}
elif 'product_type' in query_params:
params = {
'products__isnull': False,
}
tags = obj.tags.filter(**params).distinct()
return TagBaseSerializer(instance=tags, many=True, read_only=True).data
class TagCategoryShortSerializer(serializers.ModelSerializer): class TagCategoryShortSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory.""" """Serializer for model TagCategory."""
@ -174,15 +198,15 @@ class TagCategoryBindObjectSerializer(serializers.Serializer):
attrs['tag_category'] = tag_category attrs['tag_category'] = tag_category
if obj_type == self.ESTABLISHMENT_TYPE: if obj_type == self.ESTABLISHMENT_TYPE:
establishment_type = EstablishmentType.objects.filter(pk=obj_id).\ establishment_type = EstablishmentType.objects.filter(pk=obj_id). \
first() first()
if not establishment_type: if not establishment_type:
raise BindingObjectNotFound() raise BindingObjectNotFound()
if request.method == 'POST' and tag_category.establishment_types.\ if request.method == 'POST' and tag_category.establishment_types. \
filter(pk=establishment_type.pk).exists(): filter(pk=establishment_type.pk).exists():
raise ObjectAlreadyAdded() raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag_category.\ if request.method == 'DELETE' and not tag_category. \
establishment_types.filter(pk=establishment_type.pk).\ establishment_types.filter(pk=establishment_type.pk). \
exists(): exists():
raise RemovedBindingObjectNotFound() raise RemovedBindingObjectNotFound()
attrs['related_object'] = establishment_type attrs['related_object'] = establishment_type
@ -190,10 +214,10 @@ class TagCategoryBindObjectSerializer(serializers.Serializer):
news_type = NewsType.objects.filter(pk=obj_id).first() news_type = NewsType.objects.filter(pk=obj_id).first()
if not news_type: if not news_type:
raise BindingObjectNotFound() raise BindingObjectNotFound()
if request.method == 'POST' and tag_category.news_types.\ if request.method == 'POST' and tag_category.news_types. \
filter(pk=news_type.pk).exists(): filter(pk=news_type.pk).exists():
raise ObjectAlreadyAdded() raise ObjectAlreadyAdded()
if request.method == 'DELETE' and not tag_category.news_types.\ if request.method == 'DELETE' and not tag_category.news_types. \
filter(pk=news_type.pk).exists(): filter(pk=news_type.pk).exists():
raise RemovedBindingObjectNotFound() raise RemovedBindingObjectNotFound()
attrs['related_object'] = news_type attrs['related_object'] = news_type

View File

@ -39,6 +39,10 @@ class Timetable(ProjectBaseMixin):
def closed_at_str(self): def closed_at_str(self):
return str(self.closed_at) if self.closed_at else None return str(self.closed_at) if self.closed_at else None
@property
def opening_at_str(self):
return str(self.opening_at) if self.opening_at else None
@property @property
def opening_time(self): def opening_time(self):
return self.opening_at or self.lunch_start or self.dinner_start return self.opening_at or self.lunch_start or self.dinner_start

2
fabfile.py vendored
View File

@ -54,7 +54,7 @@ def collectstatic():
def deploy(branch=None): def deploy(branch=None):
role = env.roles[0] role = env.roles[0]
if env.roledefs[role]['branch'] != 'develop': if env.roledefs[role]['branch'] == 'develop':
fetch() fetch()
install_requirements() install_requirements()
migrate() migrate()

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
./manage.py transfer -a ./manage.py transfer -a
#./manage.py transfer -d ./manage.py transfer -d
./manage.py transfer -e ./manage.py transfer -e
./manage.py transfer --fill_city_gallery ./manage.py transfer --fill_city_gallery
./manage.py transfer -l ./manage.py transfer -l

View File

@ -42,6 +42,7 @@ ELASTICSEARCH_INDEX_NAMES = {
'search_indexes.documents.news': 'development_news', 'search_indexes.documents.news': 'development_news',
'search_indexes.documents.establishment': 'development_establishment', 'search_indexes.documents.establishment': 'development_establishment',
'search_indexes.documents.product': 'development_product', 'search_indexes.documents.product': 'development_product',
'search_indexes.documents.tag_category': 'development_tag_category',
} }
# ELASTICSEARCH_DSL_AUTOSYNC = False # ELASTICSEARCH_DSL_AUTOSYNC = False

View File

@ -92,6 +92,7 @@ ELASTICSEARCH_INDEX_NAMES = {
# 'search_indexes.documents.news': 'local_news', # 'search_indexes.documents.news': 'local_news',
'search_indexes.documents.establishment': 'local_establishment', 'search_indexes.documents.establishment': 'local_establishment',
'search_indexes.documents.product': 'local_product', 'search_indexes.documents.product': 'local_product',
'search_indexes.documents.tag_category': 'local_tag_category',
} }
ELASTICSEARCH_DSL_AUTOSYNC = False ELASTICSEARCH_DSL_AUTOSYNC = False

View File

@ -36,6 +36,7 @@ ELASTICSEARCH_INDEX_NAMES = {
'search_indexes.documents.news': 'development_news', # temporarily disabled 'search_indexes.documents.news': 'development_news', # temporarily disabled
'search_indexes.documents.establishment': 'development_establishment', 'search_indexes.documents.establishment': 'development_establishment',
'search_indexes.documents.product': 'development_product', 'search_indexes.documents.product': 'development_product',
'search_indexes.documents.tag_category': 'development_tag_category',
} }
sentry_sdk.init( sentry_sdk.init(

View File

@ -23,6 +23,7 @@ ELASTICSEARCH_DSL = {
ELASTICSEARCH_INDEX_NAMES = { ELASTICSEARCH_INDEX_NAMES = {
# 'search_indexes.documents.news': 'stage_news', #temporarily disabled # 'search_indexes.documents.news': 'stage_news', #temporarily disabled
'search_indexes.documents.establishment': 'stage_establishment', 'search_indexes.documents.establishment': 'stage_establishment',
'search_indexes.documents.tag_category': 'stage_tag_category',
} }
COOKIE_DOMAIN = '.id-east.ru' COOKIE_DOMAIN = '.id-east.ru'