Merge branch 'develop' into account_test
This commit is contained in:
commit
b57eb29b1c
|
|
@ -1,5 +1,6 @@
|
||||||
"""Common account serializers"""
|
"""Common account serializers"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.contrib.auth import password_validation as password_validators
|
from django.contrib.auth import password_validation as password_validators
|
||||||
from fcm_django.models import FCMDevice
|
from fcm_django.models import FCMDevice
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
|
|
@ -80,23 +81,31 @@ class ChangePasswordSerializer(serializers.ModelSerializer):
|
||||||
"""Serializer for model User."""
|
"""Serializer for model User."""
|
||||||
|
|
||||||
password = serializers.CharField(write_only=True)
|
password = serializers.CharField(write_only=True)
|
||||||
|
old_password = serializers.CharField(write_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class"""
|
"""Meta class"""
|
||||||
model = models.User
|
model = models.User
|
||||||
fields = ('password', )
|
fields = (
|
||||||
|
'password',
|
||||||
|
'old_password',
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
password = attrs.get('password')
|
password = attrs.get('password')
|
||||||
|
old_password = attrs.get('old_password')
|
||||||
try:
|
try:
|
||||||
|
# Check old password
|
||||||
|
if not self.instance.check_password(raw_password=old_password):
|
||||||
|
raise serializers.ValidationError(_('Old password mismatch.'))
|
||||||
# Compare new password with the old ones
|
# Compare new password with the old ones
|
||||||
if self.instance.check_password(raw_password=password):
|
if self.instance.check_password(raw_password=password):
|
||||||
raise utils_exceptions.PasswordsAreEqual()
|
raise serializers.ValidationError(_('Password is already in use'))
|
||||||
# Validate password
|
# Validate password
|
||||||
password_validators.validate_password(password=password)
|
password_validators.validate_password(password=password)
|
||||||
except serializers.ValidationError as e:
|
except serializers.ValidationError as e:
|
||||||
raise serializers.ValidationError(str(e))
|
raise serializers.ValidationError({'detail': e.detail})
|
||||||
else:
|
else:
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,8 @@ class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin):
|
||||||
status=status.HTTP_200_OK)
|
status=status.HTTP_200_OK)
|
||||||
return self._put_cookies_in_response(
|
return self._put_cookies_in_response(
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
cookies=self._put_data_in_cookies(access_token=access_token,
|
||||||
refresh_token=refresh_token),
|
refresh_token=refresh_token,
|
||||||
|
permanent=True),
|
||||||
response=response)
|
response=response)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,9 @@ class GuideQuerySet(models.QuerySet):
|
||||||
class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
||||||
"""Guide model."""
|
"""Guide model."""
|
||||||
parent = models.ForeignKey(
|
parent = models.ForeignKey(
|
||||||
'self', verbose_name=_('parent'), on_delete=models.CASCADE)
|
'self', verbose_name=_('parent'), on_delete=models.CASCADE,
|
||||||
|
null=True, blank=True, default=None
|
||||||
|
)
|
||||||
advertorials = JSONField(
|
advertorials = JSONField(
|
||||||
_('advertorials'), null=True, blank=True,
|
_('advertorials'), null=True, blank=True,
|
||||||
default=None, help_text='{"key":"value"}')
|
default=None, help_text='{"key":"value"}')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,82 @@
|
||||||
from django.test import TestCase
|
import json, pytz
|
||||||
|
from datetime import datetime
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from account.models import User
|
||||||
|
from rest_framework import status
|
||||||
|
from http.cookies import SimpleCookie
|
||||||
|
|
||||||
|
from collection.models import Collection, Guide
|
||||||
|
from location.models import Country
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
||||||
|
|
||||||
|
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 tokkens
|
||||||
|
tokkens = User.create_jwt_tokens(self.user)
|
||||||
|
self.client.cookies = SimpleCookie(
|
||||||
|
{'access_token': tokkens.get('access_token'),
|
||||||
|
'refresh_token': tokkens.get('refresh_token'),
|
||||||
|
'country_code': 'en'})
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionListTests(BaseTestCase):
|
||||||
|
def test_collection_list_Read(self):
|
||||||
|
response = self.client.get('/api/web/collections/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionDetailTests(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
country = Country.objects.first()
|
||||||
|
if not country:
|
||||||
|
country = Country.objects.create(
|
||||||
|
name=json.dumps({"en-GB": "Test country"}),
|
||||||
|
code="en"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.collection = Collection.objects.create(
|
||||||
|
name='Test collection',
|
||||||
|
is_publish=True,
|
||||||
|
start=datetime.now(pytz.utc),
|
||||||
|
end=datetime.now(pytz.utc),
|
||||||
|
country=country
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_collection_detail_Read(self):
|
||||||
|
response = self.client.get(f'/api/web/collections/{self.collection.id}/establishments/?country_code=en',
|
||||||
|
format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionGuideTests(CollectionDetailTests):
|
||||||
|
|
||||||
|
def test_guide_list_Read(self):
|
||||||
|
response = self.client.get('/api/web/collections/guides/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionGuideDetailTests(CollectionDetailTests):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.guide = Guide.objects.create(
|
||||||
|
collection=self.collection,
|
||||||
|
start=datetime.now(pytz.utc),
|
||||||
|
end=datetime.now(pytz.utc)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_guide_detail_Read(self):
|
||||||
|
response = self.client.get(f'/api/web/collections/guides/{self.guide.id}/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-23 09:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('establishment', '0028_auto_20190920_1205'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='establishment',
|
||||||
|
name='name_transliterated',
|
||||||
|
field=models.CharField(default='', max_length=255, verbose_name='Transliterated name'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -219,16 +219,16 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
:return: all establishments within the specified radius of specified point
|
:return: all establishments within the specified radius of specified point
|
||||||
:param unit: length unit e.g. m, km. Default is 'm'.
|
:param unit: length unit e.g. m, km. Default is 'm'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.gis.measure import Distance
|
|
||||||
kwargs = {unit: radius}
|
kwargs = {unit: radius}
|
||||||
return self.filter(address__coordinates__distance_lte=(center, Distance(**kwargs)))
|
return self.filter(address__coordinates__distance_lte=(center, DistanceMeasure(**kwargs)))
|
||||||
|
|
||||||
|
|
||||||
class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
|
||||||
"""Establishment model."""
|
"""Establishment model."""
|
||||||
|
|
||||||
name = models.CharField(_('name'), max_length=255, default='')
|
name = models.CharField(_('name'), max_length=255, default='')
|
||||||
|
name_translated = models.CharField(_('Transliterated name'),
|
||||||
|
max_length=255, default='')
|
||||||
description = TJSONField(blank=True, null=True, default=None,
|
description = TJSONField(blank=True, null=True, default=None,
|
||||||
verbose_name=_('description'),
|
verbose_name=_('description'),
|
||||||
help_text='{"en-GB":"some text"}')
|
help_text='{"en-GB":"some text"}')
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,39 @@ class EstablishmentListCreateSerializer(EstablishmentBaseSerializer):
|
||||||
'type_id',
|
'type_id',
|
||||||
'type',
|
'type',
|
||||||
'socials',
|
'socials',
|
||||||
'image_url'
|
'image_url',
|
||||||
|
# TODO: check in admin filters
|
||||||
|
'is_publish'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EstablishmentRUDSerializer(EstablishmentBaseSerializer):
|
||||||
|
"""Establishment create serializer"""
|
||||||
|
|
||||||
|
type_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
source='establishment_type',
|
||||||
|
queryset=models.EstablishmentType.objects.all(), write_only=True
|
||||||
|
)
|
||||||
|
phones = ContactPhonesSerializer(read_only=False, many=True, )
|
||||||
|
emails = ContactEmailsSerializer(read_only=False, many=True, )
|
||||||
|
socials = SocialNetworkRelatedSerializers(read_only=False, many=True, )
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.Establishment
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'website',
|
||||||
|
'phones',
|
||||||
|
'emails',
|
||||||
|
'price_level',
|
||||||
|
'toque_number',
|
||||||
|
'type_id',
|
||||||
|
'type',
|
||||||
|
'socials',
|
||||||
|
'image_url',
|
||||||
|
# TODO: check in admin filters
|
||||||
|
'is_publish'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ class EstablishmentBaseSerializer(serializers.ModelSerializer):
|
||||||
fields = [
|
fields = [
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
|
'name_transliterated',
|
||||||
'price_level',
|
'price_level',
|
||||||
'toque_number',
|
'toque_number',
|
||||||
'public_mark',
|
'public_mark',
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
import json
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
from account.models import User
|
from account.models import User
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
from establishment.models import Employee
|
from main.models import Currency
|
||||||
|
from establishment.models import Establishment, EstablishmentType, Menu
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -21,9 +24,41 @@ class BaseTestCase(APITestCase):
|
||||||
{'access_token': tokkens.get('access_token'),
|
{'access_token': tokkens.get('access_token'),
|
||||||
'refresh_token': tokkens.get('refresh_token')})
|
'refresh_token': tokkens.get('refresh_token')})
|
||||||
|
|
||||||
|
self.establishment_type = EstablishmentType.objects.create(name="Test establishment type")
|
||||||
|
|
||||||
|
|
||||||
|
class EstablishmentTests(BaseTestCase):
|
||||||
|
def test_establishment_CRUD(self):
|
||||||
|
response = self.client.get('/api/back/establishments/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'name': 'Test establishment',
|
||||||
|
'type_id': self.establishment_type.id,
|
||||||
|
'is_publish': True
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/api/back/establishments/', data=data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
establishment = response.json()
|
||||||
|
|
||||||
|
response = self.client.get(f'/api/back/establishments/{establishment["id"]}/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'name': 'Test new establishment'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch(f'/api/back/establishments/{establishment["id"]}/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete(f'/api/back/establishments/{establishment["id"]}/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
class EmployeeTests(BaseTestCase):
|
class EmployeeTests(BaseTestCase):
|
||||||
def test_employee_CRD(self):
|
def test_employee_CRUD(self):
|
||||||
response = self.client.get('/api/back/establishments/employees/', format='json')
|
response = self.client.get('/api/back/establishments/employees/', format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
@ -38,9 +73,191 @@ class EmployeeTests(BaseTestCase):
|
||||||
response = self.client.get('/api/back/establishments/employees/1/', format='json')
|
response = self.client.get('/api/back/establishments/employees/1/', format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
response = self.client.delete('/api/back/establishments/employees/1/', format='json')
|
response = self.client.delete('/api/back/establishments/employees/1/', format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
# Class to test childs
|
||||||
|
class ChildTestCase(BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.establishment = Establishment.objects.create(
|
||||||
|
name="Test establishment",
|
||||||
|
establishment_type_id=self.establishment_type.id,
|
||||||
|
is_publish=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Test childs
|
||||||
|
class EmailTests(ChildTestCase):
|
||||||
|
def test_email_CRUD(self):
|
||||||
|
response = self.client.get('/api/back/establishments/emails/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'email': "test@test.com",
|
||||||
|
'establishment': self.establishment.id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/api/back/establishments/emails/', data=data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
response = self.client.get('/api/back/establishments/emails/1/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'email': 'testnew@test.com'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch('/api/back/establishments/emails/1/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete('/api/back/establishments/emails/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class PhoneTests(ChildTestCase):
|
||||||
|
def test_phone_CRUD(self):
|
||||||
|
response = self.client.get('/api/back/establishments/phones/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'phone': "+79999999999",
|
||||||
|
'establishment': self.establishment.id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/api/back/establishments/phones/', data=data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
response = self.client.get('/api/back/establishments/phones/1/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'phone': '+79999999998'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch('/api/back/establishments/phones/1/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete('/api/back/establishments/phones/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class SocialTests(ChildTestCase):
|
||||||
|
def test_social_CRUD(self):
|
||||||
|
response = self.client.get('/api/back/establishments/socials/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'title': "Test social",
|
||||||
|
'url': 'https://testsocial.com',
|
||||||
|
'establishment': self.establishment.id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/api/back/establishments/socials/', data=data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
response = self.client.get('/api/back/establishments/socials/1/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'title': 'Test new social'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch('/api/back/establishments/socials/1/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete('/api/back/establishments/socials/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class PlateTests(ChildTestCase):
|
||||||
|
def test_plate_CRUD(self):
|
||||||
|
response = self.client.get('/api/back/establishments/plates/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
menu = Menu.objects.create(
|
||||||
|
category=json.dumps({"en-GB": "Test category"}),
|
||||||
|
establishment=self.establishment
|
||||||
|
)
|
||||||
|
currency = Currency.objects.create(name="Test currency")
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'name': json.dumps({"en-GB": "Test plate"}),
|
||||||
|
'establishment': self.establishment.id,
|
||||||
|
'price': 10,
|
||||||
|
'menu': menu.id,
|
||||||
|
'currency_id': currency.id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/api/back/establishments/plates/', data=data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
response = self.client.get('/api/back/establishments/plates/1/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'name': json.dumps({"en-GB": "Test new plate"})
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch('/api/back/establishments/plates/1/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete('/api/back/establishments/plates/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class MenuTests(ChildTestCase):
|
||||||
|
def test_menu_CRUD(self):
|
||||||
|
response = self.client.get('/api/back/establishments/menus/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'category': json.dumps({"en-GB": "Test category"}),
|
||||||
|
'establishment': self.establishment.id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/api/back/establishments/menus/', data=data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
response = self.client.get('/api/back/establishments/menus/1/', format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'category': json.dumps({"en-GB": "Test new category"})
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch('/api/back/establishments/menus/1/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete('/api/back/establishments/menus/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class EstablishmentShedulerTests(ChildTestCase):
|
||||||
|
def test_shedule_CRUD(self):
|
||||||
|
data = {
|
||||||
|
'weekday': 1
|
||||||
|
}
|
||||||
|
response = self.client.post(f'/api/back/establishments/{self.establishment.id}/schedule/', data=data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
response = self.client.get(f'/api/back/establishments/{self.establishment.id}/schedule/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
update_data = {
|
||||||
|
'weekday': 2
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.patch(f'/api/back/establishments/{self.establishment.id}/schedule/1/', data=update_data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/schedule/1/')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ app_name = 'establishment'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.EstablishmentListCreateView.as_view(), name='list'),
|
path('', views.EstablishmentListCreateView.as_view(), name='list'),
|
||||||
path('<int:pk>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
|
path('<int:pk>/', views.EstablishmentRUDView.as_view(), name='detail'),
|
||||||
path('<int:pk>/schedule/<int:schedule_id>/', views.EstablishmentScheduleRUDView.as_view(),
|
path('<int:pk>/schedule/<int:schedule_id>/', views.EstablishmentScheduleRUDView.as_view(),
|
||||||
name='schedule-rud'),
|
name='schedule-rud'),
|
||||||
path('<int:pk>/schedule/', views.EstablishmentScheduleCreateView.as_view(),
|
path('<int:pk>/schedule/', views.EstablishmentScheduleCreateView.as_view(),
|
||||||
|
|
@ -17,7 +17,7 @@ urlpatterns = [
|
||||||
path('menus/', views.MenuListCreateView.as_view(), name='menu-list'),
|
path('menus/', views.MenuListCreateView.as_view(), name='menu-list'),
|
||||||
path('menus/<int:pk>/', views.MenuRUDView.as_view(), name='menu-rud'),
|
path('menus/<int:pk>/', views.MenuRUDView.as_view(), name='menu-rud'),
|
||||||
path('plates/', views.PlateListCreateView.as_view(), name='plates'),
|
path('plates/', views.PlateListCreateView.as_view(), name='plates'),
|
||||||
path('plates/<int:pk>/', views.PlateListCreateView.as_view(), name='plate-rud'),
|
path('plates/<int:pk>/', views.PlateRUDView.as_view(), name='plate-rud'),
|
||||||
path('socials/', views.SocialListCreateView.as_view(), name='socials'),
|
path('socials/', views.SocialListCreateView.as_view(), name='socials'),
|
||||||
path('socials/<int:pk>/', views.SocialRUDView.as_view(), name='social-rud'),
|
path('socials/<int:pk>/', views.SocialRUDView.as_view(), name='social-rud'),
|
||||||
path('phones/', views.PhonesListCreateView.as_view(), name='phones'),
|
path('phones/', views.PhonesListCreateView.as_view(), name='phones'),
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@ class EstablishmentListCreateView(EstablishmentMixin, generics.ListCreateAPIView
|
||||||
serializer_class = serializers.EstablishmentListCreateSerializer
|
serializer_class = serializers.EstablishmentListCreateSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
queryset = models.Establishment.objects.all()
|
||||||
|
serializer_class = serializers.EstablishmentRUDSerializer
|
||||||
|
|
||||||
|
|
||||||
class MenuListCreateView(generics.ListCreateAPIView):
|
class MenuListCreateView(generics.ListCreateAPIView):
|
||||||
"""Menu list create view."""
|
"""Menu list create view."""
|
||||||
serializer_class = serializers.MenuSerializers
|
serializer_class = serializers.MenuSerializers
|
||||||
|
|
@ -83,7 +88,8 @@ class EmployeeListCreateView(generics.ListCreateAPIView):
|
||||||
queryset = models.Employee.objects.all()
|
queryset = models.Employee.objects.all()
|
||||||
pagination_class = None
|
pagination_class = None
|
||||||
|
|
||||||
class EmployeeRUDView(generics.RetrieveDestroyAPIView):
|
|
||||||
|
class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""Social RUD view."""
|
"""Social RUD view."""
|
||||||
serializer_class = serializers.EmployeeBackSerializers
|
serializer_class = serializers.EmployeeBackSerializers
|
||||||
queryset = models.Employee.objects.all()
|
queryset = models.Employee.objects.all()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Establishment app views."""
|
"""Establishment app views."""
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
@ -29,13 +30,8 @@ class EstablishmentSimilarListView(EstablishmentListView):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Override get_queryset method"""
|
"""Override get_queryset method"""
|
||||||
qs = super().get_queryset()
|
return super().get_queryset().similar(establishment_pk=self.kwargs.get('pk'))\
|
||||||
if not qs.filter(pk=self.kwargs.get('pk')).exists():
|
.order_by('-total_mark')[:13]
|
||||||
return qs.none()
|
|
||||||
return qs.similar(establishment_pk=self.kwargs.get('pk'))\
|
|
||||||
.order_by('-total_mark')[:13]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentRetrieveView(EstablishmentMixin, generics.RetrieveAPIView):
|
class EstablishmentRetrieveView(EstablishmentMixin, generics.RetrieveAPIView):
|
||||||
"""Resource for getting a establishment."""
|
"""Resource for getting a establishment."""
|
||||||
|
|
@ -108,23 +104,25 @@ class EstablishmentFavoritesCreateDestroyView(generics.CreateAPIView, generics.D
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentNearestRetrieveView(EstablishmentMixin, generics.ListAPIView):
|
class EstablishmentNearestRetrieveView(EstablishmentListView, generics.ListAPIView):
|
||||||
"""Resource for getting list of nearest establishments."""
|
"""Resource for getting list of nearest establishments."""
|
||||||
serializer_class = serializers.EstablishmentListSerializer
|
serializer_class = serializers.EstablishmentListSerializer
|
||||||
filter_class = filters.EstablishmentFilter
|
filter_class = filters.EstablishmentFilter
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Overrided method 'get_queryset'."""
|
"""Overridden method 'get_queryset'."""
|
||||||
from django.contrib.gis.geos import Point
|
lat = self.request.query_params.get('lat')
|
||||||
|
lon = self.request.query_params.get('lon')
|
||||||
|
radius = self.request.query_params.get('radius')
|
||||||
|
unit = self.request.query_params.get('unit')
|
||||||
|
|
||||||
center = Point(float(self.request.query_params["lat"]), float(self.request.query_params["lon"]))
|
qs = super(EstablishmentNearestRetrieveView, self).get_queryset()
|
||||||
radius = float(self.request.query_params["radius"])
|
if lat and lon and radius and unit:
|
||||||
unit = self.request.query_params.get("unit", None)
|
center = Point(float(lat), float(lon))
|
||||||
by_distance_from_point_kwargs = {"center": center, "radius": radius, "unit": unit}
|
filter_kwargs = {'center': center, 'radius': float(radius), 'unit': unit}
|
||||||
return super(EstablishmentNearestRetrieveView, self).get_queryset() \
|
return qs.by_distance_from_point(**{k: v for k, v in filter_kwargs.items()
|
||||||
.by_distance_from_point(**{k: v for k, v in by_distance_from_point_kwargs.items() if v is not None}) \
|
if v is not None})
|
||||||
.by_country_code(code=self.request.country_code) \
|
return qs
|
||||||
.annotate_in_favorites(user=self.request.user)
|
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentTagListView(generics.ListAPIView):
|
class EstablishmentTagListView(generics.ListAPIView):
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
"""Location app common serializers."""
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from location import models
|
from location import models
|
||||||
|
from utils.serializers import TranslatedField
|
||||||
|
|
||||||
|
|
||||||
class CountrySerializer(serializers.ModelSerializer):
|
class CountrySerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -20,6 +22,18 @@ class CountrySerializer(serializers.ModelSerializer):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CountrySimpleSerializer(serializers.ModelSerializer):
|
||||||
|
"""Simple country serializer."""
|
||||||
|
|
||||||
|
name_translated = TranslatedField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
|
model = models.Country
|
||||||
|
fields = ('id', 'code', 'name_translated')
|
||||||
|
|
||||||
|
|
||||||
class RegionSerializer(serializers.ModelSerializer):
|
class RegionSerializer(serializers.ModelSerializer):
|
||||||
"""Region serializer"""
|
"""Region serializer"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
"""Main app serializers."""
|
"""Main app serializers."""
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from advertisement.serializers.web import AdvertisementSerializer
|
from advertisement.serializers.web import AdvertisementSerializer
|
||||||
from location.serializers import CountrySerializer
|
from location.serializers import CountrySerializer
|
||||||
from main import models
|
from main import models
|
||||||
|
from utils.serializers import TranslatedField
|
||||||
|
|
||||||
|
|
||||||
class FeatureSerializer(serializers.ModelSerializer):
|
class FeatureSerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -109,16 +109,16 @@ class AwardSerializer(AwardBaseSerializer):
|
||||||
|
|
||||||
|
|
||||||
class MetaDataContentSerializer(serializers.ModelSerializer):
|
class MetaDataContentSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField(source='metadata.id', read_only=True, )
|
"""MetaData content serializer."""
|
||||||
label_translated = serializers.CharField(
|
|
||||||
source='metadata.label_translated', read_only=True, allow_null=True)
|
id = serializers.IntegerField(source='metadata.id', read_only=True)
|
||||||
|
label_translated = TranslatedField(source='metadata.label_translated')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
model = models.MetaDataContent
|
model = models.MetaDataContent
|
||||||
fields = [
|
fields = ('id', 'label_translated')
|
||||||
'id',
|
|
||||||
'label_translated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencySerializer(serializers.ModelSerializer):
|
class CurrencySerializer(serializers.ModelSerializer):
|
||||||
|
|
|
||||||
54
apps/news/migrations/0010_auto_20190923_1131.py
Normal file
54
apps/news/migrations/0010_auto_20190923_1131.py
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-23 11:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('news', '0009_auto_20190901_1032'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='news',
|
||||||
|
name='author',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=255, null=True, verbose_name='Author'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='news',
|
||||||
|
name='image_url',
|
||||||
|
field=models.URLField(blank=True, default=None, null=True, verbose_name='Image URL path'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='news',
|
||||||
|
name='preview_image_url',
|
||||||
|
field=models.URLField(blank=True, default=None, null=True, verbose_name='Preview image URL path'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='news',
|
||||||
|
name='address',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Address', verbose_name='address'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='news',
|
||||||
|
name='country',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country', verbose_name='country'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='news',
|
||||||
|
name='end',
|
||||||
|
field=models.DateTimeField(verbose_name='End'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='news',
|
||||||
|
name='news_type',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='news.NewsType', verbose_name='news type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='news',
|
||||||
|
name='start',
|
||||||
|
field=models.DateTimeField(verbose_name='Start'),
|
||||||
|
),
|
||||||
|
]
|
||||||
28
apps/news/migrations/0011_auto_20190923_1134.py
Normal file
28
apps/news/migrations/0011_auto_20190923_1134.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 2.2.4 on 2019-09-23 11:34
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def copy_image_links(apps, schemaeditor):
|
||||||
|
News = apps.get_model('news', 'News')
|
||||||
|
for news in News.objects.all():
|
||||||
|
if news.image:
|
||||||
|
news.image_url = f'{settings.SCHEMA_URI}://{settings.DOMAIN_URI}{news.image.image.url}'
|
||||||
|
news.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('news', '0010_auto_20190923_1131'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(copy_image_links, migrations.RunPython.noop),
|
||||||
|
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='news',
|
||||||
|
name='image',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,25 +1,31 @@
|
||||||
"""News app models."""
|
"""News app models."""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.contenttypes import fields as generic
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin
|
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin
|
||||||
|
|
||||||
|
|
||||||
class NewsType(models.Model):
|
class NewsType(models.Model):
|
||||||
"""NewsType model."""
|
"""NewsType model."""
|
||||||
|
|
||||||
name = models.CharField(_('name'), max_length=250)
|
name = models.CharField(_('name'), max_length=250)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
verbose_name_plural = _('news types')
|
verbose_name_plural = _('news types')
|
||||||
verbose_name = _('news type')
|
verbose_name = _('news type')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""Overrided __str__ method."""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class NewsQuerySet(models.QuerySet):
|
class NewsQuerySet(models.QuerySet):
|
||||||
"""QuerySet for model News"""
|
"""QuerySet for model News"""
|
||||||
|
|
||||||
def by_type(self, news_type):
|
def by_type(self, news_type):
|
||||||
"""Filter News by type"""
|
"""Filter News by type"""
|
||||||
return self.filter(news_type__name=news_type)
|
return self.filter(news_type__name=news_type)
|
||||||
|
|
@ -30,48 +36,56 @@ class NewsQuerySet(models.QuerySet):
|
||||||
|
|
||||||
def published(self):
|
def published(self):
|
||||||
"""Return only published news"""
|
"""Return only published news"""
|
||||||
return self.filter(is_publish=True)
|
now = timezone.now()
|
||||||
|
return self.filter(is_publish=True, start__lte=now, end__gte=now)
|
||||||
|
|
||||||
|
def with_related(self):
|
||||||
|
"""Return qs with related objects."""
|
||||||
|
return self.select_related('news_type', 'country').prefetch_related('tags')
|
||||||
|
|
||||||
|
|
||||||
class News(BaseAttributes, TranslatedFieldsMixin):
|
class News(BaseAttributes, TranslatedFieldsMixin):
|
||||||
"""News model."""
|
"""News model."""
|
||||||
|
|
||||||
image = models.ForeignKey(
|
news_type = models.ForeignKey(NewsType, on_delete=models.PROTECT,
|
||||||
'gallery.Image', null=True, blank=True, default=None,
|
verbose_name=_('news type'))
|
||||||
verbose_name=_('News image'), on_delete=models.CASCADE)
|
title = TJSONField(blank=True, null=True, default=None,
|
||||||
news_type = models.ForeignKey(
|
verbose_name=_('title'),
|
||||||
NewsType, verbose_name=_('news type'), on_delete=models.CASCADE)
|
help_text='{"en-GB":"some text"}')
|
||||||
|
subtitle = TJSONField(blank=True, null=True, default=None,
|
||||||
title = TJSONField(
|
verbose_name=_('subtitle'),
|
||||||
_('title'), null=True, blank=True,
|
help_text='{"en-GB":"some text"}')
|
||||||
default=None, help_text='{"en-GB":"some text"}')
|
description = TJSONField(blank=True, null=True, default=None,
|
||||||
subtitle = TJSONField(
|
verbose_name=_('description'),
|
||||||
_('subtitle'), null=True, blank=True,
|
help_text='{"en-GB":"some text"}')
|
||||||
default=None, help_text='{"en-GB":"some text"}'
|
start = models.DateTimeField(verbose_name=_('Start'))
|
||||||
)
|
end = models.DateTimeField(verbose_name=_('End'))
|
||||||
description = TJSONField(
|
|
||||||
_('description'), null=True, blank=True,
|
|
||||||
default=None, help_text='{"en-GB":"some text"}'
|
|
||||||
)
|
|
||||||
start = models.DateTimeField(_('start'))
|
|
||||||
end = models.DateTimeField(_('end'))
|
|
||||||
playlist = models.IntegerField(_('playlist'))
|
playlist = models.IntegerField(_('playlist'))
|
||||||
address = models.ForeignKey(
|
is_publish = models.BooleanField(default=False,
|
||||||
'location.Address', verbose_name=_('address'), blank=True,
|
verbose_name=_('Publish status'))
|
||||||
null=True, default=None, on_delete=models.CASCADE)
|
author = models.CharField(max_length=255, blank=True, null=True,
|
||||||
is_publish = models.BooleanField(
|
default=None,verbose_name=_('Author'))
|
||||||
default=False, verbose_name=_('Publish status'))
|
is_highlighted = models.BooleanField(default=False,
|
||||||
country = models.ForeignKey(
|
verbose_name=_('Is highlighted'))
|
||||||
'location.Country', blank=True, null=True,
|
|
||||||
verbose_name=_('country'), on_delete=models.CASCADE)
|
|
||||||
is_highlighted = models.BooleanField(
|
|
||||||
default=False, verbose_name=_('Is highlighted'))
|
|
||||||
# TODO: metadata_keys - описание ключей для динамического построения полей метаданных
|
# TODO: metadata_keys - описание ключей для динамического построения полей метаданных
|
||||||
# TODO: metadata_values - Описание значений для динамических полей из MetadataKeys
|
# TODO: metadata_values - Описание значений для динамических полей из MetadataKeys
|
||||||
|
image_url = models.URLField(blank=True, null=True, default=None,
|
||||||
|
verbose_name=_('Image URL path'))
|
||||||
|
preview_image_url = models.URLField(blank=True, null=True, default=None,
|
||||||
|
verbose_name=_('Preview image URL path'))
|
||||||
|
address = models.ForeignKey('location.Address', blank=True, null=True,
|
||||||
|
default=None, verbose_name=_('address'),
|
||||||
|
on_delete=models.SET_NULL)
|
||||||
|
country = models.ForeignKey('location.Country', blank=True, null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
verbose_name=_('country'))
|
||||||
|
tags = generic.GenericRelation(to='main.MetaDataContent')
|
||||||
|
|
||||||
objects = NewsQuerySet.as_manager()
|
objects = NewsQuerySet.as_manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
verbose_name = _('news')
|
verbose_name = _('news')
|
||||||
verbose_name_plural = _('news')
|
verbose_name_plural = _('news')
|
||||||
|
|
||||||
|
|
|
||||||
101
apps/news/serializers.py
Normal file
101
apps/news/serializers.py
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
"""News app common serializers."""
|
||||||
|
from rest_framework import serializers
|
||||||
|
from location import models as location_models
|
||||||
|
from location.serializers import CountrySimpleSerializer
|
||||||
|
from main.serializers import MetaDataContentSerializer
|
||||||
|
from news import models
|
||||||
|
from utils.serializers import TranslatedField
|
||||||
|
|
||||||
|
|
||||||
|
class NewsTypeSerializer(serializers.ModelSerializer):
|
||||||
|
"""News type serializer."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
|
model = models.NewsType
|
||||||
|
fields = ('id', 'name')
|
||||||
|
|
||||||
|
|
||||||
|
class NewsBaseSerializer(serializers.ModelSerializer):
|
||||||
|
"""Base serializer for News model."""
|
||||||
|
|
||||||
|
# read only fields
|
||||||
|
title_translated = TranslatedField()
|
||||||
|
subtitle_translated = TranslatedField()
|
||||||
|
|
||||||
|
# related fields
|
||||||
|
news_type = NewsTypeSerializer(read_only=True)
|
||||||
|
tags = MetaDataContentSerializer(read_only=True, many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
|
model = models.News
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'title_translated',
|
||||||
|
'subtitle_translated',
|
||||||
|
'image_url',
|
||||||
|
'image_url',
|
||||||
|
'preview_image_url',
|
||||||
|
'news_type',
|
||||||
|
'tags',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NewsDetailSerializer(NewsBaseSerializer):
|
||||||
|
"""News detail serializer."""
|
||||||
|
|
||||||
|
description_translated = TranslatedField()
|
||||||
|
country = CountrySimpleSerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta(NewsBaseSerializer.Meta):
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
|
fields = NewsBaseSerializer.Meta.fields + (
|
||||||
|
'description_translated',
|
||||||
|
'start',
|
||||||
|
'end',
|
||||||
|
'playlist',
|
||||||
|
'is_highlighted',
|
||||||
|
'is_publish',
|
||||||
|
'author',
|
||||||
|
'country',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
|
||||||
|
"""News back office base serializer."""
|
||||||
|
|
||||||
|
class Meta(NewsBaseSerializer.Meta):
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
|
fields = NewsBaseSerializer.Meta.fields + (
|
||||||
|
'title',
|
||||||
|
'subtitle',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
|
||||||
|
NewsDetailSerializer):
|
||||||
|
"""News detail serializer for back-office users."""
|
||||||
|
|
||||||
|
news_type_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
source='news_type', write_only=True,
|
||||||
|
queryset=models.NewsType.objects.all())
|
||||||
|
|
||||||
|
country_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
source='country', write_only=True,
|
||||||
|
queryset=location_models.Country.objects.all())
|
||||||
|
|
||||||
|
class Meta(NewsBackOfficeBaseSerializer.Meta, NewsDetailSerializer.Meta):
|
||||||
|
"""Meta class."""
|
||||||
|
|
||||||
|
fields = NewsBackOfficeBaseSerializer.Meta.fields + \
|
||||||
|
NewsDetailSerializer.Meta.fields + (
|
||||||
|
'description',
|
||||||
|
'news_type_id',
|
||||||
|
'country_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
"""News app common serializers."""
|
|
||||||
from rest_framework import serializers
|
|
||||||
from gallery import models as gallery_models
|
|
||||||
from location.models import Address
|
|
||||||
from location.serializers import AddressSerializer
|
|
||||||
from news import models
|
|
||||||
|
|
||||||
|
|
||||||
class NewsTypeSerializer(serializers.ModelSerializer):
|
|
||||||
"""News type serializer."""
|
|
||||||
class Meta:
|
|
||||||
model = models.NewsType
|
|
||||||
fields = [
|
|
||||||
'id',
|
|
||||||
'name'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class NewsSerializer(serializers.ModelSerializer):
|
|
||||||
"""News serializer."""
|
|
||||||
|
|
||||||
address = AddressSerializer()
|
|
||||||
title_translated = serializers.CharField(read_only=True, allow_null=True)
|
|
||||||
subtitle_translated = serializers.CharField(read_only=True, allow_null=True)
|
|
||||||
description_translated = serializers.CharField(read_only=True, allow_null=True)
|
|
||||||
image_url = serializers.ImageField(source='image.image', allow_null=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.News
|
|
||||||
fields = [
|
|
||||||
'id',
|
|
||||||
'news_type',
|
|
||||||
'start',
|
|
||||||
'end',
|
|
||||||
'playlist',
|
|
||||||
'address',
|
|
||||||
'is_highlighted',
|
|
||||||
'image_url',
|
|
||||||
# Localized fields
|
|
||||||
'title_translated',
|
|
||||||
'subtitle_translated',
|
|
||||||
'description_translated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class NewsCreateUpdateSerializer(NewsSerializer):
|
|
||||||
"""News update serializer."""
|
|
||||||
title = serializers.JSONField()
|
|
||||||
subtitle = serializers.JSONField()
|
|
||||||
description = serializers.JSONField()
|
|
||||||
image = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=gallery_models.Image.objects.all(), required=True,)
|
|
||||||
news_type = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=models.NewsType.objects.all(), write_only=True)
|
|
||||||
address = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=Address.objects.all(), write_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.News
|
|
||||||
read_only_fields = [
|
|
||||||
'id'
|
|
||||||
]
|
|
||||||
fields = [
|
|
||||||
'id',
|
|
||||||
'news_type',
|
|
||||||
'title',
|
|
||||||
'subtitle',
|
|
||||||
'description',
|
|
||||||
'start',
|
|
||||||
'end',
|
|
||||||
'playlist',
|
|
||||||
'address',
|
|
||||||
'image',
|
|
||||||
'is_publish',
|
|
||||||
'country'
|
|
||||||
]
|
|
||||||
|
|
@ -1,3 +1,44 @@
|
||||||
|
from http.cookies import SimpleCookie
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from news.models import NewsType, News
|
||||||
|
from account.models import User
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
||||||
|
|
||||||
|
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')})
|
||||||
|
self.test_news_type = NewsType.objects.create(name="Test news type")
|
||||||
|
self.test_news = News.objects.create(created_by=self.user, modified_by=self.user, title={"en-GB": "Test news"},
|
||||||
|
news_type=self.test_news_type, description={"en-GB": "Description test news"},
|
||||||
|
playlist=1, start="2020-12-03 12:00:00", end="2020-12-13 12:00:00",
|
||||||
|
is_publish=True)
|
||||||
|
|
||||||
|
|
||||||
|
class NewsTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
def test_news_list(self):
|
||||||
|
response = self.client.get("/api/web/news/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_news_detail(self):
|
||||||
|
response = self.client.get(f"/api/web/news/{self.test_news.id}/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_news_type_list(self):
|
||||||
|
response = self.client.get("/api/web/news/type/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
|
||||||
11
apps/news/urls/back.py
Normal file
11
apps/news/urls/back.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
"""News app urlpatterns for backoffice"""
|
||||||
|
from django.urls import path
|
||||||
|
from news import views
|
||||||
|
|
||||||
|
app_name = 'news'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.NewsBackOfficeLCView.as_view(), name='list-create'),
|
||||||
|
path('<int:pk>/', views.NewsBackOfficeRUDView.as_view(),
|
||||||
|
name='retrieve-update-destroy'),
|
||||||
|
]
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
"""Location app urlconf."""
|
"""News app urlconf."""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from news import views
|
||||||
from news.views import common
|
|
||||||
|
|
||||||
app_name = 'news'
|
app_name = 'news'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', common.NewsListView.as_view(), name='list'),
|
path('', views.NewsListView.as_view(), name='list'),
|
||||||
path('<int:pk>/', common.NewsDetailView.as_view(), name='rud'),
|
path('<int:pk>/', views.NewsDetailView.as_view(), name='rud'),
|
||||||
path('type/', common.NewsTypeListView.as_view(), name='type'),
|
path('types/', views.NewsTypeListView.as_view(), name='type'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
68
apps/news/views.py
Normal file
68
apps/news/views.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
"""News app views."""
|
||||||
|
from rest_framework import generics, permissions
|
||||||
|
from news import filters, models, serializers
|
||||||
|
|
||||||
|
|
||||||
|
class NewsMixinView:
|
||||||
|
"""News mixin."""
|
||||||
|
|
||||||
|
permission_classes = (permissions.AllowAny, )
|
||||||
|
serializer_class = serializers.NewsBaseSerializer
|
||||||
|
|
||||||
|
def get_queryset(self, *args, **kwargs):
|
||||||
|
"""Override get_queryset method."""
|
||||||
|
qs = models.News.objects.with_related().published()\
|
||||||
|
.order_by('-is_highlighted', '-created')
|
||||||
|
if self.request.country_code:
|
||||||
|
qs = qs.by_country_code(self.request.country_code)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class NewsListView(NewsMixinView, generics.ListAPIView):
|
||||||
|
"""News list view."""
|
||||||
|
|
||||||
|
filter_class = filters.NewsListFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
class NewsDetailView(NewsMixinView, generics.RetrieveAPIView):
|
||||||
|
"""News detail view."""
|
||||||
|
|
||||||
|
serializer_class = serializers.NewsDetailSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class NewsTypeListView(generics.ListAPIView):
|
||||||
|
"""NewsType list view."""
|
||||||
|
|
||||||
|
pagination_class = None
|
||||||
|
permission_classes = (permissions.AllowAny, )
|
||||||
|
queryset = models.NewsType.objects.all()
|
||||||
|
serializer_class = serializers.NewsTypeSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class NewsBackOfficeMixinView:
|
||||||
|
"""News back office mixin view."""
|
||||||
|
|
||||||
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
queryset = models.News.objects.with_related() \
|
||||||
|
.order_by('-is_highlighted', '-created')
|
||||||
|
|
||||||
|
|
||||||
|
class NewsBackOfficeLCView(NewsBackOfficeMixinView,
|
||||||
|
generics.ListCreateAPIView):
|
||||||
|
"""Resource for a list of news for back-office users."""
|
||||||
|
|
||||||
|
serializer_class = serializers.NewsBackOfficeBaseSerializer
|
||||||
|
create_serializers_class = serializers.NewsBackOfficeDetailSerializer
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
if self.request.method == 'POST':
|
||||||
|
return self.create_serializers_class
|
||||||
|
return super().get_serializer_class()
|
||||||
|
|
||||||
|
|
||||||
|
class NewsBackOfficeRUDView(NewsBackOfficeMixinView,
|
||||||
|
generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
"""Resource for detailed information about news for back-office users."""
|
||||||
|
|
||||||
|
serializer_class = serializers.NewsBackOfficeDetailSerializer
|
||||||
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
"""News app common app."""
|
|
||||||
from rest_framework import generics, permissions
|
|
||||||
|
|
||||||
from news import filters, models
|
|
||||||
from news.serializers import common as serializers
|
|
||||||
from utils.views import JWTGenericViewMixin
|
|
||||||
|
|
||||||
|
|
||||||
class NewsMixin:
|
|
||||||
"""News mixin."""
|
|
||||||
|
|
||||||
permission_classes = (permissions.AllowAny, )
|
|
||||||
serializer_class = serializers.NewsSerializer
|
|
||||||
|
|
||||||
def get_queryset(self, *args, **kwargs):
|
|
||||||
"""Override get_queryset method"""
|
|
||||||
return models.News.objects.published() \
|
|
||||||
.by_country_code(code=self.request.country_code) \
|
|
||||||
.order_by('-is_highlighted', '-created')
|
|
||||||
|
|
||||||
|
|
||||||
class NewsListView(NewsMixin, generics.ListAPIView):
|
|
||||||
"""News list view."""
|
|
||||||
|
|
||||||
filter_class = filters.NewsListFilterSet
|
|
||||||
|
|
||||||
|
|
||||||
class NewsDetailView(NewsMixin, JWTGenericViewMixin, generics.RetrieveAPIView):
|
|
||||||
"""News detail view."""
|
|
||||||
|
|
||||||
|
|
||||||
class NewsTypeListView(generics.ListAPIView):
|
|
||||||
"""NewsType list view."""
|
|
||||||
|
|
||||||
serializer_class = serializers.NewsTypeSerializer
|
|
||||||
permission_classes = (permissions.AllowAny, )
|
|
||||||
pagination_class = None
|
|
||||||
queryset = models.NewsType.objects.all()
|
|
||||||
|
|
@ -1 +1,16 @@
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from partner.models import Partner
|
||||||
|
|
||||||
|
|
||||||
|
class PartnerTestCase(APITestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.test_url = "www.example.com"
|
||||||
|
self.test_partner = Partner.objects.create(url=self.test_url)
|
||||||
|
|
||||||
|
def test_partner_list(self):
|
||||||
|
response = self.client.get("/api/web/partner/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
|
||||||
33
apps/recipe/tests.py
Normal file
33
apps/recipe/tests.py
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
from http.cookies import SimpleCookie
|
||||||
|
|
||||||
|
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from account.models import User
|
||||||
|
from recipe.models import Recipe
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
tokkens = User.create_jwt_tokens(self.user)
|
||||||
|
self.client.cookies = SimpleCookie({'access_token': tokkens.get('access_token'),
|
||||||
|
'refresh_token': tokkens.get('refresh_token')})
|
||||||
|
self.test_recipe = Recipe.objects.create(title={"en-GB": "test title"}, description={"en-GB": "test description"},
|
||||||
|
state=2, author="Test Author", created_by=self.user,
|
||||||
|
modified_by=self.user)
|
||||||
|
|
||||||
|
def test_recipe_list(self):
|
||||||
|
response = self.client.get("/api/web/recipes/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_recipe_detail(self):
|
||||||
|
print(self.test_recipe.id)
|
||||||
|
response = self.client.get(f"/api/web/recipes/{self.test_recipe.id}/")
|
||||||
|
print(response.json())
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
@ -4,7 +4,7 @@ from search_indexes import views
|
||||||
|
|
||||||
|
|
||||||
router = routers.SimpleRouter()
|
router = routers.SimpleRouter()
|
||||||
router.register(r'news', views.NewsDocumentViewSet, basename='news')
|
# router.register(r'news', views.NewsDocumentViewSet, basename='news') # temporarily disabled
|
||||||
router.register(r'establishments', views.EstablishmentDocumentViewSet, basename='establishment')
|
router.register(r'establishments', views.EstablishmentDocumentViewSet, basename='establishment')
|
||||||
|
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
"""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
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,3 +12,12 @@ class SourceSerializerMixin(serializers.Serializer):
|
||||||
source = serializers.ChoiceField(choices=PlatformMixin.SOURCES,
|
source = serializers.ChoiceField(choices=PlatformMixin.SOURCES,
|
||||||
default=PlatformMixin.WEB,
|
default=PlatformMixin.WEB,
|
||||||
write_only=True)
|
write_only=True)
|
||||||
|
|
||||||
|
|
||||||
|
class TranslatedField(serializers.CharField):
|
||||||
|
"""Translated field."""
|
||||||
|
|
||||||
|
def __init__(self, allow_null=True, required=False, read_only=True,
|
||||||
|
**kwargs):
|
||||||
|
super().__init__(allow_null=allow_null, required=required,
|
||||||
|
read_only=read_only, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ ELASTICSEARCH_DSL = {
|
||||||
|
|
||||||
|
|
||||||
ELASTICSEARCH_INDEX_NAMES = {
|
ELASTICSEARCH_INDEX_NAMES = {
|
||||||
'search_indexes.documents.news': 'development_news',
|
# 'search_indexes.documents.news': 'development_news', # temporarily disabled
|
||||||
'search_indexes.documents.establishment': 'development_establishment',
|
'search_indexes.documents.establishment': 'development_establishment',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,6 @@ ELASTICSEARCH_DSL = {
|
||||||
|
|
||||||
|
|
||||||
ELASTICSEARCH_INDEX_NAMES = {
|
ELASTICSEARCH_INDEX_NAMES = {
|
||||||
'search_indexes.documents.news': 'local_news',
|
# 'search_indexes.documents.news': 'local_news',
|
||||||
'search_indexes.documents.establishment': 'local_establishment',
|
'search_indexes.documents.establishment': 'local_establishment',
|
||||||
}
|
}
|
||||||
|
|
@ -22,6 +22,6 @@ ELASTICSEARCH_DSL = {
|
||||||
|
|
||||||
|
|
||||||
ELASTICSEARCH_INDEX_NAMES = {
|
ELASTICSEARCH_INDEX_NAMES = {
|
||||||
'search_indexes.documents.news': 'stage_news',
|
# 'search_indexes.documents.news': 'stage_news', #temporarily disabled
|
||||||
'search_indexes.documents.establishment': 'stage_establishment',
|
'search_indexes.documents.establishment': 'stage_establishment',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,5 @@ urlpatterns = [
|
||||||
namespace='gallery')),
|
namespace='gallery')),
|
||||||
path('establishments/', include('establishment.urls.back')),
|
path('establishments/', include('establishment.urls.back')),
|
||||||
path('location/', include('location.urls.back')),
|
path('location/', include('location.urls.back')),
|
||||||
|
path('news/', include('news.urls.back'))
|
||||||
]
|
]
|
||||||
Loading…
Reference in New Issue
Block a user