diff --git a/apps/account/admin.py b/apps/account/admin.py index 3661b3e5..db5db937 100644 --- a/apps/account/admin.py +++ b/apps/account/admin.py @@ -7,14 +7,14 @@ from account import models @admin.register(models.Role) class RoleAdmin(admin.ModelAdmin): - list_display = ['id', 'role', 'country'] - raw_id_fields = ['country', ] + list_display = ['id', 'role', 'country', 'establishment_subtype', ] + raw_id_fields = ['country', 'establishment_subtype', ] @admin.register(models.UserRole) class UserRoleAdmin(admin.ModelAdmin): - list_display = ['user', 'role', 'establishment', ] - raw_id_fields = ['user', 'role', 'establishment', 'requester', ] + list_display = ['user', 'role', ] + raw_id_fields = ['user', 'role', 'requester', 'establishment', ] @admin.register(models.User) diff --git a/apps/account/models.py b/apps/account/models.py index 96a4916c..c43f7e19 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -1,8 +1,11 @@ """Account models""" +from collections import Counter from datetime import datetime -from django.contrib.postgres.search import TrigramSimilarity +from typing import List + from django.conf import settings from django.contrib.auth.models import AbstractUser, UserManager as BaseUserManager +from django.contrib.postgres.search import TrigramSimilarity from django.core.mail import send_mail from django.db import models from django.template.loader import render_to_string, get_template @@ -11,9 +14,8 @@ from django.utils.encoding import force_bytes from django.utils.html import mark_safe from django.utils.http import urlsafe_base64_encode from django.utils.translation import ugettext_lazy as _ +from phonenumber_field.modelfields import PhoneNumberField from rest_framework.authtoken.models import Token -from collections import Counter -from typing import List from authorization.models import Application from establishment.models import Establishment, EstablishmentSubType @@ -22,7 +24,6 @@ from main.models import SiteSettings from utils.models import GMTokenGenerator from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin from utils.tokens import GMRefreshToken -from phonenumber_field.modelfields import PhoneNumberField class RoleQuerySet(models.QuerySet): @@ -41,31 +42,35 @@ class RoleQuerySet(models.QuerySet): class Role(ProjectBaseMixin): """Base Role model.""" STANDARD_USER = 1 - COMMENTS_MODERATOR = 2 + MODERATOR = 2 COUNTRY_ADMIN = 3 CONTENT_PAGE_MANAGER = 4 ESTABLISHMENT_MANAGER = 5 - REVIEWER_MANGER = 6 - RESTAURANT_REVIEWER = 7 + REVIEW_MANAGER = 6 + RESTAURANT_INSPECTOR = 7 SALES_MAN = 8 - WINERY_REVIEWER = 9 # Establishments subtype "winery" + WINERY_WINE_INSPECTOR = 9 SELLER = 10 - LIQUOR_REVIEWER = 11 - PRODUCT_REVIEWER = 12 + DISTILLERY_LIQUOR_INSPECTOR = 11 + PRODUCER_FOOD_INSPECTOR = 12 + ESTABLISHMENT_ADMINISTRATOR = 13 + ARTISAN_INSPECTOR = 14 ROLE_CHOICES = ( (STANDARD_USER, _('Standard user')), - (COMMENTS_MODERATOR, _('Comments moderator')), + (MODERATOR, _('Moderator')), (COUNTRY_ADMIN, _('Country admin')), (CONTENT_PAGE_MANAGER, _('Content page manager')), (ESTABLISHMENT_MANAGER, _('Establishment manager')), - (REVIEWER_MANGER, _('Reviewer manager')), - (RESTAURANT_REVIEWER, _('Restaurant reviewer')), + (REVIEW_MANAGER, _('Review manager')), + (RESTAURANT_INSPECTOR, _('Restaurant inspector')), (SALES_MAN, _('Sales man')), - (WINERY_REVIEWER, _('Winery reviewer')), + (WINERY_WINE_INSPECTOR, _('Winery and wine inspector')), (SELLER, _('Seller')), - (LIQUOR_REVIEWER, _('Liquor reviewer')), - (PRODUCT_REVIEWER, _('Product reviewer')), + (DISTILLERY_LIQUOR_INSPECTOR, _('Distillery & Liquor inspector')), + (PRODUCER_FOOD_INSPECTOR, _('Producer food inspector')), + (ESTABLISHMENT_ADMINISTRATOR, _('Establishment administrator')), + (ARTISAN_INSPECTOR, _('Artisan inspector')), ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, @@ -436,6 +441,31 @@ class User(AbstractUser): result.append(item.id) return set(result) + @property + def is_country_admin(self): + if self.userrole_set: + return self.userrole_set.country_admin().exists() + + @property + def is_establishment_manager(self): + if self.userrole_set: + return self.userrole_set.establishment_manager().exists() + + @property + def is_establishment_administrator(self): + if self.userrole_set: + return self.userrole_set.establishment_administrator().exists() + + @property + def administrated_country_codes(self) -> list: + if self.userrole_set: + return list( + self.userrole_set + .exclude(role__site__isnull=True) + .values_list('role__site__country__code', flat=True) + .distinct() + ) + def set_roles(self, ids: List[int]): """ Set user roles @@ -490,6 +520,31 @@ class UserRoleQueryset(models.QuerySet): }) return role_counter + def validated(self): + """Filter QuerySet by state.""" + return self.filter(state=self.model.VALIDATED) + + def country_admin(self): + """Return status by role and state""" + return ( + self.filter(role__role=Role.COUNTRY_ADMIN) + .validated() + ) + + def establishment_manager(self): + """Return status by role and state""" + return ( + self.filter(role__role=Role.ESTABLISHMENT_MANAGER) + .validated() + ) + + def establishment_administrator(self): + """Return status by role and state""" + return ( + self.filter(role__role=Role.ESTABLISHMENT_ADMINISTRATOR) + .validated() + ) + class UserRole(ProjectBaseMixin): """UserRole model.""" diff --git a/apps/account/views/back.py b/apps/account/views/back.py index 822ecc35..9348f9f2 100644 --- a/apps/account/views/back.py +++ b/apps/account/views/back.py @@ -3,7 +3,8 @@ from datetime import datetime from django.http import HttpResponse, HttpResponseNotFound from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions, status +from rest_framework import generics, status +from rest_framework import permissions from rest_framework.authtoken.models import Token from rest_framework.filters import OrderingFilter from rest_framework.response import Response @@ -13,16 +14,19 @@ from account import models, filters from account.models import User from account.serializers import back as serializers from account.serializers.common import RoleBaseSerializer +from utils.methods import get_permission_classes +from utils.permissions import IsReviewManager class RoleListView(generics.ListCreateAPIView): serializer_class = RoleBaseSerializer queryset = models.Role.objects.all() filter_class = filters.RoleListFilter + permission_classes = get_permission_classes() class RoleTypeRetrieveView(generics.GenericAPIView): - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes() def get(self, request, *args, **kwargs): """Implement GET-method""" @@ -39,14 +43,15 @@ class RoleTypeRetrieveView(generics.GenericAPIView): class UserRoleListView(generics.ListCreateAPIView): serializer_class = serializers.UserRoleSerializer queryset = models.UserRole.objects.all() + permission_classes = get_permission_classes() class UserListView(generics.ListCreateAPIView): """User list create view.""" serializer_class = serializers.BackUserSerializer - permission_classes = (permissions.IsAdminUser,) filter_class = filters.AccountBackOfficeFilter filter_backends = (OrderingFilter, DjangoFilterBackend) + permission_classes = get_permission_classes(IsReviewManager) ordering_fields = ( 'email_confirmed', @@ -66,8 +71,8 @@ class UserRUDView(generics.RetrieveUpdateDestroyAPIView): """User RUD view.""" queryset = User.objects.all() serializer_class = serializers.BackDetailUserSerializer - permission_classes = (permissions.IsAdminUser,) lookup_field = 'id' + permission_classes = get_permission_classes() def get_user_csv(request, id): diff --git a/apps/advertisement/views/back.py b/apps/advertisement/views/back.py index 27a016a2..3590c9b4 100644 --- a/apps/advertisement/views/back.py +++ b/apps/advertisement/views/back.py @@ -1,20 +1,21 @@ """Back office views for app advertisement""" +from django.shortcuts import get_object_or_404 from rest_framework import generics, status from rest_framework.response import Response -from rest_framework import permissions -from django.shortcuts import get_object_or_404 -from main.serializers import PageExtendedSerializer from advertisement.models import Advertisement -from advertisement.serializers import (AdvertisementBaseSerializer, - AdvertisementDetailSerializer) +from advertisement.serializers import ( + AdvertisementBaseSerializer, + AdvertisementDetailSerializer) +from main.serializers import PageExtendedSerializer +from utils.methods import get_permission_classes class AdvertisementBackOfficeViewMixin(generics.GenericAPIView): """Base back office advertisement view.""" pagination_class = None - permission_classes = (permissions.IsAuthenticated, ) + permission_classes = get_permission_classes() def get_queryset(self): """Overridden get queryset method.""" diff --git a/apps/collection/views/back.py b/apps/collection/views/back.py index 8b9d0d8e..73e1eb71 100644 --- a/apps/collection/views/back.py +++ b/apps/collection/views/back.py @@ -5,8 +5,8 @@ from rest_framework import mixins, permissions, viewsets from rest_framework import status from rest_framework.response import Response -from collection import models, serializers, filters -from collection import tasks +from collection import models, serializers, filters, tasks +from utils.methods import get_permission_classes from utils.views import BindObjectMixin @@ -14,8 +14,8 @@ class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): """ViewSet for Collection model.""" # pagination_class = None - permission_classes = (permissions.AllowAny,) serializer_class = serializers.CollectionBackOfficeSerializer + permission_classes = get_permission_classes() def get_queryset(self): """Overridden method 'get_queryset'.""" @@ -28,7 +28,7 @@ class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): class GuideBaseView(generics.GenericAPIView): """ViewSet for Guide model.""" serializer_class = serializers.GuideBaseSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes() def get_queryset(self): """Overridden get_queryset method.""" @@ -40,7 +40,7 @@ class GuideFilterBaseView(generics.GenericAPIView): pagination_class = None queryset = models.GuideFilter.objects.all() serializer_class = serializers.GuideFilterBaseSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes() class GuideElementBaseView(generics.GenericAPIView): @@ -48,7 +48,7 @@ class GuideElementBaseView(generics.GenericAPIView): pagination_class = None queryset = models.GuideElement.objects.all() serializer_class = serializers.GuideElementBaseSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes() class AdvertorialBaseView(generics.GenericAPIView): @@ -56,7 +56,7 @@ class AdvertorialBaseView(generics.GenericAPIView): pagination_class = None queryset = models.Advertorial.objects.all() serializer_class = serializers.AdvertorialBaseSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes() class CollectionBackOfficeViewSet(mixins.CreateModelMixin, @@ -67,11 +67,11 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin, CollectionViewSet): """ViewSet for Collections list for BackOffice users and Collection create.""" - permission_classes = (permissions.IsAuthenticated,) queryset = models.Collection.objects.with_base_related().order_by('-start') filter_class = filters.CollectionFilterSet serializer_class = serializers.CollectionBackOfficeSerializer bind_object_serializer_class = serializers.CollectionBindObjectSerializer + permission_classes = get_permission_classes() def perform_binding(self, serializer): data = serializer.validated_data @@ -174,7 +174,7 @@ class GuideElementExportXMLView(generics.ListAPIView): pagination_class = None queryset = models.GuideElement.objects.all() serializer_class = serializers.GuideElementBaseSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes() def get(self, request, *args, **kwargs): """Overridden get_queryset method.""" @@ -190,7 +190,7 @@ class GuideElementExportDOCView(generics.ListAPIView): pagination_class = None queryset = models.GuideElement.objects.all() serializer_class = serializers.GuideElementBaseSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes() def get(self, request, *args, **kwargs): """Overridden get_queryset method.""" diff --git a/apps/comment/views/back.py b/apps/comment/views/back.py index 68ae9a90..ffd17f6b 100644 --- a/apps/comment/views/back.py +++ b/apps/comment/views/back.py @@ -2,7 +2,8 @@ from rest_framework import generics from comment import models from comment.serializers import CommentBaseSerializer -from utils.permissions import IsCommentModerator +from utils.methods import get_permission_classes +from utils.permissions import IsModerator class CommentLstView(generics.ListCreateAPIView): @@ -28,13 +29,13 @@ class CommentLstView(generics.ListCreateAPIView): return qs.order_by('-created') serializer_class = CommentBaseSerializer - # permission_classes = [permissions.IsAuthenticatedOrReadOnly| IsCommentModerator|IsCountryAdmin] + queryset = models.Comment.objects.all() + permission_classes = get_permission_classes(IsModerator) class CommentRUDView(generics.RetrieveUpdateDestroyAPIView): """Comment RUD view.""" serializer_class = CommentBaseSerializer queryset = models.Comment.objects.all() - permission_classes = [IsCommentModerator] - # permission_classes = [IsCountryAdmin | IsCommentModerator] + permission_classes = get_permission_classes(IsModerator) lookup_field = 'id' diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 9999d25a..2ca28311 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -2,7 +2,7 @@ from datetime import datetime from functools import reduce from operator import or_ -from typing import List +from typing import List, Union import elasticsearch_dsl from django.conf import settings @@ -14,9 +14,9 @@ from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.indexes import GinIndex from django.contrib.postgres.search import TrigramSimilarity from django.core.exceptions import ValidationError -from django.core.validators import MaxValueValidator, MinValueValidator +from django.core.validators import MinValueValidator, MaxValueValidator from django.db import models -from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When +from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q, Prefetch from django.shortcuts import get_object_or_404 from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -24,15 +24,16 @@ from phonenumber_field.modelfields import PhoneNumberField from timezone_field import TimeZoneField from location.models import Address +from location.models import WineOriginAddressMixin from main.models import Award, Currency from review.models import Review from tag.models import Tag from timetable.models import Timetable from utils.methods import transform_into_readable_str -from utils.models import ( - BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin, - ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin, URLImageMixin, -) +from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, + TranslatedFieldsMixin, BaseAttributes, GalleryMixin, + IntermediateGalleryModelMixin, HasTagsMixin, + FavoritesMixin, TypeDefaultImageMixin, FileMixin) # todo: establishment type&subtypes check @@ -111,6 +112,10 @@ class EstablishmentSubType(TypeDefaultImageMixin, TranslatedFieldsMixin, Project verbose_name = _('Establishment subtype') verbose_name_plural = _('Establishment subtypes') + def __str__(self): + """Overridden str dunder.""" + return self.index_name + def clean_fields(self, exclude=None): if not self.establishment_type.use_subtypes: raise ValidationError(_('Establishment type is not use subtypes.')) @@ -179,9 +184,10 @@ class EstablishmentQuerySet(models.QuerySet): """Return establishments by country code""" return self.filter(address__city__country=country) - def by_country_code(self, code): + def by_country_code(self, codes: Union[iter, str]): """Return establishments by country code""" - return self.filter(address__city__country__code=code) + codes = codes if hasattr(codes, '__iter__') else [codes] + return self.filter(address__city__country__code__in=codes) def published(self): """ @@ -513,6 +519,23 @@ class EstablishmentQuerySet(models.QuerySet): to_attr='main_image') ) + def available_establishments(self, user, country_code: str): + """Return QuerySet with establishments that user has an access.""" + from account.models import UserRole + + if not user.is_staff: + filters = {'address__city__country__code': country_code} + if user.is_establishment_administrator and not user.is_establishment_manager: + filters.update({ + 'id__in': models.Subquery( + UserRole.objects.filter(user=user, role__site__country__code=country_code) + .distinct('user', 'establishment') + .values_list('establishment', flat=True) + ) + }) + return self.filter(**filters) + return self + def with_contacts(self): return self.prefetch_related('emails', 'phones') @@ -776,13 +799,6 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, """ return self.address.country_id if hasattr(self.address, 'country_id') else None - @property - def establishment_id(self): - """ - Return establishment id of establishment location - """ - return self.id - @property def wines(self): """Return list products with type wine""" diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 39ede5c3..beabf801 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -3,7 +3,7 @@ from django.db.models.query_utils import Q from django.http import Http404 from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions +from rest_framework import generics, status from rest_framework.response import Response from account.models import User @@ -11,7 +11,9 @@ from establishment import filters, models, serializers from establishment.models import EstablishmentEmployee from timetable.models import Timetable from timetable.serialziers import ScheduleCreateSerializer, ScheduleRUDSerializer -from utils.permissions import IsCountryAdmin, IsEstablishmentManager, IsWineryReviewer +from utils.methods import get_permission_classes +from utils.permissions import ( + IsEstablishmentManager, IsEstablishmentAdministrator, ) from utils.views import CreateDestroyGalleryViewMixin @@ -33,26 +35,34 @@ class EstablishmentMixinViews: """Establishment mixin.""" def get_queryset(self): - """Overrided method 'get_queryset'.""" - return models.Establishment.objects.with_base_related().with_contacts() + """Overridden method 'get_queryset'.""" + queryset = models.Establishment.objects.with_base_related + if hasattr(self, 'request') and \ + (hasattr(self.request, 'user') and hasattr(self.request, 'country_code')): + return queryset().available_establishments(self.request.user, self.request.country_code) + return queryset().none() class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView): """Establishment list/create view.""" filter_class = filters.EstablishmentFilter - - permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] - queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentListCreateSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class EmployeeEstablishmentPositionsView(generics.ListAPIView): """Establishment by employee view.""" - permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] queryset = models.EstablishmentEmployee.objects.all() serializer_class = serializers.EstablishmentEmployeePositionsSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) def get_queryset(self): employee_pk = self.kwargs.get('pk') @@ -62,10 +72,11 @@ class EmployeeEstablishmentPositionsView(generics.ListAPIView): class EmployeeEstablishmentsListView(generics.ListAPIView): """Establishment by employee list view.""" - - permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] - queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentListCreateSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) def get_queryset(self): pk = self.kwargs.get('pk') @@ -76,9 +87,12 @@ class EmployeeEstablishmentsListView(generics.ListAPIView): class EmployeePositionsListView(generics.ListAPIView): """Establishment position by employee list view.""" - permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] queryset = models.Establishment.objects.all() serializer_class = serializers.EstablishmentPositionListSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) def get_queryset(self): pk = self.kwargs.get('pk') @@ -86,21 +100,31 @@ class EmployeePositionsListView(generics.ListAPIView): return employee.establishments.with_extended_related() -class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): +class EstablishmentRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView): lookup_field = 'slug' - queryset = models.Establishment.objects.all().prefetch_related( - 'establishmentemployee_set', - 'establishmentemployee_set__establishment', - ) serializer_class = serializers.EstablishmentRUDSerializer - permission_classes = [IsWineryReviewer | IsCountryAdmin | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) + + def get_queryset(self): + """Overridden get_queryset method.""" + qs = super(EstablishmentRUDView, self).get_queryset() + return qs.prefetch_related( + 'establishmentemployee_set', + 'establishmentemployee_set__establishment', + ) -class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): +class EstablishmentScheduleRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView): """Establishment schedule RUD view""" lookup_field = 'slug' serializer_class = ScheduleRUDSerializer - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) def get_object(self): """ @@ -109,7 +133,7 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): establishment_slug = self.kwargs['slug'] schedule_id = self.kwargs['schedule_id'] - establishment = get_object_or_404(klass=models.Establishment.objects.all(), + establishment = get_object_or_404(klass=super(EstablishmentScheduleRUDView, self).get_queryset(), slug=establishment_slug) schedule = get_object_or_404(klass=establishment.schedule, id=schedule_id) @@ -126,15 +150,21 @@ class EstablishmentScheduleCreateView(generics.CreateAPIView): lookup_field = 'slug' serializer_class = ScheduleCreateSerializer queryset = Timetable.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class MenuListCreateView(generics.ListCreateAPIView): """Menu list create view.""" serializer_class = serializers.MenuSerializers queryset = models.Menu.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_backends = (DjangoFilterBackend,) + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) filterset_fields = ( 'establishment__id', 'establishment__slug', @@ -146,25 +176,33 @@ class MenuListCreateView(generics.ListCreateAPIView): class MenuRUDView(MenuRUDMixinViews, generics.RetrieveUpdateDestroyAPIView): """Menu RUD view.""" - lookup_field = None serializer_class = serializers.MenuRUDSerializers queryset = models.Menu.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class MenuFilesListCreateView(generics.ListCreateAPIView): """Menu files list create view.""" serializer_class = serializers.MenuFilesSerializers queryset = models.MenuFiles.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class MenuFilesRUDView(generics.RetrieveDestroyAPIView): """Menu files RUD view.""" serializer_class = serializers.MenuFilesSerializers queryset = models.MenuFiles.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class SocialChoiceListCreateView(generics.ListCreateAPIView): @@ -172,14 +210,20 @@ class SocialChoiceListCreateView(generics.ListCreateAPIView): serializer_class = serializers.SocialChoiceSerializers queryset = models.SocialChoice.objects.all() pagination_class = None - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class SocialChoiceRUDView(generics.RetrieveUpdateDestroyAPIView): """SocialChoice RUD view.""" serializer_class = serializers.SocialChoiceSerializers queryset = models.SocialChoice.objects.all() - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class SocialListCreateView(generics.ListCreateAPIView): @@ -187,14 +231,20 @@ class SocialListCreateView(generics.ListCreateAPIView): serializer_class = serializers.SocialNetworkSerializers queryset = models.SocialNetwork.objects.all() pagination_class = None - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class SocialRUDView(generics.RetrieveUpdateDestroyAPIView): """Social RUD view.""" serializer_class = serializers.SocialNetworkSerializers queryset = models.SocialNetwork.objects.all() - permission_classes = [IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class PlateListCreateView(generics.ListCreateAPIView): @@ -202,14 +252,20 @@ class PlateListCreateView(generics.ListCreateAPIView): serializer_class = serializers.PlatesSerializers queryset = models.Plate.objects.all() pagination_class = None - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) class PlateRUDView(generics.RetrieveUpdateDestroyAPIView): """Plate RUD view.""" serializer_class = serializers.PlatesSerializers queryset = models.Plate.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) class PhonesListCreateView(generics.ListCreateAPIView): @@ -217,14 +273,20 @@ class PhonesListCreateView(generics.ListCreateAPIView): serializer_class = serializers.ContactPhoneBackSerializers queryset = models.ContactPhone.objects.all() pagination_class = None - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) class PhonesRUDView(generics.RetrieveUpdateDestroyAPIView): """Phones RUD view.""" serializer_class = serializers.ContactPhoneBackSerializers queryset = models.ContactPhone.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) class EmailListCreateView(generics.ListCreateAPIView): @@ -232,38 +294,53 @@ class EmailListCreateView(generics.ListCreateAPIView): serializer_class = serializers.ContactEmailBackSerializers queryset = models.ContactEmail.objects.all() pagination_class = None - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) class EmailRUDView(generics.RetrieveUpdateDestroyAPIView): """Email RUD view.""" serializer_class = serializers.ContactEmailBackSerializers queryset = models.ContactEmail.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) class EmployeeListCreateView(generics.ListCreateAPIView): - """Emplyoee list create view.""" - permission_classes = (permissions.AllowAny,) + """Employee list create view.""" filter_class = filters.EmployeeBackFilter serializer_class = serializers.EmployeeBackSerializers queryset = models.Employee.objects.all().distinct().with_back_office_related() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class EmployeesListSearchViews(generics.ListAPIView): """Employee search view""" pagination_class = None - permission_classes = (permissions.AllowAny,) queryset = models.Employee.objects.all().with_back_office_related().select_related('photo') filter_class = filters.EmployeeBackSearchFilter serializer_class = serializers.EmployeeBackSerializers + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class EstablishmentEmployeeListView(generics.ListCreateAPIView): """Establishment emplyoees list view.""" - permission_classes = (permissions.AllowAny,) serializer_class = serializers.EstEmployeeBackSerializer pagination_class = None + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) def get_queryset(self): establishment_id = self.kwargs['establishment_id'] @@ -275,13 +352,18 @@ class EstablishmentEmployeeListView(generics.ListCreateAPIView): class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView): """Employee RUD view.""" serializer_class = serializers.EmployeeBackSerializers - queryset = models.Employee.objects.all().with_back_office_related() + queryset = models.Employee.objects.with_back_office_related() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + ) class RemoveAwardView(generics.DestroyAPIView): lookup_field = 'pk' serializer_class = serializers.EmployeeBackSerializers - queryset = models.Employee.objects.all().with_back_office_related() + queryset = models.Employee.objects.with_back_office_related() + permission_classes = get_permission_classes() def get_object(self): employee = super().get_object() @@ -296,27 +378,31 @@ class RemoveAwardView(generics.DestroyAPIView): class EstablishmentTypeListCreateView(generics.ListCreateAPIView): """Establishment type list/create view.""" serializer_class = serializers.EstablishmentTypeBaseSerializer - queryset = models.EstablishmentType.objects.all().select_related('default_image') + queryset = models.EstablishmentType.objects.select_related('default_image') pagination_class = None + permission_classes = get_permission_classes() class EstablishmentTypeRUDView(generics.RetrieveUpdateDestroyAPIView): """Establishment type retrieve/update/destroy view.""" serializer_class = serializers.EstablishmentTypeBaseSerializer - queryset = models.EstablishmentType.objects.all().select_related('default_image') + queryset = models.EstablishmentType.objects.select_related('default_image') + permission_classes = get_permission_classes() class EstablishmentSubtypeListCreateView(generics.ListCreateAPIView): """Establishment subtype list/create view.""" serializer_class = serializers.EstablishmentSubTypeBaseSerializer - queryset = models.EstablishmentSubType.objects.all().select_related('default_image') + queryset = models.EstablishmentSubType.objects.select_related('default_image') pagination_class = None + permission_classes = get_permission_classes() class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView): """Establishment subtype retrieve/update/destroy view.""" serializer_class = serializers.EstablishmentSubTypeBaseSerializer - queryset = models.EstablishmentSubType.objects.all().select_related('default_image') + queryset = models.EstablishmentSubType.objects.select_related('default_image') + permission_classes = get_permission_classes() class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews, @@ -324,6 +410,7 @@ class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews, """Resource for a create|destroy gallery for establishment for back-office users.""" lookup_field = 'slug' serializer_class = serializers.EstablishmentBackOfficeGallerySerializer + permission_classes = get_permission_classes() def get_object(self): """ @@ -346,6 +433,7 @@ class EstablishmentGalleryListView(EstablishmentMixinViews, """Resource for returning gallery for establishment for back-office users.""" lookup_field = 'slug' serializer_class = serializers.ImageBaseSerializer + permission_classes = get_permission_classes() def get_object(self): """Override get_object method.""" @@ -368,13 +456,14 @@ class EstablishmentCompanyListCreateView(EstablishmentMixinViews, lookup_field = 'slug' serializer_class = serializers.EstablishmentCompanyListCreateSerializer + permission_classes = get_permission_classes() def get_object(self): """Returns the object the view is displaying.""" - establishment_qs = models.Establishment.objects.all() - filtered_ad_qs = self.filter_queryset(establishment_qs) + establishment_qs = super(EstablishmentCompanyListCreateView, self).get_queryset() + filtered_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_ad_qs, slug=self.kwargs.get('slug')) + establishment = get_object_or_404(filtered_qs, slug=self.kwargs.get('slug')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -392,10 +481,11 @@ class EstablishmentCompanyRUDView(EstablishmentMixinViews, lookup_field = 'slug' serializer_class = serializers.CompanyBaseSerializer + permission_classes = get_permission_classes() def get_object(self): """Returns the object the view is displaying.""" - establishment_qs = models.Establishment.objects.all() + establishment_qs = super(EstablishmentCompanyRUDView, self).get_queryset() filtered_ad_qs = self.filter_queryset(establishment_qs) establishment = get_object_or_404(filtered_ad_qs, slug=self.kwargs.get('slug')) @@ -413,10 +503,14 @@ class EstablishmentNoteListCreateView(EstablishmentMixinViews, lookup_field = 'slug' serializer_class = serializers.EstablishmentNoteListCreateSerializer + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) def get_object(self): """Returns the object the view is displaying.""" - establishment_qs = models.Establishment.objects.all() + establishment_qs = super(EstablishmentNoteListCreateView, self).get_queryset() filtered_establishment_qs = self.filter_queryset(establishment_qs) establishment = get_object_or_404(filtered_establishment_qs, slug=self.kwargs.get('slug')) @@ -437,10 +531,14 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews, lookup_field = 'slug' serializer_class = serializers.EstablishmentNoteBaseSerializer + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager, + ) def get_object(self): """Returns the object the view is displaying.""" - establishment_qs = models.Establishment.objects.all() + establishment_qs = super(EstablishmentNoteRUDView, self).get_queryset() filtered_establishment_qs = self.filter_queryset(establishment_qs) establishment = get_object_or_404(filtered_establishment_qs, slug=self.kwargs.get('slug')) @@ -455,27 +553,39 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews, class EstablishmentEmployeeCreateView(generics.CreateAPIView): serializer_class = serializers.EstablishmentEmployeeCreateSerializer queryset = models.EstablishmentEmployee.objects.all() - # TODO send email to all admins and add endpoint for changing status + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class EstablishmentEmployeeDeleteView(generics.DestroyAPIView): queryset = EstablishmentEmployee.objects.all() - permission_classes = [IsEstablishmentManager | permissions.IsAdminUser] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class EstablishmentPositionListView(generics.ListAPIView): """Establishment positions list view.""" pagination_class = None - permission_classes = (permissions.AllowAny,) queryset = models.Position.objects.all() serializer_class = serializers.PositionBackSerializer + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class EstablishmentAdminView(generics.ListAPIView): """Establishment admin list view.""" serializer_class = serializers.EstablishmentAdminListSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) def get_queryset(self): establishment = get_object_or_404( @@ -487,22 +597,29 @@ class MenuDishesListView(generics.ListAPIView): """Menu (dessert, main_course, starter) list create view.""" serializer_class = serializers.MenuDishesSerializer queryset = models.Menu.objects.with_dishes() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_class = filters.MenuDishesBackFilter + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class MenuDishesRUDView(MenuRUDMixinViews, generics.RetrieveUpdateDestroyAPIView): """Menu (dessert, main_course, starter) RUD view.""" - lookup_field = None serializer_class = serializers.MenuDishesRUDSerializers queryset = models.Menu.objects.with_dishes() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] - + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class MenuDishesCreateView(generics.CreateAPIView): """Menu (dessert, main_course, starter) list create view.""" serializer_class = serializers.MenuDishesCreateSerializer queryset = models.MenuDish.objects.all() - permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_class = filters.MenuDishesBackFilter + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) diff --git a/apps/favorites/views.py b/apps/favorites/views.py index 1e7a12b3..626b8c07 100644 --- a/apps/favorites/views.py +++ b/apps/favorites/views.py @@ -1,30 +1,45 @@ """Views for app favorites.""" from rest_framework import generics -from establishment.models import Establishment + from establishment.filters import EstablishmentFilter -from establishment.serializers import EstablishmentBaseSerializer, EstablishmentSimilarSerializer +from establishment.models import Establishment +from establishment.serializers import EstablishmentSimilarSerializer from news.filters import NewsListFilterSet from news.models import News -from news.serializers import NewsBaseSerializer, NewsListSerializer +from news.serializers import NewsListSerializer +from product.filters import ProductFilterSet from product.models import Product from product.serializers import ProductBaseSerializer -from product.filters import ProductFilterSet -from .models import Favorites +from utils.methods import get_permission_classes +from utils.permissions import ( + IsApprovedUser, IsEstablishmentAdministrator, IsWineryWineInspector, + IsRestaurantInspector, IsContentPageManager, IsEstablishmentManager, + IsReviewManager, IsDistilleryLiquorInspector, IsArtisanInspector, + IsGuest, IsModerator, IsProducerFoodInspector, +) -class FavoritesBaseView(generics.GenericAPIView): - """Base view for Favorites.""" - - def get_queryset(self): - """Override get_queryset method.""" - return Favorites.objects.by_user(self.request.user) +class FavoritesPermissionMixin: + """Permissions for application favorites.""" + permission_classes = get_permission_classes( + IsApprovedUser, IsEstablishmentAdministrator, IsWineryWineInspector, + IsRestaurantInspector, IsContentPageManager, IsEstablishmentManager, + IsReviewManager, IsDistilleryLiquorInspector, IsArtisanInspector, + IsModerator, IsProducerFoodInspector, + ) -class FavoritesEstablishmentListView(generics.ListAPIView): +class FavoritesEstablishmentListView(FavoritesPermissionMixin, generics.ListAPIView): """List views for establishments in favorites.""" serializer_class = EstablishmentSimilarSerializer filter_class = EstablishmentFilter + permission_classes = get_permission_classes( + IsApprovedUser, IsEstablishmentAdministrator, IsWineryWineInspector, + IsRestaurantInspector, IsContentPageManager, IsEstablishmentManager, + IsReviewManager, IsDistilleryLiquorInspector, IsArtisanInspector, + IsGuest, IsModerator, IsProducerFoodInspector, + ) def get_queryset(self): """Override get_queryset method""" @@ -36,7 +51,7 @@ class FavoritesEstablishmentListView(generics.ListAPIView): .with_certain_tag_category_related('distillery_type', 'distillery_type') -class FavoritesProductListView(generics.ListAPIView): +class FavoritesProductListView(FavoritesPermissionMixin, generics.ListAPIView): """List views for products in favorites.""" serializer_class = ProductBaseSerializer @@ -48,7 +63,7 @@ class FavoritesProductListView(generics.ListAPIView): .order_by('-favorites') -class FavoritesNewsListView(generics.ListAPIView): +class FavoritesNewsListView(FavoritesPermissionMixin, generics.ListAPIView): """List views for news in favorites.""" serializer_class = NewsListSerializer diff --git a/apps/gallery/views.py b/apps/gallery/views.py index 1515707f..3b5063ba 100644 --- a/apps/gallery/views.py +++ b/apps/gallery/views.py @@ -3,6 +3,8 @@ from django.db.transaction import on_commit from rest_framework import generics, status from rest_framework.response import Response +from utils.methods import get_permission_classes +from utils.permissions import IsContentPageManager from . import tasks, models, serializers @@ -11,6 +13,9 @@ class ImageBaseView(generics.GenericAPIView): model = models.Image queryset = models.Image.objects.all() serializer_class = serializers.ImageSerializer + permission_classes = get_permission_classes( + IsContentPageManager + ) class ImageListCreateView(ImageBaseView, generics.ListCreateAPIView): diff --git a/apps/location/models.py b/apps/location/models.py index 579addd1..8e24967a 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -178,12 +178,12 @@ class City(models.Model, TranslatedFieldsMixin): verbose_name = _('city') def __str__(self): - return self.name_dumped + return f'{self.id}: {self.code}' @property def name_dumped(self): """Used for indexing as string""" - return f'{self.id}: {dumps(self.name)}' + return dumps(self.name) @property def image_object(self): diff --git a/apps/location/views/back.py b/apps/location/views/back.py index ec8f914c..3138fe2c 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -1,45 +1,52 @@ """Location app views.""" -from rest_framework import generics from django.contrib.postgres.fields.jsonb import KeyTextTransform -from utils.models import get_current_locale - -from location import models, serializers -from location.views import common -from utils.permissions import IsCountryAdmin -from utils.views import CreateDestroyGalleryViewMixin -from rest_framework.permissions import IsAuthenticatedOrReadOnly -from django.shortcuts import get_object_or_404 -from django.db import IntegrityError -from utils.serializers import ImageBaseSerializer -from location.filters import RegionFilter +from rest_framework import generics from location import filters +from location import models, serializers +from location.filters import RegionFilter +from location.views import common +from utils.methods import get_permission_classes +from utils.models import get_current_locale +from utils.permissions import ( + IsGuest, IsEstablishmentManager, IsEstablishmentAdministrator +) # Address - - class AddressListCreateView(common.AddressViewMixin, generics.ListCreateAPIView): """Create view for model Address.""" serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) class AddressRUDView(common.AddressViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model Address.""" serializer_class = serializers.AddressDetailSerializer queryset = models.Address.objects.all() - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) # City class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView): """Create view for model City.""" serializer_class = serializers.CityBaseSerializer - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] queryset = models.City.objects.all() filter_class = filters.CityBackFilter + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) def get_queryset(self): """Overridden method 'get_queryset'.""" @@ -53,18 +60,26 @@ class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView): class CityListSearchView(common.CityViewMixin, generics.ListCreateAPIView): """Create view for model City.""" serializer_class = serializers.CityBaseSerializer - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] queryset = models.City.objects.all()\ .annotate(locale_name=KeyTextTransform(get_current_locale(), 'name'))\ .order_by('locale_name') filter_class = filters.CityBackFilter pagination_class = None + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) class CityRUDView(common.CityViewMixin, generics.RetrieveUpdateDestroyAPIView): """RUD view for model City.""" serializer_class = serializers.CityDetailSerializer - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) # Region @@ -72,15 +87,23 @@ class RegionListCreateView(common.RegionViewMixin, generics.ListCreateAPIView): """Create view for model Region""" pagination_class = None serializer_class = serializers.RegionSerializer - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] # ordering_fields = 'name' filter_class = RegionFilter + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) class RegionRUDView(common.RegionViewMixin, generics.RetrieveUpdateDestroyAPIView): """Retrieve view for model Region""" serializer_class = serializers.RegionSerializer - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) # Country @@ -91,11 +114,19 @@ class CountryListCreateView(generics.ListCreateAPIView): .order_by('locale_name') serializer_class = serializers.CountryBackSerializer pagination_class = None - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) class CountryRUDView(generics.RetrieveUpdateDestroyAPIView): """RUD view for model Country.""" serializer_class = serializers.CountryBackSerializer - permission_classes = [IsAuthenticatedOrReadOnly | IsCountryAdmin] queryset = models.Country.objects.all() + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator, + IsGuest, + ) diff --git a/apps/main/views/back.py b/apps/main/views/back.py index 23fdc48a..4bb082cb 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -1,25 +1,26 @@ from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions, status +from rest_framework import generics, status from rest_framework.generics import get_object_or_404 from rest_framework.response import Response -from main import serializers -from main.serializers.back import PanelSerializer -from establishment.serializers.back import EmployeeBackSerializers from establishment.models import Employee +from establishment.serializers.back import EmployeeBackSerializers +from main import serializers from main import tasks from main.filters import AwardFilter from main.models import Award, Footer, PageType, Panel, SiteFeature, Feature, AwardType +from main.serializers.back import PanelSerializer from main.views import SiteSettingsView, SiteListView +from utils.methods import get_permission_classes class AwardLstView(generics.ListCreateAPIView): """Award list create view.""" queryset = Award.objects.all().with_base_related() serializer_class = serializers.BackAwardSerializer - permission_classes = (permissions.IsAdminUser,) + permission_classes = get_permission_classes() filterset_class = AwardFilter @@ -27,10 +28,10 @@ class AwardCreateAndBind(generics.CreateAPIView): """Award create and bind to employee by id""" queryset = Award.objects.all().with_base_related() serializer_class = serializers.BackAwardEmployeeCreateSerializer - permission_classes = (permissions.IsAdminUser, ) + permission_classes = get_permission_classes() def create(self, request, *args, **kwargs): - """!!!Overriden!!!""" + """Overridden create method.""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) @@ -43,7 +44,7 @@ class AwardRUDView(generics.RetrieveUpdateDestroyAPIView): """Award RUD view.""" queryset = Award.objects.all().with_base_related() serializer_class = serializers.BackAwardSerializer - permission_classes = (permissions.IsAdminUser,) + permission_classes = get_permission_classes() lookup_field = 'id' @@ -52,14 +53,14 @@ class AwardTypesListView(generics.ListAPIView): pagination_class = None queryset = AwardType.objects.all() serializer_class = serializers.AwardTypeBaseSerializer - permission_classes = (permissions.AllowAny, ) + permission_classes = get_permission_classes() class ContentTypeView(generics.ListAPIView): """ContentType list view""" queryset = ContentType.objects.all() serializer_class = serializers.ContentTypeBackSerializer - permission_classes = (permissions.IsAdminUser,) + permission_classes = get_permission_classes() filter_backends = (DjangoFilterBackend, ) ordering_fields = '__all__' lookup_field = 'id' @@ -74,6 +75,7 @@ class FeatureBackView(generics.ListCreateAPIView): """Feature list or create View.""" serializer_class = serializers.FeatureSerializer queryset = Feature.objects.all() + permission_classes = get_permission_classes() class SiteFeatureBackView(generics.ListCreateAPIView): @@ -81,79 +83,75 @@ class SiteFeatureBackView(generics.ListCreateAPIView): serializer_class = serializers.SiteFeatureSerializer queryset = SiteFeature.objects.all() pagination_class = None - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes() class FeatureRUDBackView(generics.RetrieveUpdateDestroyAPIView): """Feature RUD View.""" serializer_class = serializers.FeatureSerializer queryset = SiteFeature.objects.all() - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes() class SiteFeatureRUDBackView(generics.RetrieveUpdateDestroyAPIView): """Feature RUD View.""" serializer_class = serializers.SiteFeatureSerializer queryset = SiteFeature.objects.all() - permission_classes = [permissions.IsAdminUser] + permission_classes = get_permission_classes() class SiteSettingsBackOfficeView(SiteSettingsView): """Site settings View.""" serializer_class = serializers.SiteSerializer + permission_classes = get_permission_classes() class SiteListBackOfficeView(SiteListView): """Site settings View.""" serializer_class = serializers.SiteSerializer + permission_classes = get_permission_classes() class FooterBackView(generics.ListCreateAPIView): """Footer back list/create view.""" - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) serializer_class = serializers.FooterBackSerializer queryset = Footer.objects.all() + permission_classes = get_permission_classes() class FooterRUDBackView(generics.RetrieveUpdateDestroyAPIView): """Footer back RUD view.""" - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) serializer_class = serializers.FooterBackSerializer queryset = Footer.objects.all() + permission_classes = get_permission_classes() class PageTypeListCreateView(generics.ListCreateAPIView): """PageType back office view.""" - permission_classes = (permissions.IsAuthenticatedOrReadOnly, ) pagination_class = None serializer_class = serializers.PageTypeBaseSerializer queryset = PageType.objects.all() + permission_classes = get_permission_classes() class PanelsListCreateView(generics.ListCreateAPIView): """Custom panels view.""" - permission_classes = ( - permissions.IsAdminUser, - ) serializer_class = PanelSerializer queryset = Panel.objects.all() + permission_classes = get_permission_classes() class PanelsRUDView(generics.RetrieveUpdateDestroyAPIView): """Custom panels view.""" - permission_classes = ( - permissions.IsAdminUser, - ) serializer_class = PanelSerializer queryset = Panel.objects.all() + permission_classes = get_permission_classes() class PanelsExecuteView(generics.ListAPIView): """Custom panels view.""" - permission_classes = ( - permissions.IsAdminUser, - ) queryset = Panel.objects.all() + permission_classes = get_permission_classes() def list(self, request, *args, **kwargs): panel = get_object_or_404(Panel, id=self.kwargs['pk']) @@ -162,8 +160,8 @@ class PanelsExecuteView(generics.ListAPIView): class PanelsExportCSVView(PanelsExecuteView): """Export panels via csv view.""" - permission_classes = (permissions.IsAdminUser,) queryset = Panel.objects.all() + permission_classes = get_permission_classes() def list(self, request, *args, **kwargs): panel = get_object_or_404(Panel, id=self.kwargs['pk']) @@ -178,8 +176,8 @@ class PanelsExportCSVView(PanelsExecuteView): class PanelsExecuteXLSView(PanelsExecuteView): """Export panels via xlsx view.""" - permission_classes = (permissions.IsAdminUser,) queryset = Panel.objects.all() + permission_classes = get_permission_classes() def list(self, request, *args, **kwargs): panel = get_object_or_404(Panel, id=self.kwargs['pk']) diff --git a/apps/news/models.py b/apps/news/models.py index 758269b8..dc7f8898 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -1,15 +1,16 @@ """News app models.""" import uuid +from datetime import datetime import elasticsearch_dsl from django.conf import settings from django.contrib.contenttypes import fields as generic from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import HStoreField +from django.contrib.postgres.search import TrigramSimilarity from django.db import models from django.db.models import Case, When, Q, F from django.db.models.functions import Cast -from django.contrib.postgres.search import TrigramSimilarity from django.urls.exceptions import NoReverseMatch from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -21,7 +22,6 @@ from utils.models import (BaseAttributes, TJSONField, TranslatedFieldsMixin, Has ProjectBaseMixin, GalleryMixin, IntermediateGalleryModelMixin, FavoritesMixin, TypeDefaultImageMixin) from utils.querysets import TranslationQuerysetMixin -from datetime import datetime class Agenda(ProjectBaseMixin, TranslatedFieldsMixin): @@ -244,6 +244,10 @@ class NewsQuerySet(TranslationQuerysetMixin): 'subtitle_similarity')) ).filter(relevance__gte=0.3).order_by('-relevance') + def available_news(self, user, country_code: str): + """Return QuerySet with news that user has an access.""" + return self.filter(site__country__code=country_code) if not user.is_staff else self + class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin, FavoritesMixin): diff --git a/apps/news/views.py b/apps/news/views.py index 9503631d..ff6b165e 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -3,15 +3,17 @@ from django.conf import settings from django.http import Http404 from django.shortcuts import get_object_or_404 from django.utils import translation +from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics, permissions, response from rest_framework.filters import OrderingFilter -from django_filters.rest_framework import DjangoFilterBackend from news import filters, models, serializers from rating.tasks import add_rating -from utils.permissions import IsCountryAdmin, IsContentPageManager -from utils.views import CreateDestroyGalleryViewMixin, FavoritesCreateDestroyMixinView, CarouselCreateDestroyMixinView +from utils.methods import get_permission_classes +from utils.permissions import IsContentPageManager from utils.serializers import ImageBaseSerializer +from utils.views import CreateDestroyGalleryViewMixin, FavoritesCreateDestroyMixinView, \ + CarouselCreateDestroyMixinView class NewsMixinView: @@ -124,14 +126,21 @@ class NewsTypeListView(generics.ListAPIView): class NewsBackOfficeMixinView: """News back office mixin view.""" - permission_classes = (permissions.IsAuthenticated,) + permission_classes = get_permission_classes(IsContentPageManager, IsContentPageManager) def get_queryset(self): - """Override get_queryset method.""" - qs = models.News.objects.with_base_related() \ - .annotate_in_favorites(self.request.user) \ - .order_by('-is_highlighted', '-created') - return qs + """Overridden get_queryset method.""" + queryset = models.News.objects + if hasattr(self, 'request') and \ + (hasattr(self.request, 'user') and hasattr(self.request, 'country_code')): + user = self.request.user + return ( + queryset.with_base_related() + .annotate_in_favorites(user) + .available_news(user, self.request.country_code) + .order_by('-is_highlighted', '-created') + ) + return queryset.none() class NewsBackOfficeLCView(NewsBackOfficeMixinView, @@ -142,7 +151,6 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, filter_class = filters.NewsListFilterSet create_serializers_class = serializers.NewsBackOfficeDetailSerializer filter_backends = (OrderingFilter, DjangoFilterBackend) - permission_classes = [IsCountryAdmin | IsContentPageManager] ordering_fields = '__all__' @@ -164,8 +172,6 @@ class NewsBackOfficeLCView(NewsBackOfficeMixinView, self.request.query_params['ordering'] = self.request.query_params['ordering']\ .replace('publication_datetime', 'publication_date,publication_time') self.request.GET._mutable = False - if self.request.country_code: - qs = qs.by_country_code(self.request.country_code) return qs @@ -219,9 +225,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, class NewsBackOfficeRUDView(NewsBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView): """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/notification/views/common.py b/apps/notification/views/common.py index 0c97930d..ba7af962 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -6,11 +6,11 @@ from rest_framework.response import Response from notification import models from notification.serializers import common as serializers from utils.methods import get_user_ip +from utils.permissions import IsAuthenticatedAndTokenIsValid class CreateSubscribeView(generics.CreateAPIView): """Create subscribe View.""" - queryset = models.Subscriber.objects.all() permission_classes = (permissions.AllowAny,) serializer_class = serializers.CreateAndUpdateSubscribeSerializer @@ -30,7 +30,6 @@ class CreateSubscribeView(generics.CreateAPIView): class UpdateSubscribeView(generics.UpdateAPIView): """Subscribe info view.""" - lookup_field = 'update_code' lookup_url_kwarg = 'code' permission_classes = (permissions.AllowAny,) @@ -40,7 +39,6 @@ class UpdateSubscribeView(generics.UpdateAPIView): class SubscribeInfoView(generics.RetrieveAPIView): """Subscribe info view.""" - lookup_field = 'update_code' lookup_url_kwarg = 'code' permission_classes = (permissions.AllowAny,) @@ -50,8 +48,7 @@ class SubscribeInfoView(generics.RetrieveAPIView): class SubscribeInfoAuthUserView(generics.RetrieveAPIView): """Subscribe info auth user view.""" - - permission_classes = (permissions.IsAuthenticated,) + permission_classes = (IsAuthenticatedAndTokenIsValid,) serializer_class = serializers.SubscribeSerializer lookup_field = None @@ -61,7 +58,6 @@ class SubscribeInfoAuthUserView(generics.RetrieveAPIView): class UnsubscribeView(generics.UpdateAPIView): """Unsubscribe view.""" - lookup_field = 'update_code' lookup_url_kwarg = 'code' permission_classes = (permissions.AllowAny,) @@ -77,8 +73,7 @@ class UnsubscribeView(generics.UpdateAPIView): class UnsubscribeAuthUserView(generics.GenericAPIView): """Unsubscribe auth user view.""" - - permission_classes = (permissions.IsAuthenticated,) + permission_classes = (IsAuthenticatedAndTokenIsValid,) queryset = models.Subscriber.objects.all() serializer_class = serializers.SubscribeSerializer diff --git a/apps/partner/views/back.py b/apps/partner/views/back.py index 53182b92..c130ed4a 100644 --- a/apps/partner/views/back.py +++ b/apps/partner/views/back.py @@ -1,12 +1,13 @@ from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions +from rest_framework import generics from rest_framework.filters import OrderingFilter from partner import filters from partner.models import Partner, PartnerToEstablishment from partner.serializers import back as serializers -from utils.permissions import IsEstablishmentManager +from utils.methods import get_permission_classes +from utils.permissions import IsEstablishmentManager, IsEstablishmentAdministrator class PartnerLstView(generics.ListCreateAPIView): @@ -16,18 +17,24 @@ class PartnerLstView(generics.ListCreateAPIView): queryset = Partner.objects.with_base_related() serializer_class = serializers.BackPartnerSerializer pagination_class = None - permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] filter_class = filters.PartnerFilterSet + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class EstablishmentPartners(generics.ListAPIView): queryset = PartnerToEstablishment.objects.prefetch_related('partner', 'partner__country') serializer_class = serializers.PartnersForEstablishmentSerializer pagination_class = None - permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] filter_backends = (OrderingFilter, DjangoFilterBackend) ordering_fields = '__all__' ordering = '-partner_bind_date' + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) def get_queryset(self): return super().get_queryset().filter(establishment=self.kwargs['establishment_id']) @@ -37,25 +44,37 @@ class PartnerRUDView(generics.RetrieveUpdateDestroyAPIView): """Partner RUD view.""" queryset = Partner.objects.with_base_related() serializer_class = serializers.BackPartnerSerializer - permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] lookup_field = 'id' + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class PartnerPicturesListView(generics.RetrieveAPIView): lookup_field = 'id' serializer_class = serializers.PartnerPicturesSerializer queryset = Partner.objects.with_base_related() - permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class BindPartnerToEstablishmentView(generics.CreateAPIView): serializer_class = serializers.PartnersForEstablishmentSerializer - permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) class UnbindPartnerFromEstablishmentView(generics.DestroyAPIView): serializer_class = serializers.PartnersForEstablishmentSerializer - permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] + permission_classes = get_permission_classes( + IsEstablishmentManager, + IsEstablishmentAdministrator + ) def get_object(self): return get_object_or_404(PartnerToEstablishment, establishment_id=self.kwargs['establishment_id'], diff --git a/apps/product/models.py b/apps/product/models.py index 8be6ddd4..851bc138 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -12,11 +12,11 @@ from django.utils.translation import gettext_lazy as _ from location.models import WineOriginAddressMixin from review.models import Review +from utils.methods import transform_into_readable_str from utils.models import (BaseAttributes, ProjectBaseMixin, HasTagsMixin, TranslatedFieldsMixin, TJSONField, FavoritesMixin, GalleryMixin, IntermediateGalleryModelMixin, TypeDefaultImageMixin) -from utils.methods import transform_into_readable_str class ProductType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBaseMixin): @@ -227,6 +227,23 @@ class ProductQuerySet(models.QuerySet): .distinct(*similarity_rules['distinction'], 'id') + def available_products(self, user, country_code: str): + """Return QuerySet with products that user has an access.""" + from account.models import UserRole + + if not user.is_staff: + filters = {'establishment__address__city__country__code': country_code} + if user.is_establishment_administrator and not user.is_establishment_manager: + filters.update({ + 'establishment__id__in': models.Subquery( + UserRole.objects.filter(user=user, role__site__country__code=country_code) + .distinct('user', 'establishment') + .values_list('establishment', flat=True) + ) + }) + return self.filter(**filters) + return self + class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes, HasTagsMixin, FavoritesMixin): diff --git a/apps/product/views/back.py b/apps/product/views/back.py index 539898a4..17902917 100644 --- a/apps/product/views/back.py +++ b/apps/product/views/back.py @@ -1,52 +1,49 @@ """Product app back-office views.""" from django.shortcuts import get_object_or_404 -from rest_framework import generics, status, permissions, views +from rest_framework import generics, status from rest_framework.response import Response from product import serializers, models from product.views import ProductBaseView +from utils.methods import get_permission_classes +from utils.permissions import ( + IsEstablishmentManager, IsEstablishmentAdministrator) from utils.serializers import ImageBaseSerializer from utils.views import CreateDestroyGalleryViewMixin -from utils.permissions import IsLiquorReviewer, IsProductReviewer class ProductBackOfficeMixinView(ProductBaseView): """Product back-office mixin view.""" - - permission_classes = (permissions.IsAuthenticated, ) + permission_classes = get_permission_classes( + IsEstablishmentAdministrator, + IsEstablishmentManager + ) def get_queryset(self): """Override get_queryset method.""" - qs = models.Product.objects.annotate_in_favorites(self.request.user) - return qs + queryset = ( + models.Product.objects.with_base_related() + .with_extended_related() + .annotate_in_favorites(self.request.user) + ) + if hasattr(self, 'request') and \ + (hasattr(self.request, 'user') and hasattr(self.request, 'country_code')): + return queryset.available_products(self.request.user, self.request.country_code) + return queryset.none() class ProductTypeBackOfficeMixinView: """Product type back-office mixin view.""" - - permission_classes = (permissions.IsAuthenticated,) + permission_classes = get_permission_classes() queryset = models.ProductType.objects.all() class ProductSubTypeBackOfficeMixinView: """Product sub type back-office mixin view.""" - - permission_classes = (permissions.IsAuthenticated,) + permission_classes = get_permission_classes() queryset = models.ProductSubType.objects.all() -class BackOfficeListCreateMixin(views.APIView): - """Back-office list-create mixin view.""" - - def check_permissions(self, request): - """ - Check if the request should be permitted. - Raises an appropriate exception if the request is not permitted. - """ - if self.request.method != 'GET': - super().check_permissions(request) - - class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView, CreateDestroyGalleryViewMixin): """Resource for a create gallery for product for back-office users.""" @@ -56,7 +53,7 @@ class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView, """ Returns the object the view is displaying. """ - product_qs = self.filter_queryset(self.get_queryset()) + product_qs = self.get_queryset() product = get_object_or_404(product_qs, pk=self.kwargs.get('pk')) gallery = get_object_or_404(product.product_gallery, image_id=self.kwargs.get('image_id')) @@ -71,7 +68,6 @@ class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, generics.ListAPIView): """Resource for returning gallery for product for back-office users.""" serializer_class = ImageBaseSerializer - permission_classes = (permissions.IsAuthenticated,) def get_object(self): """Override get_object method.""" @@ -92,25 +88,21 @@ class ProductDetailBackOfficeView(ProductBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView): """Product back-office R/U/D view.""" serializer_class = serializers.ProductBackOfficeDetailSerializer - permission_classes = [IsLiquorReviewer | IsProductReviewer] -class ProductListCreateBackOfficeView(BackOfficeListCreateMixin, ProductBackOfficeMixinView, +class ProductListCreateBackOfficeView(ProductBackOfficeMixinView, generics.ListCreateAPIView): """Product back-office list-create view.""" serializer_class = serializers.ProductBackOfficeDetailSerializer - permission_classes = [IsLiquorReviewer | IsProductReviewer] -class ProductTypeListCreateBackOfficeView(BackOfficeListCreateMixin, - ProductTypeBackOfficeMixinView, +class ProductTypeListCreateBackOfficeView(ProductTypeBackOfficeMixinView, generics.ListCreateAPIView): """Product type back-office list-create view.""" serializer_class = serializers.ProductTypeBackOfficeDetailSerializer -class ProductTypeRUDBackOfficeView(BackOfficeListCreateMixin, - ProductTypeBackOfficeMixinView, +class ProductTypeRUDBackOfficeView(ProductTypeBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView): """Product type back-office retrieve-update-destroy view.""" serializer_class = serializers.ProductTypeBackOfficeDetailSerializer @@ -126,22 +118,19 @@ class ProductTypeTagCategoryCreateBackOfficeView(ProductTypeBackOfficeMixinView, return Response(status=status.HTTP_201_CREATED) -class ProductSubTypeListCreateBackOfficeView(BackOfficeListCreateMixin, - ProductSubTypeBackOfficeMixinView, +class ProductSubTypeListCreateBackOfficeView(ProductSubTypeBackOfficeMixinView, generics.ListCreateAPIView): """Product sub type back-office list-create view.""" serializer_class = serializers.ProductSubTypeBackOfficeDetailSerializer -class ProductSubTypeRUDBackOfficeView(BackOfficeListCreateMixin, - ProductSubTypeBackOfficeMixinView, +class ProductSubTypeRUDBackOfficeView(ProductSubTypeBackOfficeMixinView, generics.RetrieveUpdateDestroyAPIView): """Product sub type back-office retrieve-update-destroy view.""" serializer_class = serializers.ProductSubTypeBackOfficeDetailSerializer class ProductNoteListCreateView(ProductBackOfficeMixinView, - BackOfficeListCreateMixin, generics.ListCreateAPIView): """Retrieve|Update|Destroy product note view.""" @@ -149,7 +138,7 @@ class ProductNoteListCreateView(ProductBackOfficeMixinView, def get_object(self): """Returns the object the view is displaying.""" - product_qs = models.Product.objects.all() + product_qs = super(ProductNoteListCreateView, self).get_queryset() filtered_product_qs = self.filter_queryset(product_qs) product = get_object_or_404(filtered_product_qs, pk=self.kwargs.get('pk')) @@ -165,7 +154,6 @@ class ProductNoteListCreateView(ProductBackOfficeMixinView, class ProductNoteRUDView(ProductBackOfficeMixinView, - BackOfficeListCreateMixin, generics.RetrieveUpdateDestroyAPIView): """Create|Retrieve|Update|Destroy product note view.""" @@ -173,7 +161,7 @@ class ProductNoteRUDView(ProductBackOfficeMixinView, def get_object(self): """Returns the object the view is displaying.""" - product_qs = models.Product.objects.all() + product_qs = super(ProductNoteRUDView, self).get_queryset() filtered_product_qs = self.filter_queryset(product_qs) product = get_object_or_404(filtered_product_qs, pk=self.kwargs.get('pk')) diff --git a/apps/recipe/views/common.py b/apps/recipe/views/common.py index 31e74f20..cfce158b 100644 --- a/apps/recipe/views/common.py +++ b/apps/recipe/views/common.py @@ -1,16 +1,18 @@ """Recipe app common views.""" from django.utils import translation -from rest_framework import generics, permissions +from rest_framework import generics from recipe import models from recipe.serializers import common as serializers +from utils.methods import get_permission_classes +from utils.permissions import IsContentPageManager class RecipeViewMixin(generics.GenericAPIView): """Recipe view mixin.""" pagination_class = None - permission_classes = (permissions.AllowAny,) + permission_classes = get_permission_classes(IsContentPageManager) def get_queryset(self, *args, **kwargs): user = self.request.user diff --git a/apps/review/models.py b/apps/review/models.py index a987cf2e..f17629ba 100644 --- a/apps/review/models.py +++ b/apps/review/models.py @@ -1,11 +1,8 @@ """Review app models.""" -from pprint import pprint from django.contrib.contenttypes import fields as generic from django.core.validators import MinValueValidator, MaxValueValidator from django.db import models -from django.db.models.signals import post_init, post_save -from django.dispatch import receiver from django.utils.translation import gettext_lazy as _ from utils.models import (BaseAttributes, TranslatedFieldsMixin, @@ -16,6 +13,15 @@ from utils.models import (BaseAttributes, TranslatedFieldsMixin, class ReviewQuerySet(models.QuerySet): """QuerySets for model Review""" + def with_base_related(self): + """Return QuerySet with base related.""" + return self.select_related( + 'reviewer', + 'country', + 'child', + 'content_type', + ) + def by_reviewer(self, user): """Return reviews by user""" return self.filter(reviewer=user) diff --git a/apps/review/urls/back.py b/apps/review/urls/back.py index a59aafa8..d3f03ed3 100644 --- a/apps/review/urls/back.py +++ b/apps/review/urls/back.py @@ -6,12 +6,12 @@ from review.views import back as views app_name = 'review' urlpatterns = [ - path('', views.ReviewLstView.as_view(), name='review-list-create'), + path('', views.ReviewListView.as_view(), name='review-list-create'), path('/', views.ReviewRUDView.as_view(), name='review-crud'), - path('/inquiries/', views.InquiriesLstView.as_view(), name='inquiries-list'), - path('inquiries/', views.InquiriesLstView.as_view(), name='inquiries-list-create'), + path('/inquiries/', views.InquiriesListView.as_view(), name='inquiries-list'), + path('inquiries/', views.InquiriesListView.as_view(), name='inquiries-list-create'), path('inquiries//', views.InquiriesRUDView.as_view(), name='inquiries-crud'), - path('inquiries//grid/', views.GridItemsLstView.as_view(), name='grid-list-create'), - path('inquiries/grid/', views.GridItemsLstView.as_view(), name='grid-list-create'), + path('inquiries//grid/', views.GridItemsListView.as_view(), name='grid-list-create'), + path('inquiries/grid/', views.GridItemsListView.as_view(), name='grid-list-create'), path('inquiries/grid//', views.GridItemsRUDView.as_view(), name='grid-crud'), ] diff --git a/apps/review/views/back.py b/apps/review/views/back.py index caf12b62..0b2f7b42 100644 --- a/apps/review/views/back.py +++ b/apps/review/views/back.py @@ -1,13 +1,30 @@ -from rest_framework import generics, permissions +from rest_framework import generics from review import filters from review import models from review import serializers -from utils.permissions import IsReviewerManager, IsRestaurantReviewer from review.serializers.back import ReviewBackSerializer +from utils.methods import get_permission_classes +from utils.permissions import ( + IsReviewManager, IsRestaurantInspector, IsWineryWineInspector, + IsArtisanInspector, IsProducerFoodInspector, IsDistilleryLiquorInspector, +) -class ReviewLstView(generics.ListCreateAPIView): +class PermissionMixinView: + """Permission mixin view.""" + permission_classes = get_permission_classes( + IsReviewManager, IsRestaurantInspector, IsWineryWineInspector, + IsArtisanInspector, IsProducerFoodInspector, IsDistilleryLiquorInspector, + ) + + +class ReviewMixinView: + """Review mixin.""" + queryset = models.Review.objects.with_base_related() + + +class ReviewListView(PermissionMixinView, ReviewMixinView, generics.ListCreateAPIView): """Review list create view. status values: @@ -17,12 +34,10 @@ class ReviewLstView(generics.ListCreateAPIView): READY = 2 """ serializer_class = ReviewBackSerializer - queryset = models.Review.objects.all() - permission_classes = [permissions.IsAuthenticatedOrReadOnly, ] filterset_class = filters.ReviewFilter -class ReviewRUDView(generics.RetrieveUpdateDestroyAPIView): +class ReviewRUDView(PermissionMixinView, ReviewMixinView, generics.RetrieveUpdateDestroyAPIView): """Review RUD view. status values: @@ -32,17 +47,14 @@ class ReviewRUDView(generics.RetrieveUpdateDestroyAPIView): READY = 2 """ serializer_class = ReviewBackSerializer - queryset = models.Review.objects.all() - permission_classes = [permissions.IsAdminUser | IsReviewerManager | IsRestaurantReviewer] lookup_field = 'id' -class InquiriesLstView(generics.ListCreateAPIView): +class InquiriesListView(PermissionMixinView, generics.ListCreateAPIView): """Inquiries list create view.""" serializer_class = serializers.InquiriesBaseSerializer queryset = models.Inquiries.objects.all() - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) def get_queryset(self): review_id = self.kwargs.get('review_id') @@ -51,19 +63,17 @@ class InquiriesLstView(generics.ListCreateAPIView): return super().get_queryset() -class InquiriesRUDView(generics.RetrieveUpdateDestroyAPIView): +class InquiriesRUDView(PermissionMixinView, generics.RetrieveUpdateDestroyAPIView): """Inquiries RUD view.""" serializer_class = serializers.InquiriesBaseSerializer queryset = models.Inquiries.objects.all() - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) lookup_field = 'id' -class GridItemsLstView(generics.ListCreateAPIView): +class GridItemsListView(PermissionMixinView, generics.ListCreateAPIView): """GridItems list create view.""" serializer_class = serializers.GridItemsBaseSerializer queryset = models.GridItems.objects.all() - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) def get_queryset(self): inquiry_id = self.kwargs.get('inquiry_id') @@ -72,9 +82,8 @@ class GridItemsLstView(generics.ListCreateAPIView): return super().get_queryset() -class GridItemsRUDView(generics.RetrieveUpdateDestroyAPIView): +class GridItemsRUDView(PermissionMixinView, generics.RetrieveUpdateDestroyAPIView): """GridItems RUD view.""" serializer_class = serializers.GridItemsBaseSerializer queryset = models.GridItems.objects.all() - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) lookup_field = 'id' diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py index 29144189..d679e54f 100644 --- a/apps/search_indexes/serializers.py +++ b/apps/search_indexes/serializers.py @@ -129,7 +129,7 @@ class CityDocumentShortSerializer(serializers.Serializer): @staticmethod def get_name_translated(obj): - return get_translated_value(loads(obj.name)) + return get_translated_value(loads(obj.name)) if obj.name else None diff --git a/apps/tag/models.py b/apps/tag/models.py index 2a57a1c4..0f8b1d1b 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -11,6 +11,10 @@ from utils.models import IndexJSON class TagQuerySet(models.QuerySet): + def with_base_related(self): + """Return QuerySet with base related.""" + return self.select_related('category', 'translation') + def for_news(self): """Select chosen tags for news.""" return self.filter(category__news_types__isnull=False) diff --git a/apps/tag/views.py b/apps/tag/views.py index de49875f..8e22f9e4 100644 --- a/apps/tag/views.py +++ b/apps/tag/views.py @@ -4,6 +4,7 @@ from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ from rest_framework import generics, mixins, permissions, status, viewsets from rest_framework.decorators import action +from rest_framework.permissions import IsAdminUser from rest_framework.response import Response from rest_framework.serializers import ValidationError @@ -11,6 +12,9 @@ from location.models import WineRegion from product.models import ProductType from search_indexes import views as search_views from tag import filters, models, serializers +from utils.permissions import ( + IsEstablishmentManager +) class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet): @@ -334,8 +338,10 @@ class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, """List/create tag view.""" pagination_class = None - permission_classes = (permissions.IsAuthenticated,) - queryset = models.Tag.objects.all() + permission_classes = [ + IsAdminUser, IsEstablishmentManager + ] + queryset = models.Tag.objects.with_base_related() serializer_class = serializers.TagBackOfficeSerializer bind_object_serializer_class = serializers.TagBindObjectSerializer chosen_serializer_class = serializers.ChosenTagBindObjectSerializer @@ -388,10 +394,12 @@ class TagCategoryBackOfficeViewSet(mixins.CreateModelMixin, TagCategoryViewSet): """ViewSet for TagCategory model for BackOffice users.""" - permission_classes = (permissions.IsAuthenticated,) queryset = TagCategoryViewSet.queryset.with_extended_related() serializer_class = serializers.TagCategoryBackOfficeDetailSerializer bind_object_serializer_class = serializers.TagCategoryBindObjectSerializer + permission_classes = [ + IsAdminUser, IsEstablishmentManager + ] def perform_binding(self, serializer): data = serializer.validated_data diff --git a/apps/utils/methods.py b/apps/utils/methods.py index 839105e2..73310fd4 100644 --- a/apps/utils/methods.py +++ b/apps/utils/methods.py @@ -8,12 +8,12 @@ from functools import reduce from io import BytesIO import pathlib import requests +from PIL import Image from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.contrib.gis.geos import Point from django.http.request import HttpRequest from django.utils.timezone import datetime -from PIL import Image from rest_framework import status from rest_framework.request import Request @@ -225,3 +225,17 @@ def get_image_meta_by_url(url) -> (int, int, int): image = Image.open(BytesIO(image_raw.content)) width, height = image.size return int(image_raw.headers.get('content-length')), width, height + + +def get_permission_classes(*args) -> list: + """Return permission_class object with admin permissions.""" + from rest_framework.permissions import IsAdminUser + from utils.permissions import IsCountryAdmin + + admin_permission_classes = [IsCountryAdmin, IsAdminUser] + permission_classes = [ + reduce( + lambda a, b: a | b, admin_permission_classes + list(args) + ) + ] + return permission_classes diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py index 2fecf99d..7fe145f5 100644 --- a/apps/utils/permissions.py +++ b/apps/utils/permissions.py @@ -1,15 +1,13 @@ """Project custom permissions""" -from django.contrib.contenttypes.models import ContentType - from rest_framework import permissions +from rest_framework.permissions import SAFE_METHODS as SAFE_HTTP_METHODS from rest_framework_simplejwt.tokens import AccessToken from account.models import UserRole, Role from authorization.models import JWTRefreshToken +from establishment.models import Establishment +from product.models import Product from utils.tokens import GMRefreshToken -from establishment.models import EstablishmentSubType -from location.models import Address -from product.models import Product, ProductType class IsAuthenticatedAndTokenIsValid(permissions.BasePermission): @@ -47,9 +45,9 @@ class IsRefreshTokenValid(permissions.BasePermission): return False def has_object_permission(self, request, view, obj): - # Read permissions are allowed to any request, + # Read permissions are allowed to all request, # so we'll always allow GET, HEAD or OPTIONS requests. - if request.method in permissions.SAFE_METHODS or \ + if request.method in SAFE_HTTP_METHODS or \ obj.user == request.user or request.user.is_superuser: return True return False @@ -59,50 +57,16 @@ class IsGuest(permissions.IsAuthenticatedOrReadOnly): """ Object-level permission to only allow owners of an object to edit it. """ - SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') def has_permission(self, request, view): rules = [ - request.user.is_superuser, - request.method in permissions.SAFE_METHODS + request.user.is_anonymous, + request.method in SAFE_HTTP_METHODS ] - return any(rules) - - def has_object_permission(self, request, view, obj): - rules = [ - request.user.is_superuser, - request.method in permissions.SAFE_METHODS - ] - return any(rules) + return all(rules) -class IsStandardUser(IsGuest): - """ - Object-level permission to only allow owners of an object to edit it. - Assumes the model instance has an `owner` attribute. - """ - - def has_permission(self, request, view): - - rules = [super().has_permission(request, view), - request.user.is_authenticated, - hasattr(request, 'user') - ] - - return any(rules) - - def has_object_permission(self, request, view, obj): - # Read permissions are allowed to any request - - rules = [super().has_object_permission(request, view, obj), - request.user.is_authenticated, - hasattr(request, 'user') - ] - - return any(rules) - - -class IsContentPageManager(IsStandardUser): +class IsApprovedUser(IsAuthenticatedAndTokenIsValid): """ Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. @@ -112,486 +76,347 @@ class IsContentPageManager(IsStandardUser): rules = [ super().has_permission(request, view) ] - - if hasattr(request, 'user'): - if hasattr(request.data, 'site_id'): - role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER, - site_id=request.data.site_id,) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_permission(request, view) - ] - elif hasattr(request.data, 'country_id'): - role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER, - country_id=request.data.country_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_permission(request, view) - ] - - return any(rules) - - def has_object_permission(self, request, view, obj): - # Read permissions are allowed to any request. - if hasattr(obj, 'site_id'): - role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER, - site_id=obj.site_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_object_permission(request, view, obj) - ] - elif hasattr(obj, 'country_id'): - role = Role.objects.filter(role=Role.CONTENT_PAGE_MANAGER, - country_id=obj.country_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_object_permission(request, view, obj) - ] - - return any(rules) - - -class 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_permission(self, request, view): - - rules = [ - super().has_permission(request, view) - ] - # and request.user.email_confirmed, - if hasattr(request.data, 'user'): - if hasattr(request.data, 'site_id'): - # Read permissions are allowed to any request. - - role = Role.objects.filter(role=Role.COUNTRY_ADMIN, - site_id=request.data.site_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_permission(request, view) - ] - elif hasattr(request.data, 'country_id'): - - role = Role.objects.filter(role=Role.COUNTRY_ADMIN, - country_id=request.data.country_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_permission(request, view) - ] - return any(rules) - - def has_object_permission(self, request, view, obj): - # Read permissions are allowed to any request. - if hasattr(obj, 'site_id'): - role = Role.objects.filter(role=Role.COUNTRY_ADMIN, - site_id=obj.site_id) \ - .first() - - rules = [ - super().has_object_permission(request, view, obj) - ] - elif hasattr(obj, 'country_id'): - role = Role.objects.filter(role=Role.COUNTRY_ADMIN, - country_id=obj.country_id) \ - .first() - - rules = [ - super().has_object_permission(request, view, obj) - ] - - if hasattr(request, 'user') and request.user.is_authenticated: - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_object_permission(request, view, obj), - ] - - if hasattr(request.data, 'user'): - rules = [ - UserRole.objects.filter(user=request.data.user, role=role).exists(), - super().has_object_permission(request, view, obj), - ] - - return any(rules) - - -class IsCommentModerator(IsStandardUser): - """ - Object-level permission to only allow owners of an object to edit it. - Assumes the model instance has an `owner` attribute. - """ - - def has_permission(self, request, view): - rules = [ - super().has_permission(request, view) - ] - - if any(rules) and hasattr(request.data, 'site_id'): - # Read permissions are allowed to any request. - - role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, - site_id=request.data.site_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_permission(request, view) - ] - - return any(rules) - - def has_object_permission(self, request, view, obj): - - rules = [ - super().has_object_permission(request, view, obj) - ] - + has_permission = False if request.user.is_authenticated: - - role = Role.objects.filter(role=Role.COMMENTS_MODERATOR, - site_id=obj.site_id) \ - .first() # 'Comments moderator' - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists() and - obj.user != request.user, - super().has_object_permission(request, view, obj) - ] - return any(rules) + has_permission = request.user.email_confirmed + rules.append(has_permission) + return all(rules) -class IsEstablishmentManager(IsStandardUser): +class IsContentPageManager(IsApprovedUser): + """ + Object-level permission to only allow owners of an object to edit it. + Assumes the model instance has an `owner` attribute. + """ def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] - - if hasattr(request.data, 'user'): - if hasattr(request.data, 'establishment_id'): - role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role, - establishment_id=request.data.establishment_id - ).exists(), - super().has_permission(request, view) - ] - return any(rules) - - def has_object_permission(self, request, view, obj): - - rules = [ - # special! - super().has_permission(request, view) - ] - - role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER) \ - .first() - - if hasattr(obj, 'establishment_id'): - rules = [ - UserRole.objects.filter(user=request.user, role=role, - establishment_id=obj.establishment_id - ).exists(), - # special! - super().has_permission(request, view) - ] - - return any(rules) + has_permission = False + if (request.user.is_authenticated and hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.CONTENT_PAGE_MANAGER, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + has_permission = True if user_role.exists() else has_permission + rules.append(has_permission) + return all(rules) -class IsReviewerManager(IsStandardUser): +class IsCountryAdmin(IsApprovedUser): + """ + Object-level permission to only allow owners of an object to edit it. + Assumes the model instance has an `owner` attribute. + """ def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] - - # and request.user.email_confirmed, - if hasattr(request.data, 'user') and hasattr(request.data, 'site_id'): - role = Role.objects.filter(role=Role.REVIEWER_MANGER) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role, - establishment_id=request.data.site_id - ).exists(), - super().has_permission(request, view) - ] - return any(rules) - - def has_object_permission(self, request, view, obj): - role = Role.objects.filter(role=Role.REVIEWER_MANGER, - country_id=obj.country_id) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role).exists(), - super().has_object_permission(request, view, obj) - ] - - return any(rules) + has_permission = False + if (request.user.is_authenticated and hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.COUNTRY_ADMIN, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True) + ).only('id') + has_permission = True if user_role.exists() else has_permission + rules.append(has_permission) + return all(rules) -class IsRestaurantReviewer(IsStandardUser): +class IsModerator(IsApprovedUser): + """ + Object-level permission to only allow owners of an object to edit it. + Assumes the model instance has an `owner` attribute. + """ def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] - - # and request.user.email_confirmed, - if hasattr(request.data, 'user') and hasattr(request.data, 'object_id'): - role = Role.objects.filter(role=Role.RESTAURANT_REVIEWER) \ - .first() - - rules = [ - UserRole.objects.filter(user=request.user, role=role, - establishment_id=request.data.object_id - ).exists(), - super().has_permission(request, view) - ] - return any(rules) - - 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) + has_permission = False + if (request.user.is_authenticated and hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.MODERATOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + has_permission = True if user_role.exists() else has_permission + rules.append(has_permission) + return all(rules) -class IsWineryReviewer(IsStandardUser): +class IsEstablishmentManager(IsApprovedUser): def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + user = request.user + role = Role.objects.filter( + role=Role.ESTABLISHMENT_MANAGER, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=user, role__id__in=role.values_list('id', flat=True), + ).only('id') + has_permission = True if user_role.exists() else has_permission + rules.append(has_permission) + return all(rules) - if 'type_id' in request.data and 'address_id' in request.data and request.user: - countries = Address.objects.filter(id=request.data['address_id']) - est = EstablishmentSubType.objects.filter(establishment_type_id=request.data['type_id']) - if est.exists(): - role = Role.objects.filter(establishment_subtype_id__in=[est_type.id for est_type in est], - role=Role.WINERY_REVIEWER, - country_id__in=[country.id for country in countries]) \ - .first() +class IsEstablishmentAdministrator(IsApprovedUser): - rules.append( - UserRole.objects.filter(user=request.user, role=role).exists() - ) - - return any(rules) + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.ESTABLISHMENT_ADMINISTRATOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + has_permission = True if user_role.exists() else has_permission + rules.append(has_permission) + return all(rules) def has_object_permission(self, request, view, obj): rules = [ super().has_object_permission(request, view, obj) ] + has_object_permission = False + role = Role.objects.filter(role=Role.ESTABLISHMENT_ADMINISTRATOR).only('id') + if request.user.is_authenticated and role.exists() and hasattr(obj, 'id'): + filters = { + 'user': request.user, + 'role__id__in': role.values_list('id', flat=True), + } + if isinstance(obj, Establishment): + filters.update({'establishment__id': obj.id}) - if hasattr(obj, 'type_id') or hasattr(obj, 'establishment_type_id'): - type_id: int - if hasattr(obj, 'type_id'): - type_id = obj.type_id - else: - type_id = obj.establishment_type_id - - est = EstablishmentSubType.objects.filter(establishment_type_id=type_id) - role = Role.objects.filter(role=Role.WINERY_REVIEWER, - establishment_subtype_id__in=[est_type.id for est_type in est], - country_id=obj.country_id).first() - - object_id: int - if hasattr(obj, 'object_id'): - object_id = obj.object_id - else: - object_id = obj.establishment_id - - rules = [ - UserRole.objects.filter(user=request.user, role=role, - establishment_id=object_id - ).exists(), - super().has_object_permission(request, view, obj) - ] - return any(rules) + if isinstance(obj, Product): + filters.update({'establishment__products__id': obj.id}) + user_role = UserRole.objects.validated().filter(**filters) + has_object_permission = True if user_role.exists() else has_object_permission + rules.append(has_object_permission) + return all(rules) -class IsWineryReviewer(IsStandardUser): +class IsReviewManager(IsApprovedUser): + + MODEL_PERMISSIONS = { + 'READ': ['establishment', 'product_type', 'news', 'recipe', 'user', ], + 'WRITE': ['inquiries', 'userrole', 'review', 'establishment', 'product', 'news', 'recipe', ] + } def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] - - if 'type_id' in request.data and 'address_id' in request.data and request.user: - countries = Address.objects.filter(id=request.data['address_id']) - - est = EstablishmentSubType.objects.filter(establishment_type_id=request.data['type_id']) - if est.exists(): - role = Role.objects.filter(establishment_subtype_id__in=[est_type.id for est_type in est], - role=Role.WINERY_REVIEWER, - country_id__in=[country.id for country in countries]) \ - .first() - - rules.append( - UserRole.objects.filter(user=request.user, role=role).exists() - ) - - return any(rules) - - def has_object_permission(self, request, view, obj): - rules = [ - super().has_object_permission(request, view, obj) - ] - - if hasattr(obj, 'type_id') or hasattr(obj, 'establishment_type_id'): - type_id: int - if hasattr(obj, 'type_id'): - type_id = obj.type_id - else: - type_id = obj.establishment_type_id - - est = EstablishmentSubType.objects.filter(establishment_type_id=type_id) - role = Role.objects.filter(role=Role.WINERY_REVIEWER, - establishment_subtype_id__in=[est_type.id for est_type in est], - country_id=obj.country_id).first() - - object_id: int - if hasattr(obj, 'object_id'): - object_id = obj.object_id - else: - object_id = obj.establishment_id - - rules = [ - UserRole.objects.filter(user=request.user, role=role, - establishment_id=object_id - ).exists(), - super().has_object_permission(request, view, obj) - ] - return any(rules) + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.REVIEW_MANAGER, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + if user_role.exists(): + # check model for read + model_name = view.get_queryset().model._meta.model_name + if ((model_name in self.MODEL_PERMISSIONS.get('READ', []) and + request.method in SAFE_HTTP_METHODS) or + (model_name in self.MODEL_PERMISSIONS.get('WRITE', []))): + has_permission = True + rules.append(has_permission) + return all(rules) -class IsProductReviewer(IsStandardUser): +class IsRestaurantInspector(IsApprovedUser): + + MODEL_PERMISSIONS = { + 'READ': ['establishment', ], + 'WRITE': ['inquiries', ] + } def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] - - pk_object = None - roles = None - permission = False - - if 'site_id' in request.data: - if request.data['site_id'] is not None: - roles = Role.objects.filter(role=Role.PRODUCT_REVIEWER, - site_id=request.data['site_id']) - - if 'pk' in view.kwargs: - pk_object = view.kwargs['pk'] - - if pk_object is not None: - product = Product.objects.get(pk=pk_object) - if product.site_id is not None: - roles = Role.objects.filter(role=Role.PRODUCT_REVIEWER, - site_id=product.site_id) - - if roles is not None: - permission = UserRole.objects.filter(user=request.user, role__in=[role for role in roles])\ - .exists() - - rules.append(permission) - return any(rules) + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.RESTAURANT_INSPECTOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + if user_role.exists(): + # check model for read + model_name = view.get_queryset().model._meta.model_name + if ((model_name in self.MODEL_PERMISSIONS.get('READ', []) and + request.method in SAFE_HTTP_METHODS) or + (model_name in self.MODEL_PERMISSIONS.get('WRITE', []))): + has_permission = True + rules.append(has_permission) + return all(rules) -class IsLiquorReviewer(IsStandardUser): +class IsArtisanInspector(IsApprovedUser): + + MODEL_PERMISSIONS = { + 'READ': ['establishment', ], + 'WRITE': ['inquiries', ] + } + def has_permission(self, request, view): rules = [ super().has_permission(request, view) ] + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.ARTISAN_INSPECTOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + if user_role.exists(): + # check model for read + model_name = view.get_queryset().model._meta.model_name + if ((model_name in self.MODEL_PERMISSIONS.get('READ', []) and + request.method in SAFE_HTTP_METHODS) or + (model_name in self.MODEL_PERMISSIONS.get('WRITE', []))): + has_permission = True + rules.append(has_permission) + return all(rules) - pk_object = None - roles = None - permission = False - if 'site_id' in request.data and 'product_type_id' in request.data: - if request.data['site_id'] is not None \ - and request.data['product_type_id'] is not None: +class IsWineryWineInspector(IsApprovedUser): - product_types = ProductType.objects. \ - filter(index_name=ProductType.LIQUOR, - id=request.data['product_type_id']) + MODEL_PERMISSIONS = { + 'READ': ['establishment', 'product', ], + 'WRITE': ['inquiries', ] + } - if product_types.exists(): - roles = Role.objects.filter(role=Role.LIQUOR_REVIEWER, - site_id=request.data['site_id']) + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.WINERY_WINE_INSPECTOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + if user_role.exists(): + # check model for read + model_name = view.get_queryset().model._meta.model_name + if ((model_name in self.MODEL_PERMISSIONS.get('READ', []) and + request.method in SAFE_HTTP_METHODS) or + (model_name in self.MODEL_PERMISSIONS.get('WRITE', []))): + has_permission = True + rules.append(has_permission) + return all(rules) - if 'pk' in view.kwargs: - pk_object = view.kwargs['pk'] - if pk_object is not None: - product = Product.objects.get(pk=pk_object) - if product.site_id is not None \ - and product.product_type_id is not None: +class IsProducerFoodInspector(IsApprovedUser): - product_types = ProductType.objects. \ - filter(index_name=ProductType.LIQUOR, - id=product.product_type_id) + MODEL_PERMISSIONS = { + 'READ': ['establishment', 'product', ], + 'WRITE': ['inquiries', ] + } - if product_types.exists(): - roles = Role.objects.filter(role=Role.LIQUOR_REVIEWER, - site_id=product.site_id) + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.PRODUCER_FOOD_INSPECTOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + if user_role.exists(): + # check model for read + model_name = view.get_queryset().model._meta.model_name + if ((model_name in self.MODEL_PERMISSIONS.get('READ', []) and + request.method in SAFE_HTTP_METHODS) or + (model_name in self.MODEL_PERMISSIONS.get('WRITE', []))): + has_permission = True + rules.append(has_permission) + return all(rules) - if roles is not None: - permission = UserRole.objects.filter(user=request.user, role__in=[role for role in roles])\ - .exists() - rules.append(permission) - return any(rules) +class IsDistilleryLiquorInspector(IsApprovedUser): - # - # def has_object_permission(self, request, view, obj): - # rules = [ - # super().has_object_permission(request, view, obj) - # ] - # # pk_object = None - # # product = None - # # permission = False - # # - # # if 'pk' in view.kwargs: - # # pk_object = view.kwargs['pk'] - # # - # # if pk_object is not None: - # # product = Product.objects.get(pk=pk_object) - # # - # # if product.sites.exists(): - # # role = Role.objects.filter(role=Role.LIQUOR_REVIEWER, site__in=[site for site in product.sites]) - # # permission = UserRole.objects.filter(user=request.user, role=role).exists() - # # - # # rules.append(permission) - # return any(rules) \ No newline at end of file + MODEL_PERMISSIONS = { + 'READ': ['establishment', 'product', ], + 'WRITE': ['inquiries', ] + } + + def has_permission(self, request, view): + rules = [ + super().has_permission(request, view) + ] + has_permission = False + if (request.user.is_authenticated and + hasattr(request, 'country_code') and + request.country_code): + role = Role.objects.filter( + role=Role.DISTILLERY_LIQUOR_INSPECTOR, site__country__code=request.country_code, + ).only('id') + if role.exists(): + user_role = UserRole.objects.validated().filter( + user=request.user, role__id__in=role.values_list('id', flat=True), + ).only('id') + if user_role.exists(): + # check model for read + model_name = view.get_queryset().model._meta.model_name + if ((model_name in self.MODEL_PERMISSIONS.get('READ', []) and + request.method in SAFE_HTTP_METHODS) or + (model_name in self.MODEL_PERMISSIONS.get('WRITE', []))): + has_permission = True + rules.append(has_permission) + return all(rules)