diff --git a/apps/account/models.py b/apps/account/models.py index a1a5ede4..f34d8634 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -15,6 +15,8 @@ from django.utils.http import urlsafe_base64_encode from django.utils.translation import ugettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from rest_framework.authtoken.models import Token +from collections import Counter +from typing import List from authorization.models import Application from establishment.models import Establishment, EstablishmentSubType @@ -475,6 +477,15 @@ class User(AbstractUser): .distinct() ) + def set_roles(self, ids: List[int]): + """ + Set user roles + :param ids: list of role ids + :return: bool + """ + self.roles.set(Role.objects.filter(id__in=ids)) + return self + class UserRoleQueryset(models.QuerySet): """QuerySet for model UserRole.""" diff --git a/apps/account/serializers/back.py b/apps/account/serializers/back.py index 2baaf656..93e33fa1 100644 --- a/apps/account/serializers/back.py +++ b/apps/account/serializers/back.py @@ -19,7 +19,7 @@ class _SiteSettingsSerializer(serializers.ModelSerializer): class BackUserSerializer(UserSerializer): last_country = _SiteSettingsSerializer(read_only=True) - roles = RoleBaseSerializer(many=True, read_only=True) + roles = RoleBaseSerializer(many=True) class Meta(UserSerializer.Meta): fields = ( @@ -115,6 +115,7 @@ class BackDetailUserSerializer(BackUserSerializer): def create(self, validated_data): subscriptions_list = [] + if 'subscription_types' in validated_data: subscriptions_list = validated_data.pop('subscription_types') @@ -127,11 +128,17 @@ class BackDetailUserSerializer(BackUserSerializer): def update(self, instance, validated_data): subscriptions_list = [] + if 'subscription_types' in validated_data: subscriptions_list = validated_data.pop('subscription_types') + if 'roles' in validated_data: + roles_ids = [role['id'] for role in validated_data.pop('roles') if 'id' in role] + instance.set_roles(roles_ids) + instance = super().update(instance, validated_data) subscriptions_handler(subscriptions_list, instance) + return instance diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index 9ca3caf0..7ae6cb70 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -8,6 +8,7 @@ from rest_framework import serializers from rest_framework import validators as rest_validators from account import models, tasks +from account.models import User, Role from main.serializers.common import NavigationBarPermissionBaseSerializer from notification.models import Subscribe, Subscriber from utils import exceptions as utils_exceptions @@ -27,7 +28,7 @@ def subscriptions_handler(subscriptions_list, user): 'user': user, 'email': user.email, 'ip_address': user.last_ip, - 'country_code': user.last_country.country.code if user.last_country else None, + 'country_code': user.last_country.country.code if user.last_country and user.last_country.country else None, 'locale': user.locale, 'update_code': generate_string_code(), } @@ -42,6 +43,7 @@ def subscriptions_handler(subscriptions_list, user): class RoleBaseSerializer(serializers.ModelSerializer): """Serializer for model Role.""" + id = serializers.IntegerField() role_display = serializers.CharField(source='get_role_display', read_only=True) navigation_bar_permission = NavigationBarPermissionBaseSerializer(read_only=True) country_code = serializers.CharField(source='country.code', read_only=True, allow_null=True) @@ -87,6 +89,7 @@ class UserSerializer(serializers.ModelSerializer): class Meta: model = models.User fields = [ + 'id', 'username', 'first_name', 'last_name', @@ -122,12 +125,6 @@ class UserSerializer(serializers.ModelSerializer): subscriptions_handler(subscriptions_list, user) return user - def validate_email(self, value): - """Validate email value""" - if hasattr(self.instance, 'email') and self.instance.email and value == self.instance.email: - raise serializers.ValidationError(detail='Equal email address.') - return value - def validate_username(self, value): """Custom username validation""" valid = utils_methods.username_validator(username=value) @@ -138,29 +135,39 @@ class UserSerializer(serializers.ModelSerializer): def update(self, instance, validated_data): """Override update method""" subscriptions_list = [] + if 'subscription_types' in validated_data: subscriptions_list = validated_data.pop('subscription_types') + new_email = validated_data.get('email') old_email = instance.email instance = super().update(instance, validated_data) - if 'email' in validated_data: - instance.email_confirmed = False - instance.email = old_email - instance.unconfirmed_email = validated_data['email'] - instance.save() - # Send verification link on user email for change email address - if settings.USE_CELERY: - tasks.change_email_address.delay( - user_id=instance.id, - country_code=self.context.get('request').country_code, - emails=[validated_data['email'], ]) - else: - tasks.change_email_address( - user_id=instance.id, - country_code=self.context.get('request').country_code, - emails=[validated_data['email'], ]) + request = self.context['request'] + user = request.user + if not user.is_superuser and not user.is_staff and \ + not user.roles.filter(country__code=request.country_code, role=models.Role.COUNTRY_ADMIN).exists(): + """ + superuser or country admin changes email immediately! + """ + if new_email and new_email != old_email: + instance.email_confirmed = False + instance.email = old_email + instance.unconfirmed_email = new_email + instance.save() + # Send verification link on user email for change email address + if settings.USE_CELERY: + tasks.change_email_address.delay( + user_id=instance.id, + country_code=self.context.get('request').country_code, + emails=[validated_data['email'], ]) + else: + tasks.change_email_address( + user_id=instance.id, + country_code=self.context.get('request').country_code, + emails=[validated_data['email'], ]) subscriptions_handler(subscriptions_list, instance) + return instance @@ -195,6 +202,7 @@ class UserShortSerializer(UserSerializer): 'id', 'fullname', 'email', + 'username', ] diff --git a/apps/account/views/common.py b/apps/account/views/common.py index 8b066742..e242c393 100644 --- a/apps/account/views/common.py +++ b/apps/account/views/common.py @@ -17,7 +17,7 @@ from utils.views import JWTGenericViewMixin # User views -class UserRetrieveUpdateView(generics.RetrieveUpdateAPIView): +class UserRetrieveUpdateView(generics.RetrieveUpdateDestroyAPIView): """User update view.""" serializer_class = serializers.UserSerializer queryset = models.User.objects.active() @@ -25,6 +25,10 @@ class UserRetrieveUpdateView(generics.RetrieveUpdateAPIView): def get_object(self): return self.request.user + def delete(self, request, *args, **kwargs): + """Overridden behavior of DELETE method.""" + return Response(status=status.HTTP_204_NO_CONTENT) + class ChangePasswordView(generics.GenericAPIView): """Change password view""" diff --git a/apps/collection/filters.py b/apps/collection/filters.py new file mode 100644 index 00000000..66b5e454 --- /dev/null +++ b/apps/collection/filters.py @@ -0,0 +1,56 @@ +"""Collection app filters.""" +from django_filters import rest_framework as filters +from django.core.validators import EMPTY_VALUES + +from collection import models + + +class CollectionFilterSet(filters.FilterSet): + """Collection filter set.""" + establishment_id = filters.NumberFilter( + field_name='establishments__id', + help_text='Establishment id. Allows to filter list of collections by choosen estblishment. ' + 'Use for Establishment detail\'s sheet to content display within ' + '"Collections & Guides" tab.' + ) + + # "ordering" instead of "o" is for backward compatibility + ordering = filters.OrderingFilter( + # tuple-mapping retains order + fields=( + ('rank', 'rank'), + ('start', 'start'), + ), + help_text='Ordering by fields - rank, start', + ) + + class Meta: + """Meta class.""" + model = models.Collection + fields = ( + 'ordering', + 'establishment_id', + ) + + +class GuideFilterSet(filters.FilterSet): + """Guide filter set.""" + establishment_id = filters.NumberFilter( + method='by_establishment_id', + help_text='Establishment id. Allows to filter list of guides by choosen establishment. ' + 'Use for Establishment detail\'s sheet to content display within ' + '"Collections & Guides" tab.' + ) + + class Meta: + """Meta class.""" + model = models.Guide + fields = ( + 'establishment_id', + ) + + def by_establishment_id(self, queryset, name, value): + """Filter by establishment id.""" + if value not in EMPTY_VALUES: + return queryset.by_establishment_id(value) + return queryset diff --git a/apps/collection/models.py b/apps/collection/models.py index 0457f38d..2fe28e4f 100644 --- a/apps/collection/models.py +++ b/apps/collection/models.py @@ -57,6 +57,10 @@ class CollectionQuerySet(RelatedObjectsCountMixin): """Returned only published collection""" return self.filter(is_publish=True) + def with_base_related(self): + """Select relate objects""" + return self.select_related('country') + class Collection(ProjectBaseMixin, CollectionDateMixin, TranslatedFieldsMixin, URLImageMixin): @@ -106,7 +110,7 @@ class Collection(ProjectBaseMixin, CollectionDateMixin, """Return list of related objects.""" related_objects = [] # get related objects - for related_object in self._meta.related_objects: + for related_object in self._meta.related_objects.with_base_related(): related_objects.append(related_object) return related_objects @@ -167,17 +171,26 @@ class GuideQuerySet(models.QuerySet): """Return QuerySet with related.""" return self.select_related('site', ) + def with_extended_related(self): + """Return QuerySet with extended related.""" + return self.with_base_related().prefetch_related('guideelement_set') + def by_country_id(self, country_id): """Return QuerySet filtered by country code.""" return self.filter(country_json__id__contains=country_id) def annotate_in_restaurant_section(self): """Annotate flag if GuideElement in RestaurantSectionNode.""" + restaurant_guides = models.Subquery( + self.filter( + guideelement__guide_element_type__name='EstablishmentNode', + guideelement__parent__guide_element_type__name='RestaurantSectionNode', + ).values_list('id', flat=True).distinct() + ) return self.annotate( in_restaurant_section=models.Case( models.When( - guideelement__guide_element_type__name='EstablishmentNode', - guideelement__parent__guide_element_type__name='RestaurantSectionNode', + id__in=restaurant_guides, then=True), default=False, output_field=models.BooleanField(default=False) @@ -186,11 +199,16 @@ class GuideQuerySet(models.QuerySet): def annotate_in_shop_section(self): """Annotate flag if GuideElement in ShopSectionNode.""" + shop_guides = models.Subquery( + self.filter( + guideelement__guide_element_type__name='EstablishmentNode', + guideelement__parent__guide_element_type__name='ShopSectionNode', + ).values_list('guideelement__id', flat=True).distinct() + ) return self.annotate( in_shop_section=models.Case( models.When( - guideelement__guide_element_type__name='EstablishmentNode', - guideelement__parent__guide_element_type__name='ShopSectionNode', + id__in=shop_guides, then=True), default=False, output_field=models.BooleanField(default=False) @@ -201,37 +219,60 @@ class GuideQuerySet(models.QuerySet): """Return QuerySet with annotated field - restaurant_counter.""" return self.annotate_in_restaurant_section().annotate( restaurant_counter=models.Count( - 'guideelement', + 'guideelement__establishment', filter=models.Q(in_restaurant_section=True) & - models.Q(guideelement__parent_id__isnull=False), - distinct=True)) + models.Q(guideelement__parent_id__isnull=True), + distinct=True + ) + ) def annotate_shop_counter(self): """Return QuerySet with annotated field - shop_counter.""" return self.annotate_in_shop_section().annotate( shop_counter=models.Count( - 'guideelement', + 'guideelement__establishment', filter=models.Q(in_shop_section=True) & - models.Q(guideelement__parent_id__isnull=False), - distinct=True)) + models.Q(guideelement__parent_id__isnull=True), + distinct=True + ) + ) def annotate_wine_counter(self): """Return QuerySet with annotated field - shop_counter.""" return self.annotate_in_restaurant_section().annotate( wine_counter=models.Count( - 'guideelement', + 'guideelement__product', filter=models.Q(guideelement__guide_element_type__name='WineNode') & models.Q(guideelement__parent_id__isnull=False), - distinct=True)) + distinct=True + ) + ) def annotate_present_objects_counter(self): """Return QuerySet with annotated field - present_objects_counter.""" - return self.annotate_in_restaurant_section().annotate( - present_objects_counter=models.Count( - 'guideelement', - filter=models.Q(guideelement__guide_element_type__name__in=['EstablishmentNode', 'WineNode']) & - models.Q(guideelement__parent_id__isnull=False), - distinct=True)) + return ( + self.annotate_restaurant_counter() + .annotate_shop_counter() + .annotate_wine_counter() + .annotate( + present_objects_counter=( + models.F('restaurant_counter') + + models.F('shop_counter') + + models.F('wine_counter') + ) + ) + ) + + def annotate_counters(self): + return ( + self.annotate_restaurant_counter() + .annotate_shop_counter() + .annotate_wine_counter() + .annotate_present_objects_counter() + ) + + def by_establishment_id(self, establishment_id: int): + return self.filter(guideelement__establishment=establishment_id).distinct() class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin): diff --git a/apps/collection/serializers/common.py b/apps/collection/serializers/common.py index da33d271..8943c7a2 100644 --- a/apps/collection/serializers/common.py +++ b/apps/collection/serializers/common.py @@ -109,6 +109,7 @@ class GuideBaseSerializer(serializers.ModelSerializer): restaurant_counter = serializers.IntegerField(read_only=True) shop_counter = serializers.IntegerField(read_only=True) wine_counter = serializers.IntegerField(read_only=True) + present_objects_counter = serializers.IntegerField(read_only=True) count_objects_during_init = serializers.IntegerField(read_only=True, source='count_related_objects') @@ -131,6 +132,7 @@ class GuideBaseSerializer(serializers.ModelSerializer): 'restaurant_counter', 'shop_counter', 'wine_counter', + 'present_objects_counter', 'count_objects_during_init', ] extra_kwargs = { diff --git a/apps/collection/views/back.py b/apps/collection/views/back.py index cc44c22d..73e1eb71 100644 --- a/apps/collection/views/back.py +++ b/apps/collection/views/back.py @@ -1,14 +1,11 @@ from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ -from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics from rest_framework import mixins, permissions, viewsets from rest_framework import status -from rest_framework.filters import OrderingFilter from rest_framework.response import Response -from collection import models, serializers -from collection import tasks +from collection import models, serializers, filters, tasks from utils.methods import get_permission_classes from utils.views import BindObjectMixin @@ -22,7 +19,7 @@ class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): def get_queryset(self): """Overridden method 'get_queryset'.""" - qs = models.Collection.objects.all().order_by('-created') + qs = models.Collection.objects.all().order_by('-created').with_base_related() if self.request.country_code: qs = qs.by_country_code(self.request.country_code) return qs @@ -35,12 +32,7 @@ class GuideBaseView(generics.GenericAPIView): def get_queryset(self): """Overridden get_queryset method.""" - return models.Guide.objects.with_base_related() \ - .annotate_restaurant_counter() \ - .annotate_shop_counter() \ - .annotate_wine_counter() \ - .annotate_present_objects_counter() \ - .distinct() + return models.Guide.objects.with_extended_related().annotate_counters() class GuideFilterBaseView(generics.GenericAPIView): @@ -73,17 +65,14 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, BindObjectMixin, CollectionViewSet): - """ViewSet for Collection model for BackOffice users.""" + """ViewSet for Collections list for BackOffice users and Collection create.""" - queryset = models.Collection.objects.all() - filter_backends = [DjangoFilterBackend, OrderingFilter] + queryset = models.Collection.objects.with_base_related().order_by('-start') + filter_class = filters.CollectionFilterSet serializer_class = serializers.CollectionBackOfficeSerializer bind_object_serializer_class = serializers.CollectionBindObjectSerializer permission_classes = get_permission_classes() - ordering_fields = ('rank', 'start') - ordering = ('-start', ) - def perform_binding(self, serializer): data = serializer.validated_data collection = data.pop('collection') @@ -107,7 +96,8 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin, class GuideListCreateView(GuideBaseView, generics.ListCreateAPIView): - """View for Guide model for BackOffice users.""" + """View for Guides list for BackOffice users and Guide create.""" + filter_class = filters.GuideFilterSet class GuideFilterCreateView(GuideFilterBaseView, diff --git a/apps/comment/models.py b/apps/comment/models.py index a7645ed0..3eac6480 100644 --- a/apps/comment/models.py +++ b/apps/comment/models.py @@ -48,6 +48,9 @@ class CommentQuerySet(ContentTypeQuerySetMixin): qs = self.filter(id__in=tuple(waiting_ids)) return qs + def with_base_related(self): + return self.prefetch_related("content_object").select_related("user", "content_type") + class Comment(ProjectBaseMixin): """Comment model.""" diff --git a/apps/comment/serializers/common.py b/apps/comment/serializers/common.py index c928aca4..592534fd 100644 --- a/apps/comment/serializers/common.py +++ b/apps/comment/serializers/common.py @@ -1,9 +1,7 @@ """Common serializers for app comment.""" from rest_framework import serializers -import establishment.serializers.common as establishment_serializers from comment.models import Comment -from establishment.models import EstablishmentType class CommentBaseSerializer(serializers.ModelSerializer): @@ -22,6 +20,8 @@ class CommentBaseSerializer(serializers.ModelSerializer): user_email = serializers.CharField(read_only=True, source='user.email') + slug = serializers.CharField(read_only=True, source='content_object.slug') + class Meta: """Serializer for model Comment""" model = Comment @@ -39,18 +39,29 @@ class CommentBaseSerializer(serializers.ModelSerializer): 'status_display', 'last_ip', 'content_type', - 'content_name' + 'content_name', + 'slug', ] extra_kwargs = { # 'status': {'read_only': True}, } def get_content_type(self, instance: Comment): - if instance.content_object.establishment_type == EstablishmentType.PRODUCER: - return establishment_serializers.EstablishmentSubTypeBaseSerializer( - instance.content_object.establishment_subtypes, many=True - ).data + import establishment.serializers.common as establishment_serializers + from establishment.models import EstablishmentType, Establishment + from product.models import Product + from product.serializers import ProductTypeBaseSerializer - return establishment_serializers.EstablishmentTypeBaseSerializer( - instance.content_object.establishment_type - ).data + if isinstance(instance.content_object, Establishment): + if instance.content_object.establishment_type == EstablishmentType.PRODUCER: + return establishment_serializers.EstablishmentSubTypeBaseSerializer( + instance.content_object.establishment_subtypes, many=True + ).data + + return establishment_serializers.EstablishmentTypeBaseSerializer( + instance.content_object.establishment_type + ).data + if isinstance(instance.content_object, Product): + return ProductTypeBaseSerializer( + instance.content_object.product_type + ).data diff --git a/apps/comment/urls/back.py b/apps/comment/urls/back.py index 214eab48..79b5429e 100644 --- a/apps/comment/urls/back.py +++ b/apps/comment/urls/back.py @@ -8,4 +8,6 @@ app_name = 'comment' urlpatterns = [ path('', views.CommentLstView.as_view(), name='comment-list-create'), path('/', views.CommentRUDView.as_view(), name='comment-crud'), + path('/', views.CommentLstView.as_view(), name='comment-list-by-type-create'), + path('/', views.CommentLstView.as_view(), name='comment-list-by-type-object-create'), ] diff --git a/apps/comment/views/back.py b/apps/comment/views/back.py index 090186be..ffd17f6b 100644 --- a/apps/comment/views/back.py +++ b/apps/comment/views/back.py @@ -8,6 +8,26 @@ from utils.permissions import IsModerator class CommentLstView(generics.ListCreateAPIView): """Comment list create view.""" + def get_queryset(self): + from product.models import Product + from establishment.models import Establishment + + allowed = { + "product": Product.__name__.lower(), + "establishment": Establishment.__name__.lower() + } + + qs = models.Comment.objects.with_base_related() + + if "object" in self.kwargs: + qs = qs.by_object_id(self.kwargs["object"]) + + if "type" in self.kwargs and self.kwargs["type"] in allowed: + model = allowed[self.kwargs["type"]] + qs = qs.by_content_type(self.kwargs["type"], model) + + return qs.order_by('-created') + serializer_class = CommentBaseSerializer queryset = models.Comment.objects.all() permission_classes = get_permission_classes(IsModerator) diff --git a/apps/establishment/filters.py b/apps/establishment/filters.py index b52c909f..86d5b787 100644 --- a/apps/establishment/filters.py +++ b/apps/establishment/filters.py @@ -1,8 +1,7 @@ """Establishment app filters.""" from django.core.validators import EMPTY_VALUES from django.utils.translation import ugettext_lazy as _ -from django_filters import rest_framework as filters, Filter -from django_filters.fields import Lookup +from django_filters import rest_framework as filters from rest_framework.serializers import ValidationError from establishment import models diff --git a/apps/establishment/migrations/0080_auto_20200128_0904.py b/apps/establishment/migrations/0080_auto_20200128_0904.py new file mode 100644 index 00000000..e111fad5 --- /dev/null +++ b/apps/establishment/migrations/0080_auto_20200128_0904.py @@ -0,0 +1,40 @@ +# Generated by Django 2.2.7 on 2020-01-28 09:04 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('gallery', '0008_merge_20191212_0752'), + ('establishment', '0079_auto_20200124_0720'), + ] + + operations = [ + migrations.AlterField( + model_name='menuuploads', + name='file', + field=models.FileField(upload_to='', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('doc', 'docx', 'pdf'))], verbose_name='File'), + ), + migrations.AlterField( + model_name='menuuploads', + name='menu', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='menu_uploads', to='establishment.Menu', verbose_name='menu'), + ), + migrations.CreateModel( + name='MenuGallery', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_main', models.BooleanField(default=False, verbose_name='Is the main image')), + ('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_gallery', to='gallery.Image', verbose_name='image')), + ('menu', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_gallery', to='establishment.Menu', verbose_name='menu')), + ], + options={ + 'verbose_name': 'menu gallery', + 'verbose_name_plural': 'menu galleries', + 'unique_together': {('menu', 'image'), ('menu', 'is_main')}, + }, + ), + ] diff --git a/apps/establishment/migrations/0081_menuuploads_title.py b/apps/establishment/migrations/0081_menuuploads_title.py new file mode 100644 index 00000000..820bdcb2 --- /dev/null +++ b/apps/establishment/migrations/0081_menuuploads_title.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-28 09:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0080_auto_20200128_0904'), + ] + + operations = [ + migrations.AddField( + model_name='menuuploads', + name='title', + field=models.CharField(default='', max_length=255, verbose_name='title'), + ), + ] diff --git a/apps/establishment/migrations/0082_establishment_western_name.py b/apps/establishment/migrations/0082_establishment_western_name.py new file mode 100644 index 00000000..a5c5e942 --- /dev/null +++ b/apps/establishment/migrations/0082_establishment_western_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-28 12:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0081_menuuploads_title'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='western_name', + field=models.CharField(default='', max_length=255, verbose_name='Western name'), + ), + ] diff --git a/apps/establishment/migrations/0083_establishment_instagram.py b/apps/establishment/migrations/0083_establishment_instagram.py new file mode 100644 index 00000000..e5733f6d --- /dev/null +++ b/apps/establishment/migrations/0083_establishment_instagram.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-28 12:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0082_establishment_western_name'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='instagram', + field=models.URLField(blank=True, default=None, max_length=255, null=True, verbose_name='Instagram URL'), + ), + ] diff --git a/apps/establishment/migrations/0084_auto_20200129_1113.py b/apps/establishment/migrations/0084_auto_20200129_1113.py new file mode 100644 index 00000000..c041c7bd --- /dev/null +++ b/apps/establishment/migrations/0084_auto_20200129_1113.py @@ -0,0 +1,69 @@ +# Generated by Django 2.2.7 on 2020-01-29 11:13 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import utils.methods + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('establishment', '0083_establishment_instagram'), + ] + + operations = [ + migrations.CreateModel( + name='MenuFiles', + 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')), + ('file', models.FileField(blank=True, default=None, null=True, upload_to=utils.methods.file_path, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf'))], verbose_name='File')), + ('name', models.CharField(default='', max_length=255, verbose_name='name')), + ('type', models.CharField(default='', max_length=65, verbose_name='type')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menufiles_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menufiles_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')), + ], + options={ + 'verbose_name': 'menu upload', + 'verbose_name_plural': 'menu uploads', + }, + ), + migrations.RemoveField( + model_name='menuuploads', + name='created_by', + ), + migrations.RemoveField( + model_name='menuuploads', + name='menu', + ), + migrations.RemoveField( + model_name='menuuploads', + name='modified_by', + ), + migrations.AddField( + model_name='menu', + name='name', + field=models.CharField(default='', max_length=255, verbose_name='name'), + ), + migrations.AlterField( + model_name='menu', + name='schedule', + field=models.ManyToManyField(blank=True, to='timetable.Timetable', verbose_name='Menu schedule'), + ), + migrations.DeleteModel( + name='MenuGallery', + ), + migrations.DeleteModel( + name='MenuUploads', + ), + migrations.AddField( + model_name='menu', + name='uploads', + field=models.ManyToManyField(blank=True, to='establishment.MenuFiles', verbose_name='Menu files'), + ), + ] diff --git a/apps/establishment/migrations/0085_menu_price.py b/apps/establishment/migrations/0085_menu_price.py new file mode 100644 index 00000000..23afcba4 --- /dev/null +++ b/apps/establishment/migrations/0085_menu_price.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-29 11:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0084_auto_20200129_1113'), + ] + + operations = [ + migrations.AddField( + model_name='menu', + name='price', + field=models.IntegerField(blank=True, default=None, null=True, verbose_name='price'), + ), + ] diff --git a/apps/establishment/migrations/0086_auto_20200129_1301.py b/apps/establishment/migrations/0086_auto_20200129_1301.py new file mode 100644 index 00000000..99ad4d31 --- /dev/null +++ b/apps/establishment/migrations/0086_auto_20200129_1301.py @@ -0,0 +1,47 @@ +# Generated by Django 2.2.7 on 2020-01-29 13:01 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('establishment', '0085_menu_price'), + ] + + operations = [ + migrations.AlterField( + model_name='menufiles', + name='type', + field=models.CharField(choices=[('image', 'Image'), ('file', 'File')], default='', max_length=65, verbose_name='type'), + ), + migrations.CreateModel( + name='MenuDish', + 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')), + ('name', models.CharField(default='', max_length=255, verbose_name='name')), + ('category', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='category')), + ('price', models.IntegerField(blank=True, default=None, null=True, verbose_name='price')), + ('signature', models.BooleanField(default=False, verbose_name='signature')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menudish_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menudish_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')), + ], + options={ + 'verbose_name': 'dish', + 'verbose_name_plural': 'dishes', + 'ordering': ('-created',), + }, + ), + migrations.AddField( + model_name='menu', + name='dishes', + field=models.ManyToManyField(blank=True, to='establishment.MenuDish', verbose_name='Menu dishes'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 21ffed5f..0371ae17 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -33,7 +33,7 @@ from utils.methods import transform_into_readable_str from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, TranslatedFieldsMixin, BaseAttributes, GalleryMixin, IntermediateGalleryModelMixin, HasTagsMixin, - FavoritesMixin, TypeDefaultImageMixin) + FavoritesMixin, TypeDefaultImageMixin, FileMixin) # todo: establishment type&subtypes check @@ -541,6 +541,9 @@ class EstablishmentQuerySet(models.QuerySet): self.filter(id__in=administrator_establishment_ids) ) + def with_contacts(self): + return self.prefetch_related('emails', 'phones') + class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin, HasTagsMixin, FavoritesMixin): @@ -552,6 +555,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, name = models.CharField(_('name'), max_length=255, default='') transliterated_name = models.CharField(default='', max_length=255, verbose_name=_('Transliterated name')) + western_name = models.CharField(default='', max_length=255, + verbose_name=_('Western name')) index_name = models.CharField(_('Index name'), max_length=255, default='') description = TJSONField(blank=True, null=True, default=None, verbose_name=_('description'), @@ -583,6 +588,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, verbose_name=_('Facebook URL')) twitter = models.URLField(blank=True, null=True, default=None, max_length=255, verbose_name=_('Twitter URL')) + instagram = models.URLField(blank=True, null=True, default=None, max_length=255, + verbose_name=_('Instagram URL')) lafourchette = models.URLField(blank=True, null=True, default=None, max_length=255, verbose_name=_('Lafourchette URL')) guestonline_id = models.PositiveIntegerField(blank=True, verbose_name=_('guestonline id'), @@ -754,7 +761,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, 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 or schedule_for_today.opening_time is None or schedule_for_today.ending_time is None: + if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is \ + None: return False time_at_est_tz = now_at_est_tz.time() return schedule_for_today.ending_time > time_at_est_tz > schedule_for_today.opening_time @@ -766,8 +774,10 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, @property def tags_indexing(self): - return [{'id': tag.metadata.id, - 'label': tag.metadata.label} for tag in self.tags.all()] + return [{ + 'id': tag.metadata.id, + 'label': tag.metadata.label + } for tag in self.tags.all()] @property def last_published_review(self): @@ -837,6 +847,11 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, if self.phones: return [phone.phone.as_e164 for phone in self.phones.all()] + @property + def contact_emails(self): + if self.phones: + return [email.email for email in self.emails.all()] + @property def establishment_subtype_labels(self): if self.establishment_subtypes: @@ -1249,6 +1264,24 @@ class Plate(TranslatedFieldsMixin, models.Model): verbose_name_plural = _('plates') +class MenuDish(BaseAttributes): + """Dish model.""" + + STR_FIELD_NAME = 'category' + + name = models.CharField(_('name'), max_length=255, default='') + category = TJSONField( + blank=True, null=True, default=None, verbose_name=_('category'), + help_text='{"en-GB":"some text"}') + price = models.IntegerField(blank=True, null=True, default=None, verbose_name=_('price')) + signature = models.BooleanField(_('signature'), default=False) + + class Meta: + verbose_name = _('dish') + verbose_name_plural = _('dishes') + ordering = ('-created',) + + class MenuQuerySet(models.QuerySet): def with_schedule_plates_establishment(self): return self.select_related( @@ -1258,6 +1291,11 @@ class MenuQuerySet(models.QuerySet): 'plates', ) + def with_gallery(self): + return self.prefetch_related( + 'menu_gallery' + ) + def dishes(self): return self.filter( Q(category__icontains='starter') | @@ -1269,25 +1307,38 @@ class MenuQuerySet(models.QuerySet): """Search by category.""" return self.filter(category__icontains=value) + def with_dishes(self): + return self.filter(~Q(dishes=None)) + class Menu(TranslatedFieldsMixin, BaseAttributes): """Menu model.""" - STR_FIELD_NAME = 'category' - - category = TJSONField( - blank=True, null=True, default=None, verbose_name=_('category'), - help_text='{"en-GB":"some text"}') + name = models.CharField(_('name'), max_length=255, default='') establishment = models.ForeignKey( 'establishment.Establishment', verbose_name=_('establishment'), on_delete=models.CASCADE) is_drinks_included = models.BooleanField(_('is drinks included'), default=False) + price = models.IntegerField(blank=True, null=True, default=None, verbose_name=_('price')) schedule = models.ManyToManyField( to='timetable.Timetable', blank=True, - verbose_name=_('Establishment schedule'), - related_name='menus', + verbose_name=_('Menu schedule'), ) + uploads = models.ManyToManyField( + to='MenuFiles', + blank=True, + verbose_name=_('Menu files'), + ) + dishes = models.ManyToManyField( + to='MenuDish', + blank=True, + verbose_name=_('Menu dishes') + ) + category = TJSONField( + blank=True, null=True, default=None, verbose_name=_('category'), + help_text='{"en-GB":"some text"}') + old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) objects = MenuQuerySet.as_manager() @@ -1298,15 +1349,16 @@ class Menu(TranslatedFieldsMixin, BaseAttributes): ordering = ('-created',) -class MenuUploads(BaseAttributes): +class MenuFiles(FileMixin, BaseAttributes): """Menu files""" - - menu = models.ForeignKey(Menu, verbose_name=_('Menu'), on_delete=models.CASCADE, related_name='uploads') - file = models.FileField( - _('File'), - validators=[FileExtensionValidator(allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf')), ], + TYPES = ( + ('image', 'Image'), + ('file', 'File') ) + name = models.CharField(_('name'), max_length=255, default='') + type = models.CharField(_('type'), choices=TYPES, max_length=65, default='') + class Meta: verbose_name = _('menu upload') verbose_name_plural = _('menu uploads') diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 5991a1fd..b184e033 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -1,20 +1,22 @@ from functools import lru_cache +from pprint import pprint from django.db.models import F +from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from account.serializers.common import UserShortSerializer from establishment import models, serializers as model_serializers -from establishment.models import ContactPhone, EstablishmentEmployee +from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee +from establishment.serializers import MenuDishSerializer from gallery.models import Image from location.models import Address from location.serializers import AddressDetailSerializer, TranslatedField from main.models import Currency from main.serializers import AwardSerializer -from timetable.serialziers import ScheduleRUDSerializer from utils.decorators import with_base_attributes -from utils.serializers import ImageBaseSerializer, TimeZoneChoiceField, ProjectModelSerializer +from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField def phones_handler(phones_list, establishment): @@ -27,6 +29,16 @@ def phones_handler(phones_list, establishment): ContactPhone.objects.create(establishment=establishment, phone=new_phone) +def emails_handler(emails_list, establishment): + """ + create or update emails for establishment + """ + ContactEmail.objects.filter(establishment=establishment).delete() + + for new_email in emails_list: + ContactEmail.objects.create(establishment=establishment, email=new_email) + + class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSerializer): """Establishment create serializer""" @@ -40,14 +52,14 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria queryset=models.Address.objects.all(), write_only=True ) - emails = model_serializers.ContactEmailsSerializer(read_only=True, - many=True, ) - socials = model_serializers.SocialNetworkRelatedSerializers(read_only=True, - many=True, ) - type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', - read_only=True) - address_id = serializers.PrimaryKeyRelatedField(write_only=True, source='address', - queryset=Address.objects.all()) + socials = model_serializers.SocialNetworkRelatedSerializers( + read_only=True, + many=True, + ) + type = model_serializers.EstablishmentTypeBaseSerializer( + source='establishment_type', + read_only=True, + ) tz = TimeZoneChoiceField() phones = serializers.ListField( source='contact_phones', @@ -56,6 +68,13 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria child=serializers.CharField(max_length=128), required=False, ) + emails = serializers.ListField( + source='contact_emails', + allow_null=True, + allow_empty=True, + child=serializers.CharField(max_length=128), + required=False, + ) class Meta: model = models.Establishment @@ -81,6 +100,7 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria 'tags', 'tz', 'address_id', + 'western_name', ] def create(self, validated_data): @@ -88,11 +108,29 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria if 'contact_phones' in validated_data: phones_list = validated_data.pop('contact_phones') + emails_list = [] + if 'contact_emails' in validated_data: + emails_list = validated_data.pop('contact_emails') + instance = super().create(validated_data) phones_handler(phones_list, instance) + emails_handler(emails_list, instance) return instance +class EstablishmentPositionListSerializer(model_serializers.EstablishmentBaseSerializer): + """Establishment position serializer""" + + class Meta: + model = models.Establishment + fields = [ + 'id', + 'name', + 'transliterated_name', + 'index_name', + ] + + class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): """Establishment create serializer""" @@ -100,9 +138,14 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): source='establishment_type', queryset=models.EstablishmentType.objects.all(), write_only=True ) - address = AddressDetailSerializer() - emails = model_serializers.ContactEmailsSerializer(read_only=False, - many=True, ) + address = AddressDetailSerializer(read_only=True) + emails = serializers.ListField( + source='contact_emails', + allow_null=True, + allow_empty=True, + child=serializers.CharField(max_length=128), + required=False, + ) socials = model_serializers.SocialNetworkRelatedSerializers(read_only=False, many=True, ) type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type') @@ -119,6 +162,7 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): fields = [ 'id', 'slug', + 'western_name', 'name', 'website', 'phones', @@ -132,6 +176,7 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): # TODO: check in admin filters 'is_publish', 'address', + 'transportation', 'tags', ] @@ -140,8 +185,13 @@ class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): if 'contact_phones' in validated_data: phones_list = validated_data.pop('contact_phones') + emails_list = [] + if 'contact_emails' in validated_data: + emails_list = validated_data.pop('contact_emails') + instance = super().update(instance, validated_data) phones_handler(phones_list, instance) + emails_handler(emails_list, instance) return instance @@ -516,52 +566,79 @@ class EstablishmentAdminListSerializer(UserShortSerializer): ] -class _PlateSerializer(ProjectModelSerializer): - name_translated = TranslatedField() +class EstablishmentEmployeePositionsSerializer(serializers.ModelSerializer): + """Establishments from employee serializer""" + + restaurant_name = serializers.CharField(read_only=True, source='establishment.name') + position = PositionBackSerializer(read_only=True) + state = serializers.CharField(read_only=True, source='status') + start = serializers.DateTimeField(read_only=True, source='from_date') + end = serializers.DateTimeField(read_only=True, source='to_date') class Meta: - model = models.Plate + model = models.EstablishmentEmployee fields = [ - 'name_translated', - 'price', + 'restaurant_name', + 'position', + 'state', + 'start', + 'end', ] class MenuDishesSerializer(ProjectModelSerializer): """for dessert, main_course and starter category""" - schedule = ScheduleRUDSerializer(many=True, allow_null=True) - plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) - category_translated = serializers.CharField(read_only=True) - last_update = serializers.DateTimeField(source='created') + establishment_id = serializers.PrimaryKeyRelatedField(read_only=True) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + dishes = MenuDishSerializer(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'category_translated', - 'establishment', - 'is_drinks_included', - 'schedule', - 'plates', - 'last_update', + 'establishment_id', + 'establishment_slug', + 'dishes', ] +class MenuDishesCreateSerializer(ProjectModelSerializer): + """Menu dishes create serializer""" + + menu_id = serializers.IntegerField(write_only=True) + + class Meta: + model = models.MenuDish + fields = [ + 'id', + 'name', + 'category', + 'price', + 'signature', + 'menu_id' + ] + + def create(self, validated_data): + menu_id = validated_data.pop('menu_id') + menu = get_object_or_404(models.Menu, pk=menu_id) + instance = models.MenuDish.objects.create(**validated_data) + menu.dishes.add(instance) + return instance + + class MenuDishesRUDSerializers(ProjectModelSerializer): """for dessert, main_course and starter category""" - plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) - schedule = ScheduleRUDSerializer(read_only=True, many=True, allow_null=True) + establishment_id = serializers.PrimaryKeyRelatedField(queryset=models.Establishment.objects.all()) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + dishes = MenuDishSerializer(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'plates', - 'establishment', - 'is_drinks_included', - 'schedule', + 'establishment_id', + 'establishment_slug', + 'dishes', ] diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index cd60e9f1..3af43e27 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -2,6 +2,7 @@ import logging from django.conf import settings +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from phonenumber_field.phonenumber import to_python as str_to_phonenumber from rest_framework import serializers @@ -9,18 +10,19 @@ from rest_framework import serializers from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models -from location.serializers import AddressBaseSerializer, CityBaseSerializer, AddressDetailSerializer, \ - CityShortSerializer -from location.serializers import EstablishmentWineRegionBaseSerializer, \ - EstablishmentWineOriginBaseSerializer +from location.serializers import ( + AddressBaseSerializer, AddressDetailSerializer, CityBaseSerializer, + CityShortSerializer, EstablishmentWineOriginBaseSerializer, EstablishmentWineRegionBaseSerializer, +) from main.serializers import AwardSerializer, CurrencySerializer from review.serializers import ReviewShortSerializer from tag.serializers import TagBaseSerializer from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions -from utils.serializers import ImageBaseSerializer, CarouselCreateSerializer -from utils.serializers import (ProjectModelSerializer, TranslatedField, - FavoritesCreateSerializer) +from utils.serializers import ( + CarouselCreateSerializer, FavoritesCreateSerializer, ImageBaseSerializer, + ProjectModelSerializer, TranslatedField, +) logger = logging.getLogger(__name__) @@ -70,31 +72,87 @@ class PlateSerializer(ProjectModelSerializer): ] -class MenuSerializers(ProjectModelSerializer): - plates = PlateSerializer(read_only=True, many=True, source='plate_set') - category_translated = serializers.CharField(read_only=True) - +class MenuDishSerializer(ProjectModelSerializer): class Meta: - model = models.Menu + model = models.MenuDish fields = [ 'id', + 'name', 'category', - 'category_translated', - 'plates', - 'establishment' + 'price', + 'signature' ] -class MenuRUDSerializers(ProjectModelSerializer): - plates = PlateSerializer(read_only=True, many=True, source='plate_set') +class MenuFilesSerializers(ProjectModelSerializer): + menu_id = serializers.IntegerField(write_only=True) + + class Meta: + model = models.MenuFiles + fields = [ + 'id', + 'name', + 'type', + 'file', + 'menu_id' + ] + + def create(self, validated_data): + menu_id = validated_data.pop('menu_id') + menu = get_object_or_404(models.Menu, pk=menu_id) + instance = models.MenuFiles.objects.create(**validated_data) + menu.uploads.add(instance) + return instance + + +class MenuSerializers(ProjectModelSerializer): + name = serializers.CharField() + establishment_id = serializers.PrimaryKeyRelatedField(queryset=models.Establishment.objects.all()) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + price = serializers.IntegerField(required=False) + drinks_included = serializers.BooleanField(source='is_drinks_included', required=False) + schedules = ScheduleRUDSerializer(many=True, allow_null=True, required=False) + uploads = MenuFilesSerializers(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'plates', - 'establishment' + 'name', + 'establishment_id', + 'establishment_slug', + 'price', + 'drinks_included', + 'schedules', + 'uploads', + ] + + def create(self, validated_data): + validated_data['establishment'] = validated_data.pop('establishment_id') + instance = models.Menu.objects.create(**validated_data) + return instance + + +class MenuRUDSerializers(ProjectModelSerializer): + name = serializers.CharField() + establishment_id = serializers.PrimaryKeyRelatedField(read_only=True) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + price = serializers.IntegerField(required=False) + drinks_included = serializers.BooleanField(source='is_drinks_included', required=False) + schedules = ScheduleRUDSerializer(many=True, allow_null=True, required=False) + uploads = MenuFilesSerializers(many=True) + + class Meta: + model = models.Menu + fields = [ + 'id', + 'name', + 'establishment_id', + 'establishment_slug', + 'price', + 'drinks_included', + 'schedules', + 'uploads', ] @@ -464,6 +522,7 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer): 'website', 'facebook', 'twitter', + 'instagram', 'lafourchette', 'booking', 'phones', @@ -527,7 +586,7 @@ class EstablishmentCommentBaseSerializer(comment_serializers.CommentBaseSerializ 'created', 'text', 'mark', - 'nickname', + # 'nickname', 'profile_pic', 'status', 'status_display', @@ -567,7 +626,7 @@ class EstablishmentCommentRUDSerializer(comment_serializers.CommentBaseSerialize 'created', 'text', 'mark', - 'nickname', + # 'nickname', 'profile_pic', ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index 3129a060..09c2f2a8 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -30,10 +30,15 @@ urlpatterns = [ name='note-rud'), path('slug//admin/', views.EstablishmentAdminView.as_view(), name='establishment-admin-list'), - path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), + path('menus/dishes/', views.MenuDishesListView.as_view(), name='menu-dishes-list'), + path('menus/dishes/create/', views.MenuDishesCreateView.as_view(), name='menu-dishes-create'), path('menus/dishes//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), + path('menus/dishes/slug//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), + path('menus/slug//', views.MenuRUDView.as_view(), name='menu-rud'), + path('menus/uploads/', views.MenuFilesListCreateView.as_view(), name='menu-files-list'), + path('menus/uploads//', views.MenuFilesRUDView.as_view(), name='menu-files-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), path('plates//', views.PlateRUDView.as_view(), name='plate-rud'), path('social_choice/', views.SocialChoiceListCreateView.as_view(), name='socials_choice'), @@ -60,6 +65,10 @@ urlpatterns = [ path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'), path('subtypes//', views.EstablishmentSubtypeRUDView.as_view(), name='subtype-rud'), path('positions/', views.EstablishmentPositionListView.as_view(), name='position-list'), + path('employee_positions//', views.EmployeePositionsListView.as_view(), + name='employee-positions-list'), path('employee_establishments//', views.EmployeeEstablishmentsListView.as_view(), - name='employee-establishments-list') + name='employee-establishments-list'), + path('employee_establishment_positions//', views.EmployeeEstablishmentPositionsView.as_view(), + name='employee-establishment-positions') ] diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index ee17fc94..e1d97b91 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -1,7 +1,9 @@ """Establishment app views.""" +from django.db.models.query_utils import Q +from django.http import Http404 from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, status +from rest_framework import generics, status, permissions from rest_framework.response import Response from account.models import User @@ -16,6 +18,20 @@ from utils.permissions import ( from utils.views import CreateDestroyGalleryViewMixin +class MenuRUDMixinViews: + """Menu mixin""" + + def get_object(self): + instance = self.get_queryset().filter( + Q(establishment__slug=self.kwargs.get('slug')) | Q(establishment__id=self.kwargs.get('pk')) + ).first() + + if instance is None: + raise Http404 + + return instance + + class EstablishmentMixinViews: """Establishment mixin.""" @@ -34,12 +50,27 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP filter_class = filters.EstablishmentFilter serializer_class = serializers.EstablishmentListCreateSerializer permission_classes = get_permission_classes( - IsReviewManager, IsEstablishmentManager, IsEstablishmentAdministrator, ) +class EmployeeEstablishmentPositionsView(generics.ListAPIView): + """Establishment by employee view.""" + + queryset = models.EstablishmentEmployee.objects.all() + serializer_class = serializers.EstablishmentEmployeePositionsSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + def get_queryset(self): + employee_pk = self.kwargs.get('pk') + return super().get_queryset().filter(employee__id=employee_pk).all().prefetch_related( + 'establishment').select_related('position') + + class EmployeeEstablishmentsListView(generics.ListAPIView): """Establishment by employee list view.""" serializer_class = serializers.EstablishmentListCreateSerializer @@ -54,6 +85,22 @@ class EmployeeEstablishmentsListView(generics.ListAPIView): return employee.establishments.with_extended_related() +class EmployeePositionsListView(generics.ListAPIView): + """Establishment position by employee list view.""" + + queryset = models.Establishment.objects.all() + serializer_class = serializers.EstablishmentPositionListSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + def get_queryset(self): + pk = self.kwargs.get('pk') + employee = get_object_or_404(models.Employee, pk=pk) + return employee.establishments.with_extended_related() + + class EstablishmentRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView): lookup_field = 'slug' serializer_class = serializers.EstablishmentRUDSerializer @@ -120,13 +167,17 @@ class MenuListCreateView(generics.ListCreateAPIView): IsEstablishmentAdministrator, ) filterset_fields = ( - 'establishment', + 'establishment__id', 'establishment__slug', ) + def get_queryset(self): + return super().get_queryset().prefetch_related('establishment') -class MenuRUDView(generics.RetrieveUpdateDestroyAPIView): + +class MenuRUDView(MenuRUDMixinViews, generics.RetrieveUpdateDestroyAPIView): """Menu RUD view.""" + lookup_field = None serializer_class = serializers.MenuRUDSerializers queryset = models.Menu.objects.all() permission_classes = get_permission_classes( @@ -135,6 +186,26 @@ class MenuRUDView(generics.RetrieveUpdateDestroyAPIView): ) +class MenuFilesListCreateView(generics.ListCreateAPIView): + """Menu files list create view.""" + serializer_class = serializers.MenuFilesSerializers + queryset = models.MenuFiles.objects.all() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + +class MenuFilesRUDView(generics.RetrieveDestroyAPIView): + """Menu files RUD view.""" + serializer_class = serializers.MenuFilesSerializers + queryset = models.MenuFiles.objects.all() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + class SocialChoiceListCreateView(generics.ListCreateAPIView): """SocialChoice list create view.""" serializer_class = serializers.SocialChoiceSerializers @@ -523,10 +594,10 @@ class EstablishmentAdminView(generics.ListAPIView): return User.objects.establishment_admin(establishment).distinct() -class MenuDishesListCreateView(generics.ListCreateAPIView): +class MenuDishesListView(generics.ListAPIView): """Menu (dessert, main_course, starter) list create view.""" serializer_class = serializers.MenuDishesSerializer - queryset = models.Menu.objects.with_schedule_plates_establishment().dishes().distinct() + queryset = models.Menu.objects.with_dishes() filter_class = filters.MenuDishesBackFilter permission_classes = get_permission_classes( IsEstablishmentManager, @@ -534,10 +605,21 @@ class MenuDishesListCreateView(generics.ListCreateAPIView): ) -class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): +class MenuDishesRUDView(MenuRUDMixinViews, generics.RetrieveUpdateDestroyAPIView): """Menu (dessert, main_course, starter) RUD view.""" + lookup_field = None serializer_class = serializers.MenuDishesRUDSerializers - queryset = models.Menu.objects.dishes().distinct() + queryset = models.Menu.objects.with_dishes() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + +class MenuDishesCreateView(generics.CreateAPIView): + """Menu (dessert, main_course, starter) list create view.""" + serializer_class = serializers.MenuDishesCreateSerializer + queryset = models.MenuDish.objects.all() + filter_class = filters.MenuDishesBackFilter permission_classes = get_permission_classes( IsEstablishmentManager, IsEstablishmentAdministrator diff --git a/apps/location/migrations/0036_auto_20200127_2004.py b/apps/location/migrations/0036_auto_20200127_2004.py new file mode 100644 index 00000000..4dcb5132 --- /dev/null +++ b/apps/location/migrations/0036_auto_20200127_2004.py @@ -0,0 +1,45 @@ +# Generated by Django 2.2.7 on 2020-01-27 20:04 + +from django.db import migrations, models +import utils.models + + +def clear_data(apps, schema_editor): + City = apps.get_model('location', 'City') + for city in City.objects.all(): + city.name = None + city.save() + + +def preserve_field_data(apps, schema_editor): + City = apps.get_model('location', 'City') + for city in City.objects.all(): + city.name = city.name_translated + city.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('location', '0035_auto_20200115_1117'), + ] + + + operations = [ + migrations.AlterField( + model_name='city', + name='name', + field=models.CharField(max_length=250, null=True, verbose_name='name'), + ), + migrations.RunPython(clear_data, migrations.RunPython.noop), + migrations.AlterField( + model_name='city', + name='name', + field=utils.models.TJSONField(default=None, help_text='{"en-GB":"some city name"}', null=True, verbose_name='City name json'), + ), + migrations.RunPython(preserve_field_data, migrations.RunPython.noop), + migrations.RemoveField( + model_name='city', + name='name_translated', + ) + ] diff --git a/apps/location/migrations/0037_address_district_name.py b/apps/location/migrations/0037_address_district_name.py new file mode 100644 index 00000000..294989e6 --- /dev/null +++ b/apps/location/migrations/0037_address_district_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-28 12:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('location', '0036_auto_20200127_2004'), + ] + + operations = [ + migrations.AddField( + model_name='address', + name='district_name', + field=models.CharField(blank=True, default='', max_length=500, verbose_name='District name'), + ), + ] diff --git a/apps/location/models.py b/apps/location/models.py index 97c02dd7..579addd1 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -1,20 +1,20 @@ """Location app models.""" +from functools import reduce +from json import dumps +from typing import List + from django.conf import settings from django.contrib.gis.db import models +from django.contrib.postgres.fields import ArrayField 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 functools import reduce -from typing import List -from django.contrib.postgres.fields.jsonb import KeyTextTransform - -from django.contrib.postgres.fields import ArrayField from translation.models import Language from utils.models import (ProjectBaseMixin, SVGImageMixin, TJSONField, TranslatedFieldsMixin, get_current_locale, - IntermediateGalleryModelMixin, GalleryMixin) + IntermediateGalleryModelMixin) class CountryQuerySet(models.QuerySet): @@ -139,13 +139,14 @@ class CityQuerySet(models.QuerySet): """Return establishments by country code""" return self.filter(country__code=code) + def with_base_related(self): + return self.prefetch_related('country', 'region', 'region__country') -class City(models.Model): + +class City(models.Model, TranslatedFieldsMixin): """Region model.""" - name = models.CharField(_('name'), max_length=250) - name_translated = TJSONField(blank=True, null=True, default=None, - verbose_name=_('Translated name'), - help_text='{"en-GB":"some text"}') + name = TJSONField(default=None, null=True, help_text='{"en-GB":"some city name"}', + verbose_name=_('City name json')) code = models.CharField(_('code'), max_length=250) region = models.ForeignKey(Region, on_delete=models.CASCADE, blank=True, null=True, @@ -177,7 +178,12 @@ class City(models.Model): verbose_name = _('city') def __str__(self): - return self.name + return self.name_dumped + + @property + def name_dumped(self): + """Used for indexing as string""" + return f'{self.id}: {dumps(self.name)}' @property def image_object(self): @@ -217,6 +223,8 @@ class Address(models.Model): default='', help_text=_('Ex.: 350018')) coordinates = models.PointField( _('Coordinates'), blank=True, null=True, default=None) + district_name = models.CharField( + _('District name'), max_length=500, blank=True, default='') old_id = models.IntegerField(null=True, blank=True, default=None) class Meta: diff --git a/apps/location/serializers/back.py b/apps/location/serializers/back.py index c178f7fd..4f2b1ea8 100644 --- a/apps/location/serializers/back.py +++ b/apps/location/serializers/back.py @@ -1,6 +1,8 @@ from location import models from location.serializers import common +from utils.serializers import TranslatedField + class AddressCreateSerializer(common.AddressDetailSerializer): """Address create serializer.""" @@ -9,6 +11,8 @@ class AddressCreateSerializer(common.AddressDetailSerializer): class CountryBackSerializer(common.CountrySerializer): """Country back-office serializer.""" + name_translated = TranslatedField() + class Meta: model = models.Country fields = [ @@ -16,5 +20,6 @@ class CountryBackSerializer(common.CountrySerializer): 'code', 'svg_image', 'name', + 'name_translated', 'country_id' ] diff --git a/apps/location/serializers/common.py b/apps/location/serializers/common.py index 1da144f8..00fc893a 100644 --- a/apps/location/serializers/common.py +++ b/apps/location/serializers/common.py @@ -34,6 +34,27 @@ class CountrySimpleSerializer(serializers.ModelSerializer): fields = ('id', 'code', 'name_translated') +class ParentRegionSerializer(serializers.ModelSerializer): + """Region serializer""" + + country = CountrySerializer(read_only=True) + country_id = serializers.PrimaryKeyRelatedField( + source='country', + queryset=models.Country.objects.all(), + write_only=True + ) + + class Meta: + model = models.Region + fields = [ + 'id', + 'name', + 'code', + 'country', + 'country_id' + ] + + class RegionSerializer(serializers.ModelSerializer): """Region serializer""" @@ -43,6 +64,7 @@ class RegionSerializer(serializers.ModelSerializer): queryset=models.Country.objects.all(), write_only=True ) + parent_region = ParentRegionSerializer(read_only=True) class Meta: model = models.Region @@ -59,13 +81,14 @@ class RegionSerializer(serializers.ModelSerializer): class CityShortSerializer(serializers.ModelSerializer): """Short city serializer""" country = CountrySerializer(read_only=True) + name_translated = TranslatedField() class Meta: """Meta class""" model = models.City fields = ( 'id', - 'name', + 'name_translated', 'code', 'country', ) @@ -91,12 +114,14 @@ class CityBaseSerializer(serializers.ModelSerializer): required=False, ) country = CountrySerializer(read_only=True) + name_translated = TranslatedField() class Meta: model = models.City fields = [ 'id', 'name', + 'name_translated', 'region', 'region_id', 'country_id', @@ -146,6 +171,7 @@ class AddressBaseSerializer(serializers.ModelSerializer): 'postal_code', 'latitude', 'longitude', + 'district_name', # todo: remove this fields (backward compatibility) 'geo_lon', diff --git a/apps/location/views/back.py b/apps/location/views/back.py index b57c315d..3138fe2c 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -50,8 +50,8 @@ class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView): def get_queryset(self): """Overridden method 'get_queryset'.""" - qs = models.City.objects.all().annotate(locale_name=KeyTextTransform(get_current_locale(), 'name_translated'))\ - .order_by('locale_name') + qs = models.City.objects.all().annotate(locale_name=KeyTextTransform(get_current_locale(), 'name'))\ + .order_by('locale_name').with_base_related() if self.request.country_code: qs = qs.by_country_code(self.request.country_code) return qs @@ -61,7 +61,7 @@ class CityListSearchView(common.CityViewMixin, generics.ListCreateAPIView): """Create view for model City.""" serializer_class = serializers.CityBaseSerializer queryset = models.City.objects.all()\ - .annotate(locale_name=KeyTextTransform(get_current_locale(), 'name_translated'))\ + .annotate(locale_name=KeyTextTransform(get_current_locale(), 'name'))\ .order_by('locale_name') filter_class = filters.CityBackFilter pagination_class = None diff --git a/apps/location/views/common.py b/apps/location/views/common.py index d9968755..475288fe 100644 --- a/apps/location/views/common.py +++ b/apps/location/views/common.py @@ -24,7 +24,7 @@ class RegionViewMixin(generics.GenericAPIView): class CityViewMixin(generics.GenericAPIView): """View Mixin for model City""" model = models.City - queryset = models.City.objects.all() + queryset = models.City.objects.with_base_related() class AddressViewMixin(generics.GenericAPIView): @@ -101,7 +101,7 @@ class CityListView(CityViewMixin, generics.ListAPIView): def get_queryset(self): qs = super().get_queryset() if self.request.country_code: - qs = qs.by_country_code(self.request.country_code) + qs = qs.by_country_code(self.request.country_code).with_base_related() return qs diff --git a/apps/news/filters.py b/apps/news/filters.py index 064f7175..7a2e4382 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -24,6 +24,8 @@ class NewsListFilterSet(filters.FilterSet): state = filters.NumberFilter() + state__in = filters.CharFilter(method='by_states_list') + SORT_BY_CREATED_CHOICE = "created" SORT_BY_START_CHOICE = "start" SORT_BY_CHOICES = ( @@ -54,6 +56,10 @@ class NewsListFilterSet(filters.FilterSet): return queryset.es_search(value, relevance_order='ordering' not in self.request.query_params) return queryset + def by_states_list(self, queryset, name, value): + states = value.split('__') + return queryset.filter(state__in=states) + def in_tags(self, queryset, name, value): tags = value.split('__') return queryset.filter(tags__value__in=tags) diff --git a/apps/news/migrations/0053_auto_20200128_1431.py b/apps/news/migrations/0053_auto_20200128_1431.py new file mode 100644 index 00000000..b759a1a7 --- /dev/null +++ b/apps/news/migrations/0053_auto_20200128_1431.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-28 14:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('news', '0052_auto_20200121_0940'), + ] + + operations = [ + migrations.AlterField( + model_name='news', + name='state', + field=models.PositiveSmallIntegerField(choices=[(0, 'remove'), (1, 'hidden'), (2, 'published'), (3, 'not published')], default=3, verbose_name='State'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 89dacf5a..758269b8 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -101,6 +101,10 @@ class NewsQuerySet(TranslationQuerysetMixin): """Return qs with related objects.""" return self.select_related('created_by', 'agenda', 'banner') + def visible(self): + """Narrows qs by excluding invisible for API (at all) news""" + return self.exclude(state=self.model.REMOVE) + def by_type(self, news_type): """Filter News by type""" return self.filter(news_type__name=news_type) @@ -128,14 +132,21 @@ class NewsQuerySet(TranslationQuerysetMixin): return self.exclude(models.Q(publication_date__isnull=True) | models.Q(publication_time__isnull=True)). \ filter(models.Q(models.Q(end__gte=now) | models.Q(end__isnull=True)), - state__in=self.model.PUBLISHED_STATES, publication_date__lte=date_now, - publication_time__lte=time_now) + state__in=self.model.PUBLISHED_STATES)\ + .annotate(visible_now=Case( + When(publication_date__gt=date_now, then=False), + When(Q(publication_date=date_now) & Q(publication_time__gt=time_now), then=False), + default=True, + output_field=models.BooleanField() + ))\ + .exclude(visible_now=False) # todo: filter by best score # todo: filter by country? def should_read(self, news, user): return self.model.objects.exclude(pk=news.pk).published(). \ annotate_in_favorites(user). \ + filter(country=news.country). \ with_base_related().by_type(news.news_type).distinct().order_by('?') def same_theme(self, news, user): @@ -252,18 +263,18 @@ class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin, ) # STATE CHOICES - WAITING = 0 + REMOVE = 0 HIDDEN = 1 PUBLISHED = 2 - PUBLISHED_EXCLUSIVE = 3 + UNPUBLISHED = 3 - PUBLISHED_STATES = [PUBLISHED, PUBLISHED_EXCLUSIVE] + PUBLISHED_STATES = [PUBLISHED] STATE_CHOICES = ( - (WAITING, _('Waiting')), - (HIDDEN, _('Hidden')), - (PUBLISHED, _('Published')), - (PUBLISHED_EXCLUSIVE, _('Published exclusive')), + (REMOVE, _('remove')), # simply stored in DB news. not shown anywhere + (HIDDEN, _('hidden')), # not shown in api/web or api/mobile + (PUBLISHED, _('published')), # shown everywhere + (UNPUBLISHED, _('not published')), # newly created news ) INTERNATIONAL_TAG_VALUE = 'international' @@ -295,7 +306,7 @@ class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin, slugs = HStoreField(null=True, blank=True, default=dict, verbose_name=_('Slugs for current news obj'), help_text='{"en-GB":"some slug"}') - state = models.PositiveSmallIntegerField(default=WAITING, choices=STATE_CHOICES, + state = models.PositiveSmallIntegerField(default=UNPUBLISHED, choices=STATE_CHOICES, verbose_name=_('State')) is_highlighted = models.BooleanField(default=False, verbose_name=_('Is highlighted')) @@ -340,7 +351,7 @@ class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin, def create_duplicate(self, new_country, view_count_model): self.pk = None - self.state = self.WAITING + self.state = self.UNPUBLISHED self.slugs = {locale: f'{slug}-{new_country.code}' for locale, slug in self.slugs.items()} self.country = new_country self.views_count = view_count_model diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 564cd2c9..7ff7d285 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -155,6 +155,9 @@ class NewsDetailSerializer(NewsBaseSerializer): 'gallery', ) + def update(self, instance, validated_data): + return super().update(instance, validated_data) + class NewsDetailWebSerializer(NewsDetailSerializer): """News detail serializer for web users..""" @@ -207,7 +210,8 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): """News back office base serializer.""" is_published = serializers.BooleanField(source='is_publish', read_only=True) descriptions = serializers.ListField(required=False) - agenda = AgendaSerializer() + agenda = AgendaSerializer(required=False, allow_null=True) + state_display = serializers.CharField(source='get_state_display', read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" @@ -226,7 +230,9 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): 'created', 'modified', 'descriptions', - 'agenda' + 'agenda', + 'state', + 'state_display', ) extra_kwargs = { 'created': {'read_only': True}, @@ -234,6 +240,8 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): 'duplication_date': {'read_only': True}, 'locale_to_description_is_active': {'allow_null': False}, 'must_of_the_week': {'read_only': True}, + # 'state': {'read_only': True}, + 'state_display': {'read_only': True}, } def validate(self, attrs): @@ -255,7 +263,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): instance = models.News.objects.get(pk=self.context['request'].data['id']) for key in ['slugs', 'title', 'locale_to_description_is_active', 'description']: for locale in locales: - if not attrs[key].get(locale): + if locale not in attrs[key]: attrs[key][locale] = getattr(instance, key).get(locale) return attrs @@ -299,7 +307,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): slugs_set = set(slugs_list) if models.News.objects.filter( slugs__values__contains=list(slugs.values()) - ).exists() or len(slugs_list) != len(slugs_set): + ).exclude(pk=instance.pk).exists() or len(slugs_list) != len(slugs_set): raise serializers.ValidationError({'slugs': _('Slug should be unique')}) agenda_data = validated_data.get('agenda') @@ -357,8 +365,9 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, template_display = serializers.CharField(source='get_template_display', read_only=True) duplicates = NewsBackOfficeDuplicationInfoSerializer(many=True, allow_null=True, read_only=True) + agenda = AgendaSerializer(required=False, allow_null=True, read_only=True) - class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta): + class Meta(NewsDetailSerializer.Meta, NewsBackOfficeBaseSerializer.Meta): """Meta class.""" fields = NewsBackOfficeBaseSerializer.Meta.fields + \ @@ -373,6 +382,13 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, 'duplicates', ) + def validate(self, attrs): + """Overridden validate method.""" + return super().validate(attrs) + + def update(self, instance, validated_data): + return super().update(instance, validated_data) + class NewsBackOfficeGallerySerializer(serializers.ModelSerializer): """Serializer class for model NewsGallery.""" @@ -503,3 +519,8 @@ class NewsCloneCreateSerializer(NewsBackOfficeBaseSerializer, view_count_model = rating_models.ViewCount.objects.create(count=0) instance.create_duplicate(new_country, view_count_model) return get_object_or_404(models.News, pk=kwargs['pk']) + + +class NewsStatesSerializer(serializers.Serializer): + value = serializers.IntegerField() + state_translated = serializers.CharField() diff --git a/apps/news/urls/back.py b/apps/news/urls/back.py index 54f40513..bf9ab856 100644 --- a/apps/news/urls/back.py +++ b/apps/news/urls/back.py @@ -8,6 +8,7 @@ app_name = 'news' urlpatterns = [ path('', views.NewsBackOfficeLCView.as_view(), name='list-create'), + path('states/', views.NewsStatesView.as_view(), name='possible-news-states-list'), path('/', views.NewsBackOfficeRUDView.as_view(), name='retrieve-update-destroy'), path('/gallery/', views.NewsBackOfficeGalleryListView.as_view(), name='gallery-list'), path('/gallery//', views.NewsBackOfficeGalleryCreateDestroyView.as_view(), diff --git a/apps/news/views.py b/apps/news/views.py index 3fc8284e..665eb60d 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -26,6 +26,7 @@ class NewsMixinView: """Override get_queryset method.""" qs = models.News.objects.published() \ .with_base_related() \ + .visible() \ .annotate_in_favorites(self.request.user) \ .order_by('-is_highlighted', '-publication_date', '-publication_time') @@ -43,7 +44,7 @@ class NewsMixinView: return qs def get_object(self): - instance = self.get_queryset().filter( + instance = self.get_queryset().visible().with_base_related().filter( slugs__values__contains=[self.kwargs['slug']] ).first() @@ -53,6 +54,23 @@ class NewsMixinView: return instance +class NewsStatesView(generics.ListAPIView): + """Possible project news states""" + pagination_class = None + serializer_class = serializers.NewsStatesSerializer + + def get_queryset(self): + return None + + def list(self, request, *args, **kwargs): + mutated_for_serializer = [{ + 'value': state[0], + 'state_translated': state[1], + } for state in models.News.STATE_CHOICES] + serializer = self.get_serializer(mutated_for_serializer, many=True) + return response.Response(serializer.data) + + class NewsListView(NewsMixinView, generics.ListAPIView): """News list view.""" @@ -137,7 +155,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, def get_queryset(self): """Override get_queryset method.""" - qs = super().get_queryset().with_extended_related() + qs = super().get_queryset().with_extended_related().visible() if 'ordering' in self.request.query_params: self.request.GET._mutable = True if '-publication_datetime' in self.request.query_params['ordering']: diff --git a/apps/notification/migrations/0011_auto_20200124_1351.py b/apps/notification/migrations/0011_auto_20200124_1351.py new file mode 100644 index 00000000..5b86c7ec --- /dev/null +++ b/apps/notification/migrations/0011_auto_20200124_1351.py @@ -0,0 +1,39 @@ +# Generated by Django 2.2.7 on 2020-01-24 13:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('notification', '0010_auto_20191231_0135'), + ] + + operations = [ + migrations.AddField( + model_name='subscribe', + name='old_subscriber_id', + field=models.PositiveIntegerField(null=True, verbose_name='Old subscriber id'), + ), + migrations.AddField( + model_name='subscribe', + name='old_subscription_type_id', + field=models.PositiveIntegerField(null=True, verbose_name='Old subscription type id'), + ), + migrations.AlterField( + model_name='subscribe', + name='subscribe_date', + field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Last subscribe date'), + ), + migrations.AlterField( + model_name='subscribe', + name='subscriber', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='notification.Subscriber'), + ), + migrations.AlterField( + model_name='subscribe', + name='subscription_type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='notification.SubscriptionType'), + ), + ] diff --git a/apps/notification/migrations/0012_remove_subscribe_subscribe_date.py b/apps/notification/migrations/0012_remove_subscribe_subscribe_date.py new file mode 100644 index 00000000..cc805e4b --- /dev/null +++ b/apps/notification/migrations/0012_remove_subscribe_subscribe_date.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.7 on 2020-01-27 16:37 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('notification', '0011_auto_20200124_1351'), + ] + + operations = [ + migrations.RemoveField( + model_name='subscribe', + name='subscribe_date', + ), + ] diff --git a/apps/notification/models.py b/apps/notification/models.py index e0855c62..4de7d94f 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -2,6 +2,7 @@ from django.conf import settings from django.db import models +from django.db.models import F from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ @@ -10,6 +11,7 @@ from location.models import Country from notification.tasks import send_unsubscribe_email from utils.methods import generate_string_code from utils.models import ProjectBaseMixin, TJSONField, TranslatedFieldsMixin +from notification.tasks import send_unsubscribe_email class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin): @@ -131,14 +133,23 @@ class Subscriber(ProjectBaseMixin): self.update_code = generate_string_code() return super(Subscriber, self).save(*args, **kwargs) - def unsubscribe(self, query: dict): + def unsubscribe(self): """Unsubscribe user.""" - self.subscribe_set.update(unsubscribe_date=now()) + + self.subscribe_set.update( + unsubscribe_date=now(), + old_subscriber_id=F('subscriber_id'), + old_subscription_type_id=F('subscription_type_id') + ) + self.subscribe_set.update( + subscriber_id=None, + subscription_type_id=None + ) if settings.USE_CELERY: - send_unsubscribe_email.delay(self.email) + send_unsubscribe_email.delay(self.pk) else: - send_unsubscribe_email(self.email) + send_unsubscribe_email(self.pk) @property def send_to(self): @@ -159,6 +170,10 @@ class Subscriber(ProjectBaseMixin): def active_subscriptions(self): return self.subscription_types.exclude(subscriber__subscribe__unsubscribe_date__isnull=False) + @property + def subscription_history(self): + return Subscribe.objects.subscription_history(self.pk) + class SubscribeQuerySet(models.QuerySet): @@ -166,18 +181,26 @@ class SubscribeQuerySet(models.QuerySet): """Fetches active subscriptions.""" return self.exclude(unsubscribe_date__isnull=not switcher) + def subscription_history(self, subscriber_id: int): + return self.filter(old_subscriber_id=subscriber_id) + class Subscribe(ProjectBaseMixin): """Subscribe model.""" - - subscribe_date = models.DateTimeField(_('Last subscribe date'), blank=True, null=True, default=now) unsubscribe_date = models.DateTimeField(_('Last unsubscribe date'), blank=True, null=True, default=None) - subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE) - subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE) + subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE, null=True) + subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE, null=True) + + old_subscriber_id = models.PositiveIntegerField(_("Old subscriber id"), null=True) + old_subscription_type_id = models.PositiveIntegerField(_("Old subscription type id"), null=True) objects = SubscribeQuerySet.as_manager() + @property + def subscribe_date(self): + return self.created + class Meta: """Meta class.""" diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 869879cb..3a061498 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -40,6 +40,7 @@ class CreateAndUpdateSubscribeSerializer(serializers.ModelSerializer): model = models.Subscriber fields = ( + 'id', 'email', 'subscription_types', 'link_to_unsubscribe', @@ -54,7 +55,7 @@ class CreateAndUpdateSubscribeSerializer(serializers.ModelSerializer): user = request.user # validate email - email = attrs.get('send_to') + email = attrs.pop('send_to') if attrs.get('email'): email = attrs.get('email') @@ -89,12 +90,20 @@ class CreateAndUpdateSubscribeSerializer(serializers.ModelSerializer): subscriber = models.Subscriber.objects.make_subscriber(**validated_data) if settings.USE_CELERY: - send_subscribes_update_email.delay(subscriber.email) + send_subscribes_update_email.delay(subscriber.pk) else: - send_subscribes_update_email(subscriber.email) + send_subscribes_update_email(subscriber.pk) return subscriber + def update(self, instance, validated_data): + if settings.USE_CELERY: + send_subscribes_update_email.delay(instance.pk) + else: + send_subscribes_update_email(instance.pk) + + return super().update(instance, validated_data) + class UpdateSubscribeSerializer(serializers.ModelSerializer): """Update with code Subscribe serializer.""" @@ -141,14 +150,27 @@ class UpdateSubscribeSerializer(serializers.ModelSerializer): class SubscribeObjectSerializer(serializers.ModelSerializer): - """Subscribe serializer.""" + """Subscription type serializer.""" + + subscription_type = serializers.SerializerMethodField() class Meta: """Meta class.""" - model = models.Subscriber - fields = ('subscriber',) - read_only_fields = ('subscribe_date', 'unsubscribe_date',) + model = models.Subscribe + fields = ( + 'subscribe_date', + 'unsubscribe_date', + 'subscription_type' + ) + extra_kwargs = { + 'subscribe_date': {'read_only': True}, + } + + def get_subscription_type(self, instance): + return SubscriptionTypeSerializer( + models.SubscriptionType.objects.get(pk=instance.old_subscription_type_id) + ).data class SubscribeSerializer(serializers.ModelSerializer): @@ -156,6 +178,7 @@ class SubscribeSerializer(serializers.ModelSerializer): email = serializers.EmailField(required=False, source='send_to') subscription_types = SubscriptionTypeSerializer(source='active_subscriptions', read_only=True, many=True) + history = SubscribeObjectSerializer(source='subscription_history', many=True) class Meta: """Meta class.""" @@ -165,4 +188,16 @@ class SubscribeSerializer(serializers.ModelSerializer): 'email', 'subscription_types', 'link_to_unsubscribe', + 'history', ) + + +class UnsubscribeSerializer(serializers.ModelSerializer): + email = serializers.EmailField(read_only=True, required=False, source='send_to') + subscription_types = SubscriptionTypeSerializer(source='active_subscriptions', read_only=True, many=True) + + class Meta: + """Meta class.""" + + model = models.Subscriber + fields = SubscribeSerializer.Meta.fields diff --git a/apps/notification/tasks.py b/apps/notification/tasks.py index 790c9650..74f0e814 100644 --- a/apps/notification/tasks.py +++ b/apps/notification/tasks.py @@ -3,16 +3,16 @@ from datetime import datetime from celery import shared_task from django.conf import settings from django.core.mail import send_mail -from django.utils.translation import gettext_lazy as _ from django.template.loader import get_template, render_to_string from main.models import SiteSettings from notification import models +from django.utils.translation import gettext_lazy as _ @shared_task -def send_subscribes_update_email(email): - subscriber = models.Subscriber.objects.filter(email=email).first() +def send_subscribes_update_email(subscriber_id): + subscriber = models.Subscriber.objects.get(pk=subscriber_id) if subscriber is None: return @@ -53,8 +53,8 @@ def send_subscribes_update_email(email): @shared_task -def send_unsubscribe_email(email): - subscriber = models.Subscriber.objects.filter(email=email).first() +def send_unsubscribe_email(subscriber_id): + subscriber = models.Subscriber.objects.get(pk=subscriber_id) if subscriber is None: return diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index bc935ace..ba7af962 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -1,6 +1,6 @@ """Notification app common 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 notification import models @@ -15,6 +15,18 @@ class CreateSubscribeView(generics.CreateAPIView): permission_classes = (permissions.AllowAny,) serializer_class = serializers.CreateAndUpdateSubscribeSerializer + def create(self, request, *args, **kwargs): + data = request.data + instance = None + if 'email' in request.data: + # we shouldn't create new subscriber if we have one + instance = models.Subscriber.objects.filter(email=request.data['email']).first() + serializer = self.get_serializer(data=data) if instance is None else self.get_serializer(instance, data=data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + class UpdateSubscribeView(generics.UpdateAPIView): """Subscribe info view.""" @@ -41,20 +53,7 @@ class SubscribeInfoAuthUserView(generics.RetrieveAPIView): lookup_field = None def get_object(self): - user = self.request.user - - subscriber = models.Subscriber.objects.filter(user=user).first() - - if subscriber is None: - subscriber = models.Subscriber.objects.make_subscriber( - email=user.email, user=user, ip_address=get_user_ip(self.request), - country_code=self.request.country_code, locale=self.request.locale - ) - - else: - return get_object_or_404(models.Subscriber, user=user) - - return subscriber + return get_object_or_404(models.Subscriber, user=self.request.user) class UnsubscribeView(generics.UpdateAPIView): @@ -65,9 +64,9 @@ class UnsubscribeView(generics.UpdateAPIView): queryset = models.Subscriber.objects.all() serializer_class = serializers.SubscribeSerializer - def patch(self, request, *args, **kw): + def put(self, request, *args, **kw): obj = self.get_object() - obj.unsubscribe(request.query_params) + obj.unsubscribe() serializer = self.get_serializer(instance=obj) return Response(data=serializer.data) @@ -81,7 +80,7 @@ class UnsubscribeAuthUserView(generics.GenericAPIView): def patch(self, request, *args, **kw): user = request.user obj = models.Subscriber.objects.filter(user=user).first() - obj.unsubscribe(request.query_params) + obj.unsubscribe() serializer = self.get_serializer(instance=obj) return Response(data=serializer.data) diff --git a/apps/partner/filters.py b/apps/partner/filters.py new file mode 100644 index 00000000..1965fd03 --- /dev/null +++ b/apps/partner/filters.py @@ -0,0 +1,34 @@ +"""Partner app filters.""" +from django_filters import rest_framework as filters + +from partner.models import Partner +from django.core.validators import EMPTY_VALUES + + +class PartnerFilterSet(filters.FilterSet): + """Establishment filter set.""" + + establishment = filters.NumberFilter( + help_text='Allows to get partner list by establishment ID.') + type = filters.ChoiceFilter( + choices=Partner.MODEL_TYPES, + help_text=f'Allows to filter partner list by partner type. ' + f'Enum: {dict(Partner.MODEL_TYPES)}') + ordering = filters.CharFilter(method='sort_partner') + + class Meta: + """Meta class.""" + model = Partner + fields = ( + 'establishment', + 'type', + 'ordering', + ) + + @staticmethod + def sort_partner(queryset, name, value): + if value not in EMPTY_VALUES: + if 'date_bind' in value: + value = value.replace('date_bind', 'partnertoestablishment__partner_bind_date') + queryset = queryset.order_by(value) + return queryset \ No newline at end of file diff --git a/apps/partner/migrations/0004_auto_20200128_1746.py b/apps/partner/migrations/0004_auto_20200128_1746.py new file mode 100644 index 00000000..27ebcec4 --- /dev/null +++ b/apps/partner/migrations/0004_auto_20200128_1746.py @@ -0,0 +1,44 @@ +# Generated by Django 2.2.7 on 2020-01-28 17:46 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +def make_relations(apps, schemaeditor): + PartnerToEstablishment = apps.get_model('partner', 'PartnerToEstablishment') + Partner = apps.get_model('partner', 'Partner') + Establishment = apps.get_model('establishment', 'Establishment') + touched_partners_ids = [] + for establishment in Establishment.objects.filter(partners__isnull=False): + for related_partner in establishment.partners.all(): + real_partner = Partner.objects.filter(name=related_partner.name, type=related_partner.type, + url=related_partner.url).first() + touched_partners_ids.append(related_partner.pk) + PartnerToEstablishment(establishment=establishment, partner=real_partner, partner_bind_date=real_partner.created).save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0083_establishment_instagram'), + ('partner', '0003_auto_20191121_1059'), + ] + + operations = [ + migrations.CreateModel( + name='PartnerToEstablishment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('partner_bind_date', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date partner binded')), + ('establishment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='establishment.Establishment')), + ('partner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='partner.Partner')), + ], + ), + migrations.AddField( + model_name='partner', + name='establishments', + field=models.ManyToManyField(null=True, related_name='new_partners', through='partner.PartnerToEstablishment', to='establishment.Establishment', verbose_name='Establishments'), + ), + migrations.RunPython(make_relations, migrations.RunPython.noop), + ] diff --git a/apps/partner/migrations/0005_auto_20200128_1754.py b/apps/partner/migrations/0005_auto_20200128_1754.py new file mode 100644 index 00000000..b26695c2 --- /dev/null +++ b/apps/partner/migrations/0005_auto_20200128_1754.py @@ -0,0 +1,45 @@ +# Generated by Django 2.2.7 on 2020-01-28 17:54 + +from django.db import migrations, models +import django.db.models.deletion + + +def delete_unused_partners(apps, schema_editor): + PartnerToEstablishment = apps.get_model('partner', 'PartnerToEstablishment') + Partner = apps.get_model('partner', 'Partner') + ids_to_preserve = [] + for p_t_e in PartnerToEstablishment.objects.all(): + ids_to_preserve.append(p_t_e.partner.pk) + Partner.objects.exclude(id__in=ids_to_preserve).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ('location', '0037_address_district_name'), + ('establishment', '0083_establishment_instagram'), + ('partner', '0004_auto_20200128_1746'), + ] + + operations = [ + migrations.RemoveField( + model_name='partner', + name='establishments', + ), + migrations.AddField( + model_name='partner', + name='country', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, + to='location.Country'), + ), + migrations.RemoveField( + model_name='partner', + name='establishment', + ), + migrations.AddField( + model_name='partner', + name='establishment', + field=models.ManyToManyField(related_name='partners', through='partner.PartnerToEstablishment', + to='establishment.Establishment', verbose_name='Establishments'), + ), + migrations.RunPython(delete_unused_partners, migrations.RunPython.noop, atomic=True) + ] diff --git a/apps/partner/migrations/0006_auto_20200129_1201.py b/apps/partner/migrations/0006_auto_20200129_1201.py new file mode 100644 index 00000000..0bb7487c --- /dev/null +++ b/apps/partner/migrations/0006_auto_20200129_1201.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.7 on 2020-01-29 12:01 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('partner', '0005_auto_20200128_1754'), + ] + + operations = [ + migrations.RemoveField( + model_name='partner', + name='image', + ), + migrations.RemoveField( + model_name='partner', + name='url', + ), + migrations.AddField( + model_name='partner', + name='images', + field=django.contrib.postgres.fields.ArrayField(base_field=models.URLField(verbose_name='Partner image URL'), blank=True, default=None, null=True, size=None), + ), + migrations.AddField( + model_name='partnertoestablishment', + name='image', + field=models.URLField(null=True, verbose_name='Partner image URL'), + ), + migrations.AddField( + model_name='partnertoestablishment', + name='url', + field=models.URLField(blank=True, default=None, null=True, verbose_name='Establishment to Partner URL'), + ), + ] diff --git a/apps/partner/models.py b/apps/partner/models.py index 5c5766c8..e50fd939 100644 --- a/apps/partner/models.py +++ b/apps/partner/models.py @@ -1,10 +1,25 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from django.utils import timezone +from django.contrib.postgres.fields import ArrayField from establishment.models import Establishment from utils.models import ImageMixin, ProjectBaseMixin +class PartnerQueryset(models.QuerySet): + + def with_base_related(self): + return self.prefetch_related('establishment__establishment_type', 'establishment__establishment_subtypes', + 'establishment__awards', 'establishment__schedule', 'establishment__phones', + 'establishment__gallery', 'establishment__menu_set', + 'establishment__menu_set__plates', 'establishment__menu_set__plates__currency', + 'establishment__currency', 'establishment__address__city', + 'establishment__address__city__region', + 'establishment__address__city__region__country', + 'establishment__address__city__country', 'country') + + class Partner(ProjectBaseMixin): """Partner model.""" @@ -17,24 +32,36 @@ class Partner(ProjectBaseMixin): old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) name = models.CharField(_('name'), max_length=255, blank=True, null=True) - url = models.URLField(verbose_name=_('Partner URL')) - image = models.URLField(verbose_name=_('Partner image URL'), null=True) - establishment = models.ForeignKey( - Establishment, - verbose_name=_('Establishment'), - related_name='partners', - on_delete=models.CASCADE, - blank=True, - null=True, + images = ArrayField( + models.URLField(verbose_name=_('Partner image URL')), blank=True, null=True, default=None, ) + establishment = models.ManyToManyField('establishment.Establishment', related_name='partners', + through='PartnerToEstablishment', + verbose_name=_('Establishments')) type = models.PositiveSmallIntegerField(choices=MODEL_TYPES, default=PARTNER) starting_date = models.DateField(_('starting date'), blank=True, null=True) expiry_date = models.DateField(_('expiry date'), blank=True, null=True) price_per_month = models.DecimalField(_('price per month'), max_digits=10, decimal_places=2, blank=True, null=True) + country = models.ForeignKey('location.Country', null=True, default=None, on_delete=models.SET_NULL) + + objects = PartnerQueryset.as_manager() class Meta: verbose_name = _('partner') verbose_name_plural = _('partners') def __str__(self): - return f'{self.url}' + return f'{self.name}' + + @property + def type_display(self): + return self.MODEL_TYPES[self.type][1] + + +class PartnerToEstablishment(models.Model): + partner_bind_date = models.DateTimeField(default=timezone.now, editable=False, + verbose_name=_('Date partner binded')) + url = models.URLField(verbose_name=_('Establishment to Partner URL'), null=True, blank=True, default=None) + image = models.URLField(verbose_name=_('Partner image URL'), null=True) + partner = models.ForeignKey(Partner, on_delete=models.CASCADE, null=True) + establishment = models.ForeignKey('establishment.Establishment', on_delete=models.CASCADE, null=True) diff --git a/apps/partner/serializers/back.py b/apps/partner/serializers/back.py index e9e03fe0..09341f78 100644 --- a/apps/partner/serializers/back.py +++ b/apps/partner/serializers/back.py @@ -1,21 +1,85 @@ """Back account serializers""" from rest_framework import serializers -from partner.models import Partner +from partner.models import Partner, PartnerToEstablishment +from establishment.serializers import EstablishmentShortSerializer +from location.serializers import CountrySimpleSerializer +from location.models import Country +from django.shortcuts import get_object_or_404 +from establishment.models import Establishment class BackPartnerSerializer(serializers.ModelSerializer): + # establishments = EstablishmentShortSerializer(many=True, read_only=True, source='establishment') + country = CountrySimpleSerializer(read_only=True) + type_display = serializers.CharField(read_only=True) + country_id = serializers.PrimaryKeyRelatedField( + queryset=Country.objects.all(), + required=False, + write_only=True, + source='country' + ) + class Meta: model = Partner fields = ( 'id', - 'old_id', 'name', - 'url', - 'image', - 'establishment', - 'establishment_id', + 'images', 'type', + 'type_display', 'starting_date', 'expiry_date', 'price_per_month', + 'country', + 'country_id', + # 'establishments', + ) + extra_kwargs = { + 'type': {'write_only': True}, + } + + +class PartnersForEstablishmentSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(source='partner.pk', read_only=True) + name = serializers.CharField(source='partner.name', read_only=True) + type = serializers.IntegerField(source='partner.type', read_only=True) + type_display = serializers.CharField(source='partner.type_display', read_only=True) + starting_date = serializers.DateField(source='partner.starting_date', read_only=True) + expiry_date = serializers.DateField(source='partner.expiry_date', read_only=True) + price_per_month = serializers.DecimalField(source='partner.price_per_month', max_digits=10, decimal_places=2, + read_only=True) + country = CountrySimpleSerializer(read_only=True, source='partner.country') + + class Meta: + model = PartnerToEstablishment + fields = ( + 'id', + 'name', + 'type', + 'type_display', + 'starting_date', + 'expiry_date', + 'price_per_month', + 'country', + 'url', # own field + 'image', # own field + ) + + def create(self, validated_data): + establishment_id = self.context['view'].kwargs['establishment_id'] + partner_id = self.context['view'].kwargs['partner_id'] + establishment = get_object_or_404(Establishment, pk=establishment_id) + partner = get_object_or_404(Partner, pk=partner_id) + instance = PartnerToEstablishment.objects.create(**validated_data, + establishment=establishment, + partner=partner) + return instance + + +class PartnerPicturesSerializer(serializers.ModelSerializer): + + class Meta: + model = Partner + fields = ( + 'images', ) diff --git a/apps/partner/serializers/common.py b/apps/partner/serializers/common.py index 1d5a6b23..83053f26 100644 --- a/apps/partner/serializers/common.py +++ b/apps/partner/serializers/common.py @@ -11,6 +11,6 @@ class PartnerSerializer(serializers.ModelSerializer): model = models.Partner fields = ( 'id', - 'image', + # 'image', 'url' ) diff --git a/apps/partner/transfer_data.py b/apps/partner/transfer_data.py index acda6c2c..0f111eba 100644 --- a/apps/partner/transfer_data.py +++ b/apps/partner/transfer_data.py @@ -1,7 +1,7 @@ from pprint import pprint from establishment.models import Establishment -from partner.models import Partner +from partner.models import Partner, PartnerToEstablishment from transfer.models import EstablishmentBacklinks from transfer.serializers.partner import PartnerSerializer diff --git a/apps/partner/urls/back.py b/apps/partner/urls/back.py index 27de2731..94829235 100644 --- a/apps/partner/urls/back.py +++ b/apps/partner/urls/back.py @@ -8,4 +8,11 @@ app_name = 'partner' urlpatterns = [ path('', views.PartnerLstView.as_view(), name='partner-list-create'), path('/', views.PartnerRUDView.as_view(), name='partner-rud'), + path('for_establishment//', views.EstablishmentPartners.as_view(), + name='partners-for-establishment'), + path('pictures//', views.PartnerPicturesListView.as_view(), name='partner-pictures-get'), + path('bind///', views.BindPartnerToEstablishmentView.as_view(), + name='bind-partner-to-establishment'), + path('unbind///', views.UnbindPartnerFromEstablishmentView.as_view(), + name='unbind-partner-from-establishment'), ] diff --git a/apps/partner/views/back.py b/apps/partner/views/back.py index aba096e6..80d70d62 100644 --- a/apps/partner/views/back.py +++ b/apps/partner/views/back.py @@ -1,34 +1,81 @@ +from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics +from rest_framework.filters import OrderingFilter -from partner.models import Partner +from partner import filters +from partner.models import Partner, PartnerToEstablishment from partner.serializers import back as serializers from utils.methods import get_permission_classes from utils.permissions import IsEstablishmentManager, IsEstablishmentAdministrator class PartnerLstView(generics.ListCreateAPIView): - """Partner list create view.""" - queryset = Partner.objects.all() + """Partner list/create view. + Allows to get partners for current country, or create a new one. + """ + queryset = Partner.objects.with_base_related() serializer_class = serializers.BackPartnerSerializer pagination_class = None - filter_backends = (DjangoFilterBackend,) permission_classes = get_permission_classes( IsEstablishmentManager, IsEstablishmentAdministrator ) - filterset_fields = ( - 'establishment', - 'type', + filter_class = filters.PartnerFilterSet + + +class EstablishmentPartners(generics.ListAPIView): + queryset = PartnerToEstablishment.objects.prefetch_related('partner', 'partner__country') + serializer_class = serializers.PartnersForEstablishmentSerializer + pagination_class = None + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator ) + filter_backends = (OrderingFilter, DjangoFilterBackend) + ordering_fields = '__all__' + ordering = '-partner_bind_date' + + def get_queryset(self): + return super().get_queryset().filter(establishment=self.kwargs['establishment_id']) class PartnerRUDView(generics.RetrieveUpdateDestroyAPIView): """Partner RUD view.""" - queryset = Partner.objects.all() + queryset = Partner.objects.with_base_related() serializer_class = serializers.BackPartnerSerializer lookup_field = 'id' permission_classes = get_permission_classes( IsEstablishmentManager, IsEstablishmentAdministrator ) + + +class PartnerPicturesListView(generics.RetrieveAPIView): + lookup_field = 'id' + serializer_class = serializers.PartnerPicturesSerializer + queryset = Partner.objects.with_base_related() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + +class BindPartnerToEstablishmentView(generics.CreateAPIView): + serializer_class = serializers.PartnersForEstablishmentSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + +class UnbindPartnerFromEstablishmentView(generics.DestroyAPIView): + serializer_class = serializers.PartnersForEstablishmentSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) + + def get_object(self): + return get_object_or_404(PartnerToEstablishment, establishment_id=self.kwargs['establishment_id'], + partner_id=self.kwargs['partner_id']) diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py index 0b221102..2a873e12 100644 --- a/apps/search_indexes/documents/establishment.py +++ b/apps/search_indexes/documents/establishment.py @@ -175,7 +175,8 @@ class EstablishmentDocument(Document): 'city': fields.ObjectField( properties={ 'id': fields.IntegerField(), - 'name': fields.KeywordField(), + 'name': fields.KeywordField(attr='name_dumped'), + 'name_translated': fields.KeywordField(), 'is_island': fields.BooleanField(), 'country': fields.ObjectField( properties={ diff --git a/apps/search_indexes/documents/product.py b/apps/search_indexes/documents/product.py index fda06492..a2bc377f 100644 --- a/apps/search_indexes/documents/product.py +++ b/apps/search_indexes/documents/product.py @@ -44,7 +44,8 @@ class ProductDocument(Document): attr='address.city', properties={ 'id': fields.IntegerField(), - 'name': fields.KeywordField(), + 'name': fields.KeywordField(attr='name_dumped'), + 'name_translated': fields.KeywordField(), 'code': fields.KeywordField(), 'country': fields.ObjectField( properties={ diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index b909fb1b..29144189 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -124,7 +124,13 @@ class CityDocumentShortSerializer(serializers.Serializer): id = serializers.IntegerField() code = serializers.CharField(allow_null=True) - name = serializers.CharField() + # todo: index and use name dict field + name_translated = serializers.SerializerMethodField() + + @staticmethod + def get_name_translated(obj): + return get_translated_value(loads(obj.name)) + class CountryDocumentSerializer(serializers.Serializer): diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index 0a6fb4f6..0f89139a 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -13,7 +13,7 @@ def update_document(sender, **kwargs): app_label_model_name_to_filter = { ('location', 'country'): 'address__city__country', - ('location', 'city'): 'address__city', + # ('location', 'city'): 'address__city', ('location', 'address'): 'address', # todo: remove after migration ('establishment', 'establishmenttype'): 'establishment_type', diff --git a/apps/transfer/serializers/partner.py b/apps/transfer/serializers/partner.py index 61f56dea..2bfc7183 100644 --- a/apps/transfer/serializers/partner.py +++ b/apps/transfer/serializers/partner.py @@ -1,7 +1,7 @@ from rest_framework import serializers from establishment.models import Establishment -from partner.models import Partner +from partner.models import Partner, PartnerToEstablishment class PartnerSerializer(serializers.Serializer): @@ -43,10 +43,32 @@ class PartnerSerializer(serializers.Serializer): return establishment def create(self, validated_data): - obj, _ = Partner.objects.update_or_create( - old_id=validated_data['old_id'], - defaults=validated_data, + establishment = validated_data.pop('establishment') + url = validated_data.pop('url') + image = validated_data.pop('image') + + old_id = validated_data.pop('old_id') + created = validated_data.pop('created') + + obj, is_created = Partner.objects.update_or_create( + # old_id=validated_data['old_id'], + **validated_data ) + + obj.old_id = old_id + obj.created = created + + obj.establishment.add(establishment) + if is_created: + obj.images = [image] + elif image not in obj.images: + obj.images.append(image) + obj.save() + + p_t_e = PartnerToEstablishment.objects.filter(establishment=establishment, partner=obj).first() + p_t_e.url = url + p_t_e.image = image + p_t_e.save() return obj diff --git a/apps/utils/methods.py b/apps/utils/methods.py index a89f9b03..73310fd4 100644 --- a/apps/utils/methods.py +++ b/apps/utils/methods.py @@ -6,7 +6,7 @@ import string from collections import namedtuple from functools import reduce from io import BytesIO - +import pathlib import requests from PIL import Image from django.conf import settings @@ -69,6 +69,15 @@ def username_random(): ) +def file_path(instance, filename): + """Determine file path method.""" + filename = '%s.%s' % (generate_code(), pathlib.Path(filename).suffix.lstrip('.')) + return 'files/%s/%s/%s' % ( + instance._meta.model_name, + datetime.now().strftime(settings.REST_DATE_FORMAT), + filename) + + def image_path(instance, filename): """Determine avatar path method.""" filename = '%s.jpeg' % generate_code() diff --git a/apps/utils/models.py b/apps/utils/models.py index d58a1bbc..281c57ca 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -8,6 +8,7 @@ from django.contrib.gis.db import models from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields.jsonb import KeyTextTransform +from django.core.validators import FileExtensionValidator from django.utils import timezone from django.utils.html import mark_safe from django.utils.translation import ugettext_lazy as _, get_language @@ -16,7 +17,7 @@ from sorl.thumbnail import get_thumbnail from sorl.thumbnail.fields import ImageField as SORLImageField from configuration.models import TranslationSettings -from utils.methods import image_path, svg_image_path +from utils.methods import image_path, svg_image_path, file_path from utils.validators import svg_image_validator logger = logging.getLogger(__name__) @@ -86,6 +87,7 @@ def translate_field(self, field_name, toggle_field_name=None): return None return value return None + return translate @@ -159,6 +161,33 @@ class BaseAttributes(ProjectBaseMixin): abstract = True +class FileMixin(models.Model): + """File model.""" + + file = models.FileField(upload_to=file_path, + blank=True, null=True, default=None, + verbose_name=_('File'), + validators=[FileExtensionValidator( + allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf') + )]) + + class Meta: + """Meta class.""" + + abstract = True + + def get_file_url(self): + """Get file url.""" + return self.file.url if self.file else None + + def get_full_file_url(self, request): + """Get full file url""" + if self.file and exists(self.file.path): + return request.build_absolute_uri(self.file.url) + else: + return None + + class ImageMixin(models.Model): """Avatar model.""" @@ -230,7 +259,7 @@ class SORLImageMixin(models.Model): else: return None - def get_cropped_image(self, geometry: str, quality: int, cropbox:str) -> dict: + def get_cropped_image(self, geometry: str, quality: int, cropbox: str) -> dict: cropped_image = get_thumbnail(self.image, geometry_string=geometry, # crop=crop, diff --git a/project/locale/de/LC_MESSAGES/django.mo b/project/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 00000000..71cbdf3e Binary files /dev/null and b/project/locale/de/LC_MESSAGES/django.mo differ diff --git a/project/locale/fr/LC_MESSAGES/django.mo b/project/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 00000000..30f3e716 Binary files /dev/null and b/project/locale/fr/LC_MESSAGES/django.mo differ diff --git a/project/locale/fr/LC_MESSAGES/django.po b/project/locale/fr/LC_MESSAGES/django.po index 7951deec..33f93c00 100644 --- a/project/locale/fr/LC_MESSAGES/django.po +++ b/project/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-01-21 11:48+0000\n" +"POT-Creation-Date: 2020-01-30 01:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,2418 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: apps/account/admin.py:32 +msgid "Personal info" +msgstr "" + +#: apps/account/admin.py:36 +msgid "Subscription" +msgstr "" + +#: apps/account/admin.py:41 +msgid "Important dates" +msgstr "" + +#: apps/account/admin.py:42 +msgid "Permissions" +msgstr "" + +#: apps/account/admin.py:61 apps/establishment/models.py:1103 +#: apps/location/models.py:41 apps/product/models.py:49 +#: apps/product/models.py:87 +msgid "Name" +msgstr "" + +#: apps/account/apps.py:7 +msgid "Account" +msgstr "" + +#: apps/account/forms.py:15 +msgid "The two password fields didn't match." +msgstr "" + +#: apps/account/forms.py:16 +msgid "Password already in use." +msgstr "" + +#: apps/account/forms.py:19 +msgid "New password" +msgstr "" + +#: apps/account/forms.py:25 +msgid "New password confirmation" +msgstr "" + +#: apps/account/models.py:57 +msgid "Standard user" +msgstr "" + +#: apps/account/models.py:58 +msgid "Comments moderator" +msgstr "" + +#: apps/account/models.py:59 +msgid "Country admin" +msgstr "" + +#: apps/account/models.py:60 +msgid "Content page manager" +msgstr "" + +#: apps/account/models.py:61 +msgid "Establishment manager" +msgstr "" + +#: apps/account/models.py:62 +msgid "Reviewer manager" +msgstr "" + +#: apps/account/models.py:63 +msgid "Restaurant reviewer" +msgstr "" + +#: apps/account/models.py:64 +msgid "Sales man" +msgstr "" + +#: apps/account/models.py:65 +msgid "Winery reviewer" +msgstr "" + +#: apps/account/models.py:66 +msgid "Seller" +msgstr "" + +#: apps/account/models.py:67 +msgid "Liquor reviewer" +msgstr "" + +#: apps/account/models.py:68 +msgid "Product reviewer" +msgstr "" + +#: apps/account/models.py:71 apps/account/models.py:511 +msgid "Role" +msgstr "" + +#: apps/account/models.py:73 apps/establishment/models.py:1425 +#: apps/location/models.py:57 apps/main/models.py:61 apps/review/models.py:57 +msgid "Country" +msgstr "" + +#: apps/account/models.py:75 apps/main/models.py:88 +msgid "Site settings" +msgstr "" + +#: apps/account/models.py:78 apps/establishment/models.py:111 +msgid "Establishment subtype" +msgstr "" + +#: apps/account/models.py:84 +msgid "navigation bar permission" +msgstr "" + +#: apps/account/models.py:208 +msgid "username" +msgstr "" + +#: apps/account/models.py:211 +msgid "Required. 150 characters or fewer. Letters, digits and ./+/-/_ only." +msgstr "" + +#: apps/account/models.py:213 +msgid "A user with that username already exists." +msgstr "" + +#: apps/account/models.py:216 apps/news/models.py:49 apps/utils/models.py:299 +msgid "Image URL path" +msgstr "" + +#: apps/account/models.py:219 +msgid "Cropped image URL path" +msgstr "" + +#: apps/account/models.py:221 +msgid "email address" +msgstr "" + +#: apps/account/models.py:223 +msgid "unconfirmed email" +msgstr "" + +#: apps/account/models.py:224 +msgid "email status" +msgstr "" + +#: apps/account/models.py:228 +msgid "User last used locale" +msgstr "" + +#: apps/account/models.py:230 +msgid "User last visited from city" +msgstr "" + +#: apps/account/models.py:231 +msgid "last IP address" +msgstr "" + +#: apps/account/models.py:234 +msgid "last site settings" +msgstr "" + +#: apps/account/models.py:241 +msgid "Phone" +msgstr "" + +#: apps/account/models.py:248 +msgid "Roles" +msgstr "" + +#: apps/account/models.py:254 apps/account/models.py:509 +#: apps/comment/models.py:74 apps/establishment/models.py:1102 +#: apps/favorites/models.py:23 apps/notification/models.py:104 +msgid "User" +msgstr "" + +#: apps/account/models.py:255 +msgid "Users" +msgstr "" + +#: apps/account/models.py:502 +msgid "validated" +msgstr "" + +#: apps/account/models.py:503 +msgid "pending" +msgstr "" + +#: apps/account/models.py:504 +msgid "cancelled" +msgstr "" + +#: apps/account/models.py:505 +msgid "rejected" +msgstr "" + +#: apps/account/models.py:513 apps/establishment/apps.py:8 +#: apps/establishment/models.py:613 apps/establishment/models.py:977 +msgid "Establishment" +msgstr "" + +#: apps/account/models.py:517 apps/collection/models.py:317 +#: apps/product/models.py:293 +msgid "state" +msgstr "" + +#: apps/account/models.py:531 +msgid "New role" +msgstr "" + +#: apps/account/models.py:532 +msgid "Old role" +msgstr "" + +#: apps/account/serializers/common.py:230 +msgid "Old password mismatch." +msgstr "" + +#: apps/account/serializers/common.py:233 apps/utils/exceptions.py:103 +msgid "Password is already in use" +msgstr "" + +#: apps/advertisement/apps.py:7 apps/main/models.py:359 +msgid "advertisement" +msgstr "" + +#: apps/advertisement/models.py:43 apps/collection/models.py:785 +#: apps/collection/models.py:1005 apps/establishment/models.py:526 +#: apps/establishment/models.py:1321 apps/establishment/models.py:1358 +#: apps/location/models.py:286 apps/location/models.py:317 +#: apps/location/models.py:344 apps/main/models.py:258 apps/main/models.py:443 +#: apps/news/models.py:283 apps/notification/models.py:118 +#: apps/partner/models.py:33 apps/product/models.py:289 +#: apps/product/models.py:526 apps/product/models.py:592 +#: apps/recipe/models.py:61 apps/review/models.py:90 apps/review/models.py:130 +#: apps/review/models.py:158 apps/review/models.py:175 apps/tag/models.py:45 +msgid "old id" +msgstr "" + +#: apps/advertisement/models.py:45 +msgid "Ad URL" +msgstr "" + +#: apps/advertisement/models.py:46 +msgid "Block level" +msgstr "" + +#: apps/advertisement/models.py:48 apps/collection/models.py:39 +#: apps/collection/models.py:306 +msgid "start" +msgstr "" + +#: apps/advertisement/models.py:49 apps/collection/models.py:41 +msgid "end" +msgstr "" + +#: apps/advertisement/models.py:52 apps/comment/models.py:81 +#: apps/main/models.py:440 apps/tag/models.py:188 +msgid "site" +msgstr "" + +#: apps/advertisement/models.py:56 apps/main/models.py:392 +msgid "page type" +msgstr "" + +#: apps/advertisement/models.py:57 +msgid "display duration in seconds" +msgstr "" + +#: apps/advertisement/models.py:60 +msgid "Probability to show" +msgstr "" + +#: apps/advertisement/models.py:61 +msgid "How many times shown" +msgstr "" + +#: apps/advertisement/models.py:65 +msgid "Advertisement" +msgstr "" + +#: apps/advertisement/models.py:66 +msgid "Advertisements" +msgstr "" + +#: apps/authorization/apps.py:8 +msgid "Authorization" +msgstr "" + +#: apps/authorization/models.py:86 +msgid "Expiration datetime" +msgstr "" + +#: apps/authorization/models.py:94 +msgid "Access token" +msgstr "" + +#: apps/authorization/models.py:95 +msgid "Access tokens" +msgstr "" + +#: apps/authorization/models.py:154 +msgid "Refresh token" +msgstr "" + +#: apps/authorization/models.py:155 +msgid "Refresh tokens" +msgstr "" + +#: apps/authorization/tasks.py:18 apps/authorization/tasks.py:19 +msgid "Email confirmation" +msgstr "" + +#: apps/authorization/views/common.py:69 +msgid "Validation OAuth2 request data error" +msgstr "" + +#: apps/booking/apps.py:7 apps/booking/models/models.py:69 +#: apps/booking/models/models.py:70 +msgid "Booking" +msgstr "" + +#: apps/booking/models/models.py:21 +msgid "Guestonline or Lastable" +msgstr "" + +#: apps/booking/models/models.py:22 +msgid "booking service establishment id" +msgstr "" + +#: apps/booking/models/models.py:23 +msgid "booking locale" +msgstr "" + +#: apps/booking/models/models.py:24 +msgid "external service pending booking" +msgstr "" + +#: apps/booking/models/models.py:25 +msgid "external service booking id" +msgstr "" + +#: apps/booking/models/models.py:27 +msgid "stripe service payment key" +msgstr "" + +#: apps/booking/models/models.py:28 +msgid "stripe service pre-payed booking token" +msgstr "" + +#: apps/booking/models/models.py:29 +msgid "prepayment price" +msgstr "" + +#: apps/booking/models/models.py:31 +msgid "booking owner" +msgstr "" + +#: apps/booking/models/services.py:37 +msgid "This field is required" +msgstr "" + +#: apps/collection/apps.py:7 apps/collection/models.py:105 +msgid "collection" +msgstr "" + +#: apps/collection/models.py:29 apps/collection/models.py:78 +#: apps/collection/models.py:727 apps/establishment/models.py:527 +#: apps/establishment/models.py:1216 apps/establishment/models.py:1251 +#: apps/establishment/models.py:1296 apps/establishment/models.py:1338 +#: apps/establishment/models.py:1455 apps/location/models.py:101 +#: apps/location/models.py:280 apps/location/models.py:313 +#: apps/location/models.py:341 apps/main/models.py:32 apps/main/models.py:223 +#: apps/main/models.py:386 apps/main/models.py:428 apps/news/models.py:64 +#: apps/notification/models.py:20 apps/partner/models.py:34 +#: apps/product/models.py:260 apps/product/models.py:490 +#: apps/product/models.py:521 apps/review/models.py:161 +msgid "name" +msgstr "" + +#: apps/collection/models.py:74 +msgid "Ordinary" +msgstr "" + +#: apps/collection/models.py:75 +msgid "Pop" +msgstr "" + +#: apps/collection/models.py:82 +msgid "Collection type" +msgstr "" + +#: apps/collection/models.py:84 apps/comment/models.py:76 +#: apps/establishment/models.py:573 +msgid "Publish status" +msgstr "" + +#: apps/collection/models.py:86 +msgid "Position on top" +msgstr "" + +#: apps/collection/models.py:88 apps/location/models.py:107 +#: apps/location/models.py:155 apps/location/models.py:283 +#: apps/main/models.py:222 apps/main/models.py:269 apps/news/models.py:319 +#: apps/notification/models.py:24 +msgid "country" +msgstr "" + +#: apps/collection/models.py:90 +msgid "collection block properties" +msgstr "" + +#: apps/collection/models.py:93 apps/establishment/models.py:534 +#: apps/establishment/models.py:1219 apps/location/models.py:289 +#: apps/main/models.py:434 apps/news/models.py:295 apps/product/models.py:261 +#: apps/review/models.py:163 +msgid "description" +msgstr "" + +#: apps/collection/models.py:96 +msgid "Collection slug" +msgstr "" + +#: apps/collection/models.py:106 +msgid "collections" +msgstr "" + +#: apps/collection/models.py:297 +msgid "Restaurant" +msgstr "" + +#: apps/collection/models.py:298 +msgid "Artisan" +msgstr "" + +#: apps/collection/models.py:299 +msgid "Wine" +msgstr "" + +#: apps/collection/models.py:304 +msgid "guide type" +msgstr "" + +#: apps/collection/models.py:310 +msgid "guide vintage year" +msgstr "" + +#: apps/collection/models.py:312 +msgid "slug" +msgstr "" + +#: apps/collection/models.py:315 apps/news/models.py:336 +msgid "site settings" +msgstr "" + +#: apps/collection/models.py:320 +msgid "* after rebuild guide, refresh count of related guide elements" +msgstr "" + +#: apps/collection/models.py:321 +msgid "count of related guide elements during initialization" +msgstr "" + +#: apps/collection/models.py:328 apps/collection/models.py:500 +msgid "guide" +msgstr "" + +#: apps/collection/models.py:329 +msgid "guides" +msgstr "" + +#: apps/collection/models.py:450 +msgid "number of pages" +msgstr "" + +#: apps/collection/models.py:451 +msgid "the total number of reserved pages" +msgstr "" + +#: apps/collection/models.py:453 +msgid "number of right pages" +msgstr "" + +#: apps/collection/models.py:454 +msgid "the number of right pages (which are part of total number)." +msgstr "" + +#: apps/collection/models.py:457 apps/collection/models.py:1011 +msgid "guide element" +msgstr "" + +#: apps/collection/models.py:464 +msgid "advertorial" +msgstr "" + +#: apps/collection/models.py:465 +msgid "advertorials" +msgstr "" + +#: apps/collection/models.py:486 +msgid "with mark" +msgstr "" + +#: apps/collection/models.py:487 +msgid "exclude empty marks?" +msgstr "" + +#: apps/collection/models.py:490 +msgid "max mark" +msgstr "" + +#: apps/collection/models.py:492 +msgid "mark under" +msgstr "" + +#: apps/collection/models.py:493 +msgid "min mark" +msgstr "" + +#: apps/collection/models.py:495 +msgid "mark over" +msgstr "" + +#: apps/collection/models.py:507 +msgid "guide filter" +msgstr "" + +#: apps/collection/models.py:508 +msgid "guide filters" +msgstr "" + +#: apps/collection/models.py:731 apps/collection/models.py:980 +msgid "guide element type" +msgstr "" + +#: apps/collection/models.py:732 +msgid "guide element types" +msgstr "" + +#: apps/collection/models.py:746 apps/collection/models.py:781 +msgid "section name" +msgstr "" + +#: apps/collection/models.py:752 +msgid "guide wine color section" +msgstr "" + +#: apps/collection/models.py:753 +msgid "guide wine color sections" +msgstr "" + +#: apps/collection/models.py:764 +msgid "category name" +msgstr "" + +#: apps/collection/models.py:770 +msgid "guide element section category" +msgstr "" + +#: apps/collection/models.py:771 +msgid "guide element section categories" +msgstr "" + +#: apps/collection/models.py:783 apps/establishment/admin.py:121 +#: apps/establishment/models.py:1253 apps/establishment/models.py:1318 +msgid "category" +msgstr "" + +#: apps/collection/models.py:791 +msgid "guide element section" +msgstr "" + +#: apps/collection/models.py:792 +msgid "guide element sections" +msgstr "" + +#: apps/collection/models.py:1003 +msgid "label photo" +msgstr "" + +#: apps/collection/models.py:1012 +msgid "guide elements" +msgstr "" + +#: apps/collection/views/back.py:168 apps/collection/views/back.py:184 +#: apps/collection/views/back.py:200 apps/main/views/back.py:174 +#: apps/main/views/back.py:190 +msgid "The file will be sent to your email." +msgstr "" + +#: apps/comment/apps.py:7 apps/review/models.py:132 +msgid "comment" +msgstr "" + +#: apps/comment/apps.py:8 +msgid "comments" +msgstr "" + +#: apps/comment/models.py:64 apps/product/models.py:253 +#: apps/recipe/models.py:42 +msgid "Waiting" +msgstr "" + +#: apps/comment/models.py:65 apps/main/models.py:158 apps/product/models.py:251 +#: apps/recipe/models.py:44 +msgid "Published" +msgstr "" + +#: apps/comment/models.py:66 +msgid "Rejected" +msgstr "" + +#: apps/comment/models.py:67 +msgid "Deleted" +msgstr "" + +#: apps/comment/models.py:70 +msgid "Comment text" +msgstr "" + +#: apps/comment/models.py:73 +msgid "Mark" +msgstr "" + +#: apps/comment/models.py:79 +msgid "status" +msgstr "" + +#: apps/comment/models.py:90 +msgid "Comment" +msgstr "" + +#: apps/comment/models.py:91 +msgid "Comments" +msgstr "" + +#: apps/configuration/apps.py:7 +msgid "configuration" +msgstr "" + +#: apps/configuration/models.py:9 +msgid "default language" +msgstr "" + +#: apps/establishment/filters.py:125 apps/news/filters.py:55 +msgid "Type at least 3 characters to search please." +msgstr "" + +#: apps/establishment/models.py:49 apps/establishment/models.py:90 +#: apps/establishment/models.py:937 apps/recipe/models.py:53 +msgid "Description" +msgstr "" + +#: apps/establishment/models.py:52 apps/establishment/models.py:93 +#: apps/establishment/models.py:532 apps/establishment/models.py:943 +#: apps/notification/models.py:18 apps/product/models.py:51 +#: apps/product/models.py:89 +msgid "Index name" +msgstr "" + +#: apps/establishment/models.py:53 apps/product/models.py:52 +msgid "Use subtypes" +msgstr "" + +#: apps/establishment/models.py:57 apps/establishment/models.py:100 +#: apps/product/models.py:56 apps/tag/models.py:176 +msgid "Tag categories" +msgstr "" + +#: apps/establishment/models.py:66 +msgid "Establishment type" +msgstr "" + +#: apps/establishment/models.py:67 +msgid "Establishment types" +msgstr "" + +#: apps/establishment/models.py:96 apps/product/models.py:266 +msgid "Type" +msgstr "" + +#: apps/establishment/models.py:112 +msgid "Establishment subtypes" +msgstr "" + +#: apps/establishment/models.py:116 +msgid "Establishment type is not use subtypes." +msgstr "" + +#: apps/establishment/models.py:529 +msgid "Transliterated name" +msgstr "" + +#: apps/establishment/models.py:531 +msgid "Western name" +msgstr "" + +#: apps/establishment/models.py:538 apps/product/models.py:275 +msgid "public mark" +msgstr "" + +#: apps/establishment/models.py:542 +msgid "toque number" +msgstr "" + +#: apps/establishment/models.py:546 apps/establishment/models.py:1339 +msgid "type" +msgstr "" + +#: apps/establishment/models.py:550 +msgid "subtype" +msgstr "" + +#: apps/establishment/models.py:553 apps/establishment/models.py:1478 +#: apps/news/models.py:34 apps/news/models.py:315 +msgid "address" +msgstr "" + +#: apps/establishment/models.py:556 +msgid "price level" +msgstr "" + +#: apps/establishment/models.py:558 +msgid "Web site URL" +msgstr "" + +#: apps/establishment/models.py:560 +msgid "Facebook URL" +msgstr "" + +#: apps/establishment/models.py:562 +msgid "Twitter URL" +msgstr "" + +#: apps/establishment/models.py:564 +msgid "Instagram URL" +msgstr "" + +#: apps/establishment/models.py:566 +msgid "Lafourchette URL" +msgstr "" + +#: apps/establishment/models.py:567 +msgid "guestonline id" +msgstr "" + +#: apps/establishment/models.py:569 +msgid "lastable id" +msgstr "" + +#: apps/establishment/models.py:572 +msgid "Booking URL" +msgstr "" + +#: apps/establishment/models.py:575 +msgid "Establishment schedule" +msgstr "" + +#: apps/establishment/models.py:578 +msgid "Transportation" +msgstr "" + +#: apps/establishment/models.py:582 +msgid "Collections" +msgstr "" + +#: apps/establishment/models.py:584 +msgid "Preview image URL path" +msgstr "" + +#: apps/establishment/models.py:587 +msgid "Establishment slug" +msgstr "" + +#: apps/establishment/models.py:592 apps/product/models.py:296 +#: apps/product/models.py:591 apps/tag/models.py:65 +msgid "Tag" +msgstr "" + +#: apps/establishment/models.py:599 apps/establishment/models.py:1225 +#: apps/main/models.py:39 +msgid "currency" +msgstr "" + +#: apps/establishment/models.py:603 apps/product/models.py:483 +msgid "purchased plaques" +msgstr "" + +#: apps/establishment/models.py:604 +msgid "" +"Attribute from legacy db.\n" +"Must be deleted after the implementation of the market." +msgstr "" + +#: apps/establishment/models.py:614 apps/partner/models.py:40 +msgid "Establishments" +msgstr "" + +#: apps/establishment/models.py:898 apps/product/models.py:609 +#: apps/review/models.py:69 +msgid "text" +msgstr "" + +#: apps/establishment/models.py:901 apps/establishment/models.py:919 +#: apps/establishment/models.py:1298 apps/establishment/models.py:1361 +#: apps/establishment/models.py:1454 apps/product/models.py:273 +#: apps/product/models.py:472 +msgid "establishment" +msgstr "" + +#: apps/establishment/models.py:905 apps/product/models.py:616 +#: apps/review/models.py:103 apps/review/models.py:138 +msgid "author" +msgstr "" + +#: apps/establishment/models.py:911 +msgid "establishment notes" +msgstr "" + +#: apps/establishment/models.py:912 +msgid "establishment note" +msgstr "" + +#: apps/establishment/models.py:923 apps/product/models.py:545 +#: apps/review/models.py:188 +msgid "image" +msgstr "" + +#: apps/establishment/models.py:927 +msgid "establishment gallery" +msgstr "" + +#: apps/establishment/models.py:928 +msgid "establishment galleries" +msgstr "" + +#: apps/establishment/models.py:948 apps/establishment/models.py:985 +msgid "Position" +msgstr "" + +#: apps/establishment/models.py:949 +msgid "Positions" +msgstr "" + +#: apps/establishment/models.py:979 apps/establishment/models.py:1142 +msgid "Employee" +msgstr "" + +#: apps/establishment/models.py:980 +msgid "From date" +msgstr "" + +#: apps/establishment/models.py:983 +msgid "To date" +msgstr "" + +#: apps/establishment/models.py:990 apps/establishment/models.py:1130 +msgid "Old id" +msgstr "" + +#: apps/establishment/models.py:1104 +msgid "Last Name" +msgstr "" + +#: apps/establishment/models.py:1112 +msgid "Male" +msgstr "" + +#: apps/establishment/models.py:1113 +msgid "Female" +msgstr "" + +#: apps/establishment/models.py:1115 +msgid "Sex" +msgstr "" + +#: apps/establishment/models.py:1117 +msgid "Birth date" +msgstr "" + +#: apps/establishment/models.py:1119 apps/notification/models.py:106 +msgid "Email" +msgstr "" + +#: apps/establishment/models.py:1121 +msgid "Toque number" +msgstr "" + +#: apps/establishment/models.py:1128 apps/news/models.py:321 +#: apps/tag/models.py:66 +msgid "Tags" +msgstr "" + +#: apps/establishment/models.py:1131 +msgid "Available for events" +msgstr "" + +#: apps/establishment/models.py:1135 apps/location/models.py:170 +msgid "image instance of model Image" +msgstr "" + +#: apps/establishment/models.py:1143 +msgid "Employees" +msgstr "" + +#: apps/establishment/models.py:1190 +msgid "contact phone" +msgstr "" + +#: apps/establishment/models.py:1191 apps/establishment/models.py:1458 +msgid "contact phones" +msgstr "" + +#: apps/establishment/models.py:1204 +msgid "contact email" +msgstr "" + +#: apps/establishment/models.py:1205 +msgid "contact emails" +msgstr "" + +#: apps/establishment/models.py:1222 apps/establishment/models.py:1255 +#: apps/establishment/models.py:1301 apps/review/models.py:141 +msgid "price" +msgstr "" + +#: apps/establishment/models.py:1223 +msgid "is signature plate" +msgstr "" + +#: apps/establishment/models.py:1228 +msgid "currency code" +msgstr "" + +#: apps/establishment/models.py:1232 apps/establishment/models.py:1326 +#: apps/establishment/models.py:1327 +msgid "menu" +msgstr "" + +#: apps/establishment/models.py:1242 +msgid "plate" +msgstr "" + +#: apps/establishment/models.py:1243 +msgid "plates" +msgstr "" + +#: apps/establishment/models.py:1256 +msgid "signature" +msgstr "" + +#: apps/establishment/models.py:1259 +msgid "dish" +msgstr "" + +#: apps/establishment/models.py:1260 +msgid "dishes" +msgstr "" + +#: apps/establishment/models.py:1300 +msgid "is drinks included" +msgstr "" + +#: apps/establishment/models.py:1305 +msgid "Menu schedule" +msgstr "" + +#: apps/establishment/models.py:1310 +msgid "Menu files" +msgstr "" + +#: apps/establishment/models.py:1315 +msgid "Menu dishes" +msgstr "" + +#: apps/establishment/models.py:1342 +msgid "menu upload" +msgstr "" + +#: apps/establishment/models.py:1343 +msgid "menu uploads" +msgstr "" + +#: apps/establishment/models.py:1347 apps/gallery/models.py:29 +#: apps/main/models.py:196 apps/main/models.py:402 apps/news/models.py:47 +#: apps/news/models.py:287 +msgid "title" +msgstr "" + +#: apps/establishment/models.py:1350 +msgid "social choice" +msgstr "" + +#: apps/establishment/models.py:1351 +msgid "social choices" +msgstr "" + +#: apps/establishment/models.py:1367 apps/establishment/models.py:1374 +msgid "social network" +msgstr "" + +#: apps/establishment/models.py:1371 +msgid "URL" +msgstr "" + +#: apps/establishment/models.py:1375 +msgid "social networks" +msgstr "" + +#: apps/establishment/models.py:1416 +msgid "One" +msgstr "" + +#: apps/establishment/models.py:1417 +msgid "Two" +msgstr "" + +#: apps/establishment/models.py:1418 +msgid "Three" +msgstr "" + +#: apps/establishment/models.py:1419 +msgid "Four" +msgstr "" + +#: apps/establishment/models.py:1420 +msgid "Five" +msgstr "" + +#: apps/establishment/models.py:1435 apps/establishment/models.py:1436 +msgid "Rating strategy" +msgstr "" + +#: apps/establishment/models.py:1461 +msgid "fax numbers" +msgstr "" + +#: apps/establishment/models.py:1464 +msgid "legal entity" +msgstr "" + +#: apps/establishment/models.py:1467 +msgid "registry number" +msgstr "" + +#: apps/establishment/models.py:1470 +msgid "VAT identification number" +msgstr "" + +#: apps/establishment/models.py:1474 +msgid "sic code" +msgstr "" + +#: apps/establishment/models.py:1484 +msgid "company" +msgstr "" + +#: apps/establishment/models.py:1485 +msgid "companies" +msgstr "" + +#: apps/establishment/serializers/back.py:473 +msgid "Establishment not found" +msgstr "" + +#: apps/establishment/serializers/back.py:476 apps/news/serializers.py:431 +#: apps/product/serializers/back.py:42 +msgid "Image not found" +msgstr "" + +#: apps/establishment/serializers/back.py:482 +#: apps/product/serializers/back.py:48 +msgid "Image is already added." +msgstr "" + +#: apps/establishment/serializers/common.py:270 +#, python-brace-format +msgid "{entity_class.__name__} not found." +msgstr "" + +#: apps/establishment/serializers/common.py:605 +#: apps/timetable/serialziers.py:61 +msgid "Establishment not found." +msgstr "" + +#: apps/establishment/serializers/common.py:644 +#: apps/establishment/serializers/common.py:672 apps/news/serializers.py:455 +#: apps/news/serializers.py:481 apps/product/serializers/common.py:183 +msgid "Object not found." +msgstr "" + +#: apps/establishment/serializers/common.py:733 +msgid "Fax is already reserved." +msgstr "" + +#: apps/establishment/serializers/common.py:737 +msgid "Phones is already reserved." +msgstr "" + +#: apps/favorites/apps.py:7 apps/favorites/models.py:32 +#: apps/favorites/models.py:33 +msgid "Favorites" +msgstr "" + +#: apps/gallery/apps.py:7 apps/news/models.py:495 +msgid "gallery" +msgstr "" + +#: apps/gallery/models.py:20 +msgid "Horizontal" +msgstr "" + +#: apps/gallery/models.py:21 +msgid "Vertical" +msgstr "" + +#: apps/gallery/models.py:25 +msgid "image file" +msgstr "" + +#: apps/gallery/models.py:28 +msgid "image orientation" +msgstr "" + +#: apps/gallery/models.py:35 apps/utils/models.py:198 apps/utils/models.py:227 +#: apps/utils/models.py:236 apps/utils/models.py:277 apps/utils/models.py:312 +msgid "Image" +msgstr "" + +#: apps/gallery/models.py:36 +msgid "Images" +msgstr "" + +#: apps/gallery/serializers.py:43 +#, python-format +msgid "File size too large: %s bytes" +msgstr "" + +#: apps/gallery/serializers.py:92 +#, python-format +msgid "Unrecognized crop option: %s" +msgstr "" + +#: apps/gallery/serializers.py:139 +msgid "Image not found." +msgstr "" + +#: apps/location/admin.py:28 apps/main/apps.py:8 apps/main/models.py:161 +msgid "Main" +msgstr "" + +#: apps/location/admin.py:31 +msgid "Location" +msgstr "" + +#: apps/location/admin.py:34 +msgid "Address detail" +msgstr "" + +#: apps/location/apps.py:7 +msgid "location" +msgstr "" + +#: apps/location/models.py:42 +msgid "Code" +msgstr "" + +#: apps/location/models.py:43 +msgid "Low price" +msgstr "" + +#: apps/location/models.py:44 +msgid "High price" +msgstr "" + +#: apps/location/models.py:45 apps/translation/models.py:43 +msgid "Languages" +msgstr "" + +#: apps/location/models.py:46 apps/translation/models.py:35 +msgid "is active" +msgstr "" + +#: apps/location/models.py:56 +msgid "Countries" +msgstr "" + +#: apps/location/models.py:102 apps/location/models.py:150 +msgid "code" +msgstr "" + +#: apps/location/models.py:104 apps/location/models.py:153 +msgid "parent region" +msgstr "" + +#: apps/location/models.py:116 +msgid "regions" +msgstr "" + +#: apps/location/models.py:117 +msgid "region" +msgstr "" + +#: apps/location/models.py:149 +msgid "City name json" +msgstr "" + +#: apps/location/models.py:158 apps/location/models.py:222 +msgid "postal code" +msgstr "" + +#: apps/location/models.py:158 apps/location/models.py:223 +msgid "Ex.: 350018" +msgstr "" + +#: apps/location/models.py:160 +msgid "is island" +msgstr "" + +#: apps/location/models.py:177 +msgid "cities" +msgstr "" + +#: apps/location/models.py:178 apps/location/models.py:215 +msgid "city" +msgstr "" + +#: apps/location/models.py:217 +msgid "street name 1" +msgstr "" + +#: apps/location/models.py:219 +msgid "street name 2" +msgstr "" + +#: apps/location/models.py:220 +msgid "number" +msgstr "" + +#: apps/location/models.py:225 apps/location/models.py:285 +#: apps/product/models.py:525 +msgid "Coordinates" +msgstr "" + +#: apps/location/models.py:227 +msgid "District name" +msgstr "" + +#: apps/location/models.py:233 apps/location/models.py:234 +msgid "Address" +msgstr "" + +#: apps/location/models.py:299 +msgid "wine regions" +msgstr "" + +#: apps/location/models.py:300 apps/location/models.py:343 +#: apps/location/models.py:362 +msgid "wine region" +msgstr "" + +#: apps/location/models.py:316 apps/location/models.py:325 +#: apps/location/models.py:365 +msgid "wine sub region" +msgstr "" + +#: apps/location/models.py:324 +msgid "wine sub regions" +msgstr "" + +#: apps/location/models.py:351 apps/product/models.py:285 +msgid "wine village" +msgstr "" + +#: apps/location/models.py:352 +msgid "wine villages" +msgstr "" + +#: apps/location/models.py:380 apps/location/models.py:398 +#: apps/product/models.py:541 apps/product/models.py:612 +msgid "product" +msgstr "" + +#: apps/location/models.py:386 +msgid "establishment wine origin address" +msgstr "" + +#: apps/location/models.py:387 +msgid "establishment wine origin addresses" +msgstr "" + +#: apps/location/models.py:404 +msgid "wine origin address" +msgstr "" + +#: apps/location/models.py:405 +msgid "wine origin addresses" +msgstr "" + +#: apps/location/serializers/common.py:184 +#: apps/location/serializers/common.py:189 +msgid "Invalid value" +msgstr "" + +#: apps/main/models.py:34 +msgid "sign" +msgstr "" + +#: apps/main/models.py:40 +msgid "currencies" +msgstr "" + +#: apps/main/models.py:58 +msgid "Subdomain" +msgstr "" + +#: apps/main/models.py:63 +msgid "Default site" +msgstr "" + +#: apps/main/models.py:65 +msgid "Pinterest page URL" +msgstr "" + +#: apps/main/models.py:67 +msgid "Twitter page URL" +msgstr "" + +#: apps/main/models.py:69 +msgid "Facebook page URL" +msgstr "" + +#: apps/main/models.py:71 +msgid "Instagram page URL" +msgstr "" + +#: apps/main/models.py:73 +msgid "Contact email" +msgstr "" + +#: apps/main/models.py:75 +msgid "Config" +msgstr "" + +#: apps/main/models.py:77 +msgid "AD config" +msgstr "" + +#: apps/main/models.py:87 +msgid "Site setting" +msgstr "" + +#: apps/main/models.py:128 +msgid "Feature" +msgstr "" + +#: apps/main/models.py:129 +msgid "Features" +msgstr "" + +#: apps/main/models.py:164 +msgid "backoffice" +msgstr "" + +#: apps/main/models.py:173 +msgid "Site feature" +msgstr "" + +#: apps/main/models.py:174 +msgid "Site features" +msgstr "" + +#: apps/main/models.py:198 apps/product/models.py:300 +msgid "vintage year" +msgstr "" + +#: apps/main/models.py:205 apps/news/models.py:310 apps/recipe/models.py:55 +msgid "State" +msgstr "" + +#: apps/main/models.py:257 +msgid "is international" +msgstr "" + +#: apps/main/models.py:259 +msgid "old title" +msgstr "" + +#: apps/main/models.py:260 +msgid "old link" +msgstr "" + +#: apps/main/models.py:261 +msgid "old attachment_suffix_url" +msgstr "" + +#: apps/main/models.py:262 +msgid "old description" +msgstr "" + +#: apps/main/models.py:263 +msgid "old link_title" +msgstr "" + +#: apps/main/models.py:271 +msgid "old active" +msgstr "" + +#: apps/main/models.py:272 +msgid "is parse" +msgstr "" + +#: apps/main/models.py:278 apps/main/models.py:279 +msgid "Carousel" +msgstr "" + +#: apps/main/models.py:361 +msgid "Block width" +msgstr "" + +#: apps/main/models.py:363 +msgid "Block height" +msgstr "" + +#: apps/main/models.py:369 +msgid "page" +msgstr "" + +#: apps/main/models.py:370 +msgid "pages" +msgstr "" + +#: apps/main/models.py:393 +msgid "page types" +msgstr "" + +#: apps/main/models.py:401 +msgid "link" +msgstr "" + +#: apps/main/models.py:407 +msgid "footer" +msgstr "" + +#: apps/main/models.py:410 +msgid "about_us" +msgstr "" + +#: apps/main/models.py:411 +msgid "copyright" +msgstr "" + +#: apps/main/models.py:412 +msgid "links" +msgstr "" + +#: apps/main/models.py:425 +msgid "table" +msgstr "" + +#: apps/main/models.py:426 +msgid "mailing" +msgstr "" + +#: apps/main/models.py:430 +msgid "display" +msgstr "" + +#: apps/main/models.py:435 +msgid "query" +msgstr "" + +#: apps/main/models.py:437 +msgid "user" +msgstr "" + +#: apps/main/models.py:448 +msgid "panel" +msgstr "" + +#: apps/main/models.py:449 +msgid "panels" +msgstr "" + +#: apps/main/models.py:557 +msgid "read" +msgstr "" + +#: apps/main/models.py:558 +msgid "write" +msgstr "" + +#: apps/main/models.py:562 +msgid "sections" +msgstr "" + +#: apps/main/models.py:568 +msgid "permission mode" +msgstr "" + +#: apps/main/models.py:572 +msgid "Navigation bar item permission" +msgstr "" + +#: apps/main/models.py:573 +msgid "Navigation bar item permissions" +msgstr "" + +#: apps/news/apps.py:7 apps/news/models.py:346 apps/news/models.py:347 +#: apps/news/models.py:491 +msgid "news" +msgstr "" + +#: apps/news/filters.py:17 apps/recipe/models.py:70 +msgid "Recipes" +msgstr "" + +#: apps/news/models.py:30 +msgid "Start datetime" +msgstr "" + +#: apps/news/models.py:32 +msgid "End datetime" +msgstr "" + +#: apps/news/models.py:37 +msgid "event name" +msgstr "" + +#: apps/news/models.py:40 +msgid "content" +msgstr "" + +#: apps/news/models.py:51 +msgid "Content URL path" +msgstr "" + +#: apps/news/models.py:78 +msgid "news types" +msgstr "" + +#: apps/news/models.py:79 apps/news/models.py:285 +msgid "news type" +msgstr "" + +#: apps/news/models.py:274 +msgid "remove" +msgstr "supprimé" + +#: apps/news/models.py:275 +msgid "hidden" +msgstr "caché" + +#: apps/news/models.py:276 +msgid "published" +msgstr "publié" + +#: apps/news/models.py:277 +msgid "not published" +msgstr "non publié" + +#: apps/news/models.py:290 +msgid "Title for searching via BO" +msgstr "" + +#: apps/news/models.py:292 +msgid "subtitle" +msgstr "" + +#: apps/news/models.py:298 +msgid "Is description for certain locale active" +msgstr "" + +#: apps/news/models.py:300 +msgid "News publication date" +msgstr "" + +#: apps/news/models.py:301 +msgid "date since when news item is published" +msgstr "" + +#: apps/news/models.py:302 +msgid "News publication time" +msgstr "" + +#: apps/news/models.py:303 +msgid "time since when news item is published" +msgstr "" + +#: apps/news/models.py:305 +msgid "End" +msgstr "" + +#: apps/news/models.py:307 +msgid "Slugs for current news obj" +msgstr "" + +#: apps/news/models.py:312 +msgid "Is highlighted" +msgstr "" + +#: apps/news/models.py:330 +msgid "agenda" +msgstr "" + +#: apps/news/models.py:334 +msgid "banner" +msgstr "" + +#: apps/news/models.py:338 +msgid "Duplication datetime" +msgstr "" + +#: apps/news/models.py:340 +msgid "Field to detect doubles" +msgstr "" + +#: apps/news/models.py:499 +msgid "news gallery" +msgstr "" + +#: apps/news/models.py:500 +msgid "news galleries" +msgstr "" + +#: apps/news/serializers.py:279 apps/news/serializers.py:311 +msgid "Slug should be unique" +msgstr "" + +#: apps/news/serializers.py:429 +msgid "News not found" +msgstr "" + +#: apps/notification/apps.py:7 +msgid "notification" +msgstr "" + +#: apps/notification/models.py:107 +msgid "IP address" +msgstr "" + +#: apps/notification/models.py:108 +msgid "Country code" +msgstr "" + +#: apps/notification/models.py:109 apps/translation/models.py:31 +msgid "Locale identifier" +msgstr "" + +#: apps/notification/models.py:116 +msgid "Token" +msgstr "" + +#: apps/notification/models.py:127 +msgid "Subscriber" +msgstr "" + +#: apps/notification/models.py:128 +msgid "Subscribers" +msgstr "" + +#: apps/notification/models.py:190 +msgid "Last unsubscribe date" +msgstr "" + +#: apps/notification/models.py:195 +msgid "Old subscriber id" +msgstr "" + +#: apps/notification/models.py:196 +msgid "Old subscription type id" +msgstr "" + +#: apps/notification/models.py:207 +msgid "Subscribe" +msgstr "" + +#: apps/notification/models.py:208 +msgid "Subscribes" +msgstr "" + +#: apps/notification/serializers/common.py:65 +msgid "Does not match user email" +msgstr "" + +#: apps/notification/serializers/common.py:68 +msgid "This field is required." +msgstr "" + +#: apps/notification/tasks.py:31 +msgid "You have subscribed on news G&M" +msgstr "Vous êtes abonné aux actualités G&M" + +#: apps/notification/tasks.py:32 +msgid "
" +msgstr "" + +#: apps/notification/tasks.py:46 +msgid "G&M Subscriptions" +msgstr "Abonnements G&M" + +#: apps/notification/tasks.py:73 +msgid "You have successfully unsubscribed from G&M news" +msgstr "Vous avez réussi à vous désinscrire des actualités G&M" + +#: apps/partner/apps.py:7 apps/partner/models.py:50 +msgid "partner" +msgstr "" + +#: apps/partner/models.py:29 +msgid "Partner" +msgstr "" + +#: apps/partner/models.py:30 +msgid "Sponsor" +msgstr "" + +#: apps/partner/models.py:36 apps/partner/models.py:65 +msgid "Partner image URL" +msgstr "" + +#: apps/partner/models.py:42 +msgid "starting date" +msgstr "" + +#: apps/partner/models.py:43 +msgid "expiry date" +msgstr "" + +#: apps/partner/models.py:44 +msgid "price per month" +msgstr "" + +#: apps/partner/models.py:51 +msgid "partners" +msgstr "" + +#: apps/partner/models.py:63 +msgid "Date partner binded" +msgstr "" + +#: apps/partner/models.py:64 +msgid "Establishment to Partner URL" +msgstr "" + +#: apps/product/apps.py:7 apps/product/models.py:323 +msgid "Product" +msgstr "" + +#: apps/product/models.py:65 apps/product/models.py:85 +msgid "Product type" +msgstr "" + +#: apps/product/models.py:66 +msgid "Product types" +msgstr "" + +#: apps/product/models.py:98 +msgid "Product subtype" +msgstr "" + +#: apps/product/models.py:99 +msgid "Product subtypes" +msgstr "" + +#: apps/product/models.py:103 +msgid "Product type is not use subtypes." +msgstr "" + +#: apps/product/models.py:242 +msgid "Common" +msgstr "" + +#: apps/product/models.py:243 +msgid "Online" +msgstr "" + +#: apps/product/models.py:252 +msgid "Out_of_production" +msgstr "" + +#: apps/product/models.py:263 +msgid "available" +msgstr "" + +#: apps/product/models.py:269 +msgid "Subtypes" +msgstr "" + +#: apps/product/models.py:278 +msgid "classifications" +msgstr "" + +#: apps/product/models.py:281 +msgid "standards" +msgstr "" + +#: apps/product/models.py:282 apps/product/models.py:294 +#: apps/product/models.py:299 +msgid "attribute from legacy db" +msgstr "" + +#: apps/product/models.py:287 apps/recipe/models.py:62 +msgid "Slug" +msgstr "" + +#: apps/product/models.py:306 +msgid "average price" +msgstr "" + +#: apps/product/models.py:314 +msgid "Serial number" +msgstr "" + +#: apps/product/models.py:324 +msgid "Products" +msgstr "" + +#: apps/product/models.py:463 +msgid "Online product" +msgstr "" + +#: apps/product/models.py:464 +msgid "Online products" +msgstr "" + +#: apps/product/models.py:475 +msgid "plaque" +msgstr "" + +#: apps/product/models.py:477 +msgid "is gifted" +msgstr "" + +#: apps/product/models.py:478 +msgid "quantity" +msgstr "" + +#: apps/product/models.py:482 +msgid "purchased plaque" +msgstr "" + +#: apps/product/models.py:492 apps/review/models.py:162 +msgid "value" +msgstr "" + +#: apps/product/models.py:496 +msgid "unit" +msgstr "" + +#: apps/product/models.py:497 +msgid "units" +msgstr "" + +#: apps/product/models.py:516 +msgid "Appellation" +msgstr "" + +#: apps/product/models.py:517 +msgid "Wine quality" +msgstr "" + +#: apps/product/models.py:518 +msgid "Yard classification" +msgstr "" + +#: apps/product/models.py:523 +msgid "standard type" +msgstr "" + +#: apps/product/models.py:532 +msgid "wine standards" +msgstr "" + +#: apps/product/models.py:533 +msgid "wine standard" +msgstr "" + +#: apps/product/models.py:549 +msgid "product gallery" +msgstr "" + +#: apps/product/models.py:550 +msgid "product galleries" +msgstr "" + +#: apps/product/models.py:558 apps/product/models.py:586 +msgid "classification type" +msgstr "" + +#: apps/product/models.py:561 +msgid "product type" +msgstr "" + +#: apps/product/models.py:564 +msgid "product subtype" +msgstr "" + +#: apps/product/models.py:565 +msgid "" +"Legacy attribute - possible_type (product type).Product type in our case is " +"product subtype." +msgstr "" + +#: apps/product/models.py:570 +msgid "wine classification type" +msgstr "" + +#: apps/product/models.py:571 +msgid "wine classification types" +msgstr "" + +#: apps/product/models.py:589 +msgid "standard" +msgstr "" + +#: apps/product/models.py:598 +msgid "product classification" +msgstr "" + +#: apps/product/models.py:599 +msgid "product classifications" +msgstr "" + +#: apps/product/models.py:622 +msgid "product note" +msgstr "" + +#: apps/product/models.py:623 +msgid "product notes" +msgstr "" + +#: apps/product/serializers/back.py:39 +msgid "Product not found" +msgstr "" + +#: apps/product/serializers/back.py:110 +msgid "Tag category is already attached." +msgstr "" + +#: apps/product/serializers/common.py:229 +msgid "Product not found." +msgstr "" + +#: apps/rating/models.py:11 +msgid "ip" +msgstr "" + +#: apps/recipe/apps.py:8 +msgid "RecipeConfig" +msgstr "" + +#: apps/recipe/models.py:43 +msgid "Hidden" +msgstr "" + +#: apps/recipe/models.py:45 +msgid "Published exclusive" +msgstr "" + +#: apps/recipe/models.py:50 +msgid "Title" +msgstr "" + +#: apps/recipe/models.py:51 +msgid "Subtitle" +msgstr "" + +#: apps/recipe/models.py:56 +msgid "Author" +msgstr "" + +#: apps/recipe/models.py:57 apps/recipe/models.py:58 +msgid "Published at" +msgstr "" + +#: apps/recipe/models.py:59 apps/recipe/models.py:60 +msgid "Published scheduled at" +msgstr "" + +#: apps/recipe/models.py:69 +msgid "Recipe" +msgstr "" + +#: apps/review/apps.py:7 +msgid "reviews" +msgstr "" + +#: apps/review/models.py:42 +msgid "To investigate" +msgstr "" + +#: apps/review/models.py:43 +msgid "To review" +msgstr "" + +#: apps/review/models.py:44 +msgid "Ready" +msgstr "" + +#: apps/review/models.py:50 +msgid "Reviewer" +msgstr "" + +#: apps/review/models.py:66 +msgid "Child review" +msgstr "" + +#: apps/review/models.py:81 +msgid "Publish datetime" +msgstr "" + +#: apps/review/models.py:85 +msgid "Review published datetime" +msgstr "" + +#: apps/review/models.py:87 +msgid "Year of review" +msgstr "" + +#: apps/review/models.py:88 apps/review/models.py:134 +msgid "mark" +msgstr "" + +#: apps/review/models.py:89 +msgid "Priority" +msgstr "" + +#: apps/review/models.py:96 +msgid "Review" +msgstr "" + +#: apps/review/models.py:97 +msgid "Reviews" +msgstr "" + +#: apps/review/models.py:109 apps/review/models.py:131 +msgid "review" +msgstr "" + +#: apps/review/models.py:113 +msgid "locale" +msgstr "" + +#: apps/review/models.py:116 +msgid "Text author" +msgstr "" + +#: apps/review/models.py:117 +msgid "Text authors" +msgstr "" + +#: apps/review/models.py:126 +msgid "none" +msgstr "" + +#: apps/review/models.py:127 +msgid "diner" +msgstr "" + +#: apps/review/models.py:128 +msgid "lanch" +msgstr "" + +#: apps/review/models.py:133 +msgid "final comment" +msgstr "" + +#: apps/review/models.py:135 +msgid "attachment" +msgstr "" + +#: apps/review/models.py:139 +msgid "bill" +msgstr "" + +#: apps/review/models.py:147 +msgid "is published" +msgstr "" + +#: apps/review/models.py:150 +msgid "Inquiry" +msgstr "" + +#: apps/review/models.py:151 +msgid "Inquiries" +msgstr "" + +#: apps/review/models.py:159 apps/review/models.py:181 +msgid "inquiry" +msgstr "" + +#: apps/review/models.py:160 +msgid "sub name" +msgstr "" + +#: apps/review/models.py:164 +msgid "dish title" +msgstr "" + +#: apps/review/models.py:167 +msgid "inquiry grid" +msgstr "" + +#: apps/review/models.py:168 +msgid "inquiry grids" +msgstr "" + +#: apps/review/models.py:192 +msgid "inquiry gallery" +msgstr "" + +#: apps/review/models.py:193 +msgid "inquiry galleries" +msgstr "" + +#: apps/search_indexes/apps.py:7 +msgid "Search indexes" +msgstr "" + +#: apps/tag/apps.py:7 apps/tag/models.py:185 +msgid "tag" +msgstr "" + +#: apps/tag/models.py:36 apps/tag/models.py:153 +msgid "indexing name" +msgstr "" + +#: apps/tag/models.py:40 +msgid "Category" +msgstr "" + +#: apps/tag/models.py:47 +msgid "old id metadata product" +msgstr "" + +#: apps/tag/models.py:50 apps/tag/models.py:160 apps/translation/apps.py:7 +msgid "Translation" +msgstr "" + +#: apps/tag/models.py:90 +msgid "Chosen tag" +msgstr "" + +#: apps/tag/models.py:91 +msgid "Chosen tags" +msgstr "" + +#: apps/tag/models.py:141 +msgid "string" +msgstr "" + +#: apps/tag/models.py:142 +msgid "list" +msgstr "" + +#: apps/tag/models.py:143 +msgid "integer" +msgstr "" + +#: apps/tag/models.py:144 +msgid "float" +msgstr "" + +#: apps/tag/models.py:145 +msgid "percentage" +msgstr "" + +#: apps/tag/models.py:146 +msgid "boolean" +msgstr "" + +#: apps/tag/models.py:155 +msgid "value type" +msgstr "" + +#: apps/tag/models.py:175 +msgid "Tag category" +msgstr "" + +#: apps/tag/views.py:234 +msgid "Missing required \"items\" parameter" +msgstr "" + +#: apps/tag/views.py:242 +msgid "news/establishments/products" +msgstr "" + +#: apps/timetable/apps.py:7 +msgid "timetable" +msgstr "" + +#: apps/timetable/models.py:22 +msgid "Monday" +msgstr "" + +#: apps/timetable/models.py:23 +msgid "Tuesday" +msgstr "" + +#: apps/timetable/models.py:24 +msgid "Wednesday" +msgstr "" + +#: apps/timetable/models.py:25 +msgid "Thursday" +msgstr "" + +#: apps/timetable/models.py:26 +msgid "Friday" +msgstr "" + +#: apps/timetable/models.py:27 +msgid "Saturday" +msgstr "" + +#: apps/timetable/models.py:28 +msgid "Sunday" +msgstr "" + +#: apps/timetable/models.py:31 +msgid "Week day" +msgstr "" + +#: apps/timetable/models.py:33 +msgid "Lunch start time" +msgstr "" + +#: apps/timetable/models.py:34 +msgid "Lunch end time" +msgstr "" + +#: apps/timetable/models.py:35 +msgid "Dinner start time" +msgstr "" + +#: apps/timetable/models.py:36 +msgid "Dinner end time" +msgstr "" + +#: apps/timetable/models.py:37 +msgid "Opening time" +msgstr "" + +#: apps/timetable/models.py:38 +msgid "Closed time" +msgstr "" + +#: apps/timetable/models.py:42 +msgid "Timetable" +msgstr "" + +#: apps/timetable/models.py:43 +msgid "Timetables" +msgstr "" + +#: apps/transfer/apps.py:7 +msgid "Transfer" +msgstr "" + +#: apps/translation/models.py:29 +msgid "Language title" +msgstr "" + +#: apps/translation/models.py:42 +msgid "Language" +msgstr "" + +#: apps/translation/models.py:97 +msgid "Page" +msgstr "" + +#: apps/translation/models.py:99 +msgid "Text" +msgstr "" + +#: apps/translation/models.py:107 apps/translation/models.py:108 +msgid "Site interface dictionary" +msgstr "" + +#: apps/utils/exceptions.py:8 +msgid "Bad request" +msgstr "" + +#: apps/utils/exceptions.py:27 +msgid "Service is temporarily unavailable" +msgstr "" + +#: apps/utils/exceptions.py:32 +msgid "User not found" +msgstr "" + +#: apps/utils/exceptions.py:38 +#, python-format +msgid "Unable to send message to mailbox %s" +msgstr "" + +#: apps/utils/exceptions.py:53 +#, python-format +msgid "Locale not found in database (%s)" +msgstr "" + +#: apps/utils/exceptions.py:68 +msgid "Wrong username" +msgstr "" + +#: apps/utils/exceptions.py:76 +msgid "Not valid token" +msgstr "" + +#: apps/utils/exceptions.py:83 +msgid "Not valid access token" +msgstr "" + +#: apps/utils/exceptions.py:90 +msgid "Not valid refresh token" +msgstr "" + +#: apps/utils/exceptions.py:95 +msgid "OAuth2 Error" +msgstr "" + +#: apps/utils/exceptions.py:111 +msgid "Email address is already confirmed" +msgstr "" + +#: apps/utils/exceptions.py:119 +msgid "Image invalid input." +msgstr "" + +#: apps/utils/exceptions.py:126 +msgid "Incorrect login or password." +msgstr "" + +#: apps/utils/exceptions.py:135 +msgid "Item is already in favorites." +msgstr "" + +#: apps/utils/exceptions.py:143 +msgid "Item is already in carousels." +msgstr "" + +#: apps/utils/exceptions.py:152 +msgid "Password reset request is already exists and valid." +msgstr "" + +#: apps/utils/exceptions.py:161 +msgid "Object has already been added." +msgstr "" + +#: apps/utils/exceptions.py:167 +msgid "Binding object not found." +msgstr "" + +#: apps/utils/exceptions.py:173 +msgid "Removed binding object not found." +msgstr "" + +#: apps/utils/exceptions.py:181 +msgid "Unprocessable entity valid." +msgstr "" + +#: apps/utils/exceptions.py:190 +msgid "Guide element not valid for Guide." +msgstr "" + +#: apps/utils/exceptions.py:199 +msgid "Advertorial already exists for this guide element." +msgstr "" + +#: apps/utils/models.py:30 +msgid "Date created" +msgstr "" + +#: apps/utils/models.py:32 +msgid "Date updated" +msgstr "" + +#: apps/utils/models.py:150 +msgid "created by" +msgstr "" + +#: apps/utils/models.py:154 +msgid "modified by" +msgstr "" + +#: apps/utils/models.py:169 +msgid "File" +msgstr "" + +#: apps/utils/models.py:287 +msgid "SVG image" +msgstr "" + +#: apps/utils/models.py:324 +msgid "Mobile" +msgstr "" + +#: apps/utils/models.py:325 +msgid "Web" +msgstr "" + +#: apps/utils/models.py:326 +msgid "All" +msgstr "" + +#: apps/utils/models.py:329 +msgid "Source" +msgstr "" + +#: apps/utils/models.py:456 +msgid "Is the main image" +msgstr "" + #: project/templates/account/change_email.html:33 #, python-format msgid "" @@ -78,16 +2490,16 @@ msgstr "" #: project/templates/notification/update_email.html:44 msgid "Follow us" -msgstr "" +msgstr "Signez maintenant" #: project/templates/notification/update_email.html:46 msgid "You can also us on our social network below" -msgstr "" +msgstr "Vous pouvez également nous sur notre réseau social ci-dessous" #: project/templates/notification/update_email.html:62 msgid "This email has been sent to" -msgstr "" +msgstr "Cet e-mail a été envoyé à" #: project/templates/notification/update_email.html:63 msgid "click here to unsubscribe" -msgstr "" +msgstr "cliquez ici pour vous désinscrire" diff --git a/project/locale/ru/LC_MESSAGES/django.mo b/project/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 00000000..bf09c5e8 Binary files /dev/null and b/project/locale/ru/LC_MESSAGES/django.mo differ diff --git a/project/locale/ru/LC_MESSAGES/django.po b/project/locale/ru/LC_MESSAGES/django.po index 8092593a..ed3b6069 100644 --- a/project/locale/ru/LC_MESSAGES/django.po +++ b/project/locale/ru/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-10-17 13:52+0000\n" +"POT-Creation-Date: 2020-01-30 01:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -20,24 +20,25 @@ msgstr "" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%100>=11 && n%100<=14)? 2 : 3);\n" -#: apps/account/admin.py:30 +#: apps/account/admin.py:32 msgid "Personal info" msgstr "" -#: apps/account/admin.py:34 +#: apps/account/admin.py:36 msgid "Subscription" msgstr "" -#: apps/account/admin.py:39 +#: apps/account/admin.py:41 msgid "Important dates" msgstr "" -#: apps/account/admin.py:40 +#: apps/account/admin.py:42 msgid "Permissions" msgstr "" -#: apps/account/admin.py:59 apps/location/models.py:18 -#: venv/lib/python3.6/site-packages/fcm_django/models.py:14 +#: apps/account/admin.py:61 apps/establishment/models.py:1103 +#: apps/location/models.py:41 apps/product/models.py:49 +#: apps/product/models.py:87 msgid "Name" msgstr "" @@ -54,7 +55,6 @@ msgid "Password already in use." msgstr "" #: apps/account/forms.py:19 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:50 msgid "New password" msgstr "" @@ -62,89 +62,246 @@ msgstr "" msgid "New password confirmation" msgstr "" -#: apps/account/models.py:31 apps/account/models.py:227 +#: apps/account/models.py:57 +msgid "Standard user" +msgstr "" + +#: apps/account/models.py:58 +msgid "Comments moderator" +msgstr "" + +#: apps/account/models.py:59 +msgid "Country admin" +msgstr "" + +#: apps/account/models.py:60 +msgid "Content page manager" +msgstr "" + +#: apps/account/models.py:61 +msgid "Establishment manager" +msgstr "" + +#: apps/account/models.py:62 +msgid "Reviewer manager" +msgstr "" + +#: apps/account/models.py:63 +msgid "Restaurant reviewer" +msgstr "" + +#: apps/account/models.py:64 +msgid "Sales man" +msgstr "" + +#: apps/account/models.py:65 +msgid "Winery reviewer" +msgstr "" + +#: apps/account/models.py:66 +msgid "Seller" +msgstr "" + +#: apps/account/models.py:67 +msgid "Liquor reviewer" +msgstr "" + +#: apps/account/models.py:68 +msgid "Product reviewer" +msgstr "" + +#: apps/account/models.py:71 apps/account/models.py:511 msgid "Role" msgstr "" -#: apps/account/models.py:33 apps/location/models.py:28 apps/main/models.py:117 +#: apps/account/models.py:73 apps/establishment/models.py:1425 +#: apps/location/models.py:57 apps/main/models.py:61 apps/review/models.py:57 msgid "Country" msgstr "" -#: apps/account/models.py:76 apps/news/models.py:126 apps/utils/models.py:194 +#: apps/account/models.py:75 apps/main/models.py:88 +msgid "Site settings" +msgstr "" + +#: apps/account/models.py:78 apps/establishment/models.py:111 +msgid "Establishment subtype" +msgstr "" + +#: apps/account/models.py:84 +msgid "navigation bar permission" +msgstr "" + +#: apps/account/models.py:208 +msgid "username" +msgstr "" + +#: apps/account/models.py:211 +msgid "Required. 150 characters or fewer. Letters, digits and ./+/-/_ only." +msgstr "" + +#: apps/account/models.py:213 +msgid "A user with that username already exists." +msgstr "" + +#: apps/account/models.py:216 apps/news/models.py:49 apps/utils/models.py:299 msgid "Image URL path" msgstr "" -#: apps/account/models.py:78 +#: apps/account/models.py:219 msgid "Cropped image URL path" msgstr "" -#: apps/account/models.py:80 +#: apps/account/models.py:221 msgid "email address" msgstr "" -#: apps/account/models.py:82 +#: apps/account/models.py:223 msgid "unconfirmed email" msgstr "" -#: apps/account/models.py:83 +#: apps/account/models.py:224 msgid "email status" msgstr "" -#: apps/account/models.py:90 +#: apps/account/models.py:228 +msgid "User last used locale" +msgstr "" + +#: apps/account/models.py:230 +msgid "User last visited from city" +msgstr "" + +#: apps/account/models.py:231 +msgid "last IP address" +msgstr "" + +#: apps/account/models.py:234 +msgid "last site settings" +msgstr "" + +#: apps/account/models.py:241 +msgid "Phone" +msgstr "" + +#: apps/account/models.py:248 msgid "Roles" msgstr "" -#: apps/account/models.py:95 apps/account/models.py:226 -#: apps/comment/models.py:38 apps/establishment/models.py:435 -#: apps/favorites/models.py:23 apps/notification/models.py:79 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/object_history.html:30 +#: apps/account/models.py:254 apps/account/models.py:509 +#: apps/comment/models.py:74 apps/establishment/models.py:1102 +#: apps/favorites/models.py:23 apps/notification/models.py:104 msgid "User" msgstr "" -#: apps/account/models.py:96 +#: apps/account/models.py:255 msgid "Users" msgstr "" -#: apps/account/serializers/common.py:121 +#: apps/account/models.py:502 +msgid "validated" +msgstr "" + +#: apps/account/models.py:503 +msgid "pending" +msgstr "" + +#: apps/account/models.py:504 +msgid "cancelled" +msgstr "" + +#: apps/account/models.py:505 +msgid "rejected" +msgstr "" + +#: apps/account/models.py:513 apps/establishment/apps.py:8 +#: apps/establishment/models.py:613 apps/establishment/models.py:977 +msgid "Establishment" +msgstr "" + +#: apps/account/models.py:517 apps/collection/models.py:317 +#: apps/product/models.py:293 +msgid "state" +msgstr "" + +#: apps/account/models.py:531 +msgid "New role" +msgstr "" + +#: apps/account/models.py:532 +msgid "Old role" +msgstr "" + +#: apps/account/serializers/common.py:230 msgid "Old password mismatch." msgstr "" -#: apps/account/serializers/common.py:124 apps/utils/exceptions.py:103 +#: apps/account/serializers/common.py:233 apps/utils/exceptions.py:103 msgid "Password is already in use" msgstr "" -#: apps/account/tasks.py:18 -msgid "Password resetting" -msgstr "" - -#: apps/account/tasks.py:31 apps/account/tasks.py:43 -msgid "Validate new email address" -msgstr "" - -#: apps/advertisement/apps.py:7 +#: apps/advertisement/apps.py:7 apps/main/models.py:359 msgid "advertisement" msgstr "" -#: apps/advertisement/models.py:15 +#: apps/advertisement/models.py:43 apps/collection/models.py:785 +#: apps/collection/models.py:1005 apps/establishment/models.py:526 +#: apps/establishment/models.py:1321 apps/establishment/models.py:1358 +#: apps/location/models.py:286 apps/location/models.py:317 +#: apps/location/models.py:344 apps/main/models.py:258 apps/main/models.py:443 +#: apps/news/models.py:283 apps/notification/models.py:118 +#: apps/partner/models.py:33 apps/product/models.py:289 +#: apps/product/models.py:526 apps/product/models.py:592 +#: apps/recipe/models.py:61 apps/review/models.py:90 apps/review/models.py:130 +#: apps/review/models.py:158 apps/review/models.py:175 apps/tag/models.py:45 +msgid "old id" +msgstr "" + +#: apps/advertisement/models.py:45 msgid "Ad URL" msgstr "" -#: apps/advertisement/models.py:16 -msgid "Block width" -msgstr "" - -#: apps/advertisement/models.py:17 -msgid "Block height" -msgstr "" - -#: apps/advertisement/models.py:18 +#: apps/advertisement/models.py:46 msgid "Block level" msgstr "" -#: apps/advertisement/models.py:22 apps/advertisement/models.py:23 +#: apps/advertisement/models.py:48 apps/collection/models.py:39 +#: apps/collection/models.py:306 +msgid "start" +msgstr "" + +#: apps/advertisement/models.py:49 apps/collection/models.py:41 +msgid "end" +msgstr "" + +#: apps/advertisement/models.py:52 apps/comment/models.py:81 +#: apps/main/models.py:440 apps/tag/models.py:188 +msgid "site" +msgstr "" + +#: apps/advertisement/models.py:56 apps/main/models.py:392 +msgid "page type" +msgstr "" + +#: apps/advertisement/models.py:57 +msgid "display duration in seconds" +msgstr "" + +#: apps/advertisement/models.py:60 +msgid "Probability to show" +msgstr "" + +#: apps/advertisement/models.py:61 +msgid "How many times shown" +msgstr "" + +#: apps/advertisement/models.py:65 msgid "Advertisement" msgstr "" +#: apps/advertisement/models.py:66 +msgid "Advertisements" +msgstr "" + #: apps/authorization/apps.py:8 msgid "Authorization" msgstr "" @@ -169,20 +326,16 @@ msgstr "" msgid "Refresh tokens" msgstr "" -#: apps/authorization/tasks.py:18 +#: apps/authorization/tasks.py:18 apps/authorization/tasks.py:19 msgid "Email confirmation" msgstr "" -#: apps/authorization/views/common.py:40 -msgid "Application is not found" +#: apps/authorization/views/common.py:69 +msgid "Validation OAuth2 request data error" msgstr "" -#: apps/authorization/views/common.py:50 -msgid "Not found an application with this source" -msgstr "" - -#: apps/booking/apps.py:7 apps/booking/models/models.py:66 -#: apps/booking/models/models.py:67 +#: apps/booking/apps.py:7 apps/booking/models/models.py:69 +#: apps/booking/models/models.py:70 msgid "Booking" msgstr "" @@ -206,89 +359,255 @@ msgstr "" msgid "external service booking id" msgstr "" +#: apps/booking/models/models.py:27 +msgid "stripe service payment key" +msgstr "" + #: apps/booking/models/models.py:28 +msgid "stripe service pre-payed booking token" +msgstr "" + +#: apps/booking/models/models.py:29 +msgid "prepayment price" +msgstr "" + +#: apps/booking/models/models.py:31 msgid "booking owner" msgstr "" -#: apps/collection/apps.py:7 apps/collection/models.py:80 -#: apps/collection/models.py:106 +#: apps/booking/models/services.py:37 +msgid "This field is required" +msgstr "" + +#: apps/collection/apps.py:7 apps/collection/models.py:105 msgid "collection" msgstr "" -#: apps/collection/models.py:17 apps/establishment/models.py:241 -#: apps/establishment/models.py:504 apps/location/models.py:34 -#: apps/location/models.py:55 apps/main/models.py:227 apps/main/models.py:278 -#: apps/news/models.py:14 +#: apps/collection/models.py:29 apps/collection/models.py:78 +#: apps/collection/models.py:727 apps/establishment/models.py:527 +#: apps/establishment/models.py:1216 apps/establishment/models.py:1251 +#: apps/establishment/models.py:1296 apps/establishment/models.py:1338 +#: apps/establishment/models.py:1455 apps/location/models.py:101 +#: apps/location/models.py:280 apps/location/models.py:313 +#: apps/location/models.py:341 apps/main/models.py:32 apps/main/models.py:223 +#: apps/main/models.py:386 apps/main/models.py:428 apps/news/models.py:64 +#: apps/notification/models.py:20 apps/partner/models.py:34 +#: apps/product/models.py:260 apps/product/models.py:490 +#: apps/product/models.py:521 apps/review/models.py:161 msgid "name" msgstr "" -#: apps/collection/models.py:26 -msgid "start" -msgstr "" - -#: apps/collection/models.py:27 -msgid "end" -msgstr "" - -#: apps/collection/models.py:54 +#: apps/collection/models.py:74 msgid "Ordinary" msgstr "" -#: apps/collection/models.py:55 +#: apps/collection/models.py:75 msgid "Pop" msgstr "" -#: apps/collection/models.py:60 +#: apps/collection/models.py:82 msgid "Collection type" msgstr "" -#: apps/collection/models.py:62 apps/establishment/models.py:280 +#: apps/collection/models.py:84 apps/comment/models.py:76 +#: apps/establishment/models.py:573 msgid "Publish status" msgstr "" -#: apps/collection/models.py:64 +#: apps/collection/models.py:86 msgid "Position on top" msgstr "" -#: apps/collection/models.py:66 apps/location/models.py:40 -#: apps/location/models.py:60 apps/main/models.py:226 apps/news/models.py:135 +#: apps/collection/models.py:88 apps/location/models.py:107 +#: apps/location/models.py:155 apps/location/models.py:283 +#: apps/main/models.py:222 apps/main/models.py:269 apps/news/models.py:319 +#: apps/notification/models.py:24 msgid "country" msgstr "" -#: apps/collection/models.py:68 +#: apps/collection/models.py:90 msgid "collection block properties" msgstr "" -#: apps/collection/models.py:71 apps/establishment/models.py:245 -#: apps/establishment/models.py:507 apps/news/models.py:111 +#: apps/collection/models.py:93 apps/establishment/models.py:534 +#: apps/establishment/models.py:1219 apps/location/models.py:289 +#: apps/main/models.py:434 apps/news/models.py:295 apps/product/models.py:261 +#: apps/review/models.py:163 msgid "description" msgstr "" -#: apps/collection/models.py:74 +#: apps/collection/models.py:96 msgid "Collection slug" msgstr "" -#: apps/collection/models.py:81 +#: apps/collection/models.py:106 msgid "collections" msgstr "" -#: apps/collection/models.py:99 -msgid "parent" +#: apps/collection/models.py:297 +msgid "Restaurant" msgstr "" -#: apps/collection/models.py:103 -msgid "advertorials" +#: apps/collection/models.py:298 +msgid "Artisan" msgstr "" -#: apps/collection/models.py:112 +#: apps/collection/models.py:299 +msgid "Wine" +msgstr "" + +#: apps/collection/models.py:304 +msgid "guide type" +msgstr "" + +#: apps/collection/models.py:310 +msgid "guide vintage year" +msgstr "" + +#: apps/collection/models.py:312 +msgid "slug" +msgstr "" + +#: apps/collection/models.py:315 apps/news/models.py:336 +msgid "site settings" +msgstr "" + +#: apps/collection/models.py:320 +msgid "* after rebuild guide, refresh count of related guide elements" +msgstr "" + +#: apps/collection/models.py:321 +msgid "count of related guide elements during initialization" +msgstr "" + +#: apps/collection/models.py:328 apps/collection/models.py:500 msgid "guide" msgstr "" -#: apps/collection/models.py:113 +#: apps/collection/models.py:329 msgid "guides" msgstr "" -#: apps/comment/apps.py:7 +#: apps/collection/models.py:450 +msgid "number of pages" +msgstr "" + +#: apps/collection/models.py:451 +msgid "the total number of reserved pages" +msgstr "" + +#: apps/collection/models.py:453 +msgid "number of right pages" +msgstr "" + +#: apps/collection/models.py:454 +msgid "the number of right pages (which are part of total number)." +msgstr "" + +#: apps/collection/models.py:457 apps/collection/models.py:1011 +msgid "guide element" +msgstr "" + +#: apps/collection/models.py:464 +msgid "advertorial" +msgstr "" + +#: apps/collection/models.py:465 +msgid "advertorials" +msgstr "" + +#: apps/collection/models.py:486 +msgid "with mark" +msgstr "" + +#: apps/collection/models.py:487 +msgid "exclude empty marks?" +msgstr "" + +#: apps/collection/models.py:490 +msgid "max mark" +msgstr "" + +#: apps/collection/models.py:492 +msgid "mark under" +msgstr "" + +#: apps/collection/models.py:493 +msgid "min mark" +msgstr "" + +#: apps/collection/models.py:495 +msgid "mark over" +msgstr "" + +#: apps/collection/models.py:507 +msgid "guide filter" +msgstr "" + +#: apps/collection/models.py:508 +msgid "guide filters" +msgstr "" + +#: apps/collection/models.py:731 apps/collection/models.py:980 +msgid "guide element type" +msgstr "" + +#: apps/collection/models.py:732 +msgid "guide element types" +msgstr "" + +#: apps/collection/models.py:746 apps/collection/models.py:781 +msgid "section name" +msgstr "" + +#: apps/collection/models.py:752 +msgid "guide wine color section" +msgstr "" + +#: apps/collection/models.py:753 +msgid "guide wine color sections" +msgstr "" + +#: apps/collection/models.py:764 +msgid "category name" +msgstr "" + +#: apps/collection/models.py:770 +msgid "guide element section category" +msgstr "" + +#: apps/collection/models.py:771 +msgid "guide element section categories" +msgstr "" + +#: apps/collection/models.py:783 apps/establishment/admin.py:121 +#: apps/establishment/models.py:1253 apps/establishment/models.py:1318 +msgid "category" +msgstr "" + +#: apps/collection/models.py:791 +msgid "guide element section" +msgstr "" + +#: apps/collection/models.py:792 +msgid "guide element sections" +msgstr "" + +#: apps/collection/models.py:1003 +msgid "label photo" +msgstr "" + +#: apps/collection/models.py:1012 +msgid "guide elements" +msgstr "" + +#: apps/collection/views/back.py:168 apps/collection/views/back.py:184 +#: apps/collection/views/back.py:200 apps/main/views/back.py:174 +#: apps/main/views/back.py:190 +msgid "The file will be sent to your email." +msgstr "" + +#: apps/comment/apps.py:7 apps/review/models.py:132 msgid "comment" msgstr "" @@ -296,23 +615,41 @@ msgstr "" msgid "comments" msgstr "" -#: apps/comment/models.py:32 +#: apps/comment/models.py:64 apps/product/models.py:253 +#: apps/recipe/models.py:42 +msgid "Waiting" +msgstr "" + +#: apps/comment/models.py:65 apps/main/models.py:158 apps/product/models.py:251 +#: apps/recipe/models.py:44 +msgid "Published" +msgstr "Опубликовано" + +#: apps/comment/models.py:66 +msgid "Rejected" +msgstr "" + +#: apps/comment/models.py:67 +msgid "Deleted" +msgstr "" + +#: apps/comment/models.py:70 msgid "Comment text" msgstr "" -#: apps/comment/models.py:34 +#: apps/comment/models.py:73 msgid "Mark" msgstr "" -#: apps/comment/models.py:44 -msgid "Locale" +#: apps/comment/models.py:79 +msgid "status" msgstr "" -#: apps/comment/models.py:48 +#: apps/comment/models.py:90 msgid "Comment" msgstr "" -#: apps/comment/models.py:49 +#: apps/comment/models.py:91 msgid "Comments" msgstr "" @@ -324,253 +661,510 @@ msgstr "" msgid "default language" msgstr "" -#: apps/establishment/admin.py:87 apps/establishment/models.py:529 -#: apps/main/models.py:248 -msgid "category" +#: apps/establishment/filters.py:125 apps/news/filters.py:55 +msgid "Type at least 3 characters to search please." msgstr "" -#: apps/establishment/apps.py:8 apps/establishment/models.py:310 -#: apps/establishment/models.py:418 -msgid "Establishment" -msgstr "" - -#: apps/establishment/models.py:30 apps/establishment/models.py:54 -#: apps/establishment/models.py:391 apps/recipe/models.py:52 +#: apps/establishment/models.py:49 apps/establishment/models.py:90 +#: apps/establishment/models.py:937 apps/recipe/models.py:53 msgid "Description" msgstr "" -#: apps/establishment/models.py:32 +#: apps/establishment/models.py:52 apps/establishment/models.py:93 +#: apps/establishment/models.py:532 apps/establishment/models.py:943 +#: apps/notification/models.py:18 apps/product/models.py:51 +#: apps/product/models.py:89 +msgid "Index name" +msgstr "" + +#: apps/establishment/models.py:53 apps/product/models.py:52 msgid "Use subtypes" msgstr "" -#: apps/establishment/models.py:37 -msgid "Establishment type" -msgstr "" - -#: apps/establishment/models.py:38 -msgid "Establishment types" -msgstr "" - -#: apps/establishment/models.py:58 -msgid "Type" -msgstr "" - -#: apps/establishment/models.py:65 -msgid "Establishment subtype" +#: apps/establishment/models.py:57 apps/establishment/models.py:100 +#: apps/product/models.py:56 apps/tag/models.py:176 +msgid "Tag categories" msgstr "" #: apps/establishment/models.py:66 +msgid "Establishment type" +msgstr "" + +#: apps/establishment/models.py:67 +msgid "Establishment types" +msgstr "" + +#: apps/establishment/models.py:96 apps/product/models.py:266 +msgid "Type" +msgstr "" + +#: apps/establishment/models.py:112 msgid "Establishment subtypes" msgstr "" -#: apps/establishment/models.py:70 +#: apps/establishment/models.py:116 msgid "Establishment type is not use subtypes." msgstr "" -#: apps/establishment/models.py:242 +#: apps/establishment/models.py:529 msgid "Transliterated name" msgstr "" -#: apps/establishment/models.py:249 +#: apps/establishment/models.py:531 +msgid "Western name" +msgstr "" + +#: apps/establishment/models.py:538 apps/product/models.py:275 msgid "public mark" msgstr "" -#: apps/establishment/models.py:252 +#: apps/establishment/models.py:542 msgid "toque number" msgstr "" -#: apps/establishment/models.py:256 +#: apps/establishment/models.py:546 apps/establishment/models.py:1339 msgid "type" msgstr "" -#: apps/establishment/models.py:259 +#: apps/establishment/models.py:550 msgid "subtype" msgstr "" -#: apps/establishment/models.py:262 apps/news/models.py:131 +#: apps/establishment/models.py:553 apps/establishment/models.py:1478 +#: apps/news/models.py:34 apps/news/models.py:315 msgid "address" msgstr "" -#: apps/establishment/models.py:265 +#: apps/establishment/models.py:556 msgid "price level" msgstr "" -#: apps/establishment/models.py:267 +#: apps/establishment/models.py:558 msgid "Web site URL" msgstr "" -#: apps/establishment/models.py:269 +#: apps/establishment/models.py:560 msgid "Facebook URL" msgstr "" -#: apps/establishment/models.py:271 +#: apps/establishment/models.py:562 msgid "Twitter URL" msgstr "" -#: apps/establishment/models.py:273 +#: apps/establishment/models.py:564 +msgid "Instagram URL" +msgstr "" + +#: apps/establishment/models.py:566 msgid "Lafourchette URL" msgstr "" -#: apps/establishment/models.py:274 +#: apps/establishment/models.py:567 msgid "guestonline id" msgstr "" -#: apps/establishment/models.py:276 +#: apps/establishment/models.py:569 msgid "lastable id" msgstr "" -#: apps/establishment/models.py:279 +#: apps/establishment/models.py:572 msgid "Booking URL" msgstr "" -#: apps/establishment/models.py:282 +#: apps/establishment/models.py:575 msgid "Establishment schedule" msgstr "" -#: apps/establishment/models.py:289 +#: apps/establishment/models.py:578 msgid "Transportation" msgstr "" -#: apps/establishment/models.py:293 +#: apps/establishment/models.py:582 msgid "Collections" msgstr "" -#: apps/establishment/models.py:294 apps/news/models.py:128 +#: apps/establishment/models.py:584 msgid "Preview image URL path" msgstr "" -#: apps/establishment/models.py:297 +#: apps/establishment/models.py:587 msgid "Establishment slug" msgstr "" -#: apps/establishment/models.py:311 -msgid "Establishments" +#: apps/establishment/models.py:592 apps/product/models.py:296 +#: apps/product/models.py:591 apps/tag/models.py:65 +msgid "Tag" msgstr "" -#: apps/establishment/models.py:399 apps/establishment/models.py:425 -msgid "Position" -msgstr "" - -#: apps/establishment/models.py:400 -msgid "Positions" -msgstr "" - -#: apps/establishment/models.py:420 apps/establishment/models.py:445 -msgid "Employee" -msgstr "" - -#: apps/establishment/models.py:421 -msgid "From date" -msgstr "" - -#: apps/establishment/models.py:423 -msgid "To date" -msgstr "" - -#: apps/establishment/models.py:436 -msgid "Last name" -msgstr "" - -#: apps/establishment/models.py:446 -msgid "Employees" -msgstr "" - -#: apps/establishment/models.py:460 -msgid "contact phone" -msgstr "" - -#: apps/establishment/models.py:461 -msgid "contact phones" -msgstr "" - -#: apps/establishment/models.py:474 -msgid "contact email" -msgstr "" - -#: apps/establishment/models.py:475 -msgid "contact emails" -msgstr "" - -#: apps/establishment/models.py:510 -msgid "price" -msgstr "" - -#: apps/establishment/models.py:511 -msgid "is signature plate" -msgstr "" - -#: apps/establishment/models.py:513 apps/main/models.py:281 +#: apps/establishment/models.py:599 apps/establishment/models.py:1225 +#: apps/main/models.py:39 msgid "currency" msgstr "" -#: apps/establishment/models.py:516 apps/establishment/models.py:536 -#: apps/establishment/models.py:537 -msgid "menu" +#: apps/establishment/models.py:603 apps/product/models.py:483 +msgid "purchased plaques" msgstr "" -#: apps/establishment/models.py:519 -msgid "plate" +#: apps/establishment/models.py:604 +msgid "" +"Attribute from legacy db.\n" +"Must be deleted after the implementation of the market." msgstr "" -#: apps/establishment/models.py:520 -msgid "plates" +#: apps/establishment/models.py:614 apps/partner/models.py:40 +msgid "Establishments" msgstr "" -#: apps/establishment/models.py:532 apps/establishment/models.py:542 +#: apps/establishment/models.py:898 apps/product/models.py:609 +#: apps/review/models.py:69 +msgid "text" +msgstr "" + +#: apps/establishment/models.py:901 apps/establishment/models.py:919 +#: apps/establishment/models.py:1298 apps/establishment/models.py:1361 +#: apps/establishment/models.py:1454 apps/product/models.py:273 +#: apps/product/models.py:472 msgid "establishment" msgstr "" -#: apps/establishment/models.py:544 apps/main/models.py:207 -#: apps/news/models.py:105 +#: apps/establishment/models.py:905 apps/product/models.py:616 +#: apps/review/models.py:103 apps/review/models.py:138 +msgid "author" +msgstr "" + +#: apps/establishment/models.py:911 +msgid "establishment notes" +msgstr "" + +#: apps/establishment/models.py:912 +msgid "establishment note" +msgstr "" + +#: apps/establishment/models.py:923 apps/product/models.py:545 +#: apps/review/models.py:188 +msgid "image" +msgstr "" + +#: apps/establishment/models.py:927 +msgid "establishment gallery" +msgstr "" + +#: apps/establishment/models.py:928 +msgid "establishment galleries" +msgstr "" + +#: apps/establishment/models.py:948 apps/establishment/models.py:985 +msgid "Position" +msgstr "" + +#: apps/establishment/models.py:949 +msgid "Positions" +msgstr "" + +#: apps/establishment/models.py:979 apps/establishment/models.py:1142 +msgid "Employee" +msgstr "" + +#: apps/establishment/models.py:980 +msgid "From date" +msgstr "" + +#: apps/establishment/models.py:983 +msgid "To date" +msgstr "" + +#: apps/establishment/models.py:990 apps/establishment/models.py:1130 +msgid "Old id" +msgstr "" + +#: apps/establishment/models.py:1104 +msgid "Last Name" +msgstr "" + +#: apps/establishment/models.py:1112 +msgid "Male" +msgstr "" + +#: apps/establishment/models.py:1113 +msgid "Female" +msgstr "" + +#: apps/establishment/models.py:1115 +msgid "Sex" +msgstr "" + +#: apps/establishment/models.py:1117 +msgid "Birth date" +msgstr "" + +#: apps/establishment/models.py:1119 apps/notification/models.py:106 +msgid "Email" +msgstr "" + +#: apps/establishment/models.py:1121 +msgid "Toque number" +msgstr "" + +#: apps/establishment/models.py:1128 apps/news/models.py:321 +#: apps/tag/models.py:66 +msgid "Tags" +msgstr "" + +#: apps/establishment/models.py:1131 +msgid "Available for events" +msgstr "" + +#: apps/establishment/models.py:1135 apps/location/models.py:170 +msgid "image instance of model Image" +msgstr "" + +#: apps/establishment/models.py:1143 +msgid "Employees" +msgstr "" + +#: apps/establishment/models.py:1190 +msgid "contact phone" +msgstr "" + +#: apps/establishment/models.py:1191 apps/establishment/models.py:1458 +msgid "contact phones" +msgstr "" + +#: apps/establishment/models.py:1204 +msgid "contact email" +msgstr "" + +#: apps/establishment/models.py:1205 +msgid "contact emails" +msgstr "" + +#: apps/establishment/models.py:1222 apps/establishment/models.py:1255 +#: apps/establishment/models.py:1301 apps/review/models.py:141 +msgid "price" +msgstr "" + +#: apps/establishment/models.py:1223 +msgid "is signature plate" +msgstr "" + +#: apps/establishment/models.py:1228 +msgid "currency code" +msgstr "" + +#: apps/establishment/models.py:1232 apps/establishment/models.py:1326 +#: apps/establishment/models.py:1327 +msgid "menu" +msgstr "" + +#: apps/establishment/models.py:1242 +msgid "plate" +msgstr "" + +#: apps/establishment/models.py:1243 +msgid "plates" +msgstr "" + +#: apps/establishment/models.py:1256 +msgid "signature" +msgstr "" + +#: apps/establishment/models.py:1259 +msgid "dish" +msgstr "" + +#: apps/establishment/models.py:1260 +msgid "dishes" +msgstr "" + +#: apps/establishment/models.py:1300 +msgid "is drinks included" +msgstr "" + +#: apps/establishment/models.py:1305 +msgid "Menu schedule" +msgstr "" + +#: apps/establishment/models.py:1310 +msgid "Menu files" +msgstr "" + +#: apps/establishment/models.py:1315 +msgid "Menu dishes" +msgstr "" + +#: apps/establishment/models.py:1342 +msgid "menu upload" +msgstr "" + +#: apps/establishment/models.py:1343 +msgid "menu uploads" +msgstr "" + +#: apps/establishment/models.py:1347 apps/gallery/models.py:29 +#: apps/main/models.py:196 apps/main/models.py:402 apps/news/models.py:47 +#: apps/news/models.py:287 msgid "title" msgstr "" -#: apps/establishment/models.py:545 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2234 -msgid "URL" +#: apps/establishment/models.py:1350 +msgid "social choice" msgstr "" -#: apps/establishment/models.py:548 +#: apps/establishment/models.py:1351 +msgid "social choices" +msgstr "" + +#: apps/establishment/models.py:1367 apps/establishment/models.py:1374 msgid "social network" msgstr "" -#: apps/establishment/models.py:549 +#: apps/establishment/models.py:1371 +msgid "URL" +msgstr "" + +#: apps/establishment/models.py:1375 msgid "social networks" msgstr "" -#: apps/establishment/serializers/common.py:237 -#: apps/timetable/serialziers.py:47 +#: apps/establishment/models.py:1416 +msgid "One" +msgstr "" + +#: apps/establishment/models.py:1417 +msgid "Two" +msgstr "" + +#: apps/establishment/models.py:1418 +msgid "Three" +msgstr "" + +#: apps/establishment/models.py:1419 +msgid "Four" +msgstr "" + +#: apps/establishment/models.py:1420 +msgid "Five" +msgstr "" + +#: apps/establishment/models.py:1435 apps/establishment/models.py:1436 +msgid "Rating strategy" +msgstr "" + +#: apps/establishment/models.py:1461 +msgid "fax numbers" +msgstr "" + +#: apps/establishment/models.py:1464 +msgid "legal entity" +msgstr "" + +#: apps/establishment/models.py:1467 +msgid "registry number" +msgstr "" + +#: apps/establishment/models.py:1470 +msgid "VAT identification number" +msgstr "" + +#: apps/establishment/models.py:1474 +msgid "sic code" +msgstr "" + +#: apps/establishment/models.py:1484 +msgid "company" +msgstr "" + +#: apps/establishment/models.py:1485 +msgid "companies" +msgstr "" + +#: apps/establishment/serializers/back.py:473 +msgid "Establishment not found" +msgstr "" + +#: apps/establishment/serializers/back.py:476 apps/news/serializers.py:431 +#: apps/product/serializers/back.py:42 +msgid "Image not found" +msgstr "" + +#: apps/establishment/serializers/back.py:482 +#: apps/product/serializers/back.py:48 +msgid "Image is already added." +msgstr "" + +#: apps/establishment/serializers/common.py:270 +#, python-brace-format +msgid "{entity_class.__name__} not found." +msgstr "" + +#: apps/establishment/serializers/common.py:605 +#: apps/timetable/serialziers.py:61 msgid "Establishment not found." msgstr "" -#: apps/establishment/serializers/common.py:288 +#: apps/establishment/serializers/common.py:644 +#: apps/establishment/serializers/common.py:672 apps/news/serializers.py:455 +#: apps/news/serializers.py:481 apps/product/serializers/common.py:183 msgid "Object not found." msgstr "" +#: apps/establishment/serializers/common.py:733 +msgid "Fax is already reserved." +msgstr "" + +#: apps/establishment/serializers/common.py:737 +msgid "Phones is already reserved." +msgstr "" + #: apps/favorites/apps.py:7 apps/favorites/models.py:32 #: apps/favorites/models.py:33 msgid "Favorites" msgstr "" -#: apps/gallery/apps.py:7 +#: apps/gallery/apps.py:7 apps/news/models.py:495 msgid "gallery" msgstr "" -#: apps/gallery/models.py:12 -msgid "Image file" +#: apps/gallery/models.py:20 +msgid "Horizontal" msgstr "" -#: apps/gallery/models.py:16 apps/utils/models.py:147 apps/utils/models.py:176 -#: apps/utils/models.py:207 -#: venv/lib/python3.6/site-packages/django/db/models/fields/files.py:360 +#: apps/gallery/models.py:21 +msgid "Vertical" +msgstr "" + +#: apps/gallery/models.py:25 +msgid "image file" +msgstr "" + +#: apps/gallery/models.py:28 +msgid "image orientation" +msgstr "" + +#: apps/gallery/models.py:35 apps/utils/models.py:198 apps/utils/models.py:227 +#: apps/utils/models.py:236 apps/utils/models.py:277 apps/utils/models.py:312 msgid "Image" msgstr "" -#: apps/gallery/models.py:17 +#: apps/gallery/models.py:36 msgid "Images" msgstr "" -#: apps/location/admin.py:28 apps/main/apps.py:8 apps/main/models.py:191 +#: apps/gallery/serializers.py:43 +#, python-format +msgid "File size too large: %s bytes" +msgstr "" + +#: apps/gallery/serializers.py:92 +#, python-format +msgid "Unrecognized crop option: %s" +msgstr "" + +#: apps/gallery/serializers.py:139 +msgid "Image not found." +msgstr "" + +#: apps/location/admin.py:28 apps/main/apps.py:8 apps/main/models.py:161 msgid "Main" msgstr "" @@ -586,296 +1180,785 @@ msgstr "" msgid "location" msgstr "" -#: apps/location/models.py:19 +#: apps/location/models.py:42 msgid "Code" msgstr "" -#: apps/location/models.py:20 +#: apps/location/models.py:43 msgid "Low price" msgstr "" -#: apps/location/models.py:21 +#: apps/location/models.py:44 msgid "High price" msgstr "" -#: apps/location/models.py:22 apps/translation/models.py:34 +#: apps/location/models.py:45 apps/translation/models.py:43 msgid "Languages" msgstr "" -#: apps/location/models.py:27 +#: apps/location/models.py:46 apps/translation/models.py:35 +msgid "is active" +msgstr "" + +#: apps/location/models.py:56 msgid "Countries" msgstr "" -#: apps/location/models.py:35 apps/location/models.py:56 +#: apps/location/models.py:102 apps/location/models.py:150 msgid "code" msgstr "" -#: apps/location/models.py:37 apps/location/models.py:58 +#: apps/location/models.py:104 apps/location/models.py:153 msgid "parent region" msgstr "" -#: apps/location/models.py:45 +#: apps/location/models.py:116 msgid "regions" msgstr "" -#: apps/location/models.py:46 +#: apps/location/models.py:117 msgid "region" msgstr "" -#: apps/location/models.py:63 apps/location/models.py:85 +#: apps/location/models.py:149 +msgid "City name json" +msgstr "" + +#: apps/location/models.py:158 apps/location/models.py:222 msgid "postal code" msgstr "" -#: apps/location/models.py:63 apps/location/models.py:86 +#: apps/location/models.py:158 apps/location/models.py:223 msgid "Ex.: 350018" msgstr "" -#: apps/location/models.py:65 +#: apps/location/models.py:160 msgid "is island" msgstr "" -#: apps/location/models.py:68 +#: apps/location/models.py:177 msgid "cities" msgstr "" -#: apps/location/models.py:69 apps/location/models.py:78 +#: apps/location/models.py:178 apps/location/models.py:215 msgid "city" msgstr "" -#: apps/location/models.py:80 +#: apps/location/models.py:217 msgid "street name 1" msgstr "" -#: apps/location/models.py:82 +#: apps/location/models.py:219 msgid "street name 2" msgstr "" -#: apps/location/models.py:83 +#: apps/location/models.py:220 msgid "number" msgstr "" -#: apps/location/models.py:88 +#: apps/location/models.py:225 apps/location/models.py:285 +#: apps/product/models.py:525 msgid "Coordinates" msgstr "" -#: apps/location/models.py:93 apps/location/models.py:94 +#: apps/location/models.py:227 +msgid "District name" +msgstr "" + +#: apps/location/models.py:233 apps/location/models.py:234 msgid "Address" msgstr "" -#: apps/location/serializers/common.py:120 -#: apps/location/serializers/common.py:125 +#: apps/location/models.py:299 +msgid "wine regions" +msgstr "" + +#: apps/location/models.py:300 apps/location/models.py:343 +#: apps/location/models.py:362 +msgid "wine region" +msgstr "" + +#: apps/location/models.py:316 apps/location/models.py:325 +#: apps/location/models.py:365 +msgid "wine sub region" +msgstr "" + +#: apps/location/models.py:324 +msgid "wine sub regions" +msgstr "" + +#: apps/location/models.py:351 apps/product/models.py:285 +msgid "wine village" +msgstr "" + +#: apps/location/models.py:352 +msgid "wine villages" +msgstr "" + +#: apps/location/models.py:380 apps/location/models.py:398 +#: apps/product/models.py:541 apps/product/models.py:612 +msgid "product" +msgstr "" + +#: apps/location/models.py:386 +msgid "establishment wine origin address" +msgstr "" + +#: apps/location/models.py:387 +msgid "establishment wine origin addresses" +msgstr "" + +#: apps/location/models.py:404 +msgid "wine origin address" +msgstr "" + +#: apps/location/models.py:405 +msgid "wine origin addresses" +msgstr "" + +#: apps/location/serializers/common.py:184 +#: apps/location/serializers/common.py:189 msgid "Invalid value" msgstr "" -#: apps/main/models.py:114 -msgid "Subdomain" +#: apps/main/models.py:34 +msgid "sign" msgstr "" -#: apps/main/models.py:119 -msgid "Default site" -msgstr "" - -#: apps/main/models.py:121 -msgid "Pinterest page URL" -msgstr "" - -#: apps/main/models.py:123 -msgid "Twitter page URL" -msgstr "" - -#: apps/main/models.py:125 -msgid "Facebook page URL" -msgstr "" - -#: apps/main/models.py:127 -msgid "Instagram page URL" -msgstr "" - -#: apps/main/models.py:129 -msgid "Contact email" -msgstr "" - -#: apps/main/models.py:131 -msgid "Config" -msgstr "" - -#: apps/main/models.py:133 -msgid "AD config" -msgstr "" - -#: apps/main/models.py:140 -msgid "Site setting" -msgstr "" - -#: apps/main/models.py:141 -msgid "Site settings" -msgstr "" - -#: apps/main/models.py:171 -msgid "Feature" -msgstr "" - -#: apps/main/models.py:172 -msgid "Features" -msgstr "" - -#: apps/main/models.py:190 apps/news/models.py:98 apps/recipe/models.py:42 -msgid "Published" -msgstr "" - -#: apps/main/models.py:198 -msgid "Site feature" -msgstr "" - -#: apps/main/models.py:199 -msgid "Site features" -msgstr "" - -#: apps/main/models.py:209 -msgid "vintage year" -msgstr "" - -#: apps/main/models.py:245 -msgid "label" -msgstr "" - -#: apps/main/models.py:251 apps/main/models.py:252 -msgid "metadata" -msgstr "" - -#: apps/main/models.py:282 +#: apps/main/models.py:40 msgid "currencies" msgstr "" -#: apps/main/models.py:302 apps/main/models.py:303 -msgid "Carousel" +#: apps/main/models.py:58 +msgid "Subdomain" msgstr "" -#: apps/main/models.py:365 apps/translation/models.py:49 -msgid "Page" +#: apps/main/models.py:63 +msgid "Default site" msgstr "" -#: apps/main/models.py:366 -msgid "Pages" +#: apps/main/models.py:65 +msgid "Pinterest page URL" msgstr "" -#: apps/news/apps.py:7 apps/news/models.py:145 apps/news/models.py:146 -msgid "news" +#: apps/main/models.py:67 +msgid "Twitter page URL" msgstr "" -#: apps/news/models.py:19 -msgid "news types" +#: apps/main/models.py:69 +msgid "Facebook page URL" msgstr "" -#: apps/news/models.py:20 apps/news/models.py:103 -msgid "news type" +#: apps/main/models.py:71 +msgid "Instagram page URL" msgstr "" -#: apps/news/models.py:96 apps/recipe/models.py:40 -msgid "Waiting" +#: apps/main/models.py:73 +msgid "Contact email" msgstr "" -#: apps/news/models.py:97 apps/recipe/models.py:41 -msgid "Hidden" +#: apps/main/models.py:75 +msgid "Config" msgstr "" -#: apps/news/models.py:99 apps/recipe/models.py:43 -msgid "Published exclusive" +#: apps/main/models.py:77 +msgid "AD config" msgstr "" -#: apps/news/models.py:108 -msgid "subtitle" +#: apps/main/models.py:87 +msgid "Site setting" msgstr "" -#: apps/news/models.py:113 -msgid "Start" +#: apps/main/models.py:128 +msgid "Feature" msgstr "" -#: apps/news/models.py:115 -msgid "End" +#: apps/main/models.py:129 +msgid "Features" msgstr "" -#: apps/news/models.py:117 -msgid "News slug" +#: apps/main/models.py:164 +msgid "backoffice" msgstr "" -#: apps/news/models.py:118 -msgid "playlist" +#: apps/main/models.py:173 +msgid "Site feature" msgstr "" -#: apps/news/models.py:120 apps/notification/models.py:89 -#: apps/recipe/models.py:55 +#: apps/main/models.py:174 +msgid "Site features" +msgstr "" + +#: apps/main/models.py:198 apps/product/models.py:300 +msgid "vintage year" +msgstr "" + +#: apps/main/models.py:205 apps/news/models.py:310 apps/recipe/models.py:55 msgid "State" msgstr "" -#: apps/news/models.py:122 +#: apps/main/models.py:257 +msgid "is international" +msgstr "" + +#: apps/main/models.py:259 +msgid "old title" +msgstr "" + +#: apps/main/models.py:260 +msgid "old link" +msgstr "" + +#: apps/main/models.py:261 +msgid "old attachment_suffix_url" +msgstr "" + +#: apps/main/models.py:262 +msgid "old description" +msgstr "" + +#: apps/main/models.py:263 +msgid "old link_title" +msgstr "" + +#: apps/main/models.py:271 +msgid "old active" +msgstr "" + +#: apps/main/models.py:272 +msgid "is parse" +msgstr "" + +#: apps/main/models.py:278 apps/main/models.py:279 +msgid "Carousel" +msgstr "" + +#: apps/main/models.py:361 +msgid "Block width" +msgstr "" + +#: apps/main/models.py:363 +msgid "Block height" +msgstr "" + +#: apps/main/models.py:369 +msgid "page" +msgstr "" + +#: apps/main/models.py:370 +msgid "pages" +msgstr "" + +#: apps/main/models.py:393 +msgid "page types" +msgstr "" + +#: apps/main/models.py:401 +msgid "link" +msgstr "" + +#: apps/main/models.py:407 +msgid "footer" +msgstr "" + +#: apps/main/models.py:410 +msgid "about_us" +msgstr "" + +#: apps/main/models.py:411 +msgid "copyright" +msgstr "" + +#: apps/main/models.py:412 +msgid "links" +msgstr "" + +#: apps/main/models.py:425 +msgid "table" +msgstr "" + +#: apps/main/models.py:426 +msgid "mailing" +msgstr "" + +#: apps/main/models.py:430 +msgid "display" +msgstr "" + +#: apps/main/models.py:435 +msgid "query" +msgstr "" + +#: apps/main/models.py:437 +msgid "user" +msgstr "" + +#: apps/main/models.py:448 +msgid "panel" +msgstr "" + +#: apps/main/models.py:449 +msgid "panels" +msgstr "" + +#: apps/main/models.py:557 +msgid "read" +msgstr "" + +#: apps/main/models.py:558 +msgid "write" +msgstr "" + +#: apps/main/models.py:562 +msgid "sections" +msgstr "" + +#: apps/main/models.py:568 +msgid "permission mode" +msgstr "" + +#: apps/main/models.py:572 +msgid "Navigation bar item permission" +msgstr "" + +#: apps/main/models.py:573 +msgid "Navigation bar item permissions" +msgstr "" + +#: apps/news/apps.py:7 apps/news/models.py:346 apps/news/models.py:347 +#: apps/news/models.py:491 +msgid "news" +msgstr "" + +#: apps/news/filters.py:17 apps/recipe/models.py:70 +msgid "Recipes" +msgstr "" + +#: apps/news/models.py:30 +msgid "Start datetime" +msgstr "" + +#: apps/news/models.py:32 +msgid "End datetime" +msgstr "" + +#: apps/news/models.py:37 +msgid "event name" +msgstr "" + +#: apps/news/models.py:40 +msgid "content" +msgstr "" + +#: apps/news/models.py:51 +msgid "Content URL path" +msgstr "" + +#: apps/news/models.py:78 +msgid "news types" +msgstr "" + +#: apps/news/models.py:79 apps/news/models.py:285 +msgid "news type" +msgstr "" + +#: apps/news/models.py:274 +msgid "remove" +msgstr "удалено" + +#: apps/news/models.py:275 +msgid "hidden" +msgstr "скрыто" + +#: apps/news/models.py:276 +msgid "published" +msgstr "опубликовано" + +#: apps/news/models.py:277 +msgid "not published" +msgstr "не опубликовано" + +#: apps/news/models.py:290 +msgid "Title for searching via BO" +msgstr "" + +#: apps/news/models.py:292 +msgid "subtitle" +msgstr "" + +#: apps/news/models.py:298 +msgid "Is description for certain locale active" +msgstr "" + +#: apps/news/models.py:300 +msgid "News publication date" +msgstr "" + +#: apps/news/models.py:301 +msgid "date since when news item is published" +msgstr "" + +#: apps/news/models.py:302 +msgid "News publication time" +msgstr "" + +#: apps/news/models.py:303 +msgid "time since when news item is published" +msgstr "" + +#: apps/news/models.py:305 +msgid "End" +msgstr "" + +#: apps/news/models.py:307 +msgid "Slugs for current news obj" +msgstr "" + +#: apps/news/models.py:312 msgid "Is highlighted" msgstr "" +#: apps/news/models.py:330 +msgid "agenda" +msgstr "" + +#: apps/news/models.py:334 +msgid "banner" +msgstr "" + +#: apps/news/models.py:338 +msgid "Duplication datetime" +msgstr "" + +#: apps/news/models.py:340 +msgid "Field to detect doubles" +msgstr "" + +#: apps/news/models.py:499 +msgid "news gallery" +msgstr "" + +#: apps/news/models.py:500 +msgid "news galleries" +msgstr "" + +#: apps/news/serializers.py:279 apps/news/serializers.py:311 +msgid "Slug should be unique" +msgstr "" + +#: apps/news/serializers.py:429 +msgid "News not found" +msgstr "" + #: apps/notification/apps.py:7 msgid "notification" msgstr "" -#: apps/notification/models.py:73 -msgid "Unusable" -msgstr "" - -#: apps/notification/models.py:74 -msgid "Usable" -msgstr "" - -#: apps/notification/models.py:81 -msgid "Email" -msgstr "" - -#: apps/notification/models.py:83 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1891 +#: apps/notification/models.py:107 msgid "IP address" msgstr "" -#: apps/notification/models.py:85 +#: apps/notification/models.py:108 msgid "Country code" msgstr "" -#: apps/notification/models.py:87 apps/translation/models.py:26 +#: apps/notification/models.py:109 apps/translation/models.py:31 msgid "Locale identifier" msgstr "" -#: apps/notification/models.py:91 +#: apps/notification/models.py:116 msgid "Token" msgstr "" -#: apps/notification/models.py:98 +#: apps/notification/models.py:127 msgid "Subscriber" msgstr "" -#: apps/notification/models.py:99 +#: apps/notification/models.py:128 msgid "Subscribers" msgstr "" -#: apps/notification/serializers/common.py:29 +#: apps/notification/models.py:190 +msgid "Last unsubscribe date" +msgstr "" + +#: apps/notification/models.py:195 +msgid "Old subscriber id" +msgstr "" + +#: apps/notification/models.py:196 +msgid "Old subscription type id" +msgstr "" + +#: apps/notification/models.py:207 +msgid "Subscribe" +msgstr "" + +#: apps/notification/models.py:208 +msgid "Subscribes" +msgstr "" + +#: apps/notification/serializers/common.py:65 msgid "Does not match user email" msgstr "" -#: apps/notification/serializers/common.py:32 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:53 +#: apps/notification/serializers/common.py:68 msgid "This field is required." msgstr "" -#: apps/partner/apps.py:7 apps/partner/models.py:12 +#: apps/notification/tasks.py:31 +msgid "You have subscribed on news G&M" +msgstr "Вы подписались на новости GM" + +#: apps/notification/tasks.py:32 +msgid "
" +msgstr "" + +#: apps/notification/tasks.py:46 +msgid "G&M Subscriptions" +msgstr "Подписки GM" + +#: apps/notification/tasks.py:73 +msgid "You have successfully unsubscribed from G&M news" +msgstr "Вы успешно отпписались от рассылки GM" + +#: apps/partner/apps.py:7 apps/partner/models.py:50 msgid "partner" msgstr "" -#: apps/partner/models.py:9 -msgid "Partner URL" +#: apps/partner/models.py:29 +msgid "Partner" msgstr "" -#: apps/partner/models.py:13 +#: apps/partner/models.py:30 +msgid "Sponsor" +msgstr "" + +#: apps/partner/models.py:36 apps/partner/models.py:65 +msgid "Partner image URL" +msgstr "" + +#: apps/partner/models.py:42 +msgid "starting date" +msgstr "" + +#: apps/partner/models.py:43 +msgid "expiry date" +msgstr "" + +#: apps/partner/models.py:44 +msgid "price per month" +msgstr "" + +#: apps/partner/models.py:51 msgid "partners" msgstr "" -#: apps/products/apps.py:8 -msgid "products" +#: apps/partner/models.py:63 +msgid "Date partner binded" +msgstr "" + +#: apps/partner/models.py:64 +msgid "Establishment to Partner URL" +msgstr "" + +#: apps/product/apps.py:7 apps/product/models.py:323 +msgid "Product" +msgstr "" + +#: apps/product/models.py:65 apps/product/models.py:85 +msgid "Product type" +msgstr "" + +#: apps/product/models.py:66 +msgid "Product types" +msgstr "" + +#: apps/product/models.py:98 +msgid "Product subtype" +msgstr "" + +#: apps/product/models.py:99 +msgid "Product subtypes" +msgstr "" + +#: apps/product/models.py:103 +msgid "Product type is not use subtypes." +msgstr "" + +#: apps/product/models.py:242 +msgid "Common" +msgstr "" + +#: apps/product/models.py:243 +msgid "Online" +msgstr "" + +#: apps/product/models.py:252 +msgid "Out_of_production" +msgstr "" + +#: apps/product/models.py:263 +msgid "available" +msgstr "" + +#: apps/product/models.py:269 +msgid "Subtypes" +msgstr "" + +#: apps/product/models.py:278 +msgid "classifications" +msgstr "" + +#: apps/product/models.py:281 +msgid "standards" +msgstr "" + +#: apps/product/models.py:282 apps/product/models.py:294 +#: apps/product/models.py:299 +msgid "attribute from legacy db" +msgstr "" + +#: apps/product/models.py:287 apps/recipe/models.py:62 +msgid "Slug" +msgstr "" + +#: apps/product/models.py:306 +msgid "average price" +msgstr "" + +#: apps/product/models.py:314 +msgid "Serial number" +msgstr "" + +#: apps/product/models.py:324 +msgid "Products" +msgstr "" + +#: apps/product/models.py:463 +msgid "Online product" +msgstr "" + +#: apps/product/models.py:464 +msgid "Online products" +msgstr "" + +#: apps/product/models.py:475 +msgid "plaque" +msgstr "" + +#: apps/product/models.py:477 +msgid "is gifted" +msgstr "" + +#: apps/product/models.py:478 +msgid "quantity" +msgstr "" + +#: apps/product/models.py:482 +msgid "purchased plaque" +msgstr "" + +#: apps/product/models.py:492 apps/review/models.py:162 +msgid "value" +msgstr "" + +#: apps/product/models.py:496 +msgid "unit" +msgstr "" + +#: apps/product/models.py:497 +msgid "units" +msgstr "" + +#: apps/product/models.py:516 +msgid "Appellation" +msgstr "" + +#: apps/product/models.py:517 +msgid "Wine quality" +msgstr "" + +#: apps/product/models.py:518 +msgid "Yard classification" +msgstr "" + +#: apps/product/models.py:523 +msgid "standard type" +msgstr "" + +#: apps/product/models.py:532 +msgid "wine standards" +msgstr "" + +#: apps/product/models.py:533 +msgid "wine standard" +msgstr "" + +#: apps/product/models.py:549 +msgid "product gallery" +msgstr "" + +#: apps/product/models.py:550 +msgid "product galleries" +msgstr "" + +#: apps/product/models.py:558 apps/product/models.py:586 +msgid "classification type" +msgstr "" + +#: apps/product/models.py:561 +msgid "product type" +msgstr "" + +#: apps/product/models.py:564 +msgid "product subtype" +msgstr "" + +#: apps/product/models.py:565 +msgid "" +"Legacy attribute - possible_type (product type).Product type in our case is " +"product subtype." +msgstr "" + +#: apps/product/models.py:570 +msgid "wine classification type" +msgstr "" + +#: apps/product/models.py:571 +msgid "wine classification types" +msgstr "" + +#: apps/product/models.py:589 +msgid "standard" +msgstr "" + +#: apps/product/models.py:598 +msgid "product classification" +msgstr "" + +#: apps/product/models.py:599 +msgid "product classifications" +msgstr "" + +#: apps/product/models.py:622 +msgid "product note" +msgstr "" + +#: apps/product/models.py:623 +msgid "product notes" +msgstr "" + +#: apps/product/serializers/back.py:39 +msgid "Product not found" +msgstr "" + +#: apps/product/serializers/back.py:110 +msgid "Tag category is already attached." +msgstr "" + +#: apps/product/serializers/common.py:229 +msgid "Product not found." msgstr "" #: apps/rating/models.py:11 @@ -886,183 +1969,331 @@ msgstr "" msgid "RecipeConfig" msgstr "" -#: apps/recipe/models.py:48 -msgid "Title" +#: apps/recipe/models.py:43 +msgid "Hidden" +msgstr "" + +#: apps/recipe/models.py:45 +msgid "Published exclusive" msgstr "" #: apps/recipe/models.py:50 +msgid "Title" +msgstr "" + +#: apps/recipe/models.py:51 msgid "Subtitle" msgstr "" -#: apps/recipe/models.py:57 +#: apps/recipe/models.py:56 msgid "Author" msgstr "" -#: apps/recipe/models.py:58 apps/recipe/models.py:60 +#: apps/recipe/models.py:57 apps/recipe/models.py:58 msgid "Published at" msgstr "" -#: apps/recipe/models.py:61 apps/recipe/models.py:63 +#: apps/recipe/models.py:59 apps/recipe/models.py:60 msgid "Published scheduled at" msgstr "" -#: apps/recipe/models.py:70 +#: apps/recipe/models.py:69 msgid "Recipe" msgstr "" -#: apps/recipe/models.py:71 -msgid "Recipes" -msgstr "" - #: apps/review/apps.py:7 msgid "reviews" msgstr "" -#: apps/review/models.py:37 +#: apps/review/models.py:42 msgid "To investigate" msgstr "" -#: apps/review/models.py:38 +#: apps/review/models.py:43 msgid "To review" msgstr "" -#: apps/review/models.py:39 +#: apps/review/models.py:44 msgid "Ready" msgstr "" -#: apps/review/models.py:45 +#: apps/review/models.py:50 msgid "Reviewer" msgstr "" -#: apps/review/models.py:47 -msgid "text" -msgstr "" - -#: apps/review/models.py:55 -msgid "Review language" -msgstr "" - -#: apps/review/models.py:60 +#: apps/review/models.py:66 msgid "Child review" msgstr "" -#: apps/review/models.py:61 +#: apps/review/models.py:81 msgid "Publish datetime" msgstr "" -#: apps/review/models.py:63 +#: apps/review/models.py:85 msgid "Review published datetime" msgstr "" -#: apps/review/models.py:64 +#: apps/review/models.py:87 msgid "Year of review" msgstr "" -#: apps/review/models.py:72 +#: apps/review/models.py:88 apps/review/models.py:134 +msgid "mark" +msgstr "" + +#: apps/review/models.py:89 +msgid "Priority" +msgstr "" + +#: apps/review/models.py:96 msgid "Review" msgstr "" -#: apps/review/models.py:73 +#: apps/review/models.py:97 msgid "Reviews" msgstr "" +#: apps/review/models.py:109 apps/review/models.py:131 +msgid "review" +msgstr "" + +#: apps/review/models.py:113 +msgid "locale" +msgstr "" + +#: apps/review/models.py:116 +msgid "Text author" +msgstr "" + +#: apps/review/models.py:117 +msgid "Text authors" +msgstr "" + +#: apps/review/models.py:126 +msgid "none" +msgstr "" + +#: apps/review/models.py:127 +msgid "diner" +msgstr "" + +#: apps/review/models.py:128 +msgid "lanch" +msgstr "" + +#: apps/review/models.py:133 +msgid "final comment" +msgstr "" + +#: apps/review/models.py:135 +msgid "attachment" +msgstr "" + +#: apps/review/models.py:139 +msgid "bill" +msgstr "" + +#: apps/review/models.py:147 +msgid "is published" +msgstr "" + +#: apps/review/models.py:150 +msgid "Inquiry" +msgstr "" + +#: apps/review/models.py:151 +msgid "Inquiries" +msgstr "" + +#: apps/review/models.py:159 apps/review/models.py:181 +msgid "inquiry" +msgstr "" + +#: apps/review/models.py:160 +msgid "sub name" +msgstr "" + +#: apps/review/models.py:164 +msgid "dish title" +msgstr "" + +#: apps/review/models.py:167 +msgid "inquiry grid" +msgstr "" + +#: apps/review/models.py:168 +msgid "inquiry grids" +msgstr "" + +#: apps/review/models.py:192 +msgid "inquiry gallery" +msgstr "" + +#: apps/review/models.py:193 +msgid "inquiry galleries" +msgstr "" + #: apps/search_indexes/apps.py:7 msgid "Search indexes" msgstr "" +#: apps/tag/apps.py:7 apps/tag/models.py:185 +msgid "tag" +msgstr "" + +#: apps/tag/models.py:36 apps/tag/models.py:153 +msgid "indexing name" +msgstr "" + +#: apps/tag/models.py:40 +msgid "Category" +msgstr "" + +#: apps/tag/models.py:47 +msgid "old id metadata product" +msgstr "" + +#: apps/tag/models.py:50 apps/tag/models.py:160 apps/translation/apps.py:7 +msgid "Translation" +msgstr "" + +#: apps/tag/models.py:90 +msgid "Chosen tag" +msgstr "" + +#: apps/tag/models.py:91 +msgid "Chosen tags" +msgstr "" + +#: apps/tag/models.py:141 +msgid "string" +msgstr "" + +#: apps/tag/models.py:142 +msgid "list" +msgstr "" + +#: apps/tag/models.py:143 +msgid "integer" +msgstr "" + +#: apps/tag/models.py:144 +msgid "float" +msgstr "" + +#: apps/tag/models.py:145 +msgid "percentage" +msgstr "" + +#: apps/tag/models.py:146 +msgid "boolean" +msgstr "" + +#: apps/tag/models.py:155 +msgid "value type" +msgstr "" + +#: apps/tag/models.py:175 +msgid "Tag category" +msgstr "" + +#: apps/tag/views.py:234 +msgid "Missing required \"items\" parameter" +msgstr "" + +#: apps/tag/views.py:242 +msgid "news/establishments/products" +msgstr "" + #: apps/timetable/apps.py:7 msgid "timetable" msgstr "" -#: apps/timetable/models.py:18 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:6 +#: apps/timetable/models.py:22 msgid "Monday" msgstr "" -#: apps/timetable/models.py:19 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:6 +#: apps/timetable/models.py:23 msgid "Tuesday" msgstr "" -#: apps/timetable/models.py:20 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:6 +#: apps/timetable/models.py:24 msgid "Wednesday" msgstr "" -#: apps/timetable/models.py:21 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:6 +#: apps/timetable/models.py:25 msgid "Thursday" msgstr "" -#: apps/timetable/models.py:22 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:6 +#: apps/timetable/models.py:26 msgid "Friday" msgstr "" -#: apps/timetable/models.py:23 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:7 +#: apps/timetable/models.py:27 msgid "Saturday" msgstr "" -#: apps/timetable/models.py:24 -#: venv/lib/python3.6/site-packages/django/utils/dates.py:7 +#: apps/timetable/models.py:28 msgid "Sunday" msgstr "" -#: apps/timetable/models.py:26 +#: apps/timetable/models.py:31 msgid "Week day" msgstr "" -#: apps/timetable/models.py:28 +#: apps/timetable/models.py:33 msgid "Lunch start time" msgstr "" -#: apps/timetable/models.py:29 +#: apps/timetable/models.py:34 msgid "Lunch end time" msgstr "" -#: apps/timetable/models.py:30 +#: apps/timetable/models.py:35 msgid "Dinner start time" msgstr "" -#: apps/timetable/models.py:31 +#: apps/timetable/models.py:36 msgid "Dinner end time" msgstr "" -#: apps/timetable/models.py:32 +#: apps/timetable/models.py:37 msgid "Opening time" msgstr "" -#: apps/timetable/models.py:33 +#: apps/timetable/models.py:38 msgid "Closed time" msgstr "" -#: apps/timetable/models.py:37 +#: apps/timetable/models.py:42 msgid "Timetable" msgstr "" -#: apps/timetable/models.py:38 +#: apps/timetable/models.py:43 msgid "Timetables" msgstr "" -#: apps/translation/apps.py:7 -msgid "Translation" +#: apps/transfer/apps.py:7 +msgid "Transfer" msgstr "" -#: apps/translation/models.py:24 +#: apps/translation/models.py:29 msgid "Language title" msgstr "" -#: apps/translation/models.py:33 +#: apps/translation/models.py:42 msgid "Language" msgstr "" -#: apps/translation/models.py:51 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2075 +#: apps/translation/models.py:97 +msgid "Page" +msgstr "" + +#: apps/translation/models.py:99 msgid "Text" msgstr "" -#: apps/translation/models.py:59 apps/translation/models.py:60 +#: apps/translation/models.py:107 apps/translation/models.py:108 msgid "Site interface dictionary" msgstr "" @@ -1124,1968 +2355,153 @@ msgstr "Неправильный логин или пароль." msgid "Item is already in favorites." msgstr "" -#: apps/utils/exceptions.py:144 +#: apps/utils/exceptions.py:143 +msgid "Item is already in carousels." +msgstr "" + +#: apps/utils/exceptions.py:152 msgid "Password reset request is already exists and valid." msgstr "" -#: apps/utils/models.py:21 +#: apps/utils/exceptions.py:161 +msgid "Object has already been added." +msgstr "" + +#: apps/utils/exceptions.py:167 +msgid "Binding object not found." +msgstr "" + +#: apps/utils/exceptions.py:173 +msgid "Removed binding object not found." +msgstr "" + +#: apps/utils/exceptions.py:181 +msgid "Unprocessable entity valid." +msgstr "" + +#: apps/utils/exceptions.py:190 +msgid "Guide element not valid for Guide." +msgstr "" + +#: apps/utils/exceptions.py:199 +msgid "Advertorial already exists for this guide element." +msgstr "" + +#: apps/utils/models.py:30 msgid "Date created" msgstr "" -#: apps/utils/models.py:23 +#: apps/utils/models.py:32 msgid "Date updated" msgstr "" -#: apps/utils/models.py:126 +#: apps/utils/models.py:150 msgid "created by" msgstr "" -#: apps/utils/models.py:130 +#: apps/utils/models.py:154 msgid "modified by" msgstr "" -#: apps/utils/models.py:186 +#: apps/utils/models.py:169 +msgid "File" +msgstr "" + +#: apps/utils/models.py:287 msgid "SVG image" msgstr "" -#: apps/utils/models.py:219 +#: apps/utils/models.py:324 msgid "Mobile" msgstr "" -#: apps/utils/models.py:220 +#: apps/utils/models.py:325 msgid "Web" msgstr "" -#: apps/utils/models.py:221 +#: apps/utils/models.py:326 msgid "All" msgstr "" -#: apps/utils/models.py:224 +#: apps/utils/models.py:329 msgid "Source" msgstr "" -#: project/templates/account/change_email.html:2 +#: apps/utils/models.py:456 +msgid "Is the main image" +msgstr "" + +#: project/templates/account/change_email.html:33 #, python-format msgid "" "You're receiving this email because you want to change email address at " "%(site_name)s." msgstr "" -#: project/templates/account/change_email.html:4 +#: project/templates/account/change_email.html:35 msgid "Please go to the following page for confirmation new email address:" msgstr "" -#: project/templates/account/change_email.html:8 -#: project/templates/account/password_reset_email.html:8 -#: project/templates/authorization/confirm_email.html:7 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_email.html:10 +#: project/templates/account/change_email.html:39 +#: project/templates/account/password_change_email.html:36 +#: project/templates/account/password_reset_email.html:39 +#: project/templates/authorization/confirm_email.html:42 msgid "Thanks for using our site!" msgstr "" -#: project/templates/account/change_email.html:10 -#: project/templates/account/password_reset_email.html:10 -#: project/templates/authorization/confirm_email.html:9 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_email.html:12 +#: project/templates/account/change_email.html:42 +#: project/templates/account/password_change_email.html:39 +#: project/templates/account/password_reset_email.html:41 +#: project/templates/authorization/confirm_email.html:44 #, python-format msgid "The %(site_name)s team" msgstr "" -#: project/templates/account/password_reset_email.html:2 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_email.html:2 +#: project/templates/account/password_change_email.html:34 +#, python-format +msgid "" +"You're receiving this email because your account's password address at " +"%(site_name)s." +msgstr "" + +#: project/templates/account/password_reset_email.html:33 #, python-format msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -#: project/templates/account/password_reset_email.html:4 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_email.html:4 +#: project/templates/account/password_reset_email.html:35 msgid "Please go to the following page and choose a new password:" msgstr "" -#: project/templates/authorization/confirm_email.html:2 +#: project/templates/authorization/confirm_email.html:34 #, python-format msgid "" "You're receiving this email because you trying to register new account at " "%(site_name)s." msgstr "" -#: project/templates/authorization/confirm_email.html:4 +#: project/templates/authorization/confirm_email.html:36 msgid "Please confirm your email address to complete the registration:" msgstr "" -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/404.html:4 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/404.html:8 -msgid "Page not found" +#: project/templates/authorization/confirm_email.html:39 +msgid "If you use the mobile app, enter the following code in the form:" msgstr "" -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/404.html:10 -msgid "We're sorry, but the requested page could not be found." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/500.html:6 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/app_index.html:10 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:19 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:163 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:22 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list.html:32 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:14 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_selected_confirmation.html:15 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/object_history.html:7 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_done.html:7 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:12 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_complete.html:7 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:7 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_done.html:7 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_form.html:7 -#: venv/lib/python3.6/site-packages/solo/templates/admin/solo/change_form.html:7 -#: venv/lib/python3.6/site-packages/solo/templates/admin/solo/object_history.html:6 -msgid "Home" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/500.html:7 -msgid "Server error" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/500.html:11 -msgid "Server error (500)" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/500.html:14 -msgid "Server Error (500)" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/500.html:15 -msgid "" -"There's been an error. It's been reported to the site administrators via " -"email and should be fixed shortly. Thanks for your patience." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/actions.html:7 -msgid "Run the selected action" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/actions.html:7 -msgid "Go" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/actions.html:19 -msgid "Click here to select the objects across all pages" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/actions.html:19 -#, python-format -msgid "Select all %(total_count)s %(module_name)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/actions.html:21 -msgid "Clear selection" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/add_form.html:7 -msgid "" -"First, enter a username and password. Then, you'll be able to edit more user " -"options." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/add_form.html:10 -msgid "Enter a username and password." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:31 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:75 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:143 -msgid "Change password" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:44 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:65 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list.html:58 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/login.html:44 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:31 -msgid "Please correct the error below." -msgid_plural "Please correct the errors below." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:44 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:65 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:31 -msgid "Please correct the errors below." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:50 -#, python-format -msgid "Enter a new password for the user %(username)s." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:60 -msgid "Password" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:68 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:57 -msgid "Password (again)" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/auth/user/change_password.html:69 -msgid "Enter the same password as above, for verification." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:72 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:116 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:119 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base_site.html:9 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/login.html:31 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/login.html:36 -msgid "Django administration" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:81 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:52 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:58 -#, python-format -msgid "Models in the %(name)s application" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:87 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:93 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:143 -msgid "You don't have permission to edit anything." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:138 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:152 -msgid "Documentation" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:147 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:154 -msgid "Log out" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base.html:173 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list.html:57 -msgid "Close" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/base_site.html:4 -msgid "Django site admin" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:39 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:65 -msgid "Add" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:114 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:116 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/object_history.html:18 -#: venv/lib/python3.6/site-packages/solo/templates/admin/solo/change_form.html:14 -#: venv/lib/python3.6/site-packages/solo/templates/admin/solo/object_history.html:9 -msgid "History" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:121 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_form.html:123 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/edit_inline/stacked.html:24 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/edit_inline/tabular.html:37 -#: venv/lib/python3.6/site-packages/solo/templates/admin/solo/change_form.html:15 -msgid "View on site" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list.html:47 -msgid "Filter" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list.html:81 -#, python-format -msgid "Add %(name)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list.html:143 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:5 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:7 -msgid "Save" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list_results.html:13 -msgid "Remove from sorting" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list_results.html:16 -#, python-format -msgid "Sorting priority: %(priority_number)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/change_list_results.html:17 -msgid "Toggle sorting" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:25 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:34 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:36 -#: venv/lib/python3.6/site-packages/django/forms/formsets.py:375 -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html:13 -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:38 -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/authorized-token-delete.html:7 -msgid "Delete" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:34 -#, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:48 -#, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " -"following protected related objects:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:62 -#, python-format -msgid "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:70 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_confirmation.html:72 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_selected_confirmation.html:74 -msgid "Yes, I'm sure" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_selected_confirmation.html:23 -msgid "Delete multiple objects" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_selected_confirmation.html:32 -#, python-format -msgid "" -"Deleting the selected %(objects_name)s would result in deleting related " -"objects, but your account doesn't have permission to delete the following " -"types of objects:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_selected_confirmation.html:46 -#, python-format -msgid "" -"Deleting the selected %(objects_name)s would require deleting the following " -"protected related objects:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/delete_selected_confirmation.html:60 -#, python-format -msgid "" -"Are you sure you want to delete the selected %(objects_name)s? All of the " -"following objects and their related items will be deleted:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/edit_inline/tabular.html:22 -msgid "Delete?" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/filter.html:2 -#, python-format -msgid " By %(filter_title)s " -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/includes/object_delete_summary.html:2 -msgid "Summary" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:18 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:21 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:22 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:31 -msgid "Apps" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:37 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:71 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/search_form.html:11 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/search_form.html:13 -msgid "Search" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:42 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/object_history.html:31 -msgid "Action" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:67 -msgid "View" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:69 -#: venv/lib/python3.6/site-packages/django/forms/widgets.py:397 -msgid "Change" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:84 -msgid "Recent actions" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:89 -msgid "None available" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/index.html:115 -msgid "Unknown content" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/invalid_setup.html:5 -msgid "" -"Something's wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/login.html:21 -#, python-format -msgid "" -"You are authenticated as %(username)s, but are not authorized to access this " -"page. Would you like to login to a different account?" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/login.html:85 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_complete.html:23 -msgid "Log in" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/login.html:96 -msgid "Forgotten your password or username?" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/object_history.html:29 -msgid "Date/time" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/object_history.html:45 -msgid "" -"This object doesn't have a change history. It probably wasn't added via this " -"admin site." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/pagination.html:19 -msgid "Show all" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/popup_response.html:3 -msgid "Popup closing..." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/related_widget_wrapper.html:9 -#, python-format -msgid "Change selected %(model)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/related_widget_wrapper.html:16 -#, python-format -msgid "Add another %(model)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/related_widget_wrapper.html:23 -#, python-format -msgid "Delete selected %(model)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/search_form.html:4 -#, python-format -msgid "%(counter)s result" -msgid_plural "%(counter)s results" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/search_form.html:4 -#, python-format -msgid "%(full_result_count)s total" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:12 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:14 -msgid "Save as new" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:19 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:21 -msgid "Save and add another" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:26 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/admin/submit_line.html:28 -msgid "Save and continue editing" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/logged_out.html:18 -msgid "Thanks for spending some quality time with the Web site today." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/logged_out.html:20 -msgid "Log in again" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_done.html:10 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:15 -msgid "Password change" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_done.html:20 -msgid "Your password was changed." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:37 -msgid "" -"Please enter your old password, for security's sake, and then enter your new " -"password twice so we can verify you typed it in correctly." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:43 -msgid "Old password" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_change_form.html:66 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:46 -msgid "Change my password" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_complete.html:10 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_done.html:10 -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_form.html:10 -msgid "Password reset" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_complete.html:20 -msgid "Your password has been set. You may go ahead and log in now." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:10 -msgid "Password reset confirmation" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:24 -msgid "" -"Please enter your new password twice so we can verify you typed it in " -"correctly." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:30 -msgid "New password:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:37 -msgid "Confirm password:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_confirm.html:55 -msgid "" -"The password reset link was invalid, possibly because it has already been " -"used. Please request a new password reset." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_done.html:19 -msgid "" -"We've emailed you instructions for setting your password. You should be " -"receiving them shortly." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_done.html:21 -msgid "" -"If you don't receive an email, please make sure you've entered the address " -"you registered with, and check your spam folder." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_email.html:8 -msgid "Your username, in case you've forgotten:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_form.html:23 -msgid "" -"Forgotten your password? Enter your email address below, and we'll email " -"instructions for setting a new one." -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_form.html:29 -msgid "Email address:" -msgstr "" - -#: venv/lib/python3.6/site-packages/bootstrap_admin/templates/registration/password_reset_form.html:38 -msgid "Reset my password" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/contrib/messages/apps.py:7 -msgid "Messages" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/contrib/sitemaps/apps.py:7 -msgid "Site Maps" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/contrib/staticfiles/apps.py:9 -msgid "Static Files" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/contrib/syndication/apps.py:7 -msgid "Syndication" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/paginator.py:45 -msgid "That page number is not an integer" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/paginator.py:47 -msgid "That page number is less than 1" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/paginator.py:52 -msgid "That page contains no results" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:31 -msgid "Enter a valid value." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:102 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:658 -msgid "Enter a valid URL." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:154 -msgid "Enter a valid integer." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:165 -msgid "Enter a valid email address." -msgstr "" - -#. Translators: "letters" means latin letters: a-z and A-Z. -#: venv/lib/python3.6/site-packages/django/core/validators.py:239 -msgid "" -"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:246 -msgid "" -"Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, or " -"hyphens." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:255 -#: venv/lib/python3.6/site-packages/django/core/validators.py:275 -msgid "Enter a valid IPv4 address." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:260 -#: venv/lib/python3.6/site-packages/django/core/validators.py:276 -msgid "Enter a valid IPv6 address." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:270 -#: venv/lib/python3.6/site-packages/django/core/validators.py:274 -msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:304 -msgid "Enter only digits separated by commas." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:310 -#, python-format -msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:342 -#, python-format -msgid "Ensure this value is less than or equal to %(limit_value)s." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:351 -#, python-format -msgid "Ensure this value is greater than or equal to %(limit_value)s." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:361 -#, python-format -msgid "" -"Ensure this value has at least %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at least %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:376 -#, python-format -msgid "" -"Ensure this value has at most %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at most %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:395 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:290 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:325 -msgid "Enter a number." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:397 -#, python-format -msgid "Ensure that there are no more than %(max)s digit in total." -msgid_plural "Ensure that there are no more than %(max)s digits in total." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:402 -#, python-format -msgid "Ensure that there are no more than %(max)s decimal place." -msgid_plural "Ensure that there are no more than %(max)s decimal places." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:407 -#, python-format -msgid "" -"Ensure that there are no more than %(max)s digit before the decimal point." -msgid_plural "" -"Ensure that there are no more than %(max)s digits before the decimal point." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:469 -#, python-format -msgid "" -"File extension '%(extension)s' is not allowed. Allowed extensions are: " -"'%(allowed_extensions)s'." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/core/validators.py:521 -msgid "Null characters are not allowed." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/base.py:1162 -#: venv/lib/python3.6/site-packages/django/forms/models.py:756 -msgid "and" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/base.py:1164 -#, python-format -msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:104 -#, python-format -msgid "Value %(value)r is not a valid choice." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:105 -msgid "This field cannot be null." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:106 -msgid "This field cannot be blank." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:107 -#, python-format -msgid "%(model_name)s with this %(field_label)s already exists." -msgstr "" - -#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. -#. Eg: "Title must be unique for pub_date year" -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:111 -#, python-format -msgid "" -"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:128 -#, python-format -msgid "Field of type: %(field_type)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:899 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1766 -msgid "Integer" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:903 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1764 -#, python-format -msgid "'%(value)s' value must be an integer." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:978 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1844 -msgid "Big (8 byte) integer" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:990 -#, python-format -msgid "'%(value)s' value must be either True or False." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:991 -#, python-format -msgid "'%(value)s' value must be either True, False, or None." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:993 -msgid "Boolean (Either True or False)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1034 -#, python-format -msgid "String (up to %(max_length)s)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1098 -msgid "Comma-separated integers" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1147 -#, python-format -msgid "" -"'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " -"format." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1149 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1292 -#, python-format -msgid "" -"'%(value)s' value has the correct format (YYYY-MM-DD) but it is an invalid " -"date." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1152 -msgid "Date (without time)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1290 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][TZ] format." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1294 -#, python-format -msgid "" -"'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" -"[TZ]) but it is an invalid date/time." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1298 -msgid "Date (with time)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1446 -#, python-format -msgid "'%(value)s' value must be a decimal number." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1448 -msgid "Decimal number" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1587 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." -"uuuuuu] format." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1590 -msgid "Duration" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1640 -msgid "Email address" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1663 -msgid "File path" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1729 -#, python-format -msgid "'%(value)s' value must be a float." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1731 -msgid "Floating point number" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1860 -msgid "IPv4 address" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1971 -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1972 -#, python-format -msgid "'%(value)s' value must be either None, True or False." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:1974 -msgid "Boolean (Either True, False or None)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2009 -msgid "Positive integer" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2022 -msgid "Positive small integer" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2036 -#, python-format -msgid "Slug (up to %(max_length)s)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2068 -msgid "Small integer" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2103 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " -"format." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2105 -#, python-format -msgid "" -"'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " -"invalid time." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2108 -msgid "Time" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2256 -msgid "Raw binary data" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2306 -#, python-format -msgid "'%(value)s' is not a valid UUID." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2308 -msgid "Universally unique identifier" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/files.py:221 -msgid "File" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/related.py:778 -#, python-format -msgid "%(model)s instance with %(field)s %(value)r does not exist." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/related.py:780 -msgid "Foreign Key (type determined by related field)" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/related.py:1007 -msgid "One-to-one relationship" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/related.py:1057 -#, python-format -msgid "%(from)s-%(to)s relationship" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/related.py:1058 -#, python-format -msgid "%(from)s-%(to)s relationships" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/db/models/fields/related.py:1100 -msgid "Many-to-many relationship" -msgstr "" - -#. Translators: If found as last label character, these punctuation -#. characters will prevent the default label_suffix to be appended to the label -#: venv/lib/python3.6/site-packages/django/forms/boundfield.py:146 -msgid ":?.!" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:245 -msgid "Enter a whole number." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:396 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:1126 -msgid "Enter a valid date." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:420 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:1127 -msgid "Enter a valid time." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:442 -msgid "Enter a valid date/time." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:471 -msgid "Enter a valid duration." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:472 -#, python-brace-format -msgid "The number of days must be between {min_days} and {max_days}." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:532 -msgid "No file was submitted. Check the encoding type on the form." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:533 -msgid "No file was submitted." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:534 -msgid "The submitted file is empty." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:536 -#, python-format -msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." -msgid_plural "" -"Ensure this filename has at most %(max)d characters (it has %(length)d)." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:539 -msgid "Please either submit a file or check the clear checkbox, not both." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:600 -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:762 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:852 -#: venv/lib/python3.6/site-packages/django/forms/models.py:1270 -#, python-format -msgid "Select a valid choice. %(value)s is not one of the available choices." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:853 -#: venv/lib/python3.6/site-packages/django/forms/fields.py:968 -#: venv/lib/python3.6/site-packages/django/forms/models.py:1269 -msgid "Enter a list of values." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:969 -msgid "Enter a complete value." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/fields.py:1185 -msgid "Enter a valid UUID." -msgstr "" - -#. Translators: This is the default suffix added to form field labels -#: venv/lib/python3.6/site-packages/django/forms/forms.py:86 -msgid ":" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/forms.py:212 -#, python-format -msgid "(Hidden field %(name)s) %(error)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/formsets.py:91 -msgid "ManagementForm data is missing or has been tampered with" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/formsets.py:338 -#, python-format -msgid "Please submit %d or fewer forms." -msgid_plural "Please submit %d or fewer forms." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/forms/formsets.py:345 -#, python-format -msgid "Please submit %d or more forms." -msgid_plural "Please submit %d or more forms." -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/forms/formsets.py:371 -#: venv/lib/python3.6/site-packages/django/forms/formsets.py:373 -msgid "Order" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:751 -#, python-format -msgid "Please correct the duplicate data for %(field)s." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:755 -#, python-format -msgid "Please correct the duplicate data for %(field)s, which must be unique." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:761 -#, python-format -msgid "" -"Please correct the duplicate data for %(field_name)s which must be unique " -"for the %(lookup)s in %(date_field)s." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:770 -msgid "Please correct the duplicate values below." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:1091 -msgid "The inline value did not match the parent instance." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:1158 -msgid "Select a valid choice. That choice is not one of the available choices." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/models.py:1272 -#, python-format -msgid "\"%(pk)s\" is not a valid value." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/utils.py:162 -#, python-format -msgid "" -"%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; it " -"may be ambiguous or it may not exist." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/widgets.py:395 -msgid "Clear" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/widgets.py:396 -msgid "Currently" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/widgets.py:711 -msgid "Unknown" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/widgets.py:712 -msgid "Yes" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/forms/widgets.py:713 -msgid "No" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:788 -msgid "yes,no,maybe" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:817 -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:834 -#, python-format -msgid "%(size)d byte" -msgid_plural "%(size)d bytes" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:836 -#, python-format -msgid "%s KB" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:838 -#, python-format -msgid "%s MB" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:840 -#, python-format -msgid "%s GB" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:842 -#, python-format -msgid "%s TB" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/template/defaultfilters.py:844 -#, python-format -msgid "%s PB" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dateformat.py:62 -msgid "p.m." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dateformat.py:63 -msgid "a.m." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dateformat.py:68 -msgid "PM" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dateformat.py:69 -msgid "AM" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dateformat.py:150 -msgid "midnight" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dateformat.py:152 -msgid "noon" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:10 -msgid "Mon" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:10 -msgid "Tue" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:10 -msgid "Wed" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:10 -msgid "Thu" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:10 -msgid "Fri" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:11 -msgid "Sat" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:11 -msgid "Sun" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:14 -msgid "January" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:14 -msgid "February" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:14 -msgid "March" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:14 -msgid "April" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:14 -msgid "May" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:14 -msgid "June" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:15 -msgid "July" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:15 -msgid "August" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:15 -msgid "September" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:15 -msgid "October" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:15 -msgid "November" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:16 -msgid "December" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:19 -msgid "jan" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:19 -msgid "feb" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:19 -msgid "mar" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:19 -msgid "apr" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:19 -msgid "may" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:19 -msgid "jun" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:20 -msgid "jul" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:20 -msgid "aug" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:20 -msgid "sep" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:20 -msgid "oct" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:20 -msgid "nov" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:20 -msgid "dec" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:23 -msgctxt "abbrev. month" -msgid "Jan." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:24 -msgctxt "abbrev. month" -msgid "Feb." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:25 -msgctxt "abbrev. month" -msgid "March" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:26 -msgctxt "abbrev. month" -msgid "April" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:27 -msgctxt "abbrev. month" -msgid "May" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:28 -msgctxt "abbrev. month" -msgid "June" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:29 -msgctxt "abbrev. month" -msgid "July" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:30 -msgctxt "abbrev. month" -msgid "Aug." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:31 -msgctxt "abbrev. month" -msgid "Sept." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:32 -msgctxt "abbrev. month" -msgid "Oct." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:33 -msgctxt "abbrev. month" -msgid "Nov." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:34 -msgctxt "abbrev. month" -msgid "Dec." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:37 -msgctxt "alt. month" -msgid "January" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:38 -msgctxt "alt. month" -msgid "February" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:39 -msgctxt "alt. month" -msgid "March" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:40 -msgctxt "alt. month" -msgid "April" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:41 -msgctxt "alt. month" -msgid "May" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:42 -msgctxt "alt. month" -msgid "June" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:43 -msgctxt "alt. month" -msgid "July" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:44 -msgctxt "alt. month" -msgid "August" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:45 -msgctxt "alt. month" -msgid "September" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:46 -msgctxt "alt. month" -msgid "October" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:47 -msgctxt "alt. month" -msgid "November" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/dates.py:48 -msgctxt "alt. month" -msgid "December" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/ipv6.py:8 -msgid "This is not a valid IPv6 address." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/text.py:67 -#, python-format -msgctxt "String to return when truncating text" -msgid "%(truncated_text)s…" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/text.py:233 -msgid "or" -msgstr "" - -#. Translators: This string is used as a separator between list elements -#: venv/lib/python3.6/site-packages/django/utils/text.py:252 -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:83 -msgid ", " -msgstr "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:9 -#, python-format -msgid "%d year" -msgid_plural "%d years" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:10 -#, python-format -msgid "%d month" -msgid_plural "%d months" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:11 -#, python-format -msgid "%d week" -msgid_plural "%d weeks" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:12 -#, python-format -msgid "%d day" -msgid_plural "%d days" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:13 -#, python-format -msgid "%d hour" -msgid_plural "%d hours" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:14 -#, python-format -msgid "%d minute" -msgid_plural "%d minutes" -msgstr[0] "" -msgstr[1] "" - -#: venv/lib/python3.6/site-packages/django/utils/timesince.py:72 -msgid "0 minutes" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:110 -msgid "Forbidden" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:111 -msgid "CSRF verification failed. Request aborted." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:115 -msgid "" -"You are seeing this message because this HTTPS site requires a 'Referer " -"header' to be sent by your Web browser, but none was sent. This header is " -"required for security reasons, to ensure that your browser is not being " -"hijacked by third parties." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:120 -msgid "" -"If you have configured your browser to disable 'Referer' headers, please re-" -"enable them, at least for this site, or for HTTPS connections, or for 'same-" -"origin' requests." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:124 -msgid "" -"If you are using the tag or " -"including the 'Referrer-Policy: no-referrer' header, please remove them. The " -"CSRF protection requires the 'Referer' header to do strict referer checking. " -"If you're concerned about privacy, use alternatives like for links to third-party sites." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:132 -msgid "" -"You are seeing this message because this site requires a CSRF cookie when " -"submitting forms. This cookie is required for security reasons, to ensure " -"that your browser is not being hijacked by third parties." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:137 -msgid "" -"If you have configured your browser to disable cookies, please re-enable " -"them, at least for this site, or for 'same-origin' requests." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/csrf.py:142 -msgid "More information is available with DEBUG=True." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:41 -msgid "No year specified" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:61 -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:111 -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:208 -msgid "Date out of range" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:90 -msgid "No month specified" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:142 -msgid "No day specified" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:188 -msgid "No week specified" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:338 -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:367 -#, python-format -msgid "No %(verbose_name_plural)s available" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:589 -#, python-format -msgid "" -"Future %(verbose_name_plural)s not available because %(class_name)s." -"allow_future is False." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/dates.py:623 -#, python-format -msgid "Invalid date string '%(datestr)s' given format '%(format)s'" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/detail.py:54 -#, python-format -msgid "No %(verbose_name)s found matching the query" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/list.py:67 -msgid "Page is not 'last', nor can it be converted to an int." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/list.py:72 -#, python-format -msgid "Invalid page (%(page_number)s): %(message)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/generic/list.py:154 -#, python-format -msgid "Empty list and '%(class_name)s.allow_empty' is False." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/static.py:40 -msgid "Directory indexes are not allowed here." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/static.py:42 -#, python-format -msgid "\"%(path)s\" does not exist" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/static.py:80 -#, python-format -msgid "Index of %(directory)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:6 -msgid "Django: the Web framework for perfectionists with deadlines." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:345 -#, python-format -msgid "" -"View release notes for Django %(version)s" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:367 -msgid "The install worked successfully! Congratulations!" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:368 -#, python-format -msgid "" -"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " -"URLs." -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:383 -msgid "Django Documentation" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:384 -msgid "Topics, references, & how-to's" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:395 -msgid "Tutorial: A Polling App" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:396 -msgid "Get started with Django" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:407 -msgid "Django Community" -msgstr "" - -#: venv/lib/python3.6/site-packages/django/views/templates/default_urlconf.html:408 -msgid "Connect, get help, or contribute" -msgstr "" - -#: venv/lib/python3.6/site-packages/easy_select2/forms.py:7 -msgid "" -"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:64 -#, python-format -msgid "Some messages were sent: %s" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:66 -#, python-format -msgid "All messages were sent: %s" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:72 -#, python-format -msgid "Some messages failed to send. %d devices were marked as inactive." -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:80 -msgid "Send test notification" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:85 -msgid "Send test notification in bulk" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:90 -msgid "Send test data message" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:96 -msgid "Send test data message in bulk" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:101 -msgid "Enable selected devices" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/admin.py:106 -msgid "Disable selected devices" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/apps.py:7 -msgid "FCM Django" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/fields.py:52 -msgid "Enter a valid hexadecimal number" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:19 -msgid "Is active" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:20 -msgid "Inactive devices will not be sent notifications" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:25 -msgid "Creation date" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:161 -msgid "Device ID" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:162 -msgid "Unique device identifier" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:165 -msgid "Registration token" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:171 -#: venv/lib/python3.6/site-packages/fcm_django/models.py:255 -msgid "FCM device" -msgstr "" - -#: venv/lib/python3.6/site-packages/fcm_django/models.py:256 -msgid "FCM devices" -msgstr "" - -#: venv/lib/python3.6/site-packages/kombu/transport/qpid.py:1301 -#, python-format -msgid "Attempting to connect to qpid with SASL mechanism %s" -msgstr "" - -#: venv/lib/python3.6/site-packages/kombu/transport/qpid.py:1306 -#, python-format -msgid "Connected to qpid with SASL mechanism %s" -msgstr "" - -#: venv/lib/python3.6/site-packages/kombu/transport/qpid.py:1324 -#, python-format -msgid "Unable to connect to qpid with SASL mechanism %s" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:41 -msgid "Confidential" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:42 -msgid "Public" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:50 -msgid "Authorization code" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:51 -msgid "Implicit" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:52 -msgid "Resource owner password-based" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:53 -msgid "Client credentials" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:67 -msgid "Allowed URIs list, space separated" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:143 -#, python-brace-format -msgid "Unauthorized redirect scheme: {scheme}" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/models.py:148 -#, python-brace-format -msgid "redirect_uris cannot be empty with grant_type {grant_type}" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/oauth2_validators.py:166 -msgid "The access token is invalid." -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/oauth2_validators.py:171 -msgid "The access token has expired." -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/oauth2_validators.py:176 -msgid "The access token is valid but does not have enough scope." -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html:6 -msgid "Are you sure to delete the application" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_confirm_delete.html:12 -msgid "Cancel" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:10 -msgid "Client id" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:15 -msgid "Client secret" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:20 -msgid "Client type" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:25 -msgid "Authorization Grant Type" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:30 -msgid "Redirect Uris" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:36 -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_form.html:35 -msgid "Go Back" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_detail.html:37 -msgid "Edit" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_form.html:9 -msgid "Edit application" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_list.html:6 -msgid "Your applications" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_list.html:16 -msgid "No applications defined" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_list.html:16 -msgid "Click here" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_list.html:16 -msgid "if you want to register a new one" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/application_registration_form.html:5 -msgid "Register a new application" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/authorize.html:8 -msgid "Authorize" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/authorize.html:17 -msgid "Application requires following permissions" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/authorized-token-delete.html:6 -msgid "Are you sure you want to delete this token?" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/authorized-tokens.html:6 -msgid "Tokens" -msgstr "" - -#: venv/lib/python3.6/site-packages/oauth2_provider/templates/oauth2_provider/authorized-tokens.html:19 -msgid "There are no authorized tokens yet." -msgstr "" - -#: venv/lib/python3.6/site-packages/solo/admin.py:53 -#, python-format -msgid "%(obj)s was changed successfully." -msgstr "" +#: project/templates/notification/update_email.html:44 +msgid "Follow us" +msgstr "Подпишись сейчас" -#: venv/lib/python3.6/site-packages/solo/admin.py:55 -msgid "You may edit it again below." -msgstr "" +#: project/templates/notification/update_email.html:46 +msgid "You can also us on our social network below" +msgstr "Вы также можете найти нас в нашей социальной сети ниже" -#: venv/lib/python3.6/site-packages/solo/templatetags/solo_tags.py:22 -#, python-format -msgid "" -"Templatetag requires the model dotted path: 'app_label.ModelName'. Received " -"'%s'." -msgstr "" +#: project/templates/notification/update_email.html:62 +msgid "This email has been sent to" +msgstr "Это сообщение было отослано для" -#: venv/lib/python3.6/site-packages/solo/templatetags/solo_tags.py:28 -#, python-format -msgid "" -"Could not get the model name '%(model)s' from the application named '%(app)s'" -msgstr "" +#: project/templates/notification/update_email.html:63 +msgid "click here to unsubscribe" +msgstr "нажмите здесь для отписки"