Merge branch 'develop' into feature/refactor-establishments

This commit is contained in:
evgeniy-st 2019-09-27 10:05:15 +03:00
commit 403a60623a
29 changed files with 826 additions and 157 deletions

View File

@ -1,27 +1,25 @@
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from rest_framework import status from rest_framework import status
from authorization.tests.tests import get_tokens_for_user from authorization.tests.tests_authorization import get_tokens_for_user
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
from account.models import User from account.models import User
from django.urls import reverse from django.urls import reverse
class AccountUserTests(APITestCase): class AccountUserTests(APITestCase):
url = reverse('web:account:user-retrieve-update')
def setUp(self): def setUp(self):
self.data = get_tokens_for_user() self.data = get_tokens_for_user()
def test_user_url(self): def test_user_url(self):
response = self.client.get(self.url) url = reverse('web:account:user-retrieve-update')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
self.client.cookies = SimpleCookie( self.client.cookies = SimpleCookie(
{'access_token': self.data['tokens'].get('access_token'), {'access_token': self.data['tokens'].get('access_token'),
'refresh_token': self.data['tokens'].get('access_token')}) 'refresh_token': self.data['tokens'].get('access_token')})
response = self.client.get(self.url) response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
data = { data = {
@ -33,19 +31,16 @@ class AccountUserTests(APITestCase):
"email": "sedragurdatest@desoz.com", "email": "sedragurdatest@desoz.com",
"newsletter": self.data["newsletter"] "newsletter": self.data["newsletter"]
} }
response = self.client.patch(self.url, data=data, format='json') response = self.client.patch(url, data=data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
data["email"] = "sedragurdatest2@desoz.com" data["email"] = "sedragurdatest2@desoz.com"
response = self.client.put(self.url, data=data, format='json') response = self.client.put(url, data=data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
class AccountChangePasswordTests(APITestCase): class AccountChangePasswordTests(APITestCase):
url = reverse('web:account:change-password')
def setUp(self): def setUp(self):
self.data = get_tokens_for_user() self.data = get_tokens_for_user()
@ -54,15 +49,16 @@ class AccountChangePasswordTests(APITestCase):
"old_password": self.data["password"], "old_password": self.data["password"],
"password": "new password" "password": "new password"
} }
url = reverse('web:account:change-password')
response = self.client.patch(self.url, data=data, format='json') response = self.client.patch(url, data=data, format='json')
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
self.client.cookies = SimpleCookie( self.client.cookies = SimpleCookie(
{'access_token': self.data['tokens'].get('access_token'), {'access_token': self.data['tokens'].get('access_token'),
'refresh_token': self.data['tokens'].get('access_token')}) 'refresh_token': self.data['tokens'].get('access_token')})
response = self.client.patch(self.url, data=data, format='json') response = self.client.patch(url, data=data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -0,0 +1,49 @@
from rest_framework.test import APITestCase
from rest_framework import status
from authorization.tests.tests_authorization import get_tokens_for_user
from django.urls import reverse
from account.models import User
class AccountResetPassWordTests(APITestCase):
def setUp(self):
self.data = get_tokens_for_user()
def test_reset_password(self):
url = reverse('web:account:password-reset')
data = {
"username_or_email": self.data["email"]
}
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = {
"username_or_email": self.data["username"]
}
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
class AccountResetPassWordTests(APITestCase):
def setUp(self):
self.data = get_tokens_for_user()
def test_reset_password_confirm(self):
data ={
"password": "newpasswordnewpassword"
}
user = User.objects.get(email=self.data["email"])
token = user.reset_password_token
uidb64 = user.get_user_uidb64
url = reverse('web:account:password-reset-confirm', kwargs={
'uidb64': uidb64,
'token': token
})
response = self.client.patch(url, data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

View File

@ -22,6 +22,7 @@ def get_tokens_for_user(
class AuthorizationTests(APITestCase): class AuthorizationTests(APITestCase):
def setUp(self): def setUp(self):
print("Auth!")
data = get_tokens_for_user() data = get_tokens_for_user()
self.username = data["username"] self.username = data["username"]
self.password = data["password"] self.password = data["password"]

View File

@ -1,19 +1,21 @@
"""Establishment models.""" """Establishment models."""
from functools import reduce from functools import reduce
from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.measure import Distance as DistanceMeasure from django.conf import settings
from django.contrib.gis.geos import Point
from django.contrib.contenttypes import fields as generic from django.contrib.contenttypes import fields as generic
from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance as DistanceMeasure
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.db.models import When, Case, F, ExpressionWrapper, Subquery
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from location.models import Address
from collection.models import Collection from collection.models import Collection
from main.models import MetaDataContent from main.models import MetaDataContent
from location.models import Address
from review.models import Review from review.models import Review
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
TranslatedFieldsMixin, BaseAttributes) TranslatedFieldsMixin, BaseAttributes)
@ -106,29 +108,20 @@ class EstablishmentQuerySet(models.QuerySet):
""" """
return self.filter(is_publish=True) return self.filter(is_publish=True)
def annotate_distance(self, point: Point): def has_published_reviews(self):
"""
Return QuerySet establishments with published reviews.
"""
return self.filter(reviews__status=Review.READY,)
def annotate_distance(self, point: Point = None):
""" """
Return QuerySet with annotated field - distance Return QuerySet with annotated field - distance
Description: Description:
""" """
return self.annotate(distance=models.Value( return self.annotate(distance=Distance('address__coordinates', point,
DistanceMeasure(Distance('address__coordinates', point, srid=4236)).m, srid=settings.GEO_DEFAULT_SRID))
output_field=models.FloatField()))
def annotate_distance_mark(self):
"""
Return QuerySet with annotated field - distance_mark.
Required fields: distance.
Description:
If the radius of the establishments in QuerySet does not exceed 500 meters,
then distance_mark is set to 0.6, otherwise 0.
"""
return self.annotate(distance_mark=models.Case(
models.When(distance__lte=500,
then=0.6),
default=0,
output_field=models.FloatField()))
def annotate_intermediate_public_mark(self): def annotate_intermediate_public_mark(self):
""" """
@ -137,72 +130,65 @@ class EstablishmentQuerySet(models.QuerySet):
If establishments in collection POP and its mark is null, then If establishments in collection POP and its mark is null, then
intermediate_mark is set to 10; intermediate_mark is set to 10;
""" """
return self.annotate(intermediate_public_mark=models.Case( return self.annotate(intermediate_public_mark=Case(
models.When( When(
collections__collection_type=Collection.POP, collections__collection_type=Collection.POP,
public_mark__isnull=True, public_mark__isnull=True,
then=10 then=settings.DEFAULT_ESTABLISHMENT_PUBLIC_MARK
), ),
default='public_mark', default='public_mark',
output_field=models.FloatField())) output_field=models.FloatField()))
def annotate_additional_mark(self, public_mark: float): def annotate_mark_similarity(self, mark):
""" """
Return QuerySet with annotated field - additional_mark. Return a QuerySet with annotated field - mark_similarity
Required fields: intermediate_public_mark
Description: Description:
IF Similarity mark determined by comparison with compared establishment mark
establishments public_mark + 3 > compared establishment public_mark
OR
establishments public_mark - 3 > compared establishment public_mark,
THEN
additional_mark is set to 0.4,
ELSE
set to 0.
""" """
return self.annotate(additional_mark=models.Case( return self.annotate(mark_similarity=ExpressionWrapper(
models.When( mark - F('intermediate_public_mark'),
models.Q(intermediate_public_mark__lte=public_mark + 3) | output_field=models.FloatField()
models.Q(intermediate_public_mark__lte=public_mark - 3), ))
then=0.4),
default=0,
output_field=models.FloatField()))
def annotate_total_mark(self):
"""
Return QuerySet with annotated field - total_mark.
Required fields: distance_mark, additional_mark.
Fields
Description:
Annotated field is obtained by formula:
(distance + additional marks) * intermediate_public_mark.
"""
return self.annotate(
total_mark=(models.F('distance_mark') + models.F('additional_mark')) *
models.F('intermediate_public_mark'))
def similar(self, establishment_slug: str): def similar(self, establishment_slug: str):
""" """
Return QuerySet with objects that similar to Establishment. Return QuerySet with objects that similar to Establishment.
:param establishment_slug: str Establishment slug :param establishment_slug: str Establishment slug
""" """
establishment_qs = Establishment.objects.filter(slug=establishment_slug) establishment_qs = self.filter(slug=establishment_slug,
public_mark__isnull=False)
if establishment_qs.exists(): if establishment_qs.exists():
establishment = establishment_qs.first() establishment = establishment_qs.first()
return self.exclude(slug=establishment_slug) \ subquery_filter_by_distance = Subquery(
.filter(is_publish=True, self.exclude(slug=establishment_slug)
image_url__isnull=False, .filter(image_url__isnull=False, public_mark__gte=10)
reviews__isnull=False, .has_published_reviews()
reviews__status=Review.READY, .annotate_distance(point=establishment.location)
public_mark__gte=10) \ .order_by('distance')[:settings.LIMITING_QUERY_NUMBER]
.annotate_distance(point=establishment.address.coordinates) \ .values('id')
.annotate_distance_mark() \ )
return self.filter(id__in=subquery_filter_by_distance) \
.annotate_intermediate_public_mark() \ .annotate_intermediate_public_mark() \
.annotate_additional_mark(public_mark=establishment.public_mark) \ .annotate_mark_similarity(mark=establishment.public_mark) \
.annotate_total_mark() .order_by('mark_similarity')
else: else:
return self.none() return self.none()
def last_reviewed(self, point: Point):
"""
Return QuerySet with last reviewed establishments.
:param point: location Point object, needs to ordering
"""
subquery_filter_by_distance = Subquery(
self.filter(image_url__isnull=False, public_mark__gte=10)
.has_published_reviews()
.annotate_distance(point=point)
.order_by('distance')[:settings.LIMITING_QUERY_NUMBER]
.values('id')
)
return self.filter(id__in=subquery_filter_by_distance) \
.order_by('-reviews__published_at')
def prefetch_actual_employees(self): def prefetch_actual_employees(self):
"""Prefetch actual employees.""" """Prefetch actual employees."""
return self.prefetch_related( return self.prefetch_related(
@ -216,10 +202,10 @@ class EstablishmentQuerySet(models.QuerySet):
favorite_establishments = [] favorite_establishments = []
if user.is_authenticated: if user.is_authenticated:
favorite_establishments = user.favorites.by_content_type(app_label='establishment', favorite_establishments = user.favorites.by_content_type(app_label='establishment',
model='establishment')\ model='establishment') \
.values_list('object_id', flat=True) .values_list('object_id', flat=True)
return self.annotate(in_favorites=models.Case( return self.annotate(in_favorites=Case(
models.When( When(
id__in=favorite_establishments, id__in=favorite_establishments,
then=True), then=True),
default=False, default=False,
@ -293,7 +279,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
preview_image_url = models.URLField(verbose_name=_('Preview image URL path'), preview_image_url = models.URLField(verbose_name=_('Preview image URL path'),
blank=True, null=True, default=None) blank=True, null=True, default=None)
slug = models.SlugField(unique=True, max_length=50, null=True, slug = models.SlugField(unique=True, max_length=50, null=True,
verbose_name=_('Establishment slug'), editable=True) verbose_name=_('Establishment slug'), editable=True)
awards = generic.GenericRelation(to='main.Award') awards = generic.GenericRelation(to='main.Award')
tags = generic.GenericRelation(to='main.MetaDataContent') tags = generic.GenericRelation(to='main.MetaDataContent')
@ -357,6 +343,19 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
return [{'id': tag.metadata.id, return [{'id': tag.metadata.id,
'label': tag.metadata.label} for tag in self.tags.all()] 'label': tag.metadata.label} for tag in self.tags.all()]
@property
def last_published_review(self):
"""Return last published review"""
return self.reviews.published()\
.order_by('-published_at').first()
@property
def location(self):
"""
Return Point object of establishment location
"""
return self.address.coordinates
class Position(BaseAttributes, TranslatedFieldsMixin): class Position(BaseAttributes, TranslatedFieldsMixin):
"""Position model.""" """Position model."""

View File

@ -4,7 +4,11 @@ from establishment.serializers import (
EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer, EstablishmentBaseSerializer, PlateSerializer, ContactEmailsSerializer,
ContactPhonesSerializer, SocialNetworkRelatedSerializers, ContactPhonesSerializer, SocialNetworkRelatedSerializers,
EstablishmentTypeSerializer) EstablishmentTypeSerializer)
from utils.decorators import with_base_attributes
from main.models import Currency from main.models import Currency
from utils.serializers import TJSONSerializer
class EstablishmentListCreateSerializer(EstablishmentBaseSerializer): class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
@ -85,7 +89,7 @@ class SocialNetworkSerializers(serializers.ModelSerializer):
class PlatesSerializers(PlateSerializer): class PlatesSerializers(PlateSerializer):
"""Social network serializers.""" """Social network serializers."""
name = serializers.JSONField() name = TJSONSerializer
currency_id = serializers.PrimaryKeyRelatedField( currency_id = serializers.PrimaryKeyRelatedField(
source='currency', source='currency',
queryset=Currency.objects.all(), write_only=True queryset=Currency.objects.all(), write_only=True
@ -122,7 +126,10 @@ class ContactEmailBackSerializers(PlateSerializer):
] ]
# TODO: test decorator
@with_base_attributes
class EmployeeBackSerializers(serializers.ModelSerializer): class EmployeeBackSerializers(serializers.ModelSerializer):
"""Social network serializers.""" """Social network serializers."""
class Meta: class Meta:
model = models.Employee model = models.Employee
@ -130,4 +137,5 @@ class EmployeeBackSerializers(serializers.ModelSerializer):
'id', 'id',
'user', 'user',
'name' 'name'
] ]

View File

@ -1,6 +1,6 @@
"""Establishment serializers.""" """Establishment serializers."""
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import gettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from comment import models as comment_models from comment import models as comment_models
from comment.serializers import common as comment_serializers from comment.serializers import common as comment_serializers
from establishment import models from establishment import models
@ -12,6 +12,7 @@ from review import models as review_models
from timetable.serialziers import ScheduleRUDSerializer from timetable.serialziers import ScheduleRUDSerializer
from utils import exceptions as utils_exceptions from utils import exceptions as utils_exceptions
from utils.serializers import TranslatedField from utils.serializers import TranslatedField
from utils.serializers import TJSONSerializer
class ContactPhonesSerializer(serializers.ModelSerializer): class ContactPhonesSerializer(serializers.ModelSerializer):
@ -60,7 +61,7 @@ class PlateSerializer(serializers.ModelSerializer):
class MenuSerializers(serializers.ModelSerializer): class MenuSerializers(serializers.ModelSerializer):
plates = PlateSerializer(read_only=True, many=True, source='plate_set') plates = PlateSerializer(read_only=True, many=True, source='plate_set')
category = serializers.JSONField() category = TJSONSerializer()
category_translated = serializers.CharField(read_only=True) category_translated = serializers.CharField(read_only=True)
class Meta: class Meta:
@ -74,9 +75,9 @@ class MenuSerializers(serializers.ModelSerializer):
] ]
class MenuRUDSerializers(serializers.ModelSerializer): class MenuRUDSerializers(serializers.ModelSerializer, ):
plates = PlateSerializer(read_only=True, many=True, source='plate_set') plates = PlateSerializer(read_only=True, many=True, source='plate_set')
category = serializers.JSONField() category = TJSONSerializer()
class Meta: class Meta:
model = models.Menu model = models.Menu
@ -191,7 +192,7 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer):
schedule = ScheduleRUDSerializer(many=True, allow_null=True) schedule = ScheduleRUDSerializer(many=True, allow_null=True)
phones = ContactPhonesSerializer(read_only=True, many=True) phones = ContactPhonesSerializer(read_only=True, many=True)
emails = ContactEmailsSerializer(read_only=True, many=True) emails = ContactEmailsSerializer(read_only=True, many=True)
review = serializers.SerializerMethodField() review = ReviewSerializer(source='last_published_review', allow_null=True)
employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees', employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees',
many=True) many=True)
menu = MenuSerializers(source='menu_set', many=True, read_only=True) menu = MenuSerializers(source='menu_set', many=True, read_only=True)
@ -223,12 +224,6 @@ class EstablishmentDetailSerializer(EstablishmentListSerializer):
'transportation', 'transportation',
] ]
# todo: refactor this s (make as prefetch to model as attr or make as model property)
def get_review(self, obj):
"""Serializer method for getting last published review"""
return ReviewSerializer(obj.reviews.by_status(status=review_models.Review.READY)
.order_by('-published_at').first()).data
# def get_in_favorites(self, obj): # def get_in_favorites(self, obj):
# """Get in_favorites status flag""" # """Get in_favorites status flag"""
# user = self.context.get('request').user # user = self.context.get('request').user
@ -308,20 +303,22 @@ class EstablishmentFavoritesCreateSerializer(serializers.ModelSerializer):
def validate(self, attrs): def validate(self, attrs):
"""Override validate method""" """Override validate method"""
# Check establishment object # Check establishment object
establishment_id = self.context.get('request').parser_context.get('kwargs').get('pk') establishment_slug = self.context.get('request').parser_context.get('kwargs').get('slug')
establishment_qs = models.Establishment.objects.filter(id=establishment_id) establishment_qs = models.Establishment.objects.filter(slug=establishment_slug)
# Check establishment obj by pk from lookup_kwarg # Check establishment obj by slug from lookup_kwarg
if not establishment_qs.exists(): if not establishment_qs.exists():
raise serializers.ValidationError({'detail': _('Object not found.')}) raise serializers.ValidationError({'detail': _('Object not found.')})
else:
establishment = establishment_qs.first()
# Check existence in favorites # Check existence in favorites
if self.get_user().favorites.by_content_type(app_label='establishment', if self.get_user().favorites.by_content_type(app_label='establishment',
model='establishment')\ model='establishment')\
.by_object_id(object_id=establishment_id).exists(): .by_object_id(object_id=establishment.id).exists():
raise utils_exceptions.FavoritesError() raise utils_exceptions.FavoritesError()
attrs['establishment'] = establishment_qs.first() attrs['establishment'] = establishment
return attrs return attrs
def create(self, validated_data, *args, **kwargs): def create(self, validated_data, *args, **kwargs):

