Merge branch 'develop' into feature/gm-148

This commit is contained in:
evgeniy-st 2019-10-21 20:26:31 +03:00
commit 6c195bc185
43 changed files with 836 additions and 188 deletions

View File

@ -12,7 +12,7 @@ class RoleAdmin(admin.ModelAdmin):
@admin.register(models.UserRole)
class UserRoleAdmin(admin.ModelAdmin):
list_display = ['user', 'role']
list_display = ['user', 'role', 'establishment']
@admin.register(models.User)

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.4 on 2019-10-14 08:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0009_auto_20191011_1123'),
('account', '0010_user_password_confirmed'),
]
operations = [
]

View File

@ -0,0 +1,14 @@
# Generated by Django 2.2.4 on 2019-10-15 07:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0011_merge_20191014_0839'),
('account', '0011_merge_20191014_1258'),
]
operations = [
]

View File

@ -0,0 +1,30 @@
# Generated by Django 2.2.4 on 2019-10-16 08:10
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('establishment', '0033_auto_20191003_0943_squashed_0034_auto_20191003_1036'),
('account', '0012_merge_20191015_0708'),
]
operations = [
migrations.AddField(
model_name='userrole',
name='establishment',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='establishment.Establishment', verbose_name='Establishment'),
),
migrations.AlterField(
model_name='role',
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='role',
name='role',
field=models.PositiveIntegerField(choices=[(1, 'Standard user'), (2, 'Comments moderator'), (3, 'Country admin'), (4, 'Content page manager'), (5, 'Establishment manager')], verbose_name='Role'),
),
]

View File

@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework.authtoken.models import Token
from authorization.models import Application
from establishment.models import Establishment
from location.models import Country
from utils.models import GMTokenGenerator
from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin
@ -23,14 +24,25 @@ class Role(ProjectBaseMixin):
"""Base Role model."""
STANDARD_USER = 1
COMMENTS_MODERATOR = 2
COUNTRY_ADMIN = 3
CONTENT_PAGE_MANAGER = 4
ESTABLISHMENT_MANAGER = 5
REVIEWER_MANGER = 6
RESTAURANT_REVIEWER = 7
ROLE_CHOICES =(
ROLE_CHOICES = (
(STANDARD_USER, 'Standard user'),
(COMMENTS_MODERATOR, 'Comments moderator'),
(COUNTRY_ADMIN, 'Country admin'),
(CONTENT_PAGE_MANAGER, 'Content page manager'),
(ESTABLISHMENT_MANAGER, 'Establishment manager'),
(REVIEWER_MANGER, 'Reviewer manager'),
(RESTAURANT_REVIEWER, 'Restaurant reviewer')
)
role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES,
null=False, blank=False)
country = models.ForeignKey(Country, verbose_name=_('Country'), on_delete=models.CASCADE)
country = models.ForeignKey(Country, verbose_name=_('Country'),
null=True, blank=True, on_delete=models.SET_NULL)
# is_list = models.BooleanField(verbose_name=_('list'), default=True, null=False)
# is_create = models.BooleanField(verbose_name=_('create'), default=False, null=False)
# is_update = models.BooleanField(verbose_name=_('update'), default=False, null=False)
@ -224,4 +236,6 @@ class User(AbstractUser):
class UserRole(ProjectBaseMixin):
"""UserRole model."""
user = models.ForeignKey(User, verbose_name=_('User'), on_delete=models.CASCADE)
role = models.ForeignKey(Role, verbose_name=_('Role'), on_delete=models.SET_NULL, null=True)
role = models.ForeignKey(Role, verbose_name=_('Role'), on_delete=models.SET_NULL, null=True)
establishment = models.ForeignKey(Establishment, verbose_name=_('Establishment'),
on_delete=models.SET_NULL, null=True, blank=True)

View File

@ -40,12 +40,13 @@ 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"
)
# country = Country.objects.first()
# if not country:
country = Country.objects.create(
name=json.dumps({"en-GB": "Test country"}),
code="en"
)
country.save()
self.collection = Collection.objects.create(
name='Test collection',
@ -56,6 +57,8 @@ class CollectionDetailTests(BaseTestCase):
slug='test-collection-slug',
)
self.collection.save()
def test_collection_detail_Read(self):
response = self.client.get(f'/api/web/collections/{self.collection.slug}/establishments/?country_code=en',
format='json')
@ -66,7 +69,7 @@ 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)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
class CollectionGuideDetailTests(CollectionDetailTests):
@ -78,6 +81,7 @@ class CollectionGuideDetailTests(CollectionDetailTests):
start=datetime.now(pytz.utc),
end=datetime.now(pytz.utc)
)
self.guide.save()
def test_guide_detail_Read(self):
response = self.client.get(f'/api/web/collections/guides/{self.guide.id}/', format='json')

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.4 on 2019-10-15 07:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('location', '0012_data_migrate'),
('comment', '0002_comment_language'),
]
operations = [
migrations.RemoveField(
model_name='comment',
name='language',
),
migrations.AddField(
model_name='comment',
name='country',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country', verbose_name='Country'),
),
]

View File

@ -7,6 +7,8 @@ from account.models import User
from utils.models import ProjectBaseMixin
from utils.querysets import ContentTypeQuerySetMixin
from translation.models import Language
from location.models import Country
class CommentQuerySet(ContentTypeQuerySetMixin):
"""QuerySets for Comment model."""
@ -41,7 +43,8 @@ class Comment(ProjectBaseMixin):
content_object = generic.GenericForeignKey('content_type', 'object_id')
objects = CommentQuerySet.as_manager()
language = models.ForeignKey(Language, verbose_name=_('Locale'), on_delete=models.SET_NULL, null=True)
country = models.ForeignKey(Country, verbose_name=_('Country'),
on_delete=models.SET_NULL, null=True)
class Meta:
"""Meta class"""

