diff --git a/account/serializers.py b/account/serializers.py index 5686931..4065b5c 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -154,10 +154,9 @@ class AdminLoginSerializer(serializers.ModelSerializer): user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True ).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first() - if not user: - raise serializers.ValidationError({'details': ERROR_CODE['2002']}) - elif not user.check_password(attrs['password']): + if not user or not user.check_password(attrs['password']): raise serializers.ValidationError({'details': ERROR_CODE['2002']}) + self.context.update({'user': user}) return attrs diff --git a/account/views.py b/account/views.py index 80530ad..c814100 100644 --- a/account/views.py +++ b/account/views.py @@ -334,10 +334,9 @@ class UserLogin(viewsets.ViewSet): user = User.objects.filter(email__iexact=email, is_superuser=True ).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first() - if not user: - return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST) - elif not user.check_password(password): + if not user or not user.check_password(password): return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST) + serializer = SuperUserSerializer(user) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py new file mode 100644 index 0000000..5b653a6 --- /dev/null +++ b/web_admin/serializers/analytics_serializer.py @@ -0,0 +1,3 @@ +""" +web_admin analytics serializer file +""" diff --git a/web_admin/serializers/auth_serializer.py b/web_admin/serializers/auth_serializer.py index 9f603ce..712e284 100644 --- a/web_admin/serializers/auth_serializer.py +++ b/web_admin/serializers/auth_serializer.py @@ -87,9 +87,9 @@ class AdminVerifyOTPSerializer(serializers.Serializer): # fetch email otp object of the user user_otp_details = UserEmailOtp.objects.filter(email=email, otp=otp).last() if not user_otp_details: - raise serializers.ValidationError({'details': ERROR_CODE['2064']}) + raise serializers.ValidationError({'details': ERROR_CODE['2008']}) if user_otp_details.user_type != dict(USER_TYPE).get('3'): - raise serializers.ValidationError({'details': ERROR_CODE['2063']}) + raise serializers.ValidationError({'details': ERROR_CODE['2008']}) if user_otp_details.expired_at.replace(tzinfo=None) < datetime.utcnow(): raise serializers.ValidationError({'details': ERROR_CODE['2029']}) user_otp_details.is_verified = True diff --git a/web_admin/urls.py b/web_admin/urls.py index 065d30d..d586e50 100644 --- a/web_admin/urls.py +++ b/web_admin/urls.py @@ -6,6 +6,7 @@ from django.urls import path, include from rest_framework import routers # local imports +from web_admin.views.analytics import AnalyticsViewSet from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet from web_admin.views.auth import ForgotAndResetPasswordViewSet from web_admin.views.user_management import UserManagementViewSet @@ -17,6 +18,9 @@ router.register('article', ArticleViewSet, basename='article') router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images') router.register('user-management', UserManagementViewSet, basename='user') router.register('article-list', ArticleListViewSet, basename='article-list') +router.register('analytics', AnalyticsViewSet, basename='user-analytics') +# router.register('task-analytics', TaskAnalyticsViewSet, basename='task-analytics') + # forgot and reset password api for admin router.register('admin', ForgotAndResetPasswordViewSet, basename='admin') diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py new file mode 100644 index 0000000..f769c18 --- /dev/null +++ b/web_admin/views/analytics.py @@ -0,0 +1,98 @@ +""" +web_admin analytics view file +""" +import datetime + +from rest_framework.viewsets import GenericViewSet +from rest_framework.decorators import action +from django.contrib.auth import get_user_model +from django.db.models import Q +from django.db.models import Count +from django.db.models.functions import TruncDate + +from account.utils import custom_response +from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED +from guardian.models import JuniorTask + +USER = get_user_model() + + +class AnalyticsViewSet(GenericViewSet): + """ + analytics api view + """ + serializer_class = None + + def get_queryset(self): + user_qs = USER.objects.filter( + (Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)), + is_superuser=False + ).prefetch_related('guardian_profile', + 'junior_profile' + ).exclude(junior_profile__isnull=True, + guardian_profile__isnull=True).order_by('date_joined') + return user_qs + + @action(methods=['get'], url_name='users-count', url_path='users-count', detail=False) + def total_users_count(self, request, *args, **kwargs): + """ + api method to get total users, guardians and juniors + :param request: query params {start_date and end_date}, date format (yyyy-mm-dd) + :return: + """ + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + + user_qs = self.get_queryset() + queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) + + data = {'total_users': queryset.count(), + 'total_guardians': queryset.filter(junior_profile__isnull=True).count(), + 'total_juniors': queryset.filter(guardian_profile__isnull=True).count()} + + return custom_response(None, data) + + @action(methods=['get'], url_name='new-signups', url_path='new-signups', detail=False) + def new_signups(self, request, *args, **kwargs): + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + + user_qs = self.get_queryset() + signup_data = user_qs.filter(date_joined__range=[start_date, (end_date + datetime.timedelta(days=1))] + ).annotate(date=TruncDate('date_joined') + ).values('date').annotate(signups=Count('id')).order_by('date') + + return custom_response(None, signup_data) + + @action(methods=['get'], url_name='assign-tasks', url_path='assign-tasks', detail=False) + def assign_tasks_report(self, request, *args, **kwargs): + + end_date = datetime.date.today() + start_date = end_date - datetime.timedelta(days=6) + + if request.query_params.get('start_date') and request.query_params.get('end_date'): + start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d') + end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d') + + assign_tasks = JuniorTask.objects.filter( + created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] + ).exclude(task_status__in=[PENDING, EXPIRED]) + + data = { + 'task_completed': assign_tasks.filter(task_status=COMPLETED).count(), + 'task_in_progress': assign_tasks.filter(task_status=IN_PROGRESS).count(), + 'task_requested': assign_tasks.filter(task_status=REQUESTED).count(), + 'task_rejected': assign_tasks.filter(task_status=REJECTED).count(), + } + + return custom_response(None, data) diff --git a/web_admin/views/user_management.py b/web_admin/views/user_management.py index 3bd8f64..c16b62a 100644 --- a/web_admin/views/user_management.py +++ b/web_admin/views/user_management.py @@ -29,11 +29,12 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, """ serializer_class = UserManagementListSerializer permission_classes = [IsAuthenticated, AdminPermission] - queryset = USER.objects.filter(is_superuser=False - ).prefetch_related('guardian_profile', - 'junior_profile' - ).exclude(junior_profile__isnull=True, - guardian_profile__isnull=True).order_by('date_joined') + queryset = USER.objects.filter( + (Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)), + is_superuser=False).prefetch_related('guardian_profile', + 'junior_profile' + ).exclude(junior_profile__isnull=True, + guardian_profile__isnull=True).order_by('date_joined') filter_backends = (SearchFilter,) search_fields = ['first_name', 'last_name'] http_method_names = ['get', 'post', 'patch'] @@ -55,8 +56,6 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin, :return: """ queryset = self.get_queryset() - queryset = queryset.filter( - (Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True))) paginator = self.pagination_class() paginated_queryset = paginator.paginate_queryset(queryset, request) serializer = self.serializer_class(paginated_queryset, many=True)