View File

@ -5,7 +5,6 @@ from rest_framework import status
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
from main.models import Currency from main.models import Currency
from establishment.models import Establishment, EstablishmentType, Menu from establishment.models import Establishment, EstablishmentType, Menu
# Create your tests here. # Create your tests here.
@ -27,7 +26,7 @@ class BaseTestCase(APITestCase):
self.establishment_type = EstablishmentType.objects.create(name="Test establishment type") self.establishment_type = EstablishmentType.objects.create(name="Test establishment type")
class EstablishmentTests(BaseTestCase): class EstablishmentBTests(BaseTestCase):
def test_establishment_CRUD(self): def test_establishment_CRUD(self):
params = {'page': 1, 'page_size': 1,} params = {'page': 1, 'page_size': 1,}
response = self.client.get('/api/back/establishments/', params, format='json') response = self.client.get('/api/back/establishments/', params, format='json')
@ -93,7 +92,8 @@ class ChildTestCase(BaseTestCase):
self.establishment = Establishment.objects.create( self.establishment = Establishment.objects.create(
name="Test establishment", name="Test establishment",
establishment_type_id=self.establishment_type.id, establishment_type_id=self.establishment_type.id,
is_publish=True is_publish=True,
slug="test"
) )
@ -263,3 +263,89 @@ class EstablishmentShedulerTests(ChildTestCase):
response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/schedule/1/') response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/schedule/1/')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
# Web tests
class EstablishmentWebTests(BaseTestCase):
def test_establishment_Read(self):
params = {'page': 1, 'page_size': 1,}
response = self.client.get('/api/web/establishments/', params, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
class EstablishmentWebTagTests(BaseTestCase):
def test_tag_Read(self):
response = self.client.get('/api/web/establishments/tags/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
class EstablishmentWebSlugTests(ChildTestCase):
def test_slug_Read(self):
response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
class EstablishmentWebSimilarTests(ChildTestCase):
def test_similar_Read(self):
response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/similar/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
class EstablishmentWebCommentsTests(ChildTestCase):
def test_comments_CRUD(self):
response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/comments/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = {
'text': 'test',
'user': self.user.id,
'mark': 4
}
response = self.client.post(f'/api/web/establishments/slug/{self.establishment.slug}/comments/create/',
data=data)
comment = response.json()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get(f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/',
format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
update_data = {
'text': 'Test new establishment'
}
response = self.client.patch(f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/',
data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete(
f'/api/web/establishments/slug/{self.establishment.slug}/comments/{comment["id"]}/',
format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
class EstablishmentWebFavoriteTests(ChildTestCase):
def test_favorite_CR(self):
data = {
"user": self.user.id,
"object_id": self.establishment.id
}
response = self.client.post(f'/api/web/establishments/slug/{self.establishment.slug}/favorites/',
data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.delete(
f'/api/web/establishments/slug/{self.establishment.slug}/favorites/',
format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

View File

@ -8,13 +8,15 @@ app_name = 'establishment'
urlpatterns = [ urlpatterns = [
path('', views.EstablishmentListView.as_view(), name='list'), path('', views.EstablishmentListView.as_view(), name='list'),
path('tags/', views.EstablishmentTagListView.as_view(), name='tags'), path('tags/', views.EstablishmentTagListView.as_view(), name='tags'),
path('<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'), path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
path('<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'), name='recent-reviews'),
path('<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'), path('slug/<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
path('<slug:slug>/comments/create/', views.EstablishmentCommentCreateView.as_view(), path('slug/<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),
path('slug/<slug:slug>/comments/create/', views.EstablishmentCommentCreateView.as_view(),
name='create-comment'), name='create-comment'),
path('<slug:slug>/comments/<int:comment_id>/', views.EstablishmentCommentRUDView.as_view(), path('slug/<slug:slug>/comments/<int:comment_id>/', views.EstablishmentCommentRUDView.as_view(),
name='rud-comment'), name='rud-comment'),
path('<slug:slug>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(), path('slug/<slug:slug>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
name='add-to-favorites') name='add-to-favorites')
] ]

View File

@ -1 +0,0 @@
"""Establishment app views."""

View File

@ -1,12 +1,17 @@
"""Establishment app views.""" """Establishment app views."""
from django.conf import settings
from django.contrib.gis.geos import Point from django.contrib.gis.geos import Point
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import generics, permissions from rest_framework import generics, permissions
from comment import models as comment_models from comment import models as comment_models
from establishment import filters, models, serializers from establishment import filters
from establishment import models, serializers
from establishment.views import EstablishmentMixin
from main import methods
from main.models import MetaDataContent from main.models import MetaDataContent
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
from utils.pagination import EstablishmentPortionPagination
class EstablishmentMixinView: class EstablishmentMixinView:
@ -44,14 +49,35 @@ class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView
return super().get_queryset().with_extended_related() return super().get_queryset().with_extended_related()
class EstablishmentRecentReviewListView(EstablishmentListView):
"""List view for last reviewed establishments."""
pagination_class = EstablishmentPortionPagination
def get_queryset(self):
"""Overridden method 'get_queryset'."""
qs = super().get_queryset()
user_ip = methods.get_user_ip(self.request)
query_params = self.request.query_params
if 'longitude' in query_params and 'latitude' in query_params:
longitude, latitude = query_params.get('longitude'), query_params.get('latitude')
else:
longitude, latitude = methods.determine_coordinates(user_ip)
if not longitude or not latitude:
return qs.none()
point = Point(x=float(longitude), y=float(latitude), srid=settings.GEO_DEFAULT_SRID)
return qs.last_reviewed(point=point)
class EstablishmentSimilarListView(EstablishmentListView): class EstablishmentSimilarListView(EstablishmentListView):
"""Resource for getting a list of establishments.""" """Resource for getting a list of establishments."""
serializer_class = serializers.EstablishmentListSerializer serializer_class = serializers.EstablishmentListSerializer
pagination_class = EstablishmentPortionPagination
def get_queryset(self): def get_queryset(self):
"""Override get_queryset method""" """Override get_queryset method"""
return super().get_queryset().similar(establishment_slug=self.kwargs.get('slug'))\ qs = super().get_queryset()
.order_by('-total_mark')[:13] return qs.similar(establishment_slug=self.kwargs.get('slug'))
class EstablishmentTypeListView(generics.ListAPIView): class EstablishmentTypeListView(generics.ListAPIView):

View File

@ -19,4 +19,5 @@ class FavoritesEstablishmentListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
"""Override get_queryset method""" """Override get_queryset method"""
return Establishment.objects.filter(favorites__user=self.request.user) return Establishment.objects.filter(favorites__user=self.request.user)\
.order_by('-favorites')

View File

@ -1,6 +1,5 @@
from rest_framework import generics from rest_framework import generics
from utils.permissions import IsAuthenticatedAndTokenIsValid
from . import models, serializers from . import models, serializers
@ -9,4 +8,3 @@ class ImageUploadView(generics.CreateAPIView):
model = models.Image model = models.Image
queryset = models.Image.objects.all() queryset = models.Image.objects.all()
serializer_class = serializers.ImageSerializer serializer_class = serializers.ImageSerializer
permission_classes = (IsAuthenticatedAndTokenIsValid, )

View File

@ -1,3 +1,193 @@
from django.test import TestCase import json
# Create your tests here. from rest_framework.test import APITestCase
from account.models import User
from rest_framework import status
from http.cookies import SimpleCookie
from location.models import City, Region, Country
class BaseTestCase(APITestCase):
def setUp(self):
self.username = 'sedragurda'
self.password = 'sedragurdaredips19'
self.email = 'sedragurda@desoz.com'
self.newsletter = True
self.user = User.objects.create_user(
username=self.username, email=self.email, password=self.password)
# get tokens
tokkens = User.create_jwt_tokens(self.user)
self.client.cookies = SimpleCookie(
{'access_token': tokkens.get('access_token'),
'refresh_token': tokkens.get('refresh_token')})
class CountryTests(BaseTestCase):
def test_country_CRUD(self):
response = self.client.get('/api/back/location/countries/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = {
'name': 'Test country',
'code': 'test'
}
response = self.client.post('/api/back/location/countries/', data=data, format='json')
response_data = response.json()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get(f'/api/back/location/countries/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
update_data = {
'name': json.dumps({"en-GB": "Test new country"})
}
response = self.client.patch(f'/api/back/location/countries/{response_data["id"]}/', data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete(f'/api/back/location/countries/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
class RegionTests(BaseTestCase):
def setUp(self):
super().setUp()
self.country = Country.objects.create(
name=json.dumps({"en-GB": "Test country"}),
code="test"
)
def test_region_CRUD(self):
response = self.client.get('/api/back/location/regions/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = {
'name': 'Test country',
'code': 'test',
'country_id': self.country.id
}
response = self.client.post('/api/back/location/regions/', data=data, format='json')
response_data = response.json()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get(f'/api/back/location/regions/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
update_data = {
'name': json.dumps({"en-GB": "Test new country"})
}
response = self.client.patch(f'/api/back/location/regions/{response_data["id"]}/', data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete(f'/api/back/location/regions/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
class CityTests(BaseTestCase):
def setUp(self):
super().setUp()
self.country = Country.objects.create(
name=json.dumps({"en-GB": "Test country"}),
code="test"
)
self.region = Region.objects.create(
name="Test region",
code="812",
country=self.country
)
def test_city_CRUD(self):
response = self.client.get('/api/back/location/cities/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = {
'name': 'Test country',
'code': 'test',
'country_id': self.country.id,
'region_id': self.region.id
}
response = self.client.post('/api/back/location/cities/', data=data, format='json')
response_data = response.json()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get(f'/api/back/location/cities/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
update_data = {
'name': json.dumps({"en-GB": "Test new country"})
}
response = self.client.patch(f'/api/back/location/cities/{response_data["id"]}/', data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete(f'/api/back/location/cities/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
class AddressTests(BaseTestCase):
def setUp(self):
super().setUp()
self.country = Country.objects.create(
name=json.dumps({"en-GB": "Test country"}),
code="test"
)
self.region = Region.objects.create(
name="Test region",
code="812",
country=self.country
)
self.city = City.objects.create(
name="Test region",
code="812",
region=self.region,
country=self.country
)
def test_address_CRUD(self):
response = self.client.get('/api/back/location/addresses/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = {
'city_id': self.city.id,
'number': '+79999999',
"coordinates": {
"latitude": 37.0625,
"longitude": -95.677068
},
"geo_lon": -95.677068,
"geo_lat": 37.0625
}
response = self.client.post('/api/back/location/addresses/', data=data, format='json')
response_data = response.json()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get(f'/api/back/location/addresses/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
update_data = {
'number': '+79999991'
}
response = self.client.patch(f'/api/back/location/addresses/{response_data["id"]}/', data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete(f'/api/back/location/addresses/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

View File

@ -1,9 +1,10 @@
"""Main app methods.""" """Main app methods."""
import logging import logging
from django.conf import settings from django.conf import settings
from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception
from main import models
from main import models
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -38,6 +39,19 @@ def determine_country_code(ip_addr):
return country_code return country_code
def determine_coordinates(ip_addr):
longitude, latitude = None, None
if ip_addr:
try:
geoip = GeoIP2()
longitude, latitude = geoip.coords(ip_addr)
except GeoIP2Exception as ex:
logger.info(f'GEOIP Exception: {ex}. ip: {ip_addr}')
except Exception as ex:
logger.error(f'GEOIP Base exception: {ex}')
return longitude, latitude
def determine_user_site_url(country_code): def determine_user_site_url(country_code):
"""Determine user's site url.""" """Determine user's site url."""
try: try:

View File

@ -136,8 +136,8 @@ class CarouselListSerializer(serializers.ModelSerializer):
"""Serializer for retrieving list of carousel items.""" """Serializer for retrieving list of carousel items."""
model_name = serializers.CharField() model_name = serializers.CharField()
name = serializers.CharField() name = serializers.CharField()
toque_number = serializers.CharField() toque_number = serializers.IntegerField()
public_mark = serializers.CharField() public_mark = serializers.IntegerField()
image = serializers.URLField(source='image_url') image = serializers.URLField(source='image_url')
awards = AwardBaseSerializer(many=True) awards = AwardBaseSerializer(many=True)
vintage_year = serializers.IntegerField() vintage_year = serializers.IntegerField()

View File

@ -35,11 +35,18 @@ class NewsTestCase(BaseTestCase):
response = self.client.get("/api/web/news/") response = self.client.get("/api/web/news/")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_detail(self): def test_news_web_detail(self):
response = self.client.get(f"/api/web/news/{self.test_news.slug}/") response = self.client.get(f"/api/web/news/slug/{self.test_news.slug}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_back_detail(self):
response = self.client.get(f"/api/back/news/{self.test_news.id}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_list_back(self):
response = self.client.get("/api/back/news/")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_type_list(self): def test_news_type_list(self):
response = self.client.get("/api/web/news/types/") response = self.client.get("/api/web/news/types/")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -6,6 +6,6 @@ app_name = 'news'
urlpatterns = [ urlpatterns = [
path('', views.NewsListView.as_view(), name='list'), path('', views.NewsListView.as_view(), name='list'),
path('<slug:slug>/', views.NewsDetailView.as_view(), name='rud'),
path('types/', views.NewsTypeListView.as_view(), name='type'), path('types/', views.NewsTypeListView.as_view(), name='type'),
path('slug/<slug:slug>/', views.NewsDetailView.as_view(), name='rud'),
] ]

116
apps/notification/tests.py Normal file
View File

@ -0,0 +1,116 @@
from http.cookies import SimpleCookie
from django.test import TestCase
from rest_framework.test import APITestCase
from rest_framework import status
from account.models import User
from notification.models import Subscriber
class BaseTestCase(APITestCase):
def setUp(self):
self.username = 'sedragurda'
self.password = 'sedragurdaredips19'
self.email = 'sedragurda@desoz.com'
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
# get tokkens
tokkens = User.create_jwt_tokens(self.user)
self.client.cookies = SimpleCookie({'access_token': tokkens.get('access_token'),
'refresh_token': tokkens.get('refresh_token')})
class NotificationAnonSubscribeTestCase(APITestCase):
def test_subscribe(self):
test_data = {
"email": "test@email.com",
"state": 1
}
response = self.client.post("/api/web/notifications/subscribe/", data=test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()["email"], test_data["email"])
self.assertEqual(response.json()["state"], test_data["state"])
class NotificationSubscribeTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.test_data = {
"email": self.email,
"state": 1
}
def test_subscribe(self):
response = self.client.post("/api/web/notifications/subscribe/", data=self.test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()["email"], self.email)
self.assertEqual(response.json()["state"], self.test_data["state"])
def test_subscribe_info_auth_user(self):
Subscriber.objects.create(user=self.user, email=self.email, state=1)
response = self.client.get("/api/web/notifications/subscribe-info/", data=self.test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
class NotificationSubscribeInfoTestCase(APITestCase):
def test_subscribe_info(self):
self.username = 'sedragurda'
self.password = 'sedragurdaredips19'
self.email = 'sedragurda@desoz.com'
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email, state=1)
response = self.client.get(f"/api/web/notifications/subscribe-info/{test_subscriber.update_code}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
class NotificationUnsubscribeAuthUserTestCase(BaseTestCase):
def test_unsubscribe_auth_user(self):
Subscriber.objects.create(user=self.user, email=self.email, state=1)
self.test_data = {
"email": self.email,
"state": 1
}
response = self.client.patch("/api/web/notifications/unsubscribe/", data=self.test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
class NotificationUnsubscribeTestCase(APITestCase):
def test_unsubscribe(self):
self.username = 'sedragurda'
self.password = 'sedragurdaredips19'
self.email = 'sedragurda@desoz.com'
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
self.test_data = {
"email": self.email,
"state": 1
}
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email, state=1)
response = self.client.patch(f"/api/web/notifications/unsubscribe/{test_subscriber.update_code}/",
data=self.test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -23,6 +23,10 @@ class ReviewQuerySet(models.QuerySet):
"""Filter by status""" """Filter by status"""
return self.filter(status=status) return self.filter(status=status)
def published(self):
"""Return published reviews"""
return self.filter(status=Review.READY)
class Review(BaseAttributes, TranslatedFieldsMixin): class Review(BaseAttributes, TranslatedFieldsMixin):
"""Review model""" """Review model"""

View File

@ -84,6 +84,8 @@ class EstablishmentDocument(Document):
'name', 'name',
'toque_number', 'toque_number',
'price_level', 'price_level',
'preview_image_url',
'slug',
) )
def get_queryset(self): def get_queryset(self):

View File

@ -63,6 +63,8 @@ class EstablishmentDocumentSerializer(DocumentSerializer):
'collections', 'collections',
'establishment_type', 'establishment_type',
'establishment_subtypes', 'establishment_subtypes',
'preview_image_url',
'slug',
) )
@staticmethod @staticmethod

21
apps/utils/decorators.py Normal file
View File

@ -0,0 +1,21 @@
def with_base_attributes(cls):
def validate(self, data):
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
if user is not None:
data.update({'modified_by': user})
if not self.instance:
data.update({'created_by': user})
return data
setattr(cls, "validate", validate)
return cls

View File

@ -31,14 +31,3 @@ def parse_cookies(get_response):
response = get_response(request) response = get_response(request)
return response return response
return middleware return middleware
class CORSMiddleware:
"""Added parameter {Access-Control-Allow-Origin: *} to response"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response["Access-Control-Allow-Origin"] = '*'
return response

View File

@ -10,6 +10,8 @@ from django.utils.translation import ugettext_lazy as _, get_language
from easy_thumbnails.fields import ThumbnailerImageField from easy_thumbnails.fields import ThumbnailerImageField
from utils.methods import image_path, svg_image_path from utils.methods import image_path, svg_image_path
from utils.validators import svg_image_validator from utils.validators import svg_image_validator
from django.db.models.fields import Field
from django.core import exceptions
class ProjectBaseMixin(models.Model): class ProjectBaseMixin(models.Model):
@ -26,6 +28,10 @@ class ProjectBaseMixin(models.Model):
abstract = True abstract = True
def valid(value):
print("Run")
class TJSONField(JSONField): class TJSONField(JSONField):
"""Overrided JsonField.""" """Overrided JsonField."""
@ -52,6 +58,7 @@ def translate_field(self, field_name):
if isinstance(field, dict): if isinstance(field, dict):
return field.get(to_locale(get_language())) return field.get(to_locale(get_language()))
return None return None
return translate return translate
@ -70,6 +77,7 @@ def index_field(self, field_name):
for key, value in field.items(): for key, value in field.items():
setattr(obj, key, value) setattr(obj, key, value)
return obj return obj
return index return index
@ -236,7 +244,8 @@ class LocaleManagerMixin(models.Manager):
queryset = self.filter(**filters) queryset = self.filter(**filters)
# Prepare field for annotator # Prepare field for annotator
localized_fields = {f'{field}_{prefix}': KeyTextTransform(f'{locale}', field) for field in fields} localized_fields = {f'{field}_{prefix}': KeyTextTransform(f'{locale}', field) for field in
fields}
# Annotate them # Annotate them
for _ in fields: for _ in fields:
@ -245,7 +254,6 @@ class LocaleManagerMixin(models.Manager):
class GMTokenGenerator(PasswordResetTokenGenerator): class GMTokenGenerator(PasswordResetTokenGenerator):
CHANGE_EMAIL = 0 CHANGE_EMAIL = 0
RESET_PASSWORD = 1 RESET_PASSWORD = 1
CHANGE_PASSWORD = 2 CHANGE_PASSWORD = 2
@ -268,10 +276,10 @@ class GMTokenGenerator(PasswordResetTokenGenerator):
""" """
fields = [str(timestamp), str(user.is_active), str(user.pk)] fields = [str(timestamp), str(user.is_active), str(user.pk)]
if self.purpose == self.CHANGE_EMAIL or \ if self.purpose == self.CHANGE_EMAIL or \
self.purpose == self.CONFIRM_EMAIL: self.purpose == self.CONFIRM_EMAIL:
fields.extend([str(user.email_confirmed), str(user.email)]) fields.extend([str(user.email_confirmed), str(user.email)])
elif self.purpose == self.RESET_PASSWORD or \ elif self.purpose == self.RESET_PASSWORD or \
self.purpose == self.CHANGE_PASSWORD: self.purpose == self.CHANGE_PASSWORD:
fields.append(str(user.password)) fields.append(str(user.password))
return fields return fields

View File

@ -1,6 +1,8 @@
"""Pagination settings.""" """Pagination settings."""
from base64 import b64encode from base64 import b64encode
from urllib import parse as urlparse from urllib import parse as urlparse
from django.conf import settings
from rest_framework.pagination import PageNumberPagination, CursorPagination from rest_framework.pagination import PageNumberPagination, CursorPagination
@ -44,3 +46,10 @@ class ProjectMobilePagination(ProjectPageNumberPagination):
if not self.page.has_previous(): if not self.page.has_previous():
return None return None
return self.page.previous_page_number() return self.page.previous_page_number()
class EstablishmentPortionPagination(ProjectMobilePagination):
"""
Pagination for app establishments with limit page size equal to 12
"""
page_size = settings.LIMITING_OUTPUT_OBJECTS

View File

@ -1,6 +1,8 @@
"""Utils app serializer.""" """Utils app serializer."""
from rest_framework import serializers from rest_framework import serializers
from utils.models import PlatformMixin from utils.models import PlatformMixin
from django.core import exceptions
from translation.models import Language
class EmptySerializer(serializers.Serializer): class EmptySerializer(serializers.Serializer):
@ -21,3 +23,26 @@ class TranslatedField(serializers.CharField):
**kwargs): **kwargs):
super().__init__(allow_null=allow_null, required=required, super().__init__(allow_null=allow_null, required=required,
read_only=read_only, **kwargs) read_only=read_only, **kwargs)
def validate_tjson(value):
if not isinstance(value, dict):
raise exceptions.ValidationError(
'invalid_json',
code='invalid_json',
params={'value': value},
)
lang_count = Language.objects.filter(locale__in=value.keys()).count()
if lang_count == 0:
raise exceptions.ValidationError(
'invalid_translated_keys',
code='invalid_translated_keys',
params={'value': value},
)
class TJSONSerializer(serializers.JSONField):
validators = [validate_tjson]

View File

@ -8,6 +8,13 @@ from http.cookies import SimpleCookie
from account.models import User from account.models import User
from news.models import News, NewsType from news.models import News, NewsType
from django.test import TestCase
from translation.models import Language
from django.core import exceptions
from .serializers import validate_tjson
from establishment.models import Establishment, EstablishmentType, Employee
class BaseTestCase(APITestCase): class BaseTestCase(APITestCase):
@ -28,6 +35,12 @@ class BaseTestCase(APITestCase):
'locale': "en" 'locale': "en"
}) })
class TranslateFieldTests(BaseTestCase):
def setUp(self):
super().setUp()
self.news_type = NewsType.objects.create(name="Test news type") self.news_type = NewsType.objects.create(name="Test news type")
self.news_item = News.objects.create( self.news_item = News.objects.create(
@ -45,15 +58,9 @@ class BaseTestCase(APITestCase):
news_type=self.news_type news_type=self.news_type
) )
class TranslateFieldModel(BaseTestCase):
def test_model_field(self): def test_model_field(self):
self.assertIsNotNone(getattr(self.news_item, "title_translated", None)) self.assertIsNotNone(getattr(self.news_item, "title_translated", None))
class TranslateFieldReview(BaseTestCase):
def test_read_locale(self): def test_read_locale(self):
response = self.client.get(f"/api/web/news/{self.news_item.id}/", format='json') response = self.client.get(f"/api/web/news/{self.news_item.id}/", format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
@ -62,3 +69,90 @@ class TranslateFieldReview(BaseTestCase):
self.assertIn("title_translated", news_data) self.assertIn("title_translated", news_data)
self.assertEqual(news_data['title_translated'], "Test news item") self.assertEqual(news_data['title_translated'], "Test news item")
class BaseAttributeTests(BaseTestCase):
def setUp(self):
super().setUp()
self.establishment_type = EstablishmentType.objects.create(name="Test establishment type")
self.establishment = Establishment.objects.create(
name="Test establishment",
establishment_type_id=self.establishment_type.id,
is_publish=True
)
def test_base_attr_api(self):
data = {
'user': self.user.id,
'name': 'Test name'
}
response = self.client.post('/api/back/establishments/employees/', data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response_data = response.json()
self.assertIn("id", response_data)
employee = Employee.objects.get(id=response_data['id'])
self.assertEqual(self.user, employee.created_by)
self.assertEqual(self.user, employee.modified_by)
modify_user = User.objects.create_user(
username='sedragurda2',
password='sedragurdaredips192',
email='sedragurda2@desoz.com',
)
modify_tokkens = User.create_jwt_tokens(modify_user)
self.client.cookies = SimpleCookie(
{'access_token': modify_tokkens.get('access_token'),
'refresh_token': modify_tokkens.get('refresh_token'),
'locale': "en"
})
update_data = {
'name': 'Test new name'
}
response = self.client.patch('/api/back/establishments/employees/1/', data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
employee.refresh_from_db()
self.assertEqual(modify_user, employee.modified_by)
self.assertEqual(self.user, employee.created_by)
class ValidJSONTest(TestCase):
def test_valid_json(self):
lang = Language.objects.create(title='English', locale='en-GB')
lang.save()
data = 'str'
with self.assertRaises(exceptions.ValidationError) as err:
validate_tjson(data)
self.assertEqual(err.exception.code, 'invalid_json')
data = {
"string": "value"
}
with self.assertRaises(exceptions.ValidationError) as err:
validate_tjson(data)
self.assertEqual(err.exception.code, 'invalid_translated_keys')
data = {
"en-GB": "English"
}
try:
validate_tjson(data)
self.assertTrue(True)
except exceptions.ValidationError:
self.assert_(False, "Test json translated FAILED")

View File

@ -327,15 +327,30 @@ FCM_DJANGO_SETTINGS = {
# Thumbnail settings # Thumbnail settings
THUMBNAIL_ALIASES = { THUMBNAIL_ALIASES = {
'': { 'news_preview': {
'tiny': {'size': (100, 0), }, 'web': {'size': (300, 260), }
'small': {'size': (480, 0), }, },
'middle': {'size': (700, 0), }, 'news_promo_horizontal': {
'large': {'size': (1500, 0), }, 'web': {'size': (1900, 600), },
'default': {'size': (300, 200), 'crop': True}, 'mobile': {'size': (375, 260), },
'gallery': {'size': (240, 160), 'crop': True}, },
'establishment_preview': {'size': (300, 280), 'crop': True}, 'news_tile_horizontal': {
} 'web': {'size': (300, 275), },
'mobile': {'size': (343, 180), },
},
'news_tile_vertical': {
'web': {'size': (300, 380), },
},
'news_highlight_vertical': {
'web': {'size': (460, 630), },
},
'news_editor': {
'web': {'size': (940, 430), }, # при загрузке через контент эдитор
'mobile': {'size': (343, 260), }, # через контент эдитор в мобильном браузерe
},
'avatar_comments': {
'web': {'size': (116, 116), },
},
} }
# Password reset # Password reset
@ -412,3 +427,14 @@ SOLO_CACHE_TIMEOUT = 300
SITE_REDIRECT_URL_UNSUBSCRIBE = '/unsubscribe/' SITE_REDIRECT_URL_UNSUBSCRIBE = '/unsubscribe/'
SITE_NAME = 'Gault & Millau' SITE_NAME = 'Gault & Millau'
# Used in annotations for establishments.
DEFAULT_ESTABLISHMENT_PUBLIC_MARK = 10
# Limit output objects (see in pagination classes).
LIMITING_OUTPUT_OBJECTS = 12
# Need to restrict objects to sort (3 times more then expected).
LIMITING_QUERY_NUMBER = LIMITING_OUTPUT_OBJECTS * 3
# GEO
# A Spatial Reference System Identifier
GEO_DEFAULT_SRID = 4326