diff --git a/base_django_project/users/serializers.py b/base_django_project/users/serializers.py index a7020b260e5aa99254b877d2b7857e05ca6816d6..e4fd3412bcb849174772bf71c6c576ee9bf7c8f0 100644 --- a/base_django_project/users/serializers.py +++ b/base_django_project/users/serializers.py @@ -9,19 +9,24 @@ from allauth.account import app_settings as allauth_settings from allauth.socialaccount.helpers import complete_social_login from requests.exceptions import HTTPError from cities_light.models import City, Region +from django.contrib.auth.models import Group import requests + User = get_user_model() -class UserSerializer(serializers.HyperlinkedModelSerializer): +class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ('id', 'image', 'name', 'email', 'biography', 'city', - 'state', 'country', 'username', 'race', 'gender', - 'occupation', 'is_superuser', 'is_staff',) + fields = ( + 'id', 'username', 'name', 'email', 'image', + 'biography', 'city', 'state', 'country', 'race', 'gender', + 'occupation', 'is_superuser', 'is_staff', 'is_active', + 'is_profile_filled', 'groups', + ) class SimpleUserSerializer(serializers.ModelSerializer): @@ -31,6 +36,57 @@ class SimpleUserSerializer(serializers.ModelSerializer): fields = ('id', 'image', 'name', 'biography') +class SearchUserSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ( + 'id', 'email', 'name', 'first_name', 'last_name', + 'image', 'groups', + ) + + +# class TimtecUserSerializer(serializers.ModelSerializer): +# name = serializers.ReadOnlyField(source='get_full_name') +# picture = serializers.ReadOnlyField(source='get_picture_thumb_url') +# is_profile_filled = serializers.BooleanField() + +# class Meta: +# model = get_user_model() +# fields = ('id', 'username', 'email', 'name', 'first_name', 'last_name', +# 'biography', 'picture', 'is_profile_filled') + + +class UserAdminSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ('id', 'username', 'name', 'email', 'is_active', 'is_superuser', 'first_name', 'last_name', 'image', 'groups', ) + depth = 1 + + +class TimtecUserAdminCertificateSerializer(serializers.ModelSerializer): + + class Meta: + model = User + fields = ('id', 'name', 'email', 'username') + + +class GroupSerializer(serializers.ModelSerializer): + class Meta: + model = Group + fields = ('id', 'name') + + +class GroupAdminSerializer(GroupSerializer): + + users = UserSerializer(many=True, read_only=True, source="user_set") + + class Meta(GroupSerializer.Meta): + fields = ('id', 'name', 'users', ) + depth = 1 + + class RegistrationSerializer(RegisterSerializer): name = serializers.CharField(max_length=255, required=False) diff --git a/base_django_project/users/urls.py b/base_django_project/users/urls.py index 9aeb8deaf709cd33c672e9aaf096eb5d72b974d2..29476529ca4efb46456296e6ff23518b108d5ef3 100644 --- a/base_django_project/users/urls.py +++ b/base_django_project/users/urls.py @@ -8,6 +8,7 @@ from . import views router = SimpleRouter() router.register(r'', views.UserViewSet) +router.register(r'search', views.SearchUserViewSet) # router.register(r'stats', views.StatsViewSet, base_name='user_stats'), app_name = 'users' diff --git a/base_django_project/users/views.py b/base_django_project/users/views.py index a888893f058af61bd87056a15ffe8c59f931c69a..54804d4be2fa87904188994def8eb68022f92323 100644 --- a/base_django_project/users/views.py +++ b/base_django_project/users/views.py @@ -5,14 +5,19 @@ from django.urls import reverse from django.http import Http404, JsonResponse from django.utils.translation import ugettext_lazy as _ from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group from django.contrib.admin.models import LogEntry, CHANGE from django.contrib.contenttypes.models import ContentType +from django.db.models import Q + from requests_oauthlib import OAuth1 from rest_framework.decorators import detail_route, parser_classes, action, list_route from rest_framework.response import Response from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.authtoken.models import Token -from rest_framework import status, generics, reverse, viewsets, permissions, mixins +from rest_framework import status, generics, reverse, viewsets, permissions, mixins, filters + +from django_filters.rest_framework import DjangoFilterBackend from allauth.socialaccount.models import SocialApp from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter @@ -26,9 +31,15 @@ from courier.emails.models import EmailProfile from .permissions import IsCurrentUserOrAdmin from .serializers import ( - UserSerializer, FixSocialLoginSerializer, SimpleUserSerializer, + UserSerializer, + FixSocialLoginSerializer, + SimpleUserSerializer, + SearchUserSerializer, + GroupAdminSerializer, + GroupSerializer, ) + User = get_user_model() @@ -225,12 +236,103 @@ class UserViewSet(viewsets.ModelViewSet): return captcha_req.json()['success'] +class SearchUserViewSet(viewsets.ReadOnlyModelViewSet): + model = User + queryset = User.objects.all() + serializer_class = SearchUserSerializer + permission_classes = [permissions.IsAuthenticated, ] + lookup_field = 'id' + search_fields = ('first_name', 'last_name', 'username', 'email', 'name',) + filter_fields = ('groups__name',) + filter_backends = (DjangoFilterBackend, filters.OrderingFilter,) + ordering = ( 'name', 'first_name', 'username',) + + def get_queryset(self): + + queryset = super().get_queryset() + + current_user = self.request.user + classes = current_user.professor_classes.all() + + # Ensure only Professors or admins may search for users + if not (classes or current_user.is_staff or is_superuser): + return queryset.none() + + course = self.request.query_params.get('course', None) + + if classes: + queryset = queryset.filter(classes__in=classes) + else: + # FIXME: if every student is in a class, this is useless. + if course is not None: + queryset = queryset.filter(studentcourse_set=course) + + query = self.request.query_params.get('name', None) + + if query is not None: + queryset = queryset.filter( + Q(name__icontains=query) | + Q(first_name__icontains=query) | + Q(last_name__icontains=query) | + Q(username__icontains=query) | + Q(email__icontains=query), + ) + return queryset + + class SimpleUserViewSet(generics.GenericAPIView, mixins.RetrieveModelMixin): serializer_class = SimpleUserSerializer queryset = User.objects.all() lookup_field = ('username', 'email') +class GroupAdminViewSet(viewsets.ModelViewSet): + queryset = Group.objects.all() + model = Group + serializer_class = GroupAdminSerializer + permission_classes = (permissions.IsAdminUser, ) + + def put(self, request, **kwargs): + User = get_user_model() + # Does a user need to be removed from a given group? + if request.data['action'] == 'remove': + group = Group.objects.get(id=request.data['id']) + group.user_set.remove(User.objects.get(id=request.data['user']['id'])) + return Response(status=200) + + # Does a user nedd to be added to a given group? + # The "add" action support multiple users + if request.data['action'] == 'add': + group = Group.objects.get(id=request.data['id']) + for user in request.data.get('users', None): + group.user_set.add(User.objects.get(id=user['id'])) + return Response(status=200) + + if request.data['action'] == 'bulk_remove': + group = Group.objects.get(id=request.data['id']) + users = User.objects.filter(email__in=request.data['users']) + for user in users: + group.user_set.remove(user) + return Response(status=200) + + if request.data['action'] == 'bulk_add': + group = Group.objects.get(id=request.data['id']) + users = User.objects.filter(email__in=request.data['users']) + for user in users: + group.user_set.add(user) + return Response(status=200) + + return Response(status=404) + + +class GroupViewSet(viewsets.ReadOnlyModelViewSet): + """ + API endpoint that allows groups to be viewed. + """ + queryset = Group.objects.all() + serializer_class = GroupSerializer + + # FIXME: the social classes may need rework when updating to v0.9.3 # FixSocialLoginSerializer class may not be needed anymore class FacebookLogin(SocialLoginView):