diff --git a/web_admin/pagination.py b/web_admin/pagination.py index 6a6aff4..c2eed5e 100644 --- a/web_admin/pagination.py +++ b/web_admin/pagination.py @@ -1,13 +1,18 @@ """ web_admin pagination file """ +# third party imports from rest_framework.pagination import PageNumberPagination +from base.constants import NUMBER + class CustomPageNumberPagination(PageNumberPagination): """ custom paginator class """ - page_size = 10 # Set the desired page size + # Set the desired page size + page_size = NUMBER['ten'] page_size_query_param = 'page_size' - max_page_size = 100 # Set a maximum page size if needed + # Set a maximum page size if needed + max_page_size = NUMBER['hundred'] diff --git a/web_admin/serializers/analytics_serializer.py b/web_admin/serializers/analytics_serializer.py index 4c0cbd5..e87c25d 100644 --- a/web_admin/serializers/analytics_serializer.py +++ b/web_admin/serializers/analytics_serializer.py @@ -1,13 +1,16 @@ """ web_admin analytics serializer file """ +# third party imports from rest_framework import serializers + +# django imports from django.contrib.auth import get_user_model +# local imports from base.constants import USER_TYPE from junior.models import JuniorPoints, Junior -from web_admin.serializers.user_management_serializer import JuniorSerializer, GuardianSerializer USER = get_user_model() @@ -74,6 +77,7 @@ class UserCSVReportSerializer(serializers.ModelSerializer): phone_number = serializers.SerializerMethodField() user_type = serializers.SerializerMethodField() is_active = serializers.SerializerMethodField() + date_joined = serializers.SerializerMethodField() class Meta: """ @@ -104,9 +108,9 @@ class UserCSVReportSerializer(serializers.ModelSerializer): :return: user type """ if obj.guardian_profile.all().first(): - return dict(USER_TYPE).get('2') + return dict(USER_TYPE).get('2').capitalize() elif obj.junior_profile.all().first(): - return dict(USER_TYPE).get('1') + return dict(USER_TYPE).get('1').capitalize() else: return None @@ -120,3 +124,12 @@ class UserCSVReportSerializer(serializers.ModelSerializer): return "Active" if profile.is_active else "Inactive" elif profile := obj.junior_profile.all().first(): return "Active" if profile.is_active else "Inactive" + + @staticmethod + def get_date_joined(obj): + """ + :param obj: user obj + :return: formatted date + """ + date = obj.date_joined.strftime("%d %b %Y") + return date diff --git a/web_admin/views/analytics.py b/web_admin/views/analytics.py index 34d1204..3a3360e 100644 --- a/web_admin/views/analytics.py +++ b/web_admin/views/analytics.py @@ -44,7 +44,7 @@ class AnalyticsViewSet(GenericViewSet): to get junior leaderboard and ranking """ serializer_class = None - # permission_classes = [IsAuthenticated, AdminPermission] + permission_classes = [IsAuthenticated, AdminPermission] def get_queryset(self): user_qs = USER.objects.filter( @@ -148,93 +148,14 @@ class AnalyticsViewSet(GenericViewSet): serializer = self.serializer_class(paginated_queryset, many=True) return custom_response(None, serializer.data) - # @action(methods=['get'], url_name='generate-report', url_path='generate-report', detail=False) - # def generate_report(self, request): - # - # response = HttpResponse(content_type='text/csv') - # response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.csv"' - # - # 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'), DATE_FORMAT) - # end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT) - # - # buffer = io.StringIO() - # csv_writer = csv.writer(buffer) - # - # # sheet 1 for Total Users - # user_qs = self.get_queryset() - # queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) - # serializer = UserCSVReportSerializer(queryset, many=True) - # - # # writer = csv.writer(response) - # csv_writer.writerow(['Users']) - # csv_writer.writerow([ - # 'First Name', 'Last Name', 'Email', 'Phone Number', 'User Type', 'status', 'Date Joined', - # # Add more fields as needed - # ]) - # - # for user_data in serializer.data: - # row = [ - # user_data['first_name'], user_data['last_name'], user_data['email'], - # user_data['phone_number'], user_data['user_type'], - # user_data['is_active'], user_data['date_joined'] - # ] - # csv_writer.writerow(row) - # - # response.write(buffer.getvalue()) - # buffer.close() - # - # # sheet 2 for Assign Task - # assign_tasks = JuniorTask.objects.filter( - # created_at__range=[start_date, (end_date + datetime.timedelta(days=1))] - # ).exclude(task_status__in=[PENDING, EXPIRED]) - # buffer = io.StringIO() - # csv_writer = csv.writer(buffer) - # csv_writer.writerow([]) - # csv_writer.writerow(['Assign Tasks']) - # csv_writer.writerow([ - # 'Task Name', 'Task Status' - # # Add more fields as needed - # ]) - # - # for task in assign_tasks: - # row = [ - # task.task_name, dict(TASK_STATUS).get(task.task_status).capitalize(), - # ] - # csv_writer.writerow(row) - # response.write(buffer.getvalue()) - # buffer.close() - # - # # sheet 3 for Juniors Leaderboard and rank - # queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( - # expression=Rank(), - # order_by=[F('total_points').desc(), 'junior__created_at'] - # )).order_by('-total_points', 'junior__created_at') - # buffer = io.StringIO() - # csv_writer = csv.writer(buffer) - # csv_writer.writerow([]) - # csv_writer.writerow(['Top 15 Juniors']) - # csv_writer.writerow([ - # 'Junior Name', 'Points', 'Rank' - # # Add more fields as needed - # ]) - # - # for junior in queryset: - # row = [ - # f"{junior.junior.auth.first_name}{junior.junior.auth.last_name}" if junior.junior.auth.last_name else junior.junior.auth.first_name, - # junior.total_points, junior.rank - # ] - # csv_writer.writerow(row) - # response.write(buffer.getvalue()) - # buffer.close() - # - # return response - - @action(methods=['get'], url_name='generate-report', url_path='generate-report', detail=False) - def generate_report(self, request): + @action(methods=['get'], url_name='export-excel', url_path='export-excel', detail=False) + def export_excel(self, request): + """ + to export users count, task details and top juniors in csv/excel file + :param request: start_date: date format (yyyy-mm-dd) + :param request: end_date: date format (yyyy-mm-dd) + :return: + """ response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.xlsx"' @@ -252,7 +173,7 @@ class AnalyticsViewSet(GenericViewSet): workbook = xlsxwriter.Workbook(buffer) # Add sheets - sheets = ['Users', 'Assign Tasks', 'Top 15 Juniors'] + sheets = ['Users', 'Assign Tasks', 'Juniors leaderboard'] for sheet_name in sheets: worksheet = workbook.add_worksheet(name=sheet_name) @@ -263,7 +184,13 @@ class AnalyticsViewSet(GenericViewSet): queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1)))) serializer = UserCSVReportSerializer(queryset, many=True) - df_users = pd.DataFrame(serializer.data) + df_users = pd.DataFrame([ + {'Name': f"{user['first_name']} {user['last_name']}", + 'Email': user['email'], 'Phone Number': user['phone_number'], + 'User Type': user['user_type'], 'Status': user['is_active'], + 'Date Joined': user['date_joined']} + for user in serializer.data + ]) for idx, col in enumerate(df_users.columns): worksheet.write(0, idx, col) # Write header for row_num, row in enumerate(df_users.values, start=1): @@ -288,7 +215,7 @@ class AnalyticsViewSet(GenericViewSet): worksheet.write(row_num, col_num, value) # sheet 3 for Juniors Leaderboard and rank - elif sheet_name == 'Top 15 Juniors': + elif sheet_name == 'Juniors leaderboard': queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( expression=Rank(), order_by=[F('total_points').desc(), 'junior__created_at'] diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 2d003c1..3e0713c 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -177,6 +177,9 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +# database query logs settings +# Allows us to check db hits +# useful to optimize db query and hit LOGGING = { "version": 1, "filters": { @@ -193,6 +196,7 @@ LOGGING = { "class": "logging.StreamHandler" } }, + # database logger "loggers": { "django.db.backends": { "level": "DEBUG", @@ -242,6 +246,7 @@ CORS_ALLOW_HEADERS = ( 'x-requested-with', ) +# CORS header settings CORS_EXPOSE_HEADERS = ( 'Access-Control-Allow-Origin: *', ) @@ -297,5 +302,7 @@ STATIC_URL = 'static/' # define static root STATIC_ROOT = 'static' +# media url MEDIA_URL = "/media/" +# media path MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media')