View File

@ -1,28 +0,0 @@
from rest_framework import permissions
from account.models import UserRole, Role, User
class IsCommentModerator(permissions.IsAuthenticatedOrReadOnly):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS or \
obj.user == request.user or request.user.is_superuser:
return True
# Must have role
role = Role.objects.filter(role=Role.COMMENTS_MODERATOR,
country__languages__id=obj.language_id)\
.first() # 'Comments moderator'
is_access = UserRole.objects.filter(user=request.user, role=role).exists()
if obj.user != request.user and is_access:
return True
return False

View File

@ -1,32 +1,16 @@
from rest_framework.test import APITestCase
from utils.tests.tests_permissions import BasePermissionTests
from rest_framework import status
from authorization.tests.tests_authorization import get_tokens_for_user
from django.urls import reverse
from django.contrib.contenttypes.models import ContentType
from http.cookies import SimpleCookie
from location.models import Country
from account.models import Role, User, UserRole
from comment.models import Comment
from translation.models import Language
class CommentModeratorPermissionTests(APITestCase):
class CommentModeratorPermissionTests(BasePermissionTests):
def setUp(self):
self.lang = Language.objects.create(
title='Russia',
locale='ru-RU'
)
self.lang.save()
self.country_ru = Country.objects.create(
name='{"ru-RU":"Russia"}',
code='23',
low_price=15,
high_price=150000,
)
self.country_ru.languages.add(self.lang)
self.country_ru.save()
super().setUp()
self.role = Role.objects.create(
role=2,
@ -51,14 +35,11 @@ class CommentModeratorPermissionTests(APITestCase):
user=self.user_test["user"],
object_id= self.country_ru.pk,
content_type_id=content_type.id,
language=self.lang
country=self.country_ru
)
self.comment.save()
self.url = reverse('back:comment:comment-crud', kwargs={"id": self.comment.id})
def test_get(self):
response = self.client.get(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_put_moderator(self):
tokens = User.create_jwt_tokens(self.moderator)
@ -76,6 +57,10 @@ class CommentModeratorPermissionTests(APITestCase):
response = self.client.put(self.url, data=data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_get(self):
response = self.client.get(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_put_other_user(self):
other_user = User.objects.create_user(username='test',
email='test@mail.com',
@ -120,4 +105,3 @@ class CommentModeratorPermissionTests(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -1,7 +1,7 @@
from rest_framework import generics, permissions
from comment.serializers import back as serializers
from comment import models
from comment.permissions import IsCommentModerator
from utils.permissions import IsCommentModerator, IsCountryAdmin
class CommentLstView(generics.ListCreateAPIView):
@ -15,5 +15,5 @@ class CommentRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Comment RUD view."""
serializer_class = serializers.CommentBaseSerializer
queryset = models.Comment.objects.all()
permission_classes = [IsCommentModerator]
permission_classes = [IsCountryAdmin|IsCommentModerator]
lookup_field = 'id'

View File

@ -24,7 +24,7 @@ class Migration(migrations.Migration):
name='index_name',
field=models.CharField(blank=True, db_index=True, max_length=50, null=True, unique=True, default=None, verbose_name='Index name'),
),
migrations.RunPython(fill_establishment_subtype),
migrations.RunPython(fill_establishment_subtype, migrations.RunPython.noop),
migrations.AlterField(
model_name='establishmentsubtype',
name='index_name',

View File

@ -466,6 +466,20 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
return Award.objects.filter(Q(establishment=self) | Q(employees__establishments=self)) \
.latest(field_name='vintage_year')
@property
def country_id(self):
"""
Return Country id of establishment location
"""
return self.address.country_id
@property
def establishment_id(self):
"""
Return establishment id of establishment location
"""
return self.id
class Position(BaseAttributes, TranslatedFieldsMixin):
"""Position model."""
@ -600,6 +614,10 @@ class Plate(TranslatedFieldsMixin, models.Model):
menu = models.ForeignKey(
'establishment.Menu', verbose_name=_('menu'), on_delete=models.CASCADE)
@property
def establishment_id(self):
return self.menu.establishment.id
class Meta:
verbose_name = _('plate')
verbose_name_plural = _('plates')

View File

@ -7,10 +7,11 @@ from main.models import Currency
from establishment.models import Establishment, EstablishmentType, Menu
# Create your tests here.
from translation.models import Language
from account.models import Role, UserRole
from location.models import Country, Address, City, Region
class BaseTestCase(APITestCase):
def setUp(self):
self.username = 'sedragurda'
self.password = 'sedragurdaredips19'
@ -27,11 +28,44 @@ class BaseTestCase(APITestCase):
self.establishment_type = EstablishmentType.objects.create(name="Test establishment type")
# Create lang object
Language.objects.create(
title='English',
locale='en-GB'
self.lang = Language.objects.get(
title='Russia',
locale='ru-RU'
)
self.country_ru = Country.objects.get(
name={"en-GB": "Russian"}
)
self.region = Region.objects.create(name='Moscow area', code='01',
country=self.country_ru)
self.region.save()
self.city = City.objects.create(name='Mosocow', code='01',
region=self.region, country=self.country_ru)
self.city.save()
self.address = Address.objects.create(city=self.city, street_name_1='Krasnaya',
number=2, postal_code='010100')
self.address.save()
self.role = Role.objects.create(role=Role.ESTABLISHMENT_MANAGER)
self.role.save()
self.establishment = Establishment.objects.create(
name="Test establishment",
establishment_type_id=self.establishment_type.id,
is_publish=True,
slug="test",
address=self.address
)
self.establishment.save()
self.user_role = UserRole.objects.create(user=self.user, role=self.role,
establishment=self.establishment)
self.user_role.save()
class EstablishmentBTests(BaseTestCase):
def test_establishment_CRUD(self):
@ -43,25 +77,25 @@ class EstablishmentBTests(BaseTestCase):
'name': 'Test establishment',
'type_id': self.establishment_type.id,
'is_publish': True,
'slug': 'test-establishment-slug',
'slug': 'test-establishment-slug'
}
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')
response = self.client.get(f'/api/back/establishments/{self.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)
response = self.client.patch(f'/api/back/establishments/{self.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')
response = self.client.delete(f'/api/back/establishments/{self.establishment.id}/',
format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
@ -96,39 +130,45 @@ class EmployeeTests(BaseTestCase):
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,
slug="test"
)
# Test childs
class EmailTests(ChildTestCase):
def test_email_CRUD(self):
def setUp(self):
super().setUp()
def test_get(self):
response = self.client.get('/api/back/establishments/emails/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_post(self):
data = {
'email': "test@test.com",
'establishment': self.establishment.id
}
response = self.client.post('/api/back/establishments/emails/', data=data)
self.id_email = response.json()['id']
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get('/api/back/establishments/emails/1/', format='json')
def test_get_by_pk(self):
self.test_post()
response = self.client.get(f'/api/back/establishments/emails/{self.id_email}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_patch(self):
self.test_post()
update_data = {
'email': 'testnew@test.com'
}
response = self.client.patch('/api/back/establishments/emails/1/', data=update_data)
response = self.client.patch(f'/api/back/establishments/emails/{self.id_email}/',
data=update_data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.delete('/api/back/establishments/emails/1/')
def test_email_CRUD(self):
self.test_post()
response = self.client.delete(f'/api/back/establishments/emails/{self.id_email}/')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
@ -285,7 +325,7 @@ 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)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
class EstablishmentWebSlugTests(ChildTestCase):

View File

@ -2,6 +2,7 @@
from django.shortcuts import get_object_or_404
from rest_framework import generics
from utils.permissions import IsCountryAdmin, IsEstablishmentManager
from establishment import models, serializers
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
@ -18,11 +19,13 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP
"""Establishment list/create view."""
queryset = models.Establishment.objects.all()
serializer_class = serializers.EstablishmentListCreateSerializer
permission_classes = [IsCountryAdmin|IsEstablishmentManager]
class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView):
queryset = models.Establishment.objects.all()
serializer_class = serializers.EstablishmentRUDSerializer
permission_classes = [IsCountryAdmin|IsEstablishmentManager]
class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
@ -57,12 +60,14 @@ class MenuListCreateView(generics.ListCreateAPIView):
"""Menu list create view."""
serializer_class = serializers.MenuSerializers
queryset = models.Menu.objects.all()
permission_classes = [IsEstablishmentManager]
class MenuRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Menu RUD view."""
serializer_class = serializers.MenuRUDSerializers
queryset = models.Menu.objects.all()
permission_classes = [IsEstablishmentManager]
class SocialListCreateView(generics.ListCreateAPIView):
@ -70,12 +75,14 @@ class SocialListCreateView(generics.ListCreateAPIView):
serializer_class = serializers.SocialNetworkSerializers
queryset = models.SocialNetwork.objects.all()
pagination_class = None
permission_classes = [IsEstablishmentManager]
class SocialRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Social RUD view."""
serializer_class = serializers.SocialNetworkSerializers
queryset = models.SocialNetwork.objects.all()
permission_classes = [IsEstablishmentManager]
class PlateListCreateView(generics.ListCreateAPIView):
@ -83,12 +90,14 @@ class PlateListCreateView(generics.ListCreateAPIView):
serializer_class = serializers.PlatesSerializers
queryset = models.Plate.objects.all()
pagination_class = None
permission_classes = [IsEstablishmentManager]
class PlateRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Social RUD view."""
serializer_class = serializers.PlatesSerializers
queryset = models.Plate.objects.all()
permission_classes = [IsEstablishmentManager]
class PhonesListCreateView(generics.ListCreateAPIView):
@ -96,12 +105,14 @@ class PhonesListCreateView(generics.ListCreateAPIView):
serializer_class = serializers.ContactPhoneBackSerializers
queryset = models.ContactPhone.objects.all()
pagination_class = None
permission_classes = [IsEstablishmentManager]
class PhonesRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Social RUD view."""
serializer_class = serializers.ContactPhoneBackSerializers
queryset = models.ContactPhone.objects.all()
permission_classes = [IsEstablishmentManager]
class EmailListCreateView(generics.ListCreateAPIView):
@ -109,12 +120,14 @@ class EmailListCreateView(generics.ListCreateAPIView):
serializer_class = serializers.ContactEmailBackSerializers
queryset = models.ContactEmail.objects.all()
pagination_class = None
permission_classes = [IsEstablishmentManager]
class EmailRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Social RUD view."""
serializer_class = serializers.ContactEmailBackSerializers
queryset = models.ContactEmail.objects.all()
permission_classes = [IsEstablishmentManager]
class EmployeeListCreateView(generics.ListCreateAPIView):

View File

@ -10,7 +10,7 @@ from establishment import models, serializers
from main import methods
from main.models import MetaDataContent
from utils.pagination import EstablishmentPortionPagination
from utils.permissions import IsCountryAdmin
class EstablishmentMixinView:
"""Establishment mixin."""

View File

@ -3,7 +3,7 @@ import os
class Migration(migrations.Migration):
# Check migration
def load_data_from_sql(apps, schema_editor):
file_path = os.path.join(os.path.dirname(__file__), 'migrate_lang.sql')
sql_statement = open(file_path).read()

View File

@ -87,7 +87,6 @@ INSERT INTO codelang (code,country) VALUES
,('es-CR','Spanish (Costa Rica)')
,('es-DO','Spanish (Dominican Republic)')
,('es-EC','Spanish (Ecuador)')
,('es-ES','Spanish (Castilian)')
,('es-ES','Spanish (Spain)')
;
INSERT INTO codelang (code,country) VALUES
@ -326,7 +325,7 @@ commit;
INSERT INTO location_country
(code, "name", low_price, high_price, created, modified)
select
select distinct
lpad((row_number() over (order by t.country asc))::text, 3, '0') as code,
jsonb_build_object('en-GB', t.country),
0 as low_price,
@ -335,7 +334,7 @@ select
now() as modified
from
(
select
select
distinct c.country
from country_code c
) t
@ -348,6 +347,7 @@ commit;
INSERT INTO translation_language
(title, locale)
select
distinct
t.country as title,
t.code as locale
from

View File

@ -23,6 +23,10 @@ class Country(TranslatedFieldsMixin, SVGImageMixin, ProjectBaseMixin):
high_price = models.IntegerField(default=50, verbose_name=_('High price'))
languages = models.ManyToManyField(Language, verbose_name=_('Languages'))
@property
def country_id(self):
return self.id
class Meta:
"""Meta class."""
@ -119,6 +123,10 @@ class Address(models.Model):
return {'lat': self.latitude,
'lon': self.longitude}
@property
def country_id(self):
return self.city.country_id
# todo: Make recalculate price levels
@receiver(post_save, sender=Country)

View File

@ -5,11 +5,12 @@ from account.models import User
from rest_framework import status
from http.cookies import SimpleCookie
from location.models import City, Region, Country
from location.models import City, Region, Country, Language
from django.contrib.gis.geos import Point
from account.models import Role, UserRole
class BaseTestCase(APITestCase):
def setUp(self):
self.username = 'sedragurda'
self.password = 'sedragurdaredips19'
@ -20,27 +21,57 @@ class BaseTestCase(APITestCase):
# get tokens
# self.user.is_superuser = True
# self.user.save()
tokkens = User.create_jwt_tokens(self.user)
self.client.cookies = SimpleCookie(
{'access_token': tokkens.get('access_token'),
'refresh_token': tokkens.get('refresh_token')})
self.lang = Language.objects.get(
title='Russia',
locale='ru-RU'
)
self.country_ru = Country.objects.get(
name={"en-GB": "Russian"}
)
self.role = Role.objects.create(role=Role.COUNTRY_ADMIN,
country=self.country_ru)
self.role.save()
self.user_role = UserRole.objects.create(user=self.user, role=self.role)
self.user_role.save()
class CountryTests(BaseTestCase):
def setUp(self):
super().setUp()
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'
'name': {"ru-RU": "NewCountry"},
'code': 'test1'
}
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)
country = Country.objects.get(pk=response_data["id"])
role = Role.objects.create(role=Role.COUNTRY_ADMIN, country=country)
role.save()
user_role = UserRole.objects.create(user=self.user, role=role)
user_role.save()
response = self.client.get('/api/back/location/countries/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.get(f'/api/back/location/countries/{response_data["id"]}/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
@ -64,6 +95,14 @@ class RegionTests(BaseTestCase):
code="test"
)
role = Role.objects.create(role=Role.COUNTRY_ADMIN, country=self.country)
role.save()
user_role = UserRole.objects.create(user=self.user, role=role)
user_role.save()
def test_region_CRUD(self):
response = self.client.get('/api/back/location/regions/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
@ -108,6 +147,13 @@ class CityTests(BaseTestCase):
country=self.country
)
role = Role.objects.create(role=Role.COUNTRY_ADMIN, country=self.country)
role.save()
user_role = UserRole.objects.create(user=self.user, role=role)
user_role.save()
def test_city_CRUD(self):
response = self.client.get('/api/back/location/cities/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
@ -142,6 +188,7 @@ class AddressTests(BaseTestCase):
def setUp(self):
super().setUp()
self.country = Country.objects.create(
name=json.dumps({"en-GB": "Test country"}),
code="test"
@ -160,6 +207,13 @@ class AddressTests(BaseTestCase):
country=self.country
)
role = Role.objects.create(role=Role.COUNTRY_ADMIN, country=self.country)
role.save()
user_role = UserRole.objects.create(user=self.user, role=role)
user_role.save()
def test_address_CRUD(self):
response = self.client.get('/api/back/location/addresses/', format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
@ -167,10 +221,8 @@ class AddressTests(BaseTestCase):
data = {
'city_id': self.city.id,
'number': '+79999999',
"coordinates": {
"latitude": 37.0625,
"longitude": -95.677068
},
"latitude": 37.0625,
"longitude": -95.677068,
"geo_lon": -95.677068,
"geo_lat": 37.0625
}

View File

@ -3,50 +3,57 @@ from rest_framework import generics
from location import models, serializers
from location.views import common
from utils.permissions import IsCountryAdmin
# Address
class AddressListCreateView(common.AddressViewMixin, generics.ListCreateAPIView):
"""Create view for model Address."""
serializer_class = serializers.AddressDetailSerializer
queryset = models.Address.objects.all()
permission_classes = [IsCountryAdmin]
class AddressRUDView(common.AddressViewMixin, generics.RetrieveUpdateDestroyAPIView):
"""RUD view for model Address."""
serializer_class = serializers.AddressDetailSerializer
queryset = models.Address.objects.all()
permission_classes = [IsCountryAdmin]
# City
class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView):
"""Create view for model City."""
serializer_class = serializers.CitySerializer
permission_classes = [IsCountryAdmin]
class CityRUDView(common.CityViewMixin, generics.RetrieveUpdateDestroyAPIView):
"""RUD view for model City."""
serializer_class = serializers.CitySerializer
permission_classes = [IsCountryAdmin]
# Region
class RegionListCreateView(common.RegionViewMixin, generics.ListCreateAPIView):
"""Create view for model Region"""
serializer_class = serializers.RegionSerializer
permission_classes = [IsCountryAdmin]
class RegionRUDView(common.RegionViewMixin, generics.RetrieveUpdateDestroyAPIView):
"""Retrieve view for model Region"""
serializer_class = serializers.RegionSerializer
permission_classes = [IsCountryAdmin]
# Country
class CountryListCreateView(common.CountryViewMixin, generics.ListCreateAPIView):
class CountryListCreateView(generics.ListCreateAPIView):
"""List/Create view for model Country."""
queryset = models.Country.objects.all()
serializer_class = serializers.CountryBackSerializer
pagination_class = None
permission_classes = [IsCountryAdmin]
class CountryRUDView(common.CountryViewMixin, generics.RetrieveUpdateDestroyAPIView):
class CountryRUDView(generics.RetrieveUpdateDestroyAPIView):
"""RUD view for model Country."""
serializer_class = serializers.CountryBackSerializer
permission_classes = [IsCountryAdmin]
queryset = models.Country.objects.all()

View File

@ -0,0 +1,57 @@
# Generated by Django 2.2.4 on 2019-10-21 13:06
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import utils.models
class Migration(migrations.Migration):
dependencies = [
('location', '0012_data_migrate'),
('news', '0021_auto_20191009_1408'),
]
operations = [
migrations.CreateModel(
name='NewsBanner',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('title', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='title')),
('image_url', models.URLField(blank=True, default=None, null=True, verbose_name='Image URL path')),
('content_url', models.URLField(blank=True, default=None, null=True, verbose_name='Content URL path')),
],
options={
'abstract': False,
},
bases=(models.Model, utils.models.TranslatedFieldsMixin),
),
migrations.CreateModel(
name='Agenda',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('event_datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Event datetime')),
('content', utils.models.TJSONField(blank=True, default=None, help_text='{"en-GB":"some text"}', null=True, verbose_name='content')),
('address', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Address', verbose_name='address')),
],
options={
'abstract': False,
},
bases=(models.Model, utils.models.TranslatedFieldsMixin),
),
migrations.AddField(
model_name='news',
name='agenda',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='news.Agenda', verbose_name='agenda'),
),
migrations.AddField(
model_name='news',
name='banner',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='news.NewsBanner', verbose_name='banner'),
),
]

