From b3eac3666b6cfa59f6667f668560d2ec9c6a9f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 14 Oct 2019 14:48:13 +0300 Subject: [PATCH 01/36] Standart user, guest. --- .../migrations/0011_merge_20191014_0839.py | 14 +++ apps/comment/permissions.py | 28 ----- apps/comment/tests.py | 103 +---------------- apps/comment/views/back.py | 2 +- apps/utils/permissions.py | 63 ++++++++++- apps/utils/tests/__init__.py | 0 apps/utils/tests/tests_json_field.py | 37 +++++++ apps/utils/tests/tests_permissions.py | 104 ++++++++++++++++++ .../{tests.py => tests/tests_translated.py} | 38 ------- 9 files changed, 220 insertions(+), 169 deletions(-) create mode 100644 apps/account/migrations/0011_merge_20191014_0839.py delete mode 100644 apps/comment/permissions.py create mode 100644 apps/utils/tests/__init__.py create mode 100644 apps/utils/tests/tests_json_field.py create mode 100644 apps/utils/tests/tests_permissions.py rename apps/utils/{tests.py => tests/tests_translated.py} (80%) diff --git a/apps/account/migrations/0011_merge_20191014_0839.py b/apps/account/migrations/0011_merge_20191014_0839.py new file mode 100644 index 00000000..653f39b7 --- /dev/null +++ b/apps/account/migrations/0011_merge_20191014_0839.py @@ -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 = [ + ] diff --git a/apps/comment/permissions.py b/apps/comment/permissions.py deleted file mode 100644 index 6d691c07..00000000 --- a/apps/comment/permissions.py +++ /dev/null @@ -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 - diff --git a/apps/comment/tests.py b/apps/comment/tests.py index 949ba597..6c6c3d6a 100644 --- a/apps/comment/tests.py +++ b/apps/comment/tests.py @@ -1,64 +1,12 @@ -from rest_framework.test import APITestCase from rest_framework import status -from authorization.tests.tests_authorization import get_tokens_for_user -from django.urls import reverse -from 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 +from account.models import User +from utils.tests.tests_permissions import BasePermissionTests -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() - - self.role = Role.objects.create( - role=2, - country=self.country_ru - ) - self.role.save() - - self.moderator = User.objects.create_user(username='moderator', - email='moderator@mail.com', - password='passwordmoderator') - - self.userRole = UserRole.objects.create( - user=self.moderator, - role=self.role - ) - self.userRole.save() - - content_type = ContentType.objects.get(app_label='location', model='country') - - self.user_test = get_tokens_for_user() - self.comment = Comment.objects.create(text='Test comment', mark=1, - user=self.user_test["user"], - object_id= self.country_ru.pk, - content_type_id=content_type.id, - language=self.lang - ) - 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) + super().setUp() def test_put_moderator(self): tokens = User.create_jwt_tokens(self.moderator) @@ -76,48 +24,5 @@ class CommentModeratorPermissionTests(APITestCase): response = self.client.put(self.url, data=data, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) - def test_put_other_user(self): - other_user = User.objects.create_user(username='test', - email='test@mail.com', - password='passwordtest') - - tokens = User.create_jwt_tokens(other_user) - - self.client.cookies = SimpleCookie( - {'access_token': tokens.get('access_token'), - 'refresh_token': tokens.get('access_token')}) - - data = { - "id": self.comment.id, - "text": "test text moderator", - "mark": 1, - "user": other_user.id - } - - response = self.client.put(self.url, data=data, format='json') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_put_super_user(self): - super_user = User.objects.create_user(username='super', - email='super@mail.com', - password='passwordtestsuper', - is_superuser=True) - - tokens = User.create_jwt_tokens(super_user) - - self.client.cookies = SimpleCookie( - {'access_token': tokens.get('access_token'), - 'refresh_token': tokens.get('access_token')}) - - data = { - "id": self.comment.id, - "text": "test text moderator", - "mark": 1, - "user": super_user.id - } - - response = self.client.put(self.url, data=data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) - diff --git a/apps/comment/views/back.py b/apps/comment/views/back.py index 77edfa97..7f066f30 100644 --- a/apps/comment/views/back.py +++ b/apps/comment/views/back.py @@ -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 class CommentLstView(generics.ListCreateAPIView): diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 09b24ecd..2d406850 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -1,12 +1,13 @@ """Project custom permissions""" -from rest_framework.permissions import BasePermission +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 +25,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 +39,59 @@ 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_object_permission(self, request, view, obj): + if request.method in permissions.SAFE_METHODS: + return True + + return False + + +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 + if super().has_object_permission(request, view, obj) or\ + obj.user == request.user or request.user.is_superuser: + return True + return False + + +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. + + if super().has_object_permission(request, view, obj): + 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 diff --git a/apps/utils/tests/__init__.py b/apps/utils/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/utils/tests/tests_json_field.py b/apps/utils/tests/tests_json_field.py new file mode 100644 index 00000000..c0207def --- /dev/null +++ b/apps/utils/tests/tests_json_field.py @@ -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") \ No newline at end of file diff --git a/apps/utils/tests/tests_permissions.py b/apps/utils/tests/tests_permissions.py new file mode 100644 index 00000000..1e1e0e81 --- /dev/null +++ b/apps/utils/tests/tests_permissions.py @@ -0,0 +1,104 @@ +from rest_framework.test import APITestCase +from rest_framework import status +from authorization.tests.tests_authorization import get_tokens_for_user +from django.urls import reverse +from 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 BasePermissionTests(APITestCase): + 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() + + self.role = Role.objects.create( + role=2, + country=self.country_ru + ) + self.role.save() + + self.moderator = User.objects.create_user(username='moderator', + email='moderator@mail.com', + password='passwordmoderator') + + self.userRole = UserRole.objects.create( + user=self.moderator, + role=self.role + ) + self.userRole.save() + + content_type = ContentType.objects.get(app_label='location', model='country') + + self.user_test = get_tokens_for_user() + self.comment = Comment.objects.create(text='Test comment', mark=1, + user=self.user_test["user"], + object_id= self.country_ru.pk, + content_type_id=content_type.id, + language=self.lang + ) + 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_other_user(self): + other_user = User.objects.create_user(username='test', + email='test@mail.com', + password='passwordtest') + + tokens = User.create_jwt_tokens(other_user) + + self.client.cookies = SimpleCookie( + {'access_token': tokens.get('access_token'), + 'refresh_token': tokens.get('access_token')}) + + data = { + "id": self.comment.id, + "text": "test text moderator", + "mark": 1, + "user": other_user.id + } + + response = self.client.put(self.url, data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_put_super_user(self): + super_user = User.objects.create_user(username='super', + email='super@mail.com', + password='passwordtestsuper', + is_superuser=True) + + tokens = User.create_jwt_tokens(super_user) + + self.client.cookies = SimpleCookie( + {'access_token': tokens.get('access_token'), + 'refresh_token': tokens.get('access_token')}) + + data = { + "id": self.comment.id, + "text": "test text moderator", + "mark": 1, + "user": super_user.id + } + + response = self.client.put(self.url, data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/apps/utils/tests.py b/apps/utils/tests/tests_translated.py similarity index 80% rename from apps/utils/tests.py rename to apps/utils/tests/tests_translated.py index 0eaf343d..8f5caaaf 100644 --- a/apps/utils/tests.py +++ b/apps/utils/tests/tests_translated.py @@ -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 @@ -125,36 +120,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") \ No newline at end of file From 7b4553663cee20a5147ec54a193a85705167b24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 14 Oct 2019 15:45:41 +0300 Subject: [PATCH 02/36] Fix --- apps/account/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/account/models.py b/apps/account/models.py index 24cc6d95..eb95fac1 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -24,7 +24,7 @@ class Role(ProjectBaseMixin): STANDARD_USER = 1 COMMENTS_MODERATOR = 2 - ROLE_CHOICES =( + ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), (COMMENTS_MODERATOR, 'Comments moderator'), ) @@ -243,4 +243,4 @@ 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) \ No newline at end of file + role = models.ForeignKey(Role, verbose_name=_('Role'), on_delete=models.SET_NULL, null=True) From 0656e2ae3299e665732ba246d9b923e3bc4dba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 14 Oct 2019 15:48:31 +0300 Subject: [PATCH 03/36] Confirmed email for user standard --- apps/utils/permissions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 2d406850..50bf78d2 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -68,7 +68,8 @@ class IsStandardUser(IsGuest): def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request if super().has_object_permission(request, view, obj) or\ - obj.user == request.user or request.user.is_superuser: + (obj.user == request.user and obj.user.email_confirmed) \ + or request.user.is_superuser: return True return False From 1bd3cc91703bf26fd98707f8256820c59c2f741a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 14 Oct 2019 17:32:34 +0300 Subject: [PATCH 04/36] 1 --- apps/account/models.py | 5 ++++- apps/utils/permissions.py | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/account/models.py b/apps/account/models.py index eb95fac1..4f227b56 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -23,14 +23,17 @@ class Role(ProjectBaseMixin): """Base Role model.""" STANDARD_USER = 1 COMMENTS_MODERATOR = 2 + COUNTRY_ADMIN = 3 ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), (COMMENTS_MODERATOR, 'Comments moderator'), + (COUNTRY_ADMIN, 'Country admin'), ) 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) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 50bf78d2..47689ed5 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -54,7 +54,7 @@ class IsGuest(permissions.IsAuthenticatedOrReadOnly): Object-level permission to only allow owners of an object to edit it. """ def has_object_permission(self, request, view, obj): - if request.method in permissions.SAFE_METHODS: + if request.method in permissions.SAFE_METHODS or request.user.is_superuser: return True return False @@ -68,8 +68,7 @@ class IsStandardUser(IsGuest): def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request if super().has_object_permission(request, view, obj) or\ - (obj.user == request.user and obj.user.email_confirmed) \ - or request.user.is_superuser: + (obj.user == request.user and obj.user.email_confirmed): return True return False @@ -96,3 +95,18 @@ class IsCommentModerator(IsStandardUser): return True return False + + +class IsCountryAdmin(IsGuest): + + def has_object_permission(self, request, view, obj): + # Read permissions are allowed to any request. + + # Must have role + role = Role.objects.filter(role=Role.COUNTRY_ADMIN).first() # 'Country admin' + is_access = UserRole.objects.filter(user=request.user, role=role).exists() + + if super().has_object_permission(request, view, obj) and is_access: + return True + + return False From 43531d88dc7ebd897737363f131eec88fb22927e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 15 Oct 2019 10:05:31 +0300 Subject: [PATCH 05/36] Country id --- .../migrations/0003_auto_20191015_0704.py | 24 +++++++++++++++++++ apps/comment/models.py | 8 ++++++- apps/utils/permissions.py | 2 +- apps/utils/tests/tests_permissions.py | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 apps/comment/migrations/0003_auto_20191015_0704.py diff --git a/apps/comment/migrations/0003_auto_20191015_0704.py b/apps/comment/migrations/0003_auto_20191015_0704.py new file mode 100644 index 00000000..09296253 --- /dev/null +++ b/apps/comment/migrations/0003_auto_20191015_0704.py @@ -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'), + ), + ] diff --git a/apps/comment/models.py b/apps/comment/models.py index 0193055d..08ae2cde 100644 --- a/apps/comment/models.py +++ b/apps/comment/models.py @@ -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,11 @@ 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) + # language = models.ForeignKey(Language, verbose_name=_('Locale'), + # on_delete=models.SET_NULL, null=True + # ) class Meta: """Meta class""" diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 47689ed5..f0da64e4 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -103,7 +103,7 @@ class IsCountryAdmin(IsGuest): # Read permissions are allowed to any request. # Must have role - role = Role.objects.filter(role=Role.COUNTRY_ADMIN).first() # 'Country admin' + role = Role.objects.filter(role=Role.COUNTRY_ADMIN, country_id=obj.country_id).first() # 'Country admin' is_access = UserRole.objects.filter(user=request.user, role=role).exists() if super().has_object_permission(request, view, obj) and is_access: diff --git a/apps/utils/tests/tests_permissions.py b/apps/utils/tests/tests_permissions.py index 1e1e0e81..f68f71d6 100644 --- a/apps/utils/tests/tests_permissions.py +++ b/apps/utils/tests/tests_permissions.py @@ -51,7 +51,7 @@ class BasePermissionTests(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}) From ff7df815f44aaca0092477e8ad8c2efaf56b07c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 15 Oct 2019 15:20:44 +0300 Subject: [PATCH 06/36] Fix country_id --- .../account/migrations/0012_merge_20191015_0708.py | 14 ++++++++++++++ apps/comment/models.py | 3 --- apps/utils/permissions.py | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 apps/account/migrations/0012_merge_20191015_0708.py diff --git a/apps/account/migrations/0012_merge_20191015_0708.py b/apps/account/migrations/0012_merge_20191015_0708.py new file mode 100644 index 00000000..91dba02e --- /dev/null +++ b/apps/account/migrations/0012_merge_20191015_0708.py @@ -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 = [ + ] diff --git a/apps/comment/models.py b/apps/comment/models.py index 08ae2cde..55c7802e 100644 --- a/apps/comment/models.py +++ b/apps/comment/models.py @@ -45,9 +45,6 @@ class Comment(ProjectBaseMixin): objects = CommentQuerySet.as_manager() country = models.ForeignKey(Country, verbose_name=_('Country'), on_delete=models.SET_NULL, null=True) - # language = models.ForeignKey(Language, verbose_name=_('Locale'), - # on_delete=models.SET_NULL, null=True - # ) class Meta: """Meta class""" diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index f0da64e4..3b408b31 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -87,7 +87,7 @@ class IsCommentModerator(IsStandardUser): # Must have role role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, - country__languages__id=obj.language_id)\ + country_id=obj.country_id)\ .first() # 'Comments moderator' is_access = UserRole.objects.filter(user=request.user, role=role).exists() From 9acef83894da6573ad991fb4b8c72dc2cf2f0a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 15 Oct 2019 15:23:42 +0300 Subject: [PATCH 07/36] Add country admin --- apps/utils/permissions.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 3b408b31..dec3b848 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -73,7 +73,28 @@ class IsStandardUser(IsGuest): return False -class IsCommentModerator(IsStandardUser): +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. + if super().has_object_permission(request, view, obj): + return True + + # Must have role + role = Role.objects.filter(role=Role.COUNTRY_ADMIN, + country_id=obj.country_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 + + +class IsCommentModerator(IsCountryAdmin): """ Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. From 322cfcd89d21570f2982bf0f83c5692cd5c50ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Tue, 15 Oct 2019 15:33:28 +0300 Subject: [PATCH 08/36] Fix test --- apps/comment/tests.py | 85 ++++++++++++++++++++++++++- apps/utils/permissions.py | 15 ----- apps/utils/tests/tests_permissions.py | 79 ------------------------- 3 files changed, 82 insertions(+), 97 deletions(-) diff --git a/apps/comment/tests.py b/apps/comment/tests.py index 6c6c3d6a..8cbcee88 100644 --- a/apps/comment/tests.py +++ b/apps/comment/tests.py @@ -1,13 +1,46 @@ -from rest_framework import status -from http.cookies import SimpleCookie -from account.models import User 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 account.models import Role, User, UserRole +from comment.models import Comment class CommentModeratorPermissionTests(BasePermissionTests): def setUp(self): super().setUp() + self.role = Role.objects.create( + role=2, + country=self.country_ru + ) + self.role.save() + + self.moderator = User.objects.create_user(username='moderator', + email='moderator@mail.com', + password='passwordmoderator') + + self.userRole = UserRole.objects.create( + user=self.moderator, + role=self.role + ) + self.userRole.save() + + content_type = ContentType.objects.get(app_label='location', model='country') + + self.user_test = get_tokens_for_user() + self.comment = Comment.objects.create(text='Test comment', mark=1, + user=self.user_test["user"], + object_id= self.country_ru.pk, + content_type_id=content_type.id, + country=self.country_ru + ) + self.comment.save() + self.url = reverse('back:comment:comment-crud', kwargs={"id": self.comment.id}) + + def test_put_moderator(self): tokens = User.create_jwt_tokens(self.moderator) self.client.cookies = SimpleCookie( @@ -24,5 +57,51 @@ class CommentModeratorPermissionTests(BasePermissionTests): 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_200_OK) + + def test_put_other_user(self): + other_user = User.objects.create_user(username='test', + email='test@mail.com', + password='passwordtest') + + tokens = User.create_jwt_tokens(other_user) + + self.client.cookies = SimpleCookie( + {'access_token': tokens.get('access_token'), + 'refresh_token': tokens.get('access_token')}) + + data = { + "id": self.comment.id, + "text": "test text moderator", + "mark": 1, + "user": other_user.id + } + + response = self.client.put(self.url, data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_put_super_user(self): + super_user = User.objects.create_user(username='super', + email='super@mail.com', + password='passwordtestsuper', + is_superuser=True) + + tokens = User.create_jwt_tokens(super_user) + + self.client.cookies = SimpleCookie( + {'access_token': tokens.get('access_token'), + 'refresh_token': tokens.get('access_token')}) + + data = { + "id": self.comment.id, + "text": "test text moderator", + "mark": 1, + "user": super_user.id + } + + response = self.client.put(self.url, data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index dec3b848..2754a3c5 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -116,18 +116,3 @@ class IsCommentModerator(IsCountryAdmin): return True return False - - -class IsCountryAdmin(IsGuest): - - def has_object_permission(self, request, view, obj): - # Read permissions are allowed to any request. - - # Must have role - role = Role.objects.filter(role=Role.COUNTRY_ADMIN, country_id=obj.country_id).first() # 'Country admin' - is_access = UserRole.objects.filter(user=request.user, role=role).exists() - - if super().has_object_permission(request, view, obj) and is_access: - return True - - return False diff --git a/apps/utils/tests/tests_permissions.py b/apps/utils/tests/tests_permissions.py index f68f71d6..ccb202ab 100644 --- a/apps/utils/tests/tests_permissions.py +++ b/apps/utils/tests/tests_permissions.py @@ -1,12 +1,5 @@ from rest_framework.test import APITestCase -from rest_framework import status -from authorization.tests.tests_authorization import get_tokens_for_user -from django.urls import reverse -from 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 @@ -28,77 +21,5 @@ class BasePermissionTests(APITestCase): self.country_ru.languages.add(self.lang) self.country_ru.save() - self.role = Role.objects.create( - role=2, - country=self.country_ru - ) - self.role.save() - self.moderator = User.objects.create_user(username='moderator', - email='moderator@mail.com', - password='passwordmoderator') - self.userRole = UserRole.objects.create( - user=self.moderator, - role=self.role - ) - self.userRole.save() - - content_type = ContentType.objects.get(app_label='location', model='country') - - self.user_test = get_tokens_for_user() - self.comment = Comment.objects.create(text='Test comment', mark=1, - user=self.user_test["user"], - object_id= self.country_ru.pk, - content_type_id=content_type.id, - 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_other_user(self): - other_user = User.objects.create_user(username='test', - email='test@mail.com', - password='passwordtest') - - tokens = User.create_jwt_tokens(other_user) - - self.client.cookies = SimpleCookie( - {'access_token': tokens.get('access_token'), - 'refresh_token': tokens.get('access_token')}) - - data = { - "id": self.comment.id, - "text": "test text moderator", - "mark": 1, - "user": other_user.id - } - - response = self.client.put(self.url, data=data, format='json') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - - def test_put_super_user(self): - super_user = User.objects.create_user(username='super', - email='super@mail.com', - password='passwordtestsuper', - is_superuser=True) - - tokens = User.create_jwt_tokens(super_user) - - self.client.cookies = SimpleCookie( - {'access_token': tokens.get('access_token'), - 'refresh_token': tokens.get('access_token')}) - - data = { - "id": self.comment.id, - "text": "test text moderator", - "mark": 1, - "user": super_user.id - } - - response = self.client.put(self.url, data=data, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) From 86692019d1983770e1add6a634511acca33fe970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 16 Oct 2019 10:14:23 +0300 Subject: [PATCH 09/36] Add Country admin to views --- apps/account/models.py | 2 ++ apps/account/permissions.py | 0 apps/comment/views/back.py | 4 +-- apps/establishment/models.py | 7 +++++ apps/establishment/views/back.py | 3 ++ apps/establishment/views/web.py | 2 +- apps/location/models.py | 4 +++ apps/location/views/back.py | 14 ++++++---- apps/news/tests.py | 32 +++++++++++++++++++++- apps/news/views.py | 2 ++ apps/utils/permissions.py | 47 ++++++++++++++++++++++++-------- 11 files changed, 97 insertions(+), 20 deletions(-) delete mode 100644 apps/account/permissions.py diff --git a/apps/account/models.py b/apps/account/models.py index 63486f96..96927c1e 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -24,11 +24,13 @@ class Role(ProjectBaseMixin): STANDARD_USER = 1 COMMENTS_MODERATOR = 2 COUNTRY_ADMIN = 3 + CONTENT_PAGE_MANAGER = 4 ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), (COMMENTS_MODERATOR, 'Comments moderator'), (COUNTRY_ADMIN, 'Country admin'), + (CONTENT_PAGE_MANAGER, 'Content page manager') ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, null=False, blank=False) diff --git a/apps/account/permissions.py b/apps/account/permissions.py deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/comment/views/back.py b/apps/comment/views/back.py index 7f066f30..2895fdbe 100644 --- a/apps/comment/views/back.py +++ b/apps/comment/views/back.py @@ -1,7 +1,7 @@ from rest_framework import generics, permissions from comment.serializers import back as serializers from comment import models -from utils.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' diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 2fc63bd5..c97562cc 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -382,6 +382,13 @@ 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 object of establishment location + """ + return self.address.city.country.id + class Position(BaseAttributes, TranslatedFieldsMixin): """Position model.""" diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 5cba8255..fe9633ab 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -4,6 +4,7 @@ from rest_framework import generics from establishment import models from establishment import serializers +from utils.permissions import IsCountryAdmin class EstablishmentMixinViews: @@ -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] class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentRUDSerializer + permission_classes = [IsCountryAdmin] class MenuListCreateView(generics.ListCreateAPIView): diff --git a/apps/establishment/views/web.py b/apps/establishment/views/web.py index 8f5d2a26..36f2a027 100644 --- a/apps/establishment/views/web.py +++ b/apps/establishment/views/web.py @@ -11,7 +11,7 @@ from main import methods from main.models import MetaDataContent from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer from utils.pagination import EstablishmentPortionPagination - +from utils.permissions import IsCountryAdmin class EstablishmentMixinView: """Establishment mixin.""" diff --git a/apps/location/models.py b/apps/location/models.py index 2298c28e..7f797811 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -112,6 +112,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) diff --git a/apps/location/views/back.py b/apps/location/views/back.py index ce6589ed..5c028545 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -3,50 +3,54 @@ 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): """List/Create view for model Country.""" serializer_class = serializers.CountryBackSerializer pagination_class = None - + permission_classes = [IsCountryAdmin] class CountryRUDView(common.CountryViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model Country.""" serializer_class = serializers.CountryBackSerializer + permission_classes = [IsCountryAdmin] \ No newline at end of file diff --git a/apps/news/tests.py b/apps/news/tests.py index 2e24ac45..27ede62c 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -1,3 +1,4 @@ +from django.urls import reverse from http.cookies import SimpleCookie from rest_framework.test import APITestCase @@ -6,7 +7,8 @@ from datetime import datetime, timedelta from news.models import NewsType, News from account.models import User - +from translation.models import Language +from location.models import Country # Create your tests here. @@ -27,7 +29,20 @@ class BaseTestCase(APITestCase): playlist=1, start=datetime.now() + timedelta(hours=-2), end=datetime.now() + timedelta(hours=2), state=News.PUBLISHED, slug='test-news-slug',) + 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() class NewsTestCase(BaseTestCase): @@ -50,3 +65,18 @@ class NewsTestCase(BaseTestCase): 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) \ No newline at end of file diff --git a/apps/news/views.py b/apps/news/views.py index 61a57251..b5273a5e 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -2,6 +2,7 @@ from rest_framework import generics, permissions from news import filters, models, serializers from rating.tasks import add_rating +from utils.permissions import IsCountryAdmin class NewsMixinView: """News mixin.""" @@ -57,6 +58,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, serializer_class = serializers.NewsBackOfficeBaseSerializer create_serializers_class = serializers.NewsBackOfficeDetailSerializer + permission_classes = [IsCountryAdmin] def get_serializer_class(self): """Override serializer class.""" diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 2754a3c5..e2a2b80a 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -67,9 +67,34 @@ class IsStandardUser(IsGuest): """ def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request - if super().has_object_permission(request, view, obj) or\ - (obj.user == request.user and obj.user.email_confirmed): + if obj.user == request.user and obj.user.email_confirmed: return True + + if super().has_object_permission(request, view, obj): + return True + + return False + + +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' + + is_access = UserRole.objects.filter(user=request.user, role=role).exists() + if obj.user != request.user and is_access: + return True + + if super().has_object_permission(request, view, obj): + return True + return False @@ -80,17 +105,18 @@ class IsCountryAdmin(IsStandardUser): """ def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. - if super().has_object_permission(request, view, obj): - return True - - # Must have role role = Role.objects.filter(role=Role.COUNTRY_ADMIN, country_id=obj.country_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 + + if super().has_object_permission(request, view, obj): + return True + return False @@ -102,17 +128,16 @@ class IsCommentModerator(IsCountryAdmin): def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request. - - if super().has_object_permission(request, view, obj): - return True - - # Must have role role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, country_id=obj.country_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 + if super().has_object_permission(request, view, obj): + return True + return False From 04a6bbea191d522d30c4714f1bfc776030646b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 16 Oct 2019 10:43:06 +0300 Subject: [PATCH 10/36] Add content page manager --- apps/news/views.py | 5 +++-- apps/utils/permissions.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/news/views.py b/apps/news/views.py index b5273a5e..105b9064 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -2,7 +2,7 @@ from rest_framework import generics, permissions from news import filters, models, serializers from rating.tasks import add_rating -from utils.permissions import IsCountryAdmin +from utils.permissions import IsCountryAdmin, IsContentPageManager class NewsMixinView: """News mixin.""" @@ -58,7 +58,7 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, serializer_class = serializers.NewsBackOfficeBaseSerializer create_serializers_class = serializers.NewsBackOfficeDetailSerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsCountryAdmin|IsContentPageManager] def get_serializer_class(self): """Override serializer class.""" @@ -76,6 +76,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'), diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index e2a2b80a..f1c2f46d 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -120,7 +120,7 @@ class IsCountryAdmin(IsStandardUser): return False -class IsCommentModerator(IsCountryAdmin): +class IsCommentModerator(IsStandardUser): """ Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. From 3271a6fed99ab72f1cd4def10754419a28c55225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 16 Oct 2019 11:02:26 +0300 Subject: [PATCH 11/36] Establishment manager --- apps/products/urls/back.py | 0 apps/products/views/back.py | 0 apps/utils/permissions.py | 5 ++++- 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 apps/products/urls/back.py create mode 100644 apps/products/views/back.py diff --git a/apps/products/urls/back.py b/apps/products/urls/back.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/products/views/back.py b/apps/products/views/back.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index f1c2f46d..ff10e117 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -75,7 +75,6 @@ class IsStandardUser(IsGuest): return False - class IsContentPageManager(IsStandardUser): """ Object-level permission to only allow owners of an object to edit it. @@ -141,3 +140,7 @@ class IsCommentModerator(IsStandardUser): return True return False + + +class IsEstablishmentManager(IsStandardUser): + pass From 9fd89bf812a508cdb1e7819fc67db0d48b6bd762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 16 Oct 2019 11:40:51 +0300 Subject: [PATCH 12/36] Add establishment manager role --- apps/account/admin.py | 2 +- .../migrations/0013_auto_20191016_0810.py | 30 +++++++++++++++++++ apps/account/models.py | 7 ++++- apps/utils/permissions.py | 15 +++++++++- 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 apps/account/migrations/0013_auto_20191016_0810.py diff --git a/apps/account/admin.py b/apps/account/admin.py index 3b247289..651e5a5a 100644 --- a/apps/account/admin.py +++ b/apps/account/admin.py @@ -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) diff --git a/apps/account/migrations/0013_auto_20191016_0810.py b/apps/account/migrations/0013_auto_20191016_0810.py new file mode 100644 index 00000000..72955cee --- /dev/null +++ b/apps/account/migrations/0013_auto_20191016_0810.py @@ -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'), + ), + ] diff --git a/apps/account/models.py b/apps/account/models.py index 96927c1e..17bbd66a 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -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 @@ -25,12 +26,14 @@ class Role(ProjectBaseMixin): COMMENTS_MODERATOR = 2 COUNTRY_ADMIN = 3 CONTENT_PAGE_MANAGER = 4 + ESTABLISHMENT_MANAGER = 5 ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), (COMMENTS_MODERATOR, 'Comments moderator'), (COUNTRY_ADMIN, 'Country admin'), - (CONTENT_PAGE_MANAGER, 'Content page manager') + (CONTENT_PAGE_MANAGER, 'Content page manager'), + (ESTABLISHMENT_MANAGER, 'Establishment manager') ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, null=False, blank=False) @@ -230,3 +233,5 @@ 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) + establishment = models.ForeignKey(Establishment, verbose_name=_('Establishment'), + on_delete=models.SET_NULL, null=True, blank=True) \ No newline at end of file diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index ff10e117..9c703f50 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -143,4 +143,17 @@ class IsCommentModerator(IsStandardUser): class IsEstablishmentManager(IsStandardUser): - pass + + def has_object_permission(self, request, view, obj): + role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER)\ + .first() # 'Comments moderator' + + is_access = UserRole.objects.filter(user=request.user, role=role, + establishment_id=obj.establishment_id).exists() + if is_access: + return True + + if super().has_object_permission(request, view, obj): + return True + + return False From 5ad3776fe30ee761842176c8ec43da6052135bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 16 Oct 2019 12:14:13 +0300 Subject: [PATCH 13/36] establishment manager --- apps/establishment/models.py | 9 ++++++++- apps/establishment/views/back.py | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index c97562cc..7938ae5a 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -385,10 +385,17 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): @property def country_id(self): """ - Return Country object of establishment location + Return Country id of establishment location """ return self.address.city.country.id + @property + def establishment_id(self): + """ + Return establishment id of establishment location + """ + return self.id + class Position(BaseAttributes, TranslatedFieldsMixin): """Position model.""" diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index fe9633ab..591fcc36 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -4,7 +4,7 @@ from rest_framework import generics from establishment import models from establishment import serializers -from utils.permissions import IsCountryAdmin +from utils.permissions import IsCountryAdmin, IsEstablishmentManager class EstablishmentMixinViews: @@ -19,25 +19,27 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP """Establishment list/create view.""" queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentListCreateSerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsCountryAdmin|IsEstablishmentManager] class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentRUDSerializer - permission_classes = [IsCountryAdmin] + permission_classes = [IsCountryAdmin|IsEstablishmentManager] 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): @@ -45,12 +47,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): @@ -58,12 +62,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): @@ -71,12 +77,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): @@ -84,12 +92,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): From 766fdcdad59402bd944a2fbeaf5d523cb6989345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 16 Oct 2019 15:18:31 +0300 Subject: [PATCH 14/36] Add reviewer manager --- apps/account/models.py | 4 +++- apps/utils/permissions.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/account/models.py b/apps/account/models.py index 17bbd66a..559d3ef8 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -27,13 +27,15 @@ class Role(ProjectBaseMixin): COUNTRY_ADMIN = 3 CONTENT_PAGE_MANAGER = 4 ESTABLISHMENT_MANAGER = 5 + REVIEWER_MANGER = 6 ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), (COMMENTS_MODERATOR, 'Comments moderator'), (COUNTRY_ADMIN, 'Country admin'), (CONTENT_PAGE_MANAGER, 'Content page manager'), - (ESTABLISHMENT_MANAGER, 'Establishment manager') + (ESTABLISHMENT_MANAGER, 'Establishment manager'), + (REVIEWER_MANGER, 'Reviewer manager') ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, null=False, blank=False) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 9c703f50..0450e4d3 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -157,3 +157,15 @@ class IsEstablishmentManager(IsStandardUser): return True return False + + +class IsReviewerManager(IsStandardUser): + def has_object_permission(self, request, view, obj): + access_models=[""] + + role = Role.objects.filter(role=Role.REVIEWER_MANGER)\ + .first() # 'Comments moderator' + + is_access = UserRole.objects.filter(user=request.user, role=role) + return False + From c80b53a0fca1dffbc8b369a39cfffc43600a333e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Thu, 17 Oct 2019 15:04:08 +0300 Subject: [PATCH 15/36] reviewer manager --- apps/comment/tests.py | 2 +- apps/utils/permissions.py | 98 +++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/apps/comment/tests.py b/apps/comment/tests.py index 8cbcee88..9b060f4e 100644 --- a/apps/comment/tests.py +++ b/apps/comment/tests.py @@ -59,7 +59,7 @@ class CommentModeratorPermissionTests(BasePermissionTests): def test_get(self): response = self.client.get(self.url, format='json') - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_put_other_user(self): other_user = User.objects.create_user(username='test', diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 0450e4d3..4cfabee8 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -53,11 +53,16 @@ class IsGuest(permissions.IsAuthenticatedOrReadOnly): """ Object-level permission to only allow owners of an object to edit it. """ - def has_object_permission(self, request, view, obj): - if request.method in permissions.SAFE_METHODS or request.user.is_superuser: - return True + def has_permission(self, request, view): + return request.user.is_authenticated - return False + 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): @@ -67,34 +72,32 @@ class IsStandardUser(IsGuest): """ def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request - if obj.user == request.user and obj.user.email_confirmed: - return True + rules = [ + obj.user == request.user and obj.user.email_confirmed, + super().has_object_permission(request, view, obj) + ] - if super().has_object_permission(request, view, obj): - return True + return any(rules) - return False 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' - is_access = UserRole.objects.filter(user=request.user, role=role).exists() - if obj.user != request.user and is_access: - return True - - if super().has_object_permission(request, view, obj): - return True - - return False + 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): @@ -108,15 +111,13 @@ class IsCountryAdmin(IsStandardUser): country_id=obj.country_id) \ .first() # 'Comments moderator' - is_access = UserRole.objects.filter(user=request.user, role=role).exists() + rules = [ + obj.user != request.user and + UserRole.objects.filter(user=request.user, role=role).exists(), + super().has_object_permission(request, view, obj), + ] - if obj.user != request.user and is_access: - return True - - if super().has_object_permission(request, view, obj): - return True - - return False + return any(rules) class IsCommentModerator(IsStandardUser): @@ -124,22 +125,18 @@ 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' - is_access = UserRole.objects.filter(user=request.user, role=role).exists() - - if obj.user != request.user and is_access: - return True - - if super().has_object_permission(request, view, obj): - return True - - return False + 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): @@ -148,24 +145,27 @@ class IsEstablishmentManager(IsStandardUser): role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER)\ .first() # 'Comments moderator' - is_access = UserRole.objects.filter(user=request.user, role=role, - establishment_id=obj.establishment_id).exists() - if is_access: - return True + rules = [ + UserRole.objects.filter(user=request.user, role=role, + establishment_id=obj.establishment_id).exists(), + super().has_object_permission(request, view, obj) + ] - if super().has_object_permission(request, view, obj): - return True - - return False + return any(rules) class IsReviewerManager(IsStandardUser): + def has_object_permission(self, request, view, obj): - access_models=[""] - role = Role.objects.filter(role=Role.REVIEWER_MANGER)\ - .first() # 'Comments moderator' + role = Role.objects.filter(role=Role.REVIEWER_MANGER, + country_id=obj.country_id)\ + .first() - is_access = UserRole.objects.filter(user=request.user, role=role) - return False + rules = [ + UserRole.objects.filter(user=request.user, role=role).exists(), + super().has_object_permission(request, view, obj) + ] + + return any(rules) From 5a058c14f4e2276847a53cf1a82569c347906323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Thu, 17 Oct 2019 15:26:26 +0300 Subject: [PATCH 16/36] Add back for review --- apps/review/migrations/0004_review_country.py | 20 +++++++++++++++++++ apps/review/models.py | 3 +++ apps/review/serializers/back.py | 18 +++++++++++++++++ apps/review/urls/back.py | 11 ++++++++++ apps/review/views/back.py | 19 ++++++++++++++++++ project/urls/back.py | 1 + 6 files changed, 72 insertions(+) create mode 100644 apps/review/migrations/0004_review_country.py create mode 100644 apps/review/serializers/back.py create mode 100644 apps/review/urls/back.py create mode 100644 apps/review/views/back.py diff --git a/apps/review/migrations/0004_review_country.py b/apps/review/migrations/0004_review_country.py new file mode 100644 index 00000000..1d4173e0 --- /dev/null +++ b/apps/review/migrations/0004_review_country.py @@ -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'), + ), + ] diff --git a/apps/review/models.py b/apps/review/models.py index 9d3a39c4..4c7f3385 100644 --- a/apps/review/models.py +++ b/apps/review/models.py @@ -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: diff --git a/apps/review/serializers/back.py b/apps/review/serializers/back.py new file mode 100644 index 00000000..3e816394 --- /dev/null +++ b/apps/review/serializers/back.py @@ -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' + ) \ No newline at end of file diff --git a/apps/review/urls/back.py b/apps/review/urls/back.py new file mode 100644 index 00000000..84ca49f3 --- /dev/null +++ b/apps/review/urls/back.py @@ -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('/', views.ReviewRUDView.as_view(), name='review-crud'), +] diff --git a/apps/review/views/back.py b/apps/review/views/back.py new file mode 100644 index 00000000..a09f8fd9 --- /dev/null +++ b/apps/review/views/back.py @@ -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 + + +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] + lookup_field = 'id' diff --git a/project/urls/back.py b/project/urls/back.py index 59758c66..eb713c97 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -10,4 +10,5 @@ urlpatterns = [ path('news/', include('news.urls.back')), path('account/', include('account.urls.back')), path('comment/', include('comment.urls.back')), + path('review/', include('review.urls.back')), ] \ No newline at end of file From c012f969afbedf204f47886b6e2594f55eb729b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Thu, 17 Oct 2019 16:17:17 +0300 Subject: [PATCH 17/36] Add RestaurantReviewer role --- apps/account/models.py | 4 +++- apps/utils/permissions.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/account/models.py b/apps/account/models.py index 559d3ef8..a9f739bd 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -28,6 +28,7 @@ class Role(ProjectBaseMixin): CONTENT_PAGE_MANAGER = 4 ESTABLISHMENT_MANAGER = 5 REVIEWER_MANGER = 6 + RESTAURANT_REVIEWER = 7 ROLE_CHOICES = ( (STANDARD_USER, 'Standard user'), @@ -35,7 +36,8 @@ class Role(ProjectBaseMixin): (COUNTRY_ADMIN, 'Country admin'), (CONTENT_PAGE_MANAGER, 'Content page manager'), (ESTABLISHMENT_MANAGER, 'Establishment manager'), - (REVIEWER_MANGER, 'Reviewer manager') + (REVIEWER_MANGER, 'Reviewer manager'), + (RESTAURANT_REVIEWER, 'Restaurant reviewer') ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, null=False, blank=False) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 4cfabee8..f979dca7 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -1,4 +1,6 @@ """Project custom permissions""" +from django.contrib.contenttypes.models import ContentType + from rest_framework import permissions from rest_framework_simplejwt.tokens import AccessToken @@ -169,3 +171,23 @@ class IsReviewerManager(IsStandardUser): 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) From 64ac59ee99e36a6c2c8b67bbd5f5a0a7c997b2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Thu, 17 Oct 2019 16:20:25 +0300 Subject: [PATCH 18/36] Add permission to view ReviewRUDView --- apps/review/views/back.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/review/views/back.py b/apps/review/views/back.py index a09f8fd9..2b4288d2 100644 --- a/apps/review/views/back.py +++ b/apps/review/views/back.py @@ -1,7 +1,7 @@ from rest_framework import generics, permissions from review.serializers import back as serializers from review import models -from utils.permissions import IsReviewerManager +from utils.permissions import IsReviewerManager, IsRestaurantReviewer class ReviewLstView(generics.ListCreateAPIView): @@ -15,5 +15,5 @@ class ReviewRUDView(generics.RetrieveUpdateDestroyAPIView): """Comment RUD view.""" serializer_class = serializers.ReviewBaseSerializer queryset = models.Review.objects.all() - permission_classes = [IsReviewerManager] + permission_classes = [IsReviewerManager|IsRestaurantReviewer] lookup_field = 'id' From 2e2ed19118a6f2018621758ada99e4ca812e89f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 18 Oct 2019 12:14:10 +0300 Subject: [PATCH 19/36] Fix establishment tests --- apps/establishment/models.py | 6 ++- apps/establishment/tests.py | 84 ++++++++++++++++++++++++++++-------- apps/utils/permissions.py | 12 ++++-- apps/utils/serializers.py | 4 +- 4 files changed, 81 insertions(+), 25 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 7938ae5a..4aa37ebe 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -387,7 +387,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin): """ Return Country id of establishment location """ - return self.address.city.country.id + return self.address.country_id @property def establishment_id(self): @@ -529,6 +529,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') diff --git a/apps/establishment/tests.py b/apps/establishment/tests.py index 39e28861..9eb8c987 100644 --- a/apps/establishment/tests.py +++ b/apps/establishment/tests.py @@ -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,10 +28,49 @@ class BaseTestCase(APITestCase): self.establishment_type = EstablishmentType.objects.create(name="Test establishment type") # Create lang object - Language.objects.create( + self.lang = Language.objects.create( title='English', locale='en-GB' ) + 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() + + 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): @@ -43,25 +83,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 +136,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) diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index f979dca7..17179483 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -75,10 +75,15 @@ class IsStandardUser(IsGuest): def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request rules = [ - obj.user == request.user and obj.user.email_confirmed, 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) @@ -114,7 +119,7 @@ class IsCountryAdmin(IsStandardUser): .first() # 'Comments moderator' rules = [ - obj.user != request.user and + # obj.user != request.user and UserRole.objects.filter(user=request.user, role=role).exists(), super().has_object_permission(request, view, obj), ] @@ -149,7 +154,8 @@ class IsEstablishmentManager(IsStandardUser): rules = [ UserRole.objects.filter(user=request.user, role=role, - establishment_id=obj.establishment_id).exists(), + establishment_id=obj.establishment_id + ).exists(), super().has_object_permission(request, view, obj) ] diff --git a/apps/utils/serializers.py b/apps/utils/serializers.py index 2b2282d1..90efea00 100644 --- a/apps/utils/serializers.py +++ b/apps/utils/serializers.py @@ -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', From c44f8c39c1ab6db474500d39a3c0a79519e97952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 18 Oct 2019 13:05:42 +0300 Subject: [PATCH 20/36] Fix tests news --- apps/news/tests.py | 44 +++++++++++++++++++++++++++------------ apps/utils/permissions.py | 5 ++--- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/apps/news/tests.py b/apps/news/tests.py index 27ede62c..dd256bac 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -6,7 +6,7 @@ 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. @@ -24,11 +24,7 @@ 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"}, - playlist=1, start=datetime.now() + timedelta(hours=-2), - end=datetime.now() + timedelta(hours=2), - state=News.PUBLISHED, slug='test-news-slug',) + self.lang = Language.objects.create( title='Russia', locale='ru-RU' @@ -44,16 +40,41 @@ class BaseTestCase(APITestCase): self.country_ru.languages.add(self.lang) self.country_ru.save() -class NewsTestCase(BaseTestCase): + role = Role.objects.create( + role=Role.CONTENT_PAGE_MANAGER, + country=self.country_ru + ) + role.save() - def test_news_list(self): + 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', + country=self.country_ru) + +class NewsTestCase(BaseTestCase): + def setUp(self): + super().setUp() + + 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) @@ -62,10 +83,6 @@ 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}) @@ -78,5 +95,6 @@ class NewsTestCase(BaseTestCase): '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) \ No newline at end of file diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 17179483..45d978a0 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -100,8 +100,8 @@ class IsContentPageManager(IsStandardUser): .first() # 'Comments moderator' rules = [ - UserRole.objects.filter(user=request.user, role=role).exists() and - obj.user != request.user, + UserRole.objects.filter(user=request.user, role=role).exists(), + # and obj.user != request.user, super().has_object_permission(request, view, obj) ] return any(rules) @@ -119,7 +119,6 @@ class IsCountryAdmin(IsStandardUser): .first() # 'Comments moderator' rules = [ - # obj.user != request.user and UserRole.objects.filter(user=request.user, role=role).exists(), super().has_object_permission(request, view, obj), ] From 5772558c4b8da5fa19b902ddf5afee9930ba9dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Fri, 18 Oct 2019 16:04:21 +0300 Subject: [PATCH 21/36] Fix --- apps/location/tests.py | 26 +++++++++++++++++++------- apps/location/views/back.py | 5 +++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/location/tests.py b/apps/location/tests.py index f68ba56b..331a9e2b 100644 --- a/apps/location/tests.py +++ b/apps/location/tests.py @@ -5,8 +5,8 @@ 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 account.models import Role, UserRole class BaseTestCase(APITestCase): @@ -20,27 +20,39 @@ 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.create( + title='Russia', + locale='ru-RU' + ) + self.lang.save() + + # role = Role.objects.create(role=Role.COUNTRY_ADMIN) + 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', + 'name': {"ru-RU":"Russia"}, 'code': 'test' } - response = self.client.post('/api/back/location/countries/', data=data, format='json') response_data = response.json() self.assertEqual(response.status_code, status.HTTP_201_CREATED) + response = self.client.get('/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) diff --git a/apps/location/views/back.py b/apps/location/views/back.py index 5c028545..3a2739b2 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -43,14 +43,15 @@ class RegionRUDView(common.RegionViewMixin, generics.RetrieveUpdateDestroyAPIVie serializer_class = serializers.RegionSerializer permission_classes = [IsCountryAdmin] + # Country -class CountryListCreateView(common.CountryViewMixin, generics.ListCreateAPIView): +class CountryListCreateView(generics.ListCreateAPIView): """List/Create view for model Country.""" 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] \ No newline at end of file From c8b3f922e9200e8188ba5844df85ab6a9045ce06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 11:08:30 +0300 Subject: [PATCH 22/36] Fix location tests --- apps/location/tests.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/location/tests.py b/apps/location/tests.py index 331a9e2b..0770227a 100644 --- a/apps/location/tests.py +++ b/apps/location/tests.py @@ -6,6 +6,7 @@ from rest_framework import status from http.cookies import SimpleCookie from location.models import City, Region, Country, Language +from django.contrib.gis.geos import Point from account.models import Role, UserRole class BaseTestCase(APITestCase): @@ -34,6 +35,23 @@ class BaseTestCase(APITestCase): ) 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() + + 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() + # role = Role.objects.create(role=Role.COUNTRY_ADMIN) @@ -154,6 +172,7 @@ class AddressTests(BaseTestCase): def setUp(self): super().setUp() + self.country = Country.objects.create( name=json.dumps({"en-GB": "Test country"}), code="test" @@ -172,6 +191,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) @@ -179,10 +205,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 } From f3389067360b70aea196c305b23048ca3b2aed0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 11:44:47 +0300 Subject: [PATCH 23/36] Fix location tests --- apps/location/models.py | 4 ++++ apps/location/tests.py | 25 +++++++++++++++++++++++++ apps/location/views/back.py | 4 +++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/location/models.py b/apps/location/models.py index 7f797811..2b7aa363 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -21,6 +21,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.""" diff --git a/apps/location/tests.py b/apps/location/tests.py index 0770227a..edb719bd 100644 --- a/apps/location/tests.py +++ b/apps/location/tests.py @@ -59,15 +59,25 @@ class CountryTests(BaseTestCase): def setUp(self): super().setUp() + def test_country_CRUD(self): data = { 'name': {"ru-RU":"Russia"}, 'code': 'test' } + response = self.client.post('/api/back/location/countries/', data=data, format='json') response_data = response.json() self.assertEqual(response.status_code, status.HTTP_201_CREATED) + 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) @@ -94,6 +104,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) @@ -138,6 +156,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) diff --git a/apps/location/views/back.py b/apps/location/views/back.py index 3a2739b2..cb8246a4 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -47,6 +47,7 @@ class RegionRUDView(common.RegionViewMixin, generics.RetrieveUpdateDestroyAPIVie # Country 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] @@ -54,4 +55,5 @@ class CountryListCreateView(generics.ListCreateAPIView): class CountryRUDView(generics.RetrieveUpdateDestroyAPIView): """RUD view for model Country.""" serializer_class = serializers.CountryBackSerializer - permission_classes = [IsCountryAdmin] \ No newline at end of file + permission_classes = [IsCountryAdmin] + queryset = models.Country.objects.all() \ No newline at end of file From 3720ac2b8e07e07c49d6623534c556dd2e435784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 12:17:12 +0300 Subject: [PATCH 24/36] Fix collection test --- apps/collection/tests.py | 18 +++++++++++------- sdtout.txt | 4 ++++ 2 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 sdtout.txt diff --git a/apps/collection/tests.py b/apps/collection/tests.py index 72b40c37..b2b8231b 100644 --- a/apps/collection/tests.py +++ b/apps/collection/tests.py @@ -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') diff --git a/sdtout.txt b/sdtout.txt new file mode 100644 index 00000000..5f575e2c --- /dev/null +++ b/sdtout.txt @@ -0,0 +1,4 @@ +System check identified no issues (0 silenced). +{'count': 1, 'next': None, 'previous': None, 'results': [{'id': 28, 'name': 'test establishment', 'name_translated': '', 'price_level': None, 'toque_number': None, 'public_mark': None, 'slug': None, 'preview_image': None, 'in_favorites': None, 'address': None, 'tags': []}]} +1 +{'id': 1, 'title_translated': 'test title', 'subtitle_translated': None, 'author': 'Test Author', 'published_at': None, 'in_favorites': False, 'description_translated': 'test description'} From 6dd66275ef3520d61a8f0c0ecc2fc200db325843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 12:18:57 +0300 Subject: [PATCH 25/36] Fix trash --- sdtout.txt | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 sdtout.txt diff --git a/sdtout.txt b/sdtout.txt deleted file mode 100644 index 5f575e2c..00000000 --- a/sdtout.txt +++ /dev/null @@ -1,4 +0,0 @@ -System check identified no issues (0 silenced). -{'count': 1, 'next': None, 'previous': None, 'results': [{'id': 28, 'name': 'test establishment', 'name_translated': '', 'price_level': None, 'toque_number': None, 'public_mark': None, 'slug': None, 'preview_image': None, 'in_favorites': None, 'address': None, 'tags': []}]} -1 -{'id': 1, 'title_translated': 'test title', 'subtitle_translated': None, 'author': 'Test Author', 'published_at': None, 'in_favorites': False, 'description_translated': 'test description'} From 019c22a0c886461878ee8debfcf942ebbad68778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 14:39:04 +0300 Subject: [PATCH 26/36] Fix test --- apps/news/serializers.py | 2 +- apps/news/views.py | 1 + apps/utils/tests/tests_translated.py | 7 +++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index c473be1d..f2e307e4 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -22,7 +22,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 diff --git a/apps/news/views.py b/apps/news/views.py index 105b9064..7e272657 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -35,6 +35,7 @@ class NewsDetailView(NewsMixinView, generics.RetrieveAPIView): """Override get_queryset method.""" return super().get_queryset().with_extended_related() + class NewsTypeListView(generics.ListAPIView): """NewsType list view.""" diff --git a/apps/utils/tests/tests_translated.py b/apps/utils/tests/tests_translated.py index 8f5caaaf..c6a990c0 100644 --- a/apps/utils/tests/tests_translated.py +++ b/apps/utils/tests/tests_translated.py @@ -37,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, @@ -53,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') @@ -64,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): From ee93cf9ff6771c7f4976456f767dce2d113a7f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 15:15:19 +0300 Subject: [PATCH 27/36] Fix migrate --- apps/location/migrations/0012_data_migrate.py | 26 +- apps/location/migrations/migrate_lang.sql | 5 +- .../migrations/0005_auto_20191021_1201.py | 22 + .../migrations/006_data_migrate.py | 25 ++ apps/translation/migrations/migrate_lang.sql | 383 +++++++++++++++++ .../translation/migrations/remigrate_lang.sql | 391 ++++++++++++++++++ apps/translation/models.py | 3 +- project/urls/back.py | 2 - 8 files changed, 839 insertions(+), 18 deletions(-) create mode 100644 apps/translation/migrations/0005_auto_20191021_1201.py create mode 100644 apps/translation/migrations/006_data_migrate.py create mode 100644 apps/translation/migrations/migrate_lang.sql create mode 100644 apps/translation/migrations/remigrate_lang.sql diff --git a/apps/location/migrations/0012_data_migrate.py b/apps/location/migrations/0012_data_migrate.py index 511990db..de75c8ad 100644 --- a/apps/location/migrations/0012_data_migrate.py +++ b/apps/location/migrations/0012_data_migrate.py @@ -3,23 +3,23 @@ import os class Migration(migrations.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() - with connection.cursor() as c: - c.execute(sql_statement) - - def revert_data(apps, schema_editor): - file_path = os.path.join(os.path.dirname(__file__), 'remigrate_lang.sql') - sql_statement = open(file_path).read() - with connection.cursor() as c: - c.execute(sql_statement) + # 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() + # with connection.cursor() as c: + # c.execute(sql_statement) + # + # def revert_data(apps, schema_editor): + # file_path = os.path.join(os.path.dirname(__file__), 'remigrate_lang.sql') + # sql_statement = open(file_path).read() + # with connection.cursor() as c: + # c.execute(sql_statement) dependencies = [ ('location', '0011_country_languages'), ] operations = [ - migrations.RunPython(load_data_from_sql, revert_data), + # migrations.RunPython(load_data_from_sql, revert_data), ] diff --git a/apps/location/migrations/migrate_lang.sql b/apps/location/migrations/migrate_lang.sql index 11c93573..9f15e9c6 100644 --- a/apps/location/migrations/migrate_lang.sql +++ b/apps/location/migrations/migrate_lang.sql @@ -326,7 +326,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 +335,7 @@ select now() as modified from ( - select + select distinct c.country from country_code c ) t @@ -348,6 +348,7 @@ commit; INSERT INTO translation_language (title, locale) select + distinct t.country as title, t.code as locale from diff --git a/apps/translation/migrations/0005_auto_20191021_1201.py b/apps/translation/migrations/0005_auto_20191021_1201.py new file mode 100644 index 00000000..61cc3294 --- /dev/null +++ b/apps/translation/migrations/0005_auto_20191021_1201.py @@ -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')}, + ), + ] diff --git a/apps/translation/migrations/006_data_migrate.py b/apps/translation/migrations/006_data_migrate.py new file mode 100644 index 00000000..d8142773 --- /dev/null +++ b/apps/translation/migrations/006_data_migrate.py @@ -0,0 +1,25 @@ +from django.db import migrations, connection +import os + + +class Migration(migrations.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() + with connection.cursor() as c: + c.execute(sql_statement) + + def revert_data(apps, schema_editor): + file_path = os.path.join(os.path.dirname(__file__), 'remigrate_lang.sql') + sql_statement = open(file_path).read() + with connection.cursor() as c: + c.execute(sql_statement) + + dependencies = [ + ('translation', '0005_auto_20191021_1201'), + ] + + operations = [ + migrations.RunPython(load_data_from_sql, revert_data), + ] diff --git a/apps/translation/migrations/migrate_lang.sql b/apps/translation/migrations/migrate_lang.sql new file mode 100644 index 00000000..9f15e9c6 --- /dev/null +++ b/apps/translation/migrations/migrate_lang.sql @@ -0,0 +1,383 @@ +SET search_path TO gm, public; + +CREATE TABLE codelang ( + code varchar(100) NULL, + country varchar(10000) NULL +); + + +INSERT INTO codelang (code,country) VALUES +('af','Afrikaans') +,('af-ZA','Afrikaans (South Africa)') +,('ar','Arabic') +,('ar-AE','Arabic (U.A.E.)') +,('ar-BH','Arabic (Bahrain)') +,('ar-DZ','Arabic (Algeria)') +,('ar-EG','Arabic (Egypt)') +,('ar-IQ','Arabic (Iraq)') +,('ar-JO','Arabic (Jordan)') +,('ar-KW','Arabic (Kuwait)') +; +INSERT INTO codelang (code,country) VALUES +('ar-LB','Arabic (Lebanon)') +,('ar-LY','Arabic (Libya)') +,('ar-MA','Arabic (Morocco)') +,('ar-OM','Arabic (Oman)') +,('ar-QA','Arabic (Qatar)') +,('ar-SA','Arabic (Saudi Arabia)') +,('ar-SY','Arabic (Syria)') +,('ar-TN','Arabic (Tunisia)') +,('ar-YE','Arabic (Yemen)') +,('az','Azeri (Latin)') +; +INSERT INTO codelang (code,country) VALUES +('az-AZ','Azeri (Latin) (Azerbaijan)') +,('az-AZ','Azeri (Cyrillic) (Azerbaijan)') +,('be','Belarusian') +,('be-BY','Belarusian (Belarus)') +,('bg','Bulgarian') +,('bg-BG','Bulgarian (Bulgaria)') +,('bs-BA','Bosnian (Bosnia and Herzegovina)') +,('ca','Catalan') +,('ca-ES','Catalan (Spain)') +,('cs','Czech') +; +INSERT INTO codelang (code,country) VALUES +('cs-CZ','Czech (Czech Republic)') +,('cy','Welsh') +,('cy-GB','Welsh (United Kingdom)') +,('da','Danish') +,('da-DK','Danish (Denmark)') +,('de','German') +,('de-AT','German (Austria)') +,('de-CH','German (Switzerland)') +,('de-DE','German (Germany)') +,('de-LI','German (Liechtenstein)') +; +INSERT INTO codelang (code,country) VALUES +('de-LU','German (Luxembourg)') +,('dv','Divehi') +,('dv-MV','Divehi (Maldives)') +,('el','Greek') +,('el-GR','Greek (Greece)') +,('en','English') +,('en-AU','English (Australia)') +,('en-BZ','English (Belize)') +,('en-CA','English (Canada)') +,('en-CB','English (Caribbean)') +; +INSERT INTO codelang (code,country) VALUES +('en-GB','English (United Kingdom)') +,('en-IE','English (Ireland)') +,('en-JM','English (Jamaica)') +,('en-NZ','English (New Zealand)') +,('en-PH','English (Republic of the Philippines)') +,('en-TT','English (Trinidad and Tobago)') +,('en-US','English (United States)') +,('en-ZA','English (South Africa)') +,('en-ZW','English (Zimbabwe)') +,('eo','Esperanto') +; +INSERT INTO codelang (code,country) VALUES +('es','Spanish') +,('es-AR','Spanish (Argentina)') +,('es-BO','Spanish (Bolivia)') +,('es-CL','Spanish (Chile)') +,('es-CO','Spanish (Colombia)') +,('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 +('es-GT','Spanish (Guatemala)') +,('es-HN','Spanish (Honduras)') +,('es-MX','Spanish (Mexico)') +,('es-NI','Spanish (Nicaragua)') +,('es-PA','Spanish (Panama)') +,('es-PE','Spanish (Peru)') +,('es-PR','Spanish (Puerto Rico)') +,('es-PY','Spanish (Paraguay)') +,('es-SV','Spanish (El Salvador)') +,('es-UY','Spanish (Uruguay)') +; +INSERT INTO codelang (code,country) VALUES +('es-VE','Spanish (Venezuela)') +,('et','Estonian') +,('et-EE','Estonian (Estonia)') +,('eu','Basque') +,('eu-ES','Basque (Spain)') +,('fa','Farsi') +,('fa-IR','Farsi (Iran)') +,('fi','Finnish') +,('fi-FI','Finnish (Finland)') +,('fo','Faroese') +; +INSERT INTO codelang (code,country) VALUES +('fo-FO','Faroese (Faroe Islands)') +,('fr','French') +,('fr-BE','French (Belgium)') +,('fr-CA','French (Canada)') +,('fr-CH','French (Switzerland)') +,('fr-FR','French (France)') +,('fr-LU','French (Luxembourg)') +,('fr-MC','French (Principality of Monaco)') +,('gl','Galician') +,('gl-ES','Galician (Spain)') +; +INSERT INTO codelang (code,country) VALUES +('gu','Gujarati') +,('gu-IN','Gujarati (India)') +,('he','Hebrew') +,('he-IL','Hebrew (Israel)') +,('hi','Hindi') +,('hi-IN','Hindi (India)') +,('hr','Croatian') +,('hr-BA','Croatian (Bosnia and Herzegovina)') +,('hr-HR','Croatian (Croatia)') +,('hu','Hungarian') +; +INSERT INTO codelang (code,country) VALUES +('hu-HU','Hungarian (Hungary)') +,('hy','Armenian') +,('hy-AM','Armenian (Armenia)') +,('id','Indonesian') +,('id-ID','Indonesian (Indonesia)') +,('is','Icelandic') +,('is-IS','Icelandic (Iceland)') +,('it','Italian') +,('it-CH','Italian (Switzerland)') +,('it-IT','Italian (Italy)') +; +INSERT INTO codelang (code,country) VALUES +('ja','Japanese') +,('ja-JP','Japanese (Japan)') +,('ka','Georgian') +,('ka-GE','Georgian (Georgia)') +,('kk','Kazakh') +,('kk-KZ','Kazakh (Kazakhstan)') +,('kn','Kannada') +,('kn-IN','Kannada (India)') +,('ko','Korean') +,('ko-KR','Korean (Korea)') +; +INSERT INTO codelang (code,country) VALUES +('kok','Konkani') +,('kok-IN','Konkani (India)') +,('ky','Kyrgyz') +,('ky-KG','Kyrgyz (Kyrgyzstan)') +,('lt','Lithuanian') +,('lt-LT','Lithuanian (Lithuania)') +,('lv','Latvian') +,('lv-LV','Latvian (Latvia)') +,('mi','Maori') +,('mi-NZ','Maori (New Zealand)') +; +INSERT INTO codelang (code,country) VALUES +('mk','FYRO Macedonian') +,('mk-MK','FYRO Macedonian (Former Yugoslav Republic of Macedonia)') +,('mn','Mongolian') +,('mn-MN','Mongolian (Mongolia)') +,('mr','Marathi') +,('mr-IN','Marathi (India)') +,('ms','Malay') +,('ms-BN','Malay (Brunei Darussalam)') +,('ms-MY','Malay (Malaysia)') +,('mt','Maltese') +; +INSERT INTO codelang (code,country) VALUES +('mt-MT','Maltese (Malta)') +,('nb','Norwegian (Bokm?l)') +,('nb-NO','Norwegian (Bokm?l) (Norway)') +,('nl','Dutch') +,('nl-BE','Dutch (Belgium)') +,('nl-NL','Dutch (Netherlands)') +,('nn-NO','Norwegian (Nynorsk) (Norway)') +,('ns','Northern Sotho') +,('ns-ZA','Northern Sotho (South Africa)') +,('pa','Punjabi') +; +INSERT INTO codelang (code,country) VALUES +('pa-IN','Punjabi (India)') +,('pl','Polish') +,('pl-PL','Polish (Poland)') +,('ps','Pashto') +,('ps-AR','Pashto (Afghanistan)') +,('pt','Portuguese') +,('pt-BR','Portuguese (Brazil)') +,('pt-PT','Portuguese (Portugal)') +,('qu','Quechua') +,('qu-BO','Quechua (Bolivia)') +; +INSERT INTO codelang (code,country) VALUES +('qu-EC','Quechua (Ecuador)') +,('qu-PE','Quechua (Peru)') +,('ro','Romanian') +,('ro-RO','Romanian (Romania)') +,('ru','Russian') +,('ru-RU','Russian (Russia)') +,('sa','Sanskrit') +,('sa-IN','Sanskrit (India)') +,('se','Sami (Northern)') +,('se-FI','Sami (Northern) (Finland)') +; +INSERT INTO codelang (code,country) VALUES +('se-FI','Sami (Skolt) (Finland)') +,('se-FI','Sami (Inari) (Finland)') +,('se-NO','Sami (Northern) (Norway)') +,('se-NO','Sami (Lule) (Norway)') +,('se-NO','Sami (Southern) (Norway)') +,('se-SE','Sami (Northern) (Sweden)') +,('se-SE','Sami (Lule) (Sweden)') +,('se-SE','Sami (Southern) (Sweden)') +,('sk','Slovak') +,('sk-SK','Slovak (Slovakia)') +; +INSERT INTO codelang (code,country) VALUES +('sl','Slovenian') +,('sl-SI','Slovenian (Slovenia)') +,('sq','Albanian') +,('sq-AL','Albanian (Albania)') +,('sr-BA','Serbian (Latin) (Bosnia and Herzegovina)') +,('sr-BA','Serbian (Cyrillic) (Bosnia and Herzegovina)') +,('sr-SP','Serbian (Latin) (Serbia and Montenegro)') +,('sr-SP','Serbian (Cyrillic) (Serbia and Montenegro)') +,('sv','Swedish') +,('sv-FI','Swedish (Finland)') +; +INSERT INTO codelang (code,country) VALUES +('sv-SE','Swedish (Sweden)') +,('sw','Swahili') +,('sw-KE','Swahili (Kenya)') +,('syr','Syriac') +,('syr-SY','Syriac (Syria)') +,('ta','Tamil') +,('ta-IN','Tamil (India)') +,('te','Telugu') +,('te-IN','Telugu (India)') +,('th','Thai') +; +INSERT INTO codelang (code,country) VALUES +('th-TH','Thai (Thailand)') +,('tl','Tagalog') +,('tl-PH','Tagalog (Philippines)') +,('tn','Tswana') +,('tn-ZA','Tswana (South Africa)') +,('tr','Turkish') +,('tr-TR','Turkish (Turkey)') +,('tt','Tatar') +,('tt-RU','Tatar (Russia)') +,('ts','Tsonga') +; +INSERT INTO codelang (code,country) VALUES +('uk','Ukrainian') +,('uk-UA','Ukrainian (Ukraine)') +,('ur','Urdu') +,('ur-PK','Urdu (Islamic Republic of Pakistan)') +,('uz','Uzbek (Latin)') +,('uz-UZ','Uzbek (Latin) (Uzbekistan)') +,('uz-UZ','Uzbek (Cyrillic) (Uzbekistan)') +,('vi','Vietnamese') +,('vi-VN','Vietnamese (Viet Nam)') +,('xh','Xhosa') +; +INSERT INTO codelang (code,country) VALUES +('xh-ZA','Xhosa (South Africa)') +,('zh','Chinese') +,('zh-CN','Chinese (S)') +,('zh-HK','Chinese (Hong Kong)') +,('zh-MO','Chinese (Macau)') +,('zh-SG','Chinese (Singapore)') +,('zh-TW','Chinese (T)') +,('zu','Zulu') +,('zu-ZA','Zulu (South Africa)') +; +/***************************/ +-- Manual migrate + +CREATE TABLE country_code ( + code varchar(100) NULL, + country varchar(10000) NULL +); + +insert into country_code(code, country) +select distinct + t.code, + coalesce( + case when length(t.country_name2) = 1 then null else t.country_name2 end, + case when length(t.contry_name1) = 1 then null else t.contry_name1 end, + t.country + ) as country +from +( + select trim(c.code) as code, + substring(trim(c.country) from '\((.+)\)') as contry_name1, + substring( + substring(trim(c.country) from '\((.+)\)') + from '\((.*)$') as country_name2, + trim(c.country) as country + from codelang as c +) t; + +commit; + +--delete from location_country as lc + +INSERT INTO location_country +(code, "name", low_price, high_price, created, modified) +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, + 100 as high_price, + now() as created, + now() as modified +from +( + select + distinct c.country + from country_code c +) t +; + +commit; + +--delete from translation_language as tl; + +INSERT INTO translation_language +(title, locale) +select + distinct + t.country as title, + t.code as locale +from +( + select + distinct c.country, c.code + from country_code c +) t +; + +commit; + + +--delete from location_country_languages + +INSERT INTO location_country_languages +(country_id, language_id) +select lc.id as country_id, + l.id as language_id +from location_country as lc +join ( + select tl.*, '"'||tl.title||'"' as country + from translation_language as tl +) l on l.country = (lc."name"::json->'en-GB')::text +; + +commit; + +drop table country_code; +drop table codelang; + +commit; \ No newline at end of file diff --git a/apps/translation/migrations/remigrate_lang.sql b/apps/translation/migrations/remigrate_lang.sql new file mode 100644 index 00000000..160ac93e --- /dev/null +++ b/apps/translation/migrations/remigrate_lang.sql @@ -0,0 +1,391 @@ +SET search_path TO gm, public; + +CREATE TABLE codelang ( + code varchar(100) NULL, + country varchar(10000) NULL +); + + +INSERT INTO codelang (code,country) VALUES +('af','Afrikaans') +,('af-ZA','Afrikaans (South Africa)') +,('ar','Arabic') +,('ar-AE','Arabic (U.A.E.)') +,('ar-BH','Arabic (Bahrain)') +,('ar-DZ','Arabic (Algeria)') +,('ar-EG','Arabic (Egypt)') +,('ar-IQ','Arabic (Iraq)') +,('ar-JO','Arabic (Jordan)') +,('ar-KW','Arabic (Kuwait)') +; +INSERT INTO codelang (code,country) VALUES +('ar-LB','Arabic (Lebanon)') +,('ar-LY','Arabic (Libya)') +,('ar-MA','Arabic (Morocco)') +,('ar-OM','Arabic (Oman)') +,('ar-QA','Arabic (Qatar)') +,('ar-SA','Arabic (Saudi Arabia)') +,('ar-SY','Arabic (Syria)') +,('ar-TN','Arabic (Tunisia)') +,('ar-YE','Arabic (Yemen)') +,('az','Azeri (Latin)') +; +INSERT INTO codelang (code,country) VALUES +('az-AZ','Azeri (Latin) (Azerbaijan)') +,('az-AZ','Azeri (Cyrillic) (Azerbaijan)') +,('be','Belarusian') +,('be-BY','Belarusian (Belarus)') +,('bg','Bulgarian') +,('bg-BG','Bulgarian (Bulgaria)') +,('bs-BA','Bosnian (Bosnia and Herzegovina)') +,('ca','Catalan') +,('ca-ES','Catalan (Spain)') +,('cs','Czech') +; +INSERT INTO codelang (code,country) VALUES +('cs-CZ','Czech (Czech Republic)') +,('cy','Welsh') +,('cy-GB','Welsh (United Kingdom)') +,('da','Danish') +,('da-DK','Danish (Denmark)') +,('de','German') +,('de-AT','German (Austria)') +,('de-CH','German (Switzerland)') +,('de-DE','German (Germany)') +,('de-LI','German (Liechtenstein)') +; +INSERT INTO codelang (code,country) VALUES +('de-LU','German (Luxembourg)') +,('dv','Divehi') +,('dv-MV','Divehi (Maldives)') +,('el','Greek') +,('el-GR','Greek (Greece)') +,('en','English') +,('en-AU','English (Australia)') +,('en-BZ','English (Belize)') +,('en-CA','English (Canada)') +,('en-CB','English (Caribbean)') +; +INSERT INTO codelang (code,country) VALUES +('en-GB','English (United Kingdom)') +,('en-IE','English (Ireland)') +,('en-JM','English (Jamaica)') +,('en-NZ','English (New Zealand)') +,('en-PH','English (Republic of the Philippines)') +,('en-TT','English (Trinidad and Tobago)') +,('en-US','English (United States)') +,('en-ZA','English (South Africa)') +,('en-ZW','English (Zimbabwe)') +,('eo','Esperanto') +; +INSERT INTO codelang (code,country) VALUES +('es','Spanish') +,('es-AR','Spanish (Argentina)') +,('es-BO','Spanish (Bolivia)') +,('es-CL','Spanish (Chile)') +,('es-CO','Spanish (Colombia)') +,('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 +('es-GT','Spanish (Guatemala)') +,('es-HN','Spanish (Honduras)') +,('es-MX','Spanish (Mexico)') +,('es-NI','Spanish (Nicaragua)') +,('es-PA','Spanish (Panama)') +,('es-PE','Spanish (Peru)') +,('es-PR','Spanish (Puerto Rico)') +,('es-PY','Spanish (Paraguay)') +,('es-SV','Spanish (El Salvador)') +,('es-UY','Spanish (Uruguay)') +; +INSERT INTO codelang (code,country) VALUES +('es-VE','Spanish (Venezuela)') +,('et','Estonian') +,('et-EE','Estonian (Estonia)') +,('eu','Basque') +,('eu-ES','Basque (Spain)') +,('fa','Farsi') +,('fa-IR','Farsi (Iran)') +,('fi','Finnish') +,('fi-FI','Finnish (Finland)') +,('fo','Faroese') +; +INSERT INTO codelang (code,country) VALUES +('fo-FO','Faroese (Faroe Islands)') +,('fr','French') +,('fr-BE','French (Belgium)') +,('fr-CA','French (Canada)') +,('fr-CH','French (Switzerland)') +,('fr-FR','French (France)') +,('fr-LU','French (Luxembourg)') +,('fr-MC','French (Principality of Monaco)') +,('gl','Galician') +,('gl-ES','Galician (Spain)') +; +INSERT INTO codelang (code,country) VALUES +('gu','Gujarati') +,('gu-IN','Gujarati (India)') +,('he','Hebrew') +,('he-IL','Hebrew (Israel)') +,('hi','Hindi') +,('hi-IN','Hindi (India)') +,('hr','Croatian') +,('hr-BA','Croatian (Bosnia and Herzegovina)') +,('hr-HR','Croatian (Croatia)') +,('hu','Hungarian') +; +INSERT INTO codelang (code,country) VALUES +('hu-HU','Hungarian (Hungary)') +,('hy','Armenian') +,('hy-AM','Armenian (Armenia)') +,('id','Indonesian') +,('id-ID','Indonesian (Indonesia)') +,('is','Icelandic') +,('is-IS','Icelandic (Iceland)') +,('it','Italian') +,('it-CH','Italian (Switzerland)') +,('it-IT','Italian (Italy)') +; +INSERT INTO codelang (code,country) VALUES +('ja','Japanese') +,('ja-JP','Japanese (Japan)') +,('ka','Georgian') +,('ka-GE','Georgian (Georgia)') +,('kk','Kazakh') +,('kk-KZ','Kazakh (Kazakhstan)') +,('kn','Kannada') +,('kn-IN','Kannada (India)') +,('ko','Korean') +,('ko-KR','Korean (Korea)') +; +INSERT INTO codelang (code,country) VALUES +('kok','Konkani') +,('kok-IN','Konkani (India)') +,('ky','Kyrgyz') +,('ky-KG','Kyrgyz (Kyrgyzstan)') +,('lt','Lithuanian') +,('lt-LT','Lithuanian (Lithuania)') +,('lv','Latvian') +,('lv-LV','Latvian (Latvia)') +,('mi','Maori') +,('mi-NZ','Maori (New Zealand)') +; +INSERT INTO codelang (code,country) VALUES +('mk','FYRO Macedonian') +,('mk-MK','FYRO Macedonian (Former Yugoslav Republic of Macedonia)') +,('mn','Mongolian') +,('mn-MN','Mongolian (Mongolia)') +,('mr','Marathi') +,('mr-IN','Marathi (India)') +,('ms','Malay') +,('ms-BN','Malay (Brunei Darussalam)') +,('ms-MY','Malay (Malaysia)') +,('mt','Maltese') +; +INSERT INTO codelang (code,country) VALUES +('mt-MT','Maltese (Malta)') +,('nb','Norwegian (Bokm?l)') +,('nb-NO','Norwegian (Bokm?l) (Norway)') +,('nl','Dutch') +,('nl-BE','Dutch (Belgium)') +,('nl-NL','Dutch (Netherlands)') +,('nn-NO','Norwegian (Nynorsk) (Norway)') +,('ns','Northern Sotho') +,('ns-ZA','Northern Sotho (South Africa)') +,('pa','Punjabi') +; +INSERT INTO codelang (code,country) VALUES +('pa-IN','Punjabi (India)') +,('pl','Polish') +,('pl-PL','Polish (Poland)') +,('ps','Pashto') +,('ps-AR','Pashto (Afghanistan)') +,('pt','Portuguese') +,('pt-BR','Portuguese (Brazil)') +,('pt-PT','Portuguese (Portugal)') +,('qu','Quechua') +,('qu-BO','Quechua (Bolivia)') +; +INSERT INTO codelang (code,country) VALUES +('qu-EC','Quechua (Ecuador)') +,('qu-PE','Quechua (Peru)') +,('ro','Romanian') +,('ro-RO','Romanian (Romania)') +,('ru','Russian') +,('ru-RU','Russian (Russia)') +,('sa','Sanskrit') +,('sa-IN','Sanskrit (India)') +,('se','Sami (Northern)') +,('se-FI','Sami (Northern) (Finland)') +; +INSERT INTO codelang (code,country) VALUES +('se-FI','Sami (Skolt) (Finland)') +,('se-FI','Sami (Inari) (Finland)') +,('se-NO','Sami (Northern) (Norway)') +,('se-NO','Sami (Lule) (Norway)') +,('se-NO','Sami (Southern) (Norway)') +,('se-SE','Sami (Northern) (Sweden)') +,('se-SE','Sami (Lule) (Sweden)') +,('se-SE','Sami (Southern) (Sweden)') +,('sk','Slovak') +,('sk-SK','Slovak (Slovakia)') +; +INSERT INTO codelang (code,country) VALUES +('sl','Slovenian') +,('sl-SI','Slovenian (Slovenia)') +,('sq','Albanian') +,('sq-AL','Albanian (Albania)') +,('sr-BA','Serbian (Latin) (Bosnia and Herzegovina)') +,('sr-BA','Serbian (Cyrillic) (Bosnia and Herzegovina)') +,('sr-SP','Serbian (Latin) (Serbia and Montenegro)') +,('sr-SP','Serbian (Cyrillic) (Serbia and Montenegro)') +,('sv','Swedish') +,('sv-FI','Swedish (Finland)') +; +INSERT INTO codelang (code,country) VALUES +('sv-SE','Swedish (Sweden)') +,('sw','Swahili') +,('sw-KE','Swahili (Kenya)') +,('syr','Syriac') +,('syr-SY','Syriac (Syria)') +,('ta','Tamil') +,('ta-IN','Tamil (India)') +,('te','Telugu') +,('te-IN','Telugu (India)') +,('th','Thai') +; +INSERT INTO codelang (code,country) VALUES +('th-TH','Thai (Thailand)') +,('tl','Tagalog') +,('tl-PH','Tagalog (Philippines)') +,('tn','Tswana') +,('tn-ZA','Tswana (South Africa)') +,('tr','Turkish') +,('tr-TR','Turkish (Turkey)') +,('tt','Tatar') +,('tt-RU','Tatar (Russia)') +,('ts','Tsonga') +; +INSERT INTO codelang (code,country) VALUES +('uk','Ukrainian') +,('uk-UA','Ukrainian (Ukraine)') +,('ur','Urdu') +,('ur-PK','Urdu (Islamic Republic of Pakistan)') +,('uz','Uzbek (Latin)') +,('uz-UZ','Uzbek (Latin) (Uzbekistan)') +,('uz-UZ','Uzbek (Cyrillic) (Uzbekistan)') +,('vi','Vietnamese') +,('vi-VN','Vietnamese (Viet Nam)') +,('xh','Xhosa') +; +INSERT INTO codelang (code,country) VALUES +('xh-ZA','Xhosa (South Africa)') +,('zh','Chinese') +,('zh-CN','Chinese (S)') +,('zh-HK','Chinese (Hong Kong)') +,('zh-MO','Chinese (Macau)') +,('zh-SG','Chinese (Singapore)') +,('zh-TW','Chinese (T)') +,('zu','Zulu') +,('zu-ZA','Zulu (South Africa)') +; +/***************************/ +-- Manual migrate + +CREATE TABLE country_code ( + code varchar(100) NULL, + country varchar(10000) NULL +); + +insert into country_code(code, country) +select distinct + t.code, + coalesce( + case when length(t.country_name2) = 1 then null else t.country_name2 end, + case when length(t.contry_name1) = 1 then null else t.contry_name1 end, + t.country + ) as country +from +( + select trim(c.code) as code, + substring(trim(c.country) from '\((.+)\)') as contry_name1, + substring( + substring(trim(c.country) from '\((.+)\)') + from '\((.*)$') as country_name2, + trim(c.country) as country + from codelang as c +) t; + +commit; + + +delete from location_country_languages as lcl +where lcl.country_id in +( + select + lc.id + from + ( + select + lpad((row_number() over (order by t.country asc))::text, 3, '0') as code, + jsonb_build_object('en-GB', t.country) as "name" + from + ( + select + distinct c.country + from country_code c + ) t + ) d + join location_country lc on lc.code = d.code and d."name"=lc."name" +) +; +commit; + + +delete from location_country as lcl +where lcl.id in +( + select + lc.id + from + ( + select + lpad((row_number() over (order by t.country asc))::text, 3, '0') as code, + jsonb_build_object('en-GB', t.country) as "name" + from + ( + select + distinct c.country + from country_code c + ) t + ) d + join location_country lc on lc.code = d.code and d."name"=lc."name" +) +; + +commit; + + +delete from translation_language tl +where tl.id in +( + SELECT tl.id + FROM + ( + select + distinct c.country, c.code + from country_code c + ) t + JOIN translation_language tl ON tl.locale = t.code and tl.title = t.country +); + +commit; + +drop table country_code; +drop table codelang; + +commit; \ No newline at end of file diff --git a/apps/translation/models.py b/apps/translation/models.py index bc9fbfbf..cb0729ea 100644 --- a/apps/translation/models.py +++ b/apps/translation/models.py @@ -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""" diff --git a/project/urls/back.py b/project/urls/back.py index 9194a44b..40b3415a 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -9,8 +9,6 @@ urlpatterns = [ path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')), path('location/', include('location.urls.back')), path('news/', include('news.urls.back')), - path('account/', include('account.urls.back')), - path('comment/', include('comment.urls.back')), path('review/', include('review.urls.back')), path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')), ] From 1ed48a43eb6ac25bc34b21313ad73050d6b01999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 15:31:19 +0300 Subject: [PATCH 28/36] Fix url --- project/urls/back.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/project/urls/back.py b/project/urls/back.py index 9194a44b..40b3415a 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -9,8 +9,6 @@ urlpatterns = [ path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')), path('location/', include('location.urls.back')), path('news/', include('news.urls.back')), - path('account/', include('account.urls.back')), - path('comment/', include('comment.urls.back')), path('review/', include('review.urls.back')), path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')), ] From 4f02fef4568677eb3190f4eac4bc104b548c84b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 15:44:20 +0300 Subject: [PATCH 29/36] Fix migrate --- apps/location/migrations/migrate_lang.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/location/migrations/migrate_lang.sql b/apps/location/migrations/migrate_lang.sql index 11c93573..021d251e 100644 --- a/apps/location/migrations/migrate_lang.sql +++ b/apps/location/migrations/migrate_lang.sql @@ -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 From 196d335213a63100b5aeba74d49e18d7f784cff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 15:47:06 +0300 Subject: [PATCH 30/36] Fix --- apps/location/migrations/0012_data_migrate.py | 24 +- .../migrations/006_data_migrate.py | 25 -- apps/translation/migrations/migrate_lang.sql | 383 ----------------- .../translation/migrations/remigrate_lang.sql | 391 ------------------ 4 files changed, 12 insertions(+), 811 deletions(-) delete mode 100644 apps/translation/migrations/006_data_migrate.py delete mode 100644 apps/translation/migrations/migrate_lang.sql delete mode 100644 apps/translation/migrations/remigrate_lang.sql diff --git a/apps/location/migrations/0012_data_migrate.py b/apps/location/migrations/0012_data_migrate.py index de75c8ad..b61c43df 100644 --- a/apps/location/migrations/0012_data_migrate.py +++ b/apps/location/migrations/0012_data_migrate.py @@ -4,22 +4,22 @@ 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() - # with connection.cursor() as c: - # c.execute(sql_statement) - # - # def revert_data(apps, schema_editor): - # file_path = os.path.join(os.path.dirname(__file__), 'remigrate_lang.sql') - # sql_statement = open(file_path).read() - # with connection.cursor() as c: - # c.execute(sql_statement) + 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() + with connection.cursor() as c: + c.execute(sql_statement) + + def revert_data(apps, schema_editor): + file_path = os.path.join(os.path.dirname(__file__), 'remigrate_lang.sql') + sql_statement = open(file_path).read() + with connection.cursor() as c: + c.execute(sql_statement) dependencies = [ ('location', '0011_country_languages'), ] operations = [ - # migrations.RunPython(load_data_from_sql, revert_data), + migrations.RunPython(load_data_from_sql, revert_data), ] diff --git a/apps/translation/migrations/006_data_migrate.py b/apps/translation/migrations/006_data_migrate.py deleted file mode 100644 index d8142773..00000000 --- a/apps/translation/migrations/006_data_migrate.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import migrations, connection -import os - - -class Migration(migrations.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() - with connection.cursor() as c: - c.execute(sql_statement) - - def revert_data(apps, schema_editor): - file_path = os.path.join(os.path.dirname(__file__), 'remigrate_lang.sql') - sql_statement = open(file_path).read() - with connection.cursor() as c: - c.execute(sql_statement) - - dependencies = [ - ('translation', '0005_auto_20191021_1201'), - ] - - operations = [ - migrations.RunPython(load_data_from_sql, revert_data), - ] diff --git a/apps/translation/migrations/migrate_lang.sql b/apps/translation/migrations/migrate_lang.sql deleted file mode 100644 index 9f15e9c6..00000000 --- a/apps/translation/migrations/migrate_lang.sql +++ /dev/null @@ -1,383 +0,0 @@ -SET search_path TO gm, public; - -CREATE TABLE codelang ( - code varchar(100) NULL, - country varchar(10000) NULL -); - - -INSERT INTO codelang (code,country) VALUES -('af','Afrikaans') -,('af-ZA','Afrikaans (South Africa)') -,('ar','Arabic') -,('ar-AE','Arabic (U.A.E.)') -,('ar-BH','Arabic (Bahrain)') -,('ar-DZ','Arabic (Algeria)') -,('ar-EG','Arabic (Egypt)') -,('ar-IQ','Arabic (Iraq)') -,('ar-JO','Arabic (Jordan)') -,('ar-KW','Arabic (Kuwait)') -; -INSERT INTO codelang (code,country) VALUES -('ar-LB','Arabic (Lebanon)') -,('ar-LY','Arabic (Libya)') -,('ar-MA','Arabic (Morocco)') -,('ar-OM','Arabic (Oman)') -,('ar-QA','Arabic (Qatar)') -,('ar-SA','Arabic (Saudi Arabia)') -,('ar-SY','Arabic (Syria)') -,('ar-TN','Arabic (Tunisia)') -,('ar-YE','Arabic (Yemen)') -,('az','Azeri (Latin)') -; -INSERT INTO codelang (code,country) VALUES -('az-AZ','Azeri (Latin) (Azerbaijan)') -,('az-AZ','Azeri (Cyrillic) (Azerbaijan)') -,('be','Belarusian') -,('be-BY','Belarusian (Belarus)') -,('bg','Bulgarian') -,('bg-BG','Bulgarian (Bulgaria)') -,('bs-BA','Bosnian (Bosnia and Herzegovina)') -,('ca','Catalan') -,('ca-ES','Catalan (Spain)') -,('cs','Czech') -; -INSERT INTO codelang (code,country) VALUES -('cs-CZ','Czech (Czech Republic)') -,('cy','Welsh') -,('cy-GB','Welsh (United Kingdom)') -,('da','Danish') -,('da-DK','Danish (Denmark)') -,('de','German') -,('de-AT','German (Austria)') -,('de-CH','German (Switzerland)') -,('de-DE','German (Germany)') -,('de-LI','German (Liechtenstein)') -; -INSERT INTO codelang (code,country) VALUES -('de-LU','German (Luxembourg)') -,('dv','Divehi') -,('dv-MV','Divehi (Maldives)') -,('el','Greek') -,('el-GR','Greek (Greece)') -,('en','English') -,('en-AU','English (Australia)') -,('en-BZ','English (Belize)') -,('en-CA','English (Canada)') -,('en-CB','English (Caribbean)') -; -INSERT INTO codelang (code,country) VALUES -('en-GB','English (United Kingdom)') -,('en-IE','English (Ireland)') -,('en-JM','English (Jamaica)') -,('en-NZ','English (New Zealand)') -,('en-PH','English (Republic of the Philippines)') -,('en-TT','English (Trinidad and Tobago)') -,('en-US','English (United States)') -,('en-ZA','English (South Africa)') -,('en-ZW','English (Zimbabwe)') -,('eo','Esperanto') -; -INSERT INTO codelang (code,country) VALUES -('es','Spanish') -,('es-AR','Spanish (Argentina)') -,('es-BO','Spanish (Bolivia)') -,('es-CL','Spanish (Chile)') -,('es-CO','Spanish (Colombia)') -,('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 -('es-GT','Spanish (Guatemala)') -,('es-HN','Spanish (Honduras)') -,('es-MX','Spanish (Mexico)') -,('es-NI','Spanish (Nicaragua)') -,('es-PA','Spanish (Panama)') -,('es-PE','Spanish (Peru)') -,('es-PR','Spanish (Puerto Rico)') -,('es-PY','Spanish (Paraguay)') -,('es-SV','Spanish (El Salvador)') -,('es-UY','Spanish (Uruguay)') -; -INSERT INTO codelang (code,country) VALUES -('es-VE','Spanish (Venezuela)') -,('et','Estonian') -,('et-EE','Estonian (Estonia)') -,('eu','Basque') -,('eu-ES','Basque (Spain)') -,('fa','Farsi') -,('fa-IR','Farsi (Iran)') -,('fi','Finnish') -,('fi-FI','Finnish (Finland)') -,('fo','Faroese') -; -INSERT INTO codelang (code,country) VALUES -('fo-FO','Faroese (Faroe Islands)') -,('fr','French') -,('fr-BE','French (Belgium)') -,('fr-CA','French (Canada)') -,('fr-CH','French (Switzerland)') -,('fr-FR','French (France)') -,('fr-LU','French (Luxembourg)') -,('fr-MC','French (Principality of Monaco)') -,('gl','Galician') -,('gl-ES','Galician (Spain)') -; -INSERT INTO codelang (code,country) VALUES -('gu','Gujarati') -,('gu-IN','Gujarati (India)') -,('he','Hebrew') -,('he-IL','Hebrew (Israel)') -,('hi','Hindi') -,('hi-IN','Hindi (India)') -,('hr','Croatian') -,('hr-BA','Croatian (Bosnia and Herzegovina)') -,('hr-HR','Croatian (Croatia)') -,('hu','Hungarian') -; -INSERT INTO codelang (code,country) VALUES -('hu-HU','Hungarian (Hungary)') -,('hy','Armenian') -,('hy-AM','Armenian (Armenia)') -,('id','Indonesian') -,('id-ID','Indonesian (Indonesia)') -,('is','Icelandic') -,('is-IS','Icelandic (Iceland)') -,('it','Italian') -,('it-CH','Italian (Switzerland)') -,('it-IT','Italian (Italy)') -; -INSERT INTO codelang (code,country) VALUES -('ja','Japanese') -,('ja-JP','Japanese (Japan)') -,('ka','Georgian') -,('ka-GE','Georgian (Georgia)') -,('kk','Kazakh') -,('kk-KZ','Kazakh (Kazakhstan)') -,('kn','Kannada') -,('kn-IN','Kannada (India)') -,('ko','Korean') -,('ko-KR','Korean (Korea)') -; -INSERT INTO codelang (code,country) VALUES -('kok','Konkani') -,('kok-IN','Konkani (India)') -,('ky','Kyrgyz') -,('ky-KG','Kyrgyz (Kyrgyzstan)') -,('lt','Lithuanian') -,('lt-LT','Lithuanian (Lithuania)') -,('lv','Latvian') -,('lv-LV','Latvian (Latvia)') -,('mi','Maori') -,('mi-NZ','Maori (New Zealand)') -; -INSERT INTO codelang (code,country) VALUES -('mk','FYRO Macedonian') -,('mk-MK','FYRO Macedonian (Former Yugoslav Republic of Macedonia)') -,('mn','Mongolian') -,('mn-MN','Mongolian (Mongolia)') -,('mr','Marathi') -,('mr-IN','Marathi (India)') -,('ms','Malay') -,('ms-BN','Malay (Brunei Darussalam)') -,('ms-MY','Malay (Malaysia)') -,('mt','Maltese') -; -INSERT INTO codelang (code,country) VALUES -('mt-MT','Maltese (Malta)') -,('nb','Norwegian (Bokm?l)') -,('nb-NO','Norwegian (Bokm?l) (Norway)') -,('nl','Dutch') -,('nl-BE','Dutch (Belgium)') -,('nl-NL','Dutch (Netherlands)') -,('nn-NO','Norwegian (Nynorsk) (Norway)') -,('ns','Northern Sotho') -,('ns-ZA','Northern Sotho (South Africa)') -,('pa','Punjabi') -; -INSERT INTO codelang (code,country) VALUES -('pa-IN','Punjabi (India)') -,('pl','Polish') -,('pl-PL','Polish (Poland)') -,('ps','Pashto') -,('ps-AR','Pashto (Afghanistan)') -,('pt','Portuguese') -,('pt-BR','Portuguese (Brazil)') -,('pt-PT','Portuguese (Portugal)') -,('qu','Quechua') -,('qu-BO','Quechua (Bolivia)') -; -INSERT INTO codelang (code,country) VALUES -('qu-EC','Quechua (Ecuador)') -,('qu-PE','Quechua (Peru)') -,('ro','Romanian') -,('ro-RO','Romanian (Romania)') -,('ru','Russian') -,('ru-RU','Russian (Russia)') -,('sa','Sanskrit') -,('sa-IN','Sanskrit (India)') -,('se','Sami (Northern)') -,('se-FI','Sami (Northern) (Finland)') -; -INSERT INTO codelang (code,country) VALUES -('se-FI','Sami (Skolt) (Finland)') -,('se-FI','Sami (Inari) (Finland)') -,('se-NO','Sami (Northern) (Norway)') -,('se-NO','Sami (Lule) (Norway)') -,('se-NO','Sami (Southern) (Norway)') -,('se-SE','Sami (Northern) (Sweden)') -,('se-SE','Sami (Lule) (Sweden)') -,('se-SE','Sami (Southern) (Sweden)') -,('sk','Slovak') -,('sk-SK','Slovak (Slovakia)') -; -INSERT INTO codelang (code,country) VALUES -('sl','Slovenian') -,('sl-SI','Slovenian (Slovenia)') -,('sq','Albanian') -,('sq-AL','Albanian (Albania)') -,('sr-BA','Serbian (Latin) (Bosnia and Herzegovina)') -,('sr-BA','Serbian (Cyrillic) (Bosnia and Herzegovina)') -,('sr-SP','Serbian (Latin) (Serbia and Montenegro)') -,('sr-SP','Serbian (Cyrillic) (Serbia and Montenegro)') -,('sv','Swedish') -,('sv-FI','Swedish (Finland)') -; -INSERT INTO codelang (code,country) VALUES -('sv-SE','Swedish (Sweden)') -,('sw','Swahili') -,('sw-KE','Swahili (Kenya)') -,('syr','Syriac') -,('syr-SY','Syriac (Syria)') -,('ta','Tamil') -,('ta-IN','Tamil (India)') -,('te','Telugu') -,('te-IN','Telugu (India)') -,('th','Thai') -; -INSERT INTO codelang (code,country) VALUES -('th-TH','Thai (Thailand)') -,('tl','Tagalog') -,('tl-PH','Tagalog (Philippines)') -,('tn','Tswana') -,('tn-ZA','Tswana (South Africa)') -,('tr','Turkish') -,('tr-TR','Turkish (Turkey)') -,('tt','Tatar') -,('tt-RU','Tatar (Russia)') -,('ts','Tsonga') -; -INSERT INTO codelang (code,country) VALUES -('uk','Ukrainian') -,('uk-UA','Ukrainian (Ukraine)') -,('ur','Urdu') -,('ur-PK','Urdu (Islamic Republic of Pakistan)') -,('uz','Uzbek (Latin)') -,('uz-UZ','Uzbek (Latin) (Uzbekistan)') -,('uz-UZ','Uzbek (Cyrillic) (Uzbekistan)') -,('vi','Vietnamese') -,('vi-VN','Vietnamese (Viet Nam)') -,('xh','Xhosa') -; -INSERT INTO codelang (code,country) VALUES -('xh-ZA','Xhosa (South Africa)') -,('zh','Chinese') -,('zh-CN','Chinese (S)') -,('zh-HK','Chinese (Hong Kong)') -,('zh-MO','Chinese (Macau)') -,('zh-SG','Chinese (Singapore)') -,('zh-TW','Chinese (T)') -,('zu','Zulu') -,('zu-ZA','Zulu (South Africa)') -; -/***************************/ --- Manual migrate - -CREATE TABLE country_code ( - code varchar(100) NULL, - country varchar(10000) NULL -); - -insert into country_code(code, country) -select distinct - t.code, - coalesce( - case when length(t.country_name2) = 1 then null else t.country_name2 end, - case when length(t.contry_name1) = 1 then null else t.contry_name1 end, - t.country - ) as country -from -( - select trim(c.code) as code, - substring(trim(c.country) from '\((.+)\)') as contry_name1, - substring( - substring(trim(c.country) from '\((.+)\)') - from '\((.*)$') as country_name2, - trim(c.country) as country - from codelang as c -) t; - -commit; - ---delete from location_country as lc - -INSERT INTO location_country -(code, "name", low_price, high_price, created, modified) -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, - 100 as high_price, - now() as created, - now() as modified -from -( - select - distinct c.country - from country_code c -) t -; - -commit; - ---delete from translation_language as tl; - -INSERT INTO translation_language -(title, locale) -select - distinct - t.country as title, - t.code as locale -from -( - select - distinct c.country, c.code - from country_code c -) t -; - -commit; - - ---delete from location_country_languages - -INSERT INTO location_country_languages -(country_id, language_id) -select lc.id as country_id, - l.id as language_id -from location_country as lc -join ( - select tl.*, '"'||tl.title||'"' as country - from translation_language as tl -) l on l.country = (lc."name"::json->'en-GB')::text -; - -commit; - -drop table country_code; -drop table codelang; - -commit; \ No newline at end of file diff --git a/apps/translation/migrations/remigrate_lang.sql b/apps/translation/migrations/remigrate_lang.sql deleted file mode 100644 index 160ac93e..00000000 --- a/apps/translation/migrations/remigrate_lang.sql +++ /dev/null @@ -1,391 +0,0 @@ -SET search_path TO gm, public; - -CREATE TABLE codelang ( - code varchar(100) NULL, - country varchar(10000) NULL -); - - -INSERT INTO codelang (code,country) VALUES -('af','Afrikaans') -,('af-ZA','Afrikaans (South Africa)') -,('ar','Arabic') -,('ar-AE','Arabic (U.A.E.)') -,('ar-BH','Arabic (Bahrain)') -,('ar-DZ','Arabic (Algeria)') -,('ar-EG','Arabic (Egypt)') -,('ar-IQ','Arabic (Iraq)') -,('ar-JO','Arabic (Jordan)') -,('ar-KW','Arabic (Kuwait)') -; -INSERT INTO codelang (code,country) VALUES -('ar-LB','Arabic (Lebanon)') -,('ar-LY','Arabic (Libya)') -,('ar-MA','Arabic (Morocco)') -,('ar-OM','Arabic (Oman)') -,('ar-QA','Arabic (Qatar)') -,('ar-SA','Arabic (Saudi Arabia)') -,('ar-SY','Arabic (Syria)') -,('ar-TN','Arabic (Tunisia)') -,('ar-YE','Arabic (Yemen)') -,('az','Azeri (Latin)') -; -INSERT INTO codelang (code,country) VALUES -('az-AZ','Azeri (Latin) (Azerbaijan)') -,('az-AZ','Azeri (Cyrillic) (Azerbaijan)') -,('be','Belarusian') -,('be-BY','Belarusian (Belarus)') -,('bg','Bulgarian') -,('bg-BG','Bulgarian (Bulgaria)') -,('bs-BA','Bosnian (Bosnia and Herzegovina)') -,('ca','Catalan') -,('ca-ES','Catalan (Spain)') -,('cs','Czech') -; -INSERT INTO codelang (code,country) VALUES -('cs-CZ','Czech (Czech Republic)') -,('cy','Welsh') -,('cy-GB','Welsh (United Kingdom)') -,('da','Danish') -,('da-DK','Danish (Denmark)') -,('de','German') -,('de-AT','German (Austria)') -,('de-CH','German (Switzerland)') -,('de-DE','German (Germany)') -,('de-LI','German (Liechtenstein)') -; -INSERT INTO codelang (code,country) VALUES -('de-LU','German (Luxembourg)') -,('dv','Divehi') -,('dv-MV','Divehi (Maldives)') -,('el','Greek') -,('el-GR','Greek (Greece)') -,('en','English') -,('en-AU','English (Australia)') -,('en-BZ','English (Belize)') -,('en-CA','English (Canada)') -,('en-CB','English (Caribbean)') -; -INSERT INTO codelang (code,country) VALUES -('en-GB','English (United Kingdom)') -,('en-IE','English (Ireland)') -,('en-JM','English (Jamaica)') -,('en-NZ','English (New Zealand)') -,('en-PH','English (Republic of the Philippines)') -,('en-TT','English (Trinidad and Tobago)') -,('en-US','English (United States)') -,('en-ZA','English (South Africa)') -,('en-ZW','English (Zimbabwe)') -,('eo','Esperanto') -; -INSERT INTO codelang (code,country) VALUES -('es','Spanish') -,('es-AR','Spanish (Argentina)') -,('es-BO','Spanish (Bolivia)') -,('es-CL','Spanish (Chile)') -,('es-CO','Spanish (Colombia)') -,('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 -('es-GT','Spanish (Guatemala)') -,('es-HN','Spanish (Honduras)') -,('es-MX','Spanish (Mexico)') -,('es-NI','Spanish (Nicaragua)') -,('es-PA','Spanish (Panama)') -,('es-PE','Spanish (Peru)') -,('es-PR','Spanish (Puerto Rico)') -,('es-PY','Spanish (Paraguay)') -,('es-SV','Spanish (El Salvador)') -,('es-UY','Spanish (Uruguay)') -; -INSERT INTO codelang (code,country) VALUES -('es-VE','Spanish (Venezuela)') -,('et','Estonian') -,('et-EE','Estonian (Estonia)') -,('eu','Basque') -,('eu-ES','Basque (Spain)') -,('fa','Farsi') -,('fa-IR','Farsi (Iran)') -,('fi','Finnish') -,('fi-FI','Finnish (Finland)') -,('fo','Faroese') -; -INSERT INTO codelang (code,country) VALUES -('fo-FO','Faroese (Faroe Islands)') -,('fr','French') -,('fr-BE','French (Belgium)') -,('fr-CA','French (Canada)') -,('fr-CH','French (Switzerland)') -,('fr-FR','French (France)') -,('fr-LU','French (Luxembourg)') -,('fr-MC','French (Principality of Monaco)') -,('gl','Galician') -,('gl-ES','Galician (Spain)') -; -INSERT INTO codelang (code,country) VALUES -('gu','Gujarati') -,('gu-IN','Gujarati (India)') -,('he','Hebrew') -,('he-IL','Hebrew (Israel)') -,('hi','Hindi') -,('hi-IN','Hindi (India)') -,('hr','Croatian') -,('hr-BA','Croatian (Bosnia and Herzegovina)') -,('hr-HR','Croatian (Croatia)') -,('hu','Hungarian') -; -INSERT INTO codelang (code,country) VALUES -('hu-HU','Hungarian (Hungary)') -,('hy','Armenian') -,('hy-AM','Armenian (Armenia)') -,('id','Indonesian') -,('id-ID','Indonesian (Indonesia)') -,('is','Icelandic') -,('is-IS','Icelandic (Iceland)') -,('it','Italian') -,('it-CH','Italian (Switzerland)') -,('it-IT','Italian (Italy)') -; -INSERT INTO codelang (code,country) VALUES -('ja','Japanese') -,('ja-JP','Japanese (Japan)') -,('ka','Georgian') -,('ka-GE','Georgian (Georgia)') -,('kk','Kazakh') -,('kk-KZ','Kazakh (Kazakhstan)') -,('kn','Kannada') -,('kn-IN','Kannada (India)') -,('ko','Korean') -,('ko-KR','Korean (Korea)') -; -INSERT INTO codelang (code,country) VALUES -('kok','Konkani') -,('kok-IN','Konkani (India)') -,('ky','Kyrgyz') -,('ky-KG','Kyrgyz (Kyrgyzstan)') -,('lt','Lithuanian') -,('lt-LT','Lithuanian (Lithuania)') -,('lv','Latvian') -,('lv-LV','Latvian (Latvia)') -,('mi','Maori') -,('mi-NZ','Maori (New Zealand)') -; -INSERT INTO codelang (code,country) VALUES -('mk','FYRO Macedonian') -,('mk-MK','FYRO Macedonian (Former Yugoslav Republic of Macedonia)') -,('mn','Mongolian') -,('mn-MN','Mongolian (Mongolia)') -,('mr','Marathi') -,('mr-IN','Marathi (India)') -,('ms','Malay') -,('ms-BN','Malay (Brunei Darussalam)') -,('ms-MY','Malay (Malaysia)') -,('mt','Maltese') -; -INSERT INTO codelang (code,country) VALUES -('mt-MT','Maltese (Malta)') -,('nb','Norwegian (Bokm?l)') -,('nb-NO','Norwegian (Bokm?l) (Norway)') -,('nl','Dutch') -,('nl-BE','Dutch (Belgium)') -,('nl-NL','Dutch (Netherlands)') -,('nn-NO','Norwegian (Nynorsk) (Norway)') -,('ns','Northern Sotho') -,('ns-ZA','Northern Sotho (South Africa)') -,('pa','Punjabi') -; -INSERT INTO codelang (code,country) VALUES -('pa-IN','Punjabi (India)') -,('pl','Polish') -,('pl-PL','Polish (Poland)') -,('ps','Pashto') -,('ps-AR','Pashto (Afghanistan)') -,('pt','Portuguese') -,('pt-BR','Portuguese (Brazil)') -,('pt-PT','Portuguese (Portugal)') -,('qu','Quechua') -,('qu-BO','Quechua (Bolivia)') -; -INSERT INTO codelang (code,country) VALUES -('qu-EC','Quechua (Ecuador)') -,('qu-PE','Quechua (Peru)') -,('ro','Romanian') -,('ro-RO','Romanian (Romania)') -,('ru','Russian') -,('ru-RU','Russian (Russia)') -,('sa','Sanskrit') -,('sa-IN','Sanskrit (India)') -,('se','Sami (Northern)') -,('se-FI','Sami (Northern) (Finland)') -; -INSERT INTO codelang (code,country) VALUES -('se-FI','Sami (Skolt) (Finland)') -,('se-FI','Sami (Inari) (Finland)') -,('se-NO','Sami (Northern) (Norway)') -,('se-NO','Sami (Lule) (Norway)') -,('se-NO','Sami (Southern) (Norway)') -,('se-SE','Sami (Northern) (Sweden)') -,('se-SE','Sami (Lule) (Sweden)') -,('se-SE','Sami (Southern) (Sweden)') -,('sk','Slovak') -,('sk-SK','Slovak (Slovakia)') -; -INSERT INTO codelang (code,country) VALUES -('sl','Slovenian') -,('sl-SI','Slovenian (Slovenia)') -,('sq','Albanian') -,('sq-AL','Albanian (Albania)') -,('sr-BA','Serbian (Latin) (Bosnia and Herzegovina)') -,('sr-BA','Serbian (Cyrillic) (Bosnia and Herzegovina)') -,('sr-SP','Serbian (Latin) (Serbia and Montenegro)') -,('sr-SP','Serbian (Cyrillic) (Serbia and Montenegro)') -,('sv','Swedish') -,('sv-FI','Swedish (Finland)') -; -INSERT INTO codelang (code,country) VALUES -('sv-SE','Swedish (Sweden)') -,('sw','Swahili') -,('sw-KE','Swahili (Kenya)') -,('syr','Syriac') -,('syr-SY','Syriac (Syria)') -,('ta','Tamil') -,('ta-IN','Tamil (India)') -,('te','Telugu') -,('te-IN','Telugu (India)') -,('th','Thai') -; -INSERT INTO codelang (code,country) VALUES -('th-TH','Thai (Thailand)') -,('tl','Tagalog') -,('tl-PH','Tagalog (Philippines)') -,('tn','Tswana') -,('tn-ZA','Tswana (South Africa)') -,('tr','Turkish') -,('tr-TR','Turkish (Turkey)') -,('tt','Tatar') -,('tt-RU','Tatar (Russia)') -,('ts','Tsonga') -; -INSERT INTO codelang (code,country) VALUES -('uk','Ukrainian') -,('uk-UA','Ukrainian (Ukraine)') -,('ur','Urdu') -,('ur-PK','Urdu (Islamic Republic of Pakistan)') -,('uz','Uzbek (Latin)') -,('uz-UZ','Uzbek (Latin) (Uzbekistan)') -,('uz-UZ','Uzbek (Cyrillic) (Uzbekistan)') -,('vi','Vietnamese') -,('vi-VN','Vietnamese (Viet Nam)') -,('xh','Xhosa') -; -INSERT INTO codelang (code,country) VALUES -('xh-ZA','Xhosa (South Africa)') -,('zh','Chinese') -,('zh-CN','Chinese (S)') -,('zh-HK','Chinese (Hong Kong)') -,('zh-MO','Chinese (Macau)') -,('zh-SG','Chinese (Singapore)') -,('zh-TW','Chinese (T)') -,('zu','Zulu') -,('zu-ZA','Zulu (South Africa)') -; -/***************************/ --- Manual migrate - -CREATE TABLE country_code ( - code varchar(100) NULL, - country varchar(10000) NULL -); - -insert into country_code(code, country) -select distinct - t.code, - coalesce( - case when length(t.country_name2) = 1 then null else t.country_name2 end, - case when length(t.contry_name1) = 1 then null else t.contry_name1 end, - t.country - ) as country -from -( - select trim(c.code) as code, - substring(trim(c.country) from '\((.+)\)') as contry_name1, - substring( - substring(trim(c.country) from '\((.+)\)') - from '\((.*)$') as country_name2, - trim(c.country) as country - from codelang as c -) t; - -commit; - - -delete from location_country_languages as lcl -where lcl.country_id in -( - select - lc.id - from - ( - select - lpad((row_number() over (order by t.country asc))::text, 3, '0') as code, - jsonb_build_object('en-GB', t.country) as "name" - from - ( - select - distinct c.country - from country_code c - ) t - ) d - join location_country lc on lc.code = d.code and d."name"=lc."name" -) -; -commit; - - -delete from location_country as lcl -where lcl.id in -( - select - lc.id - from - ( - select - lpad((row_number() over (order by t.country asc))::text, 3, '0') as code, - jsonb_build_object('en-GB', t.country) as "name" - from - ( - select - distinct c.country - from country_code c - ) t - ) d - join location_country lc on lc.code = d.code and d."name"=lc."name" -) -; - -commit; - - -delete from translation_language tl -where tl.id in -( - SELECT tl.id - FROM - ( - select - distinct c.country, c.code - from country_code c - ) t - JOIN translation_language tl ON tl.locale = t.code and tl.title = t.country -); - -commit; - -drop table country_code; -drop table codelang; - -commit; \ No newline at end of file From adbc22f1f6ad6e635f2f96a5a2cfac27c9ce4676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 16:03:19 +0300 Subject: [PATCH 31/36] Fix comment test --- apps/utils/tests/tests_permissions.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/utils/tests/tests_permissions.py b/apps/utils/tests/tests_permissions.py index ccb202ab..edc1a5d7 100644 --- a/apps/utils/tests/tests_permissions.py +++ b/apps/utils/tests/tests_permissions.py @@ -5,21 +5,14 @@ from translation.models import Language class BasePermissionTests(APITestCase): def setUp(self): - - self.lang = Language.objects.create( + self.lang = Language.objects.get( 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 = Country.objects.get( + name={"en-GB": "Russian"} ) - self.country_ru.languages.add(self.lang) - self.country_ru.save() From 87708df08446c5b0a0d76a8a971f984bfe58d8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 16:11:28 +0300 Subject: [PATCH 32/36] Fix test establishment --- apps/establishment/tests.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/apps/establishment/tests.py b/apps/establishment/tests.py index 9eb8c987..a1d8fcb5 100644 --- a/apps/establishment/tests.py +++ b/apps/establishment/tests.py @@ -28,20 +28,14 @@ class BaseTestCase(APITestCase): self.establishment_type = EstablishmentType.objects.create(name="Test establishment type") # Create lang object - self.lang = Language.objects.create( - title='English', - locale='en-GB' + self.lang = Language.objects.get( + 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 = Country.objects.get( + name={"en-GB": "Russian"} ) - self.country_ru.languages.add(self.lang) - self.country_ru.save() self.region = Region.objects.create(name='Moscow area', code='01', country=self.country_ru) @@ -331,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): From d1d092e49dfd751d3515a545128247ee7e65f73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 16:21:14 +0300 Subject: [PATCH 33/36] Fix news tests --- apps/news/tests.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/news/tests.py b/apps/news/tests.py index dd256bac..b4e2b296 100644 --- a/apps/news/tests.py +++ b/apps/news/tests.py @@ -25,20 +25,14 @@ class BaseTestCase(APITestCase): 'refresh_token': tokkens.get('refresh_token')}) self.test_news_type = NewsType.objects.create(name="Test news type") - self.lang = Language.objects.create( + self.lang = Language.objects.get( 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 = Country.objects.get( + name={"en-GB": "Russian"} ) - self.country_ru.languages.add(self.lang) - self.country_ru.save() role = Role.objects.create( role=Role.CONTENT_PAGE_MANAGER, From 23b46e69edd5a1bf08a0d29c7997d0a5b675db32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Mon, 21 Oct 2019 16:28:32 +0300 Subject: [PATCH 34/36] Fix test location --- apps/location/tests.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/location/tests.py b/apps/location/tests.py index edb719bd..cb574036 100644 --- a/apps/location/tests.py +++ b/apps/location/tests.py @@ -9,8 +9,8 @@ from location.models import City, Region, Country, Language from django.contrib.gis.geos import Point from account.models import Role, UserRole -class BaseTestCase(APITestCase): +class BaseTestCase(APITestCase): def setUp(self): self.username = 'sedragurda' self.password = 'sedragurdaredips19' @@ -29,20 +29,14 @@ class BaseTestCase(APITestCase): {'access_token': tokkens.get('access_token'), 'refresh_token': tokkens.get('refresh_token')}) - self.lang = Language.objects.create( + self.lang = Language.objects.get( 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 = Country.objects.get( + name={"en-GB": "Russian"} ) - self.country_ru.languages.add(self.lang) - self.country_ru.save() self.role = Role.objects.create(role=Role.COUNTRY_ADMIN, country=self.country_ru) @@ -52,18 +46,15 @@ class BaseTestCase(APITestCase): self.user_role.save() - # role = Role.objects.create(role=Role.COUNTRY_ADMIN) - class CountryTests(BaseTestCase): def setUp(self): super().setUp() - def test_country_CRUD(self): data = { - 'name': {"ru-RU":"Russia"}, - 'code': 'test' + 'name': {"ru-RU": "NewCountry"}, + 'code': 'test1' } response = self.client.post('/api/back/location/countries/', data=data, format='json') From afc212764687f3afd13d68c22f4a8e54f3b6c0b6 Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Mon, 21 Oct 2019 14:45:34 +0000 Subject: [PATCH 35/36] Updated news model with agenda and banner --- .../migrations/0022_auto_20191021_1306.py | 57 +++++++++++++++++++ apps/news/models.py | 42 ++++++++++++-- apps/news/serializers.py | 51 ++++++++++++++--- 3 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 apps/news/migrations/0022_auto_20191021_1306.py diff --git a/apps/news/migrations/0022_auto_20191021_1306.py b/apps/news/migrations/0022_auto_20191021_1306.py new file mode 100644 index 00000000..de8747f5 --- /dev/null +++ b/apps/news/migrations/0022_auto_20191021_1306.py @@ -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'), + ), + ] diff --git a/apps/news/models.py b/apps/news/models.py index 8a6a89f4..0460174a 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -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.""" @@ -139,6 +163,14 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('Tags')) 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: diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 6f0b73b6..c730de0d 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -2,12 +2,46 @@ from rest_framework import serializers from account.serializers.common import UserBaseSerializer 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 +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.""" @@ -76,6 +110,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.""" @@ -83,6 +119,8 @@ class NewsDetailWebSerializer(NewsDetailSerializer): fields = NewsDetailSerializer.Meta.fields + ( 'same_theme', 'should_read', + 'agenda', + 'banner' ) @@ -116,10 +154,9 @@ 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', ) - From 4ceb0e4d86aeba8a88879a8773a3c08096dcf35d Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Mon, 21 Oct 2019 20:22:47 +0300 Subject: [PATCH 36/36] fix establishment migration --- .../0039_establishmentsubtype_index_name.py | 2 +- apps/search_indexes/signals.py | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/establishment/migrations/0039_establishmentsubtype_index_name.py b/apps/establishment/migrations/0039_establishmentsubtype_index_name.py index 5473600a..a29b1ae0 100644 --- a/apps/establishment/migrations/0039_establishmentsubtype_index_name.py +++ b/apps/establishment/migrations/0039_establishmentsubtype_index_name.py @@ -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', diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index 0f6a071f..77660a2c 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -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':