From a26109a8da8f9c5a1ea5032e7600a640bda33bc1 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 17 Jan 2020 17:38:50 +0300 Subject: [PATCH] added endpoint /api/back/account/role-tab/ --- apps/account/filters.py | 29 ++++++++++++++++++ apps/account/models.py | 51 ++++++++++++++++++++++++++++---- apps/account/serializers/back.py | 6 ++++ apps/account/urls/back.py | 1 + apps/account/views/back.py | 44 ++++++++++++++++++++------- apps/tag/filters.py | 1 + 6 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 apps/account/filters.py diff --git a/apps/account/filters.py b/apps/account/filters.py new file mode 100644 index 00000000..996c09b8 --- /dev/null +++ b/apps/account/filters.py @@ -0,0 +1,29 @@ +"""Account app filters.""" +from django.core.validators import EMPTY_VALUES +from django_filters import rest_framework as filters + +from account import models + + +class AccountBackOfficeFilter(filters.FilterSet): + """Account filter set.""" + + role = filters.MultipleChoiceFilter(choices=models.Role.ROLE_CHOICES, + method='filter_by_roles') + + class Meta: + """Meta class.""" + + model = models.User + fields = ( + 'role', + 'email_confirmed', + 'is_staff', + 'is_active', + 'is_superuser', + ) + + def filter_by_roles(self, queryset, name, value): + if value not in EMPTY_VALUES: + return queryset.by_roles(value) + return queryset diff --git a/apps/account/models.py b/apps/account/models.py index ec01b68e..e39e77a4 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -22,6 +22,19 @@ from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin from utils.tokens import GMRefreshToken +class RoleQuerySet(models.QuerySet): + + def annotate_role_name(self): + return self.annotate(role_name=models.Case(*self.model.role_condition_expressions(), + output_field=models.CharField())) + + def annotate_role_counter(self): + return self.annotate( + role_counter=models.Count('userrole', + distinct=True, + filter=models.Q(userrole__state=UserRole.VALIDATED))) + + class Role(ProjectBaseMixin): """Base Role model.""" STANDARD_USER = 1 @@ -44,12 +57,12 @@ class Role(ProjectBaseMixin): (CONTENT_PAGE_MANAGER, _('Content page manager')), (ESTABLISHMENT_MANAGER, _('Establishment manager')), (REVIEWER_MANGER, _('Reviewer manager')), - (RESTAURANT_REVIEWER, 'Restaurant reviewer'), - (SALES_MAN, 'Sales man'), - (WINERY_REVIEWER, 'Winery reviewer'), - (SELLER, 'Seller'), - (LIQUOR_REVIEWER, 'Liquor reviewer'), - (PRODUCT_REVIEWER, 'Product reviewer'), + (RESTAURANT_REVIEWER, _('Restaurant reviewer')), + (SALES_MAN, _('Sales man')), + (WINERY_REVIEWER, _('Winery reviewer')), + (SELLER, _('Seller')), + (LIQUOR_REVIEWER, _('Liquor reviewer')), + (PRODUCT_REVIEWER, _('Product reviewer')), ) role = models.PositiveIntegerField(verbose_name=_('Role'), choices=ROLE_CHOICES, @@ -67,6 +80,22 @@ class Role(ProjectBaseMixin): help_text='navigation bar item permission', verbose_name=_('navigation bar permission')) + objects = RoleQuerySet.as_manager() + + @classmethod + def role_names(cls): + return [role_name._proxy____args[0] + for role_name in dict(cls.ROLE_CHOICES).values()] + + @classmethod + def role_condition_expressions(cls) -> list: + role_choices = {role_id: role_name._proxy____args[0] + for role_id, role_name in dict(cls.ROLE_CHOICES).items()} + + whens = [models.When(role=role_id, then=models.Value(role_name)) + for role_id, role_name in role_choices.items()] + return whens + class UserManager(BaseUserManager): """Extended manager for User model.""" @@ -347,6 +376,14 @@ class User(AbstractUser): return set(result) +class UserRoleQueryset(models.QuerySet): + """QuerySet for model UserRole.""" + + def country_admin_role(self): + return self.filter(role__role=self.model.role.field.target_field.model.COUNTRY_ADMIN, + state=self.model.VALIDATED) + + class UserRole(ProjectBaseMixin): """UserRole model.""" VALIDATED = 'validated' @@ -377,6 +414,8 @@ class UserRole(ProjectBaseMixin): help_text='A user (REQUESTER) who requests a ' 'role change for a USER') + objects = UserRoleQueryset.as_manager() + class Meta: unique_together = ['user', 'role', 'establishment', 'state'] diff --git a/apps/account/serializers/back.py b/apps/account/serializers/back.py index deac6f6c..05f4ece1 100644 --- a/apps/account/serializers/back.py +++ b/apps/account/serializers/back.py @@ -148,3 +148,9 @@ class UserRoleSerializer(serializers.ModelSerializer): 'user', 'establishment' ] + + +class RoleTabRetrieveSerializer(serializers.Serializer): + """Serializer for BackOffice role tab.""" + role_name = serializers.CharField() + role_counter = serializers.IntegerField() diff --git a/apps/account/urls/back.py b/apps/account/urls/back.py index b76e8110..3635c1a6 100644 --- a/apps/account/urls/back.py +++ b/apps/account/urls/back.py @@ -7,6 +7,7 @@ app_name = 'account' urlpatterns = [ path('role/', views.RoleListView.as_view(), name='role-list-create'), + path('role-tab/', views.RoleTabRetrieveView.as_view(), name='role-tab'), path('user-role/', views.UserRoleListView.as_view(), name='user-role-list-create'), path('user/', views.UserListView.as_view(), name='user-create-list'), path('user//', views.UserRUDView.as_view(), name='user-rud'), diff --git a/apps/account/views/back.py b/apps/account/views/back.py index 09ab1048..94d32a14 100644 --- a/apps/account/views/back.py +++ b/apps/account/views/back.py @@ -1,11 +1,12 @@ from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions +from rest_framework import generics, permissions, status +from rest_framework.response import Response from rest_framework.filters import OrderingFilter import csv from django.http import HttpResponse, HttpResponseNotFound from rest_framework.authtoken.models import Token -from account import models +from account import models, filters from account.models import User from account.serializers import back as serializers from account.serializers.common import RoleBaseSerializer @@ -16,6 +17,34 @@ class RoleListView(generics.ListCreateAPIView): queryset = models.Role.objects.all() +class RoleTabRetrieveView(generics.GenericAPIView): + permission_classes = [permissions.IsAdminUser] + + def get_queryset(self): + """Overridden get_queryset method.""" + additional_filters = {} + + if self.request.user.userrole_set.country_admin_role().exists(): + additional_filters.update({'userrole__country__code': self.request.country_code}) + + return models.Role.objects.filter(**additional_filters)\ + .annotate_role_name()\ + .values('role_name')\ + .annotate_role_counter()\ + .values('role_name', 'role_counter') + + def get(self, request, *args, **kwargs): + """Implement GET-method""" + data = list(self.get_queryset()) + + # todo: Need refactoring. Extend data list with non-existed role. + for role in models.Role.role_names(): + if role not in [role.get('role_name') for role in data]: + data.append({'role_name': role, 'role_number': 0}) + + return Response(data, status=status.HTTP_200_OK) + + class UserRoleListView(generics.ListCreateAPIView): serializer_class = serializers.UserRoleSerializer queryset = models.UserRole.objects.all() @@ -26,21 +55,14 @@ class UserListView(generics.ListCreateAPIView): queryset = User.objects.prefetch_related('roles') serializer_class = serializers.BackUserSerializer permission_classes = (permissions.IsAdminUser,) - filter_backends = (DjangoFilterBackend, OrderingFilter) + filter_class = filters.AccountBackOfficeFilter + filter_backends = (OrderingFilter, ) - filterset_fields = ( - 'email_confirmed', - 'is_staff', - 'is_active', - 'is_superuser', - 'roles', - ) ordering_fields = ( 'email_confirmed', 'is_staff', 'is_active', 'is_superuser', - 'roles', 'last_login', 'date_joined', ) diff --git a/apps/tag/filters.py b/apps/tag/filters.py index 7d82bf84..a9f675c6 100644 --- a/apps/tag/filters.py +++ b/apps/tag/filters.py @@ -5,6 +5,7 @@ from django.conf import settings from tag import models from product import models as product_models + class TagsBaseFilterSet(filters.FilterSet): # Object type choices