View File

@ -4,7 +4,7 @@ from django.contrib.contenttypes import fields as generic
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from rest_framework.reverse import reverse
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin, ProjectBaseMixin
from rating.models import Rating
@ -38,7 +38,7 @@ class NewsQuerySet(models.QuerySet):
def with_extended_related(self):
"""Return qs with related objects."""
return self.select_related('created_by')
return self.select_related('created_by', 'agenda', 'banner')
def by_type(self, news_type):
"""Filter News by type"""
@ -61,15 +61,39 @@ class NewsQuerySet(models.QuerySet):
# todo: filter by best score
# todo: filter by country?
def should_read(self, news):
return self.model.objects.exclude(pk=news.pk).published().\
return self.model.objects.exclude(pk=news.pk).published(). \
with_base_related().by_type(news.news_type).distinct().order_by('?')
def same_theme(self, news):
return self.model.objects.exclude(pk=news.pk).published().\
with_base_related().by_type(news.news_type).\
return self.model.objects.exclude(pk=news.pk).published(). \
with_base_related().by_type(news.news_type). \
by_tags(news.tags.all()).distinct().order_by('-start')
class Agenda(ProjectBaseMixin, TranslatedFieldsMixin):
"""News agenda model"""
event_datetime = models.DateTimeField(default=timezone.now, editable=False,
verbose_name=_('Event datetime'))
address = models.ForeignKey('location.Address', blank=True, null=True,
default=None, verbose_name=_('address'),
on_delete=models.SET_NULL)
content = TJSONField(blank=True, null=True, default=None,
verbose_name=_('content'),
help_text='{"en-GB":"some text"}')
class NewsBanner(ProjectBaseMixin, TranslatedFieldsMixin):
"""News banner model"""
title = TJSONField(blank=True, null=True, default=None,
verbose_name=_('title'),
help_text='{"en-GB":"some text"}')
image_url = models.URLField(verbose_name=_('Image URL path'),
blank=True, null=True, default=None)
content_url = models.URLField(verbose_name=_('Content URL path'),
blank=True, null=True, default=None)
class News(BaseAttributes, TranslatedFieldsMixin):
"""News model."""
@ -140,6 +164,14 @@ class News(BaseAttributes, TranslatedFieldsMixin):
gallery = models.ManyToManyField('gallery.Image', through='news.NewsGallery')
ratings = generic.GenericRelation(Rating)
agenda = models.ForeignKey('news.Agenda', blank=True, null=True,
on_delete=models.SET_NULL,
verbose_name=_('agenda'))
banner = models.ForeignKey('news.NewsBanner', blank=True, null=True,
on_delete=models.SET_NULL,
verbose_name=_('banner'))
objects = NewsQuerySet.as_manager()
class Meta:

View File

@ -5,7 +5,7 @@ from rest_framework import serializers
from account.serializers.common import UserBaseSerializer
from gallery.models import Image
from location import models as location_models
from location.serializers import CountrySimpleSerializer
from location.serializers import CountrySimpleSerializer, AddressBaseSerializer
from news import models
from tag.serializers import TagBaseSerializer
from utils.serializers import TranslatedField, ProjectModelSerializer
@ -13,6 +13,7 @@ from utils.serializers import TranslatedField, ProjectModelSerializer
class CropImageSerializer(serializers.Serializer):
"""Serializer for crop images for News object."""
preview_url = serializers.SerializerMethodField()
promo_horizontal_web_url = serializers.SerializerMethodField()
promo_horizontal_mobile_url = serializers.SerializerMethodField()
@ -81,6 +82,40 @@ class NewsImageSerializer(serializers.ModelSerializer):
}
class AgendaSerializer(ProjectModelSerializer):
event_datetime = serializers.DateTimeField()
address = AddressBaseSerializer()
content_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.Agenda
fields = (
'id',
'event_datetime',
'address',
'content_translated'
)
class NewsBannerSerializer(ProjectModelSerializer):
title_translated = TranslatedField()
image_url = serializers.URLField()
content_url = serializers.URLField()
class Meta:
"""Meta class."""
model = models.NewsBanner
fields = (
'id',
'title_translated',
'image_url',
'content_url'
)
class NewsTypeSerializer(serializers.ModelSerializer):
"""News type serializer."""
@ -95,7 +130,7 @@ class NewsBaseSerializer(ProjectModelSerializer):
"""Base serializer for News model."""
# read only fields
title_translated = TranslatedField()
title_translated = TranslatedField(source='title')
subtitle_translated = TranslatedField()
# related fields
@ -151,6 +186,8 @@ class NewsDetailWebSerializer(NewsDetailSerializer):
same_theme = NewsBaseSerializer(many=True, read_only=True)
should_read = NewsBaseSerializer(many=True, read_only=True)
agenda = AgendaSerializer()
banner = NewsBannerSerializer()
class Meta(NewsDetailSerializer.Meta):
"""Meta class."""
@ -158,6 +195,8 @@ class NewsDetailWebSerializer(NewsDetailSerializer):
fields = NewsDetailSerializer.Meta.fields + (
'same_theme',
'should_read',
'agenda',
'banner'
)
@ -191,11 +230,11 @@ class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,
fields = NewsBackOfficeBaseSerializer.Meta.fields + \
NewsDetailSerializer.Meta.fields + (
'description',
'news_type_id',
'country_id',
'template',
'template_display',
'description',
'news_type_id',
'country_id',
'template',
'template_display',
)

