Merge branch 'develop' into feature/refactor-establishments
This commit is contained in:
commit
403a60623a
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
49
apps/account/tests/tests_web.py
Normal file
49
apps/account/tests/tests_web.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0
apps/authorization/tests/__init__.py
Normal file
0
apps/authorization/tests/__init__.py
Normal 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"]
|
||||||
|
|
@ -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."""
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
"""Establishment app views."""
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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, )
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
116
apps/notification/tests.py
Normal 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)
|
||||||
|
|
@ -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"""
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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
21
apps/utils/decorators.py
Normal 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
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user