View File

@ -1,3 +1,4 @@
from django.urls import reverse
from http.cookies import SimpleCookie
from rest_framework.test import APITestCase
@ -5,8 +6,9 @@ from rest_framework import status
from datetime import datetime, timedelta
from news.models import NewsType, News
from account.models import User
from account.models import User, Role, UserRole
from translation.models import Language
from location.models import Country
# Create your tests here.
@ -22,23 +24,51 @@ class BaseTestCase(APITestCase):
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"},
self.lang = Language.objects.get(
title='Russia',
locale='ru-RU'
)
self.country_ru = Country.objects.get(
name={"en-GB": "Russian"}
)
role = Role.objects.create(
role=Role.CONTENT_PAGE_MANAGER,
country=self.country_ru
)
role.save()
user_role = UserRole.objects.create(
user=self.user,
role=role
)
user_role.save()
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=datetime.now() + timedelta(hours=-2),
end=datetime.now() + timedelta(hours=2),
state=News.PUBLISHED, slug='test-news-slug',)
state=News.PUBLISHED, slug='test-news-slug',
country=self.country_ru)
class NewsTestCase(BaseTestCase):
def setUp(self):
super().setUp()
def test_news_list(self):
def test_web_news(self):
response = self.client.get("/api/web/news/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_web_detail(self):
response = self.client.get(f"/api/web/news/slug/{self.test_news.slug}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = self.client.get("/api/web/news/types/")
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)
@ -47,6 +77,18 @@ class NewsTestCase(BaseTestCase):
response = self.client.get("/api/back/news/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_type_list(self):
response = self.client.get("/api/web/news/types/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_news_back_detail_put(self):
# retrieve-update-destroy
url = reverse('back:news:retrieve-update-destroy', kwargs={'pk': self.test_news.id})
data = {
'id': self.test_news.id,
'description': {"en-GB": "Description test news!"},
'slug': self.test_news.slug,
'start': self.test_news.start,
'playlist': self.test_news.playlist,
'news_type_id':self.test_news.news_type_id,
'country_id': self.country_ru.id
}
response = self.client.put(url, data=data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -8,6 +8,7 @@ from rest_framework.response import Response
from gallery.tasks import delete_image
from news import filters, models, serializers
from rating.tasks import add_rating
from utils.permissions import IsCountryAdmin, IsContentPageManager
class NewsMixinView:
@ -65,6 +66,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView,
serializer_class = serializers.NewsBackOfficeBaseSerializer
create_serializers_class = serializers.NewsBackOfficeDetailSerializer
permission_classes = [IsCountryAdmin|IsContentPageManager]
def get_serializer_class(self):
"""Override serializer class."""
@ -140,6 +142,7 @@ class NewsBackOfficeRUDView(NewsBackOfficeMixinView,
"""Resource for detailed information about news for back-office users."""
serializer_class = serializers.NewsBackOfficeDetailSerializer
permission_classes = [IsCountryAdmin|IsContentPageManager]
def get(self, request, pk, *args, **kwargs):
add_rating(remote_addr=request.META.get('REMOTE_ADDR'),

View File

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.4 on 2019-10-17 12:17
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('location', '0012_data_migrate'),
('review', '0003_review_text'),
]
operations = [
migrations.AddField(
model_name='review',
name='country',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='country', to='location.Country', verbose_name='Country'),
),
]

View File

@ -65,6 +65,9 @@ class Review(BaseAttributes, TranslatedFieldsMixin):
validators=[MinValueValidator(1900),
MaxValueValidator(2100)])
country = models.ForeignKey('location.Country', on_delete=models.CASCADE,
related_name='country', verbose_name=_('Country'),
null=True)
objects = ReviewQuerySet.as_manager()
class Meta:

View File

@ -0,0 +1,18 @@
"""Review app common serializers."""
from review import models
from rest_framework import serializers
class ReviewBaseSerializer(serializers.ModelSerializer):
class Meta:
model = models.Review
fields = ('id',
'reviewer',
'text',
'language',
'status',
'child',
'published_at',
'vintage',
'country'
)

11
apps/review/urls/back.py Normal file
View File

@ -0,0 +1,11 @@
"""Back review URLs"""
from django.urls import path
from review.views import back as views
app_name = 'review'
urlpatterns = [
path('', views.ReviewLstView.as_view(), name='review-list-create'),
path('<int:id>/', views.ReviewRUDView.as_view(), name='review-crud'),
]

19
apps/review/views/back.py Normal file
View File

@ -0,0 +1,19 @@
from rest_framework import generics, permissions
from review.serializers import back as serializers
from review import models
from utils.permissions import IsReviewerManager, IsRestaurantReviewer
class ReviewLstView(generics.ListCreateAPIView):
"""Comment list create view."""
serializer_class = serializers.ReviewBaseSerializer
queryset = models.Review.objects.all()
permission_classes = [permissions.IsAuthenticatedOrReadOnly,]
class ReviewRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Comment RUD view."""
serializer_class = serializers.ReviewBaseSerializer
queryset = models.Review.objects.all()
permission_classes = [IsReviewerManager|IsRestaurantReviewer]
lookup_field = 'id'

View File

@ -29,16 +29,20 @@ def update_document(sender, **kwargs):
registry.update(establishment)
if app_label == 'establishment':
# todo: remove after migration
from establishment import models as establishment_models
if model_name == 'establishmenttype':
establishments = Establishment.objects.filter(
establishment_type=instance)
for establishment in establishments:
registry.update(establishment)
if isinstance(instance, establishment_models.EstablishmentType):
establishments = Establishment.objects.filter(
establishment_type=instance)
for establishment in establishments:
registry.update(establishment)
if model_name == 'establishmentsubtype':
establishments = Establishment.objects.filter(
establishment_subtypes=instance)
for establishment in establishments:
registry.update(establishment)
if instance(instance, establishment_models.EstablishmentSubType):
establishments = Establishment.objects.filter(
establishment_subtypes=instance)
for establishment in establishments:
registry.update(establishment)
if app_label == 'tag':
if model_name == 'tag':

View File

@ -0,0 +1,22 @@
# Generated by Django 2.2.4 on 2019-10-21 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('translation', '0004_auto_20191018_0832'),
]
operations = [
migrations.AlterField(
model_name='language',
name='locale',
field=models.CharField(max_length=10, verbose_name='Locale identifier'),
),
migrations.AlterUniqueTogether(
name='language',
unique_together={('title', 'locale')},
),
]

View File

@ -22,7 +22,7 @@ class Language(models.Model):
title = models.CharField(max_length=255,
verbose_name=_('Language title'))
locale = models.CharField(max_length=10, unique=True,
locale = models.CharField(max_length=10,
verbose_name=_('Locale identifier'))
objects = LanguageQuerySet.as_manager()
@ -32,6 +32,7 @@ class Language(models.Model):
verbose_name = _('Language')
verbose_name_plural = _('Languages')
unique_together = ('title', 'locale')
def __str__(self):
"""String method"""

View File

@ -1,12 +1,15 @@
"""Project custom permissions"""
from rest_framework.permissions import BasePermission
from django.contrib.contenttypes.models import ContentType
from rest_framework import permissions
from rest_framework_simplejwt.tokens import AccessToken
from account.models import UserRole, Role
from authorization.models import JWTRefreshToken
from utils.tokens import GMRefreshToken
class IsAuthenticatedAndTokenIsValid(BasePermission):
class IsAuthenticatedAndTokenIsValid(permissions.BasePermission):
"""
Check if user has a valid token and authenticated
"""
@ -24,7 +27,7 @@ class IsAuthenticatedAndTokenIsValid(BasePermission):
return False
class IsRefreshTokenValid(BasePermission):
class IsRefreshTokenValid(permissions.BasePermission):
"""
Check if user has a valid refresh token and authenticated
"""
@ -38,3 +41,158 @@ class IsRefreshTokenValid(BasePermission):
return refresh_token_qs.exists()
else:
return False
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS or \
obj.user == request.user or request.user.is_superuser:
return True
return False
class IsGuest(permissions.IsAuthenticatedOrReadOnly):
"""
Object-level permission to only allow owners of an object to edit it.
"""
def has_permission(self, request, view):
return request.user.is_authenticated
def has_object_permission(self, request, view, obj):
rules = [
request.user.is_superuser,
request.method in permissions.SAFE_METHODS
]
return any(rules)
class IsStandardUser(IsGuest):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request
rules = [
super().has_object_permission(request, view, obj)
]
if hasattr(obj, 'user'):
rules = [
obj.user == request.user and obj.user.email_confirmed,
super().has_object_permission(request, view, obj)
]
return any(rules)
class IsContentPageManager(IsStandardUser):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request.
role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER,
country_id=obj.country_id)\
.first() # 'Comments moderator'
rules = [
UserRole.objects.filter(user=request.user, role=role).exists(),
# and obj.user != request.user,
super().has_object_permission(request, view, obj)
]
return any(rules)
class IsCountryAdmin(IsStandardUser):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request.
role = Role.objects.filter(role=Role.COUNTRY_ADMIN,
country_id=obj.country_id) \
.first() # 'Comments moderator'
rules = [
UserRole.objects.filter(user=request.user, role=role).exists(),
super().has_object_permission(request, view, obj),
]
return any(rules)
class IsCommentModerator(IsStandardUser):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request.
role = Role.objects.filter(role=Role.COMMENTS_MODERATOR,
country_id=obj.country_id)\
.first() # 'Comments moderator'
rules = [
UserRole.objects.filter(user=request.user, role=role).exists() and
obj.user != request.user,
super().has_object_permission(request, view, obj)
]
return any(rules)
class IsEstablishmentManager(IsStandardUser):
def has_object_permission(self, request, view, obj):
role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER)\
.first() # 'Comments moderator'
rules = [
UserRole.objects.filter(user=request.user, role=role,
establishment_id=obj.establishment_id
).exists(),
super().has_object_permission(request, view, obj)
]
return any(rules)
class IsReviewerManager(IsStandardUser):
def has_object_permission(self, request, view, obj):
role = Role.objects.filter(role=Role.REVIEWER_MANGER,
country_id=obj.country_id)\
.first()
rules = [
UserRole.objects.filter(user=request.user, role=role).exists(),
super().has_object_permission(request, view, obj)
]
return any(rules)
class IsRestaurantReviewer(IsStandardUser):
def has_object_permission(self, request, view, obj):
content_type = ContentType.objects.get(app_lable='establishment',
model='establishment')
role = Role.objects.filter(role=Role.RESTAURANT_REVIEWER,
country=obj.country_id).first()
rules = [
obj.content_type_id == content_type.id and
UserRole.objects.filter(user=request.user, role=role,
establishment_id=obj.object_id
).exists(),
super().has_object_permission(request, view, obj)
]
return any(rules)

View File

@ -33,8 +33,8 @@ def validate_tjson(value):
code='invalid_json',
params={'value': value},
)
lang_count = Language.objects.filter(locale__in=value.keys()).count()
if lang_count != len(value.keys()):
is_lang = Language.objects.filter(locale__in=value.keys()).exists()
if not is_lang:
raise exceptions.ValidationError(
'invalid_translated_keys',
code='invalid_translated_keys',

View File

View File

@ -0,0 +1,37 @@
from django.test import TestCase
from translation.models import Language
from django.core import exceptions
from utils.serializers import validate_tjson
class ValidJSONTest(TestCase):
def test_valid_json(self):
lang = Language.objects.create(title='English', locale='en-GB')
lang.save()
data = 'str'
with self.assertRaises(exceptions.ValidationError) as err:
validate_tjson(data)
self.assertEqual(err.exception.code, 'invalid_json')
data = {
"string": "value"
}
with self.assertRaises(exceptions.ValidationError) as err:
validate_tjson(data)
self.assertEqual(err.exception.code, 'invalid_translated_keys')
data = {
"en-GB": "English"
}
try:
validate_tjson(data)
self.assertTrue(True)
except exceptions.ValidationError:
self.assert_(False, "Test json translated FAILED")

View File

@ -0,0 +1,18 @@
from rest_framework.test import APITestCase
from location.models import Country
from translation.models import Language
class BasePermissionTests(APITestCase):
def setUp(self):
self.lang = Language.objects.get(
title='Russia',
locale='ru-RU'
)
self.country_ru = Country.objects.get(
name={"en-GB": "Russian"}
)

View File

@ -8,11 +8,6 @@ from http.cookies import SimpleCookie
from account.models import User
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
@ -42,6 +37,7 @@ class TranslateFieldTests(BaseTestCase):
super().setUp()
self.news_type = NewsType.objects.create(name="Test news type")
self.news_type.save()
self.news_item = News.objects.create(
created_by=self.user,
@ -58,9 +54,11 @@ class TranslateFieldTests(BaseTestCase):
slug='test',
state=News.PUBLISHED,
)
self.news_item.save()
def test_model_field(self):
self.assertIsNotNone(getattr(self.news_item, "title_translated", None))
self.assertTrue(hasattr(self.news_item, "title_translated"))
def test_read_locale(self):
response = self.client.get(f"/api/web/news/slug/{self.news_item.slug}/", format='json')
@ -69,7 +67,7 @@ class TranslateFieldTests(BaseTestCase):
self.assertIn("title_translated", news_data)
self.assertEqual(news_data['title_translated'], "Test news item")
self.assertIn("Test news item", news_data['title_translated'])
class BaseAttributeTests(BaseTestCase):
@ -125,36 +123,3 @@ class BaseAttributeTests(BaseTestCase):
employee.refresh_from_db()
self.assertEqual(modify_user, employee.modified_by)
self.assertEqual(self.user, employee.created_by)
class ValidJSONTest(TestCase):
def test_valid_json(self):
lang = Language.objects.create(title='English', locale='en-GB')
lang.save()
data = 'str'
with self.assertRaises(exceptions.ValidationError) as err:
validate_tjson(data)
self.assertEqual(err.exception.code, 'invalid_json')
data = {
"string": "value"
}
with self.assertRaises(exceptions.ValidationError) as err:
validate_tjson(data)
self.assertEqual(err.exception.code, 'invalid_translated_keys')
data = {
"en-GB": "English"
}
try:
validate_tjson(data)
self.assertTrue(True)
except exceptions.ValidationError:
self.assert_(False, "Test json translated FAILED")

View File

@ -9,5 +9,7 @@ urlpatterns = [
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
path('location/', include('location.urls.back')),
path('news/', include('news.urls.back')),
path('review/', include('review.urls.back')),
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
]