Compare commits

..

4 Commits

24 changed files with 349 additions and 264 deletions

View File

@ -216,17 +216,10 @@ class GuardianSerializer(serializers.ModelSerializer):
def get_user_type(self, obj):
"""user type"""
if self.context.get('user_type', ''):
return self.context.get('user_type')
# remove the below code once user_type can be passed
# from everywhere from where this serializer is being called
else:
email_verified = UserEmailOtp.objects.filter(
email=obj.user.username
).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['two'])
email_verified = UserEmailOtp.objects.filter(email=obj.user.username).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['two'])
def get_auth(self, obj):
"""user email address"""

View File

@ -1,6 +1,4 @@
"""Account utils"""
from celery import shared_task
"""Import django"""
from django.conf import settings
from rest_framework import viewsets, status
@ -22,7 +20,7 @@ from rest_framework import serializers
# Import messages from base package"""
from junior.models import Junior
from guardian.models import Guardian
from account.models import UserDelete, UserDeviceDetails
from account.models import UserDelete
from base.messages import ERROR_CODE
from django.utils import timezone
from base.constants import NUMBER
@ -46,7 +44,7 @@ from junior.models import JuniorPoints
# referral code,
# Define function for generating
# alphanumeric code
# otp expiry
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
"""delete user account"""
if user_type == '1' and user_type_data == '1':
@ -111,7 +109,6 @@ def guardian_account_update(user_tb):
for data in jun_data:
data.guardian_code.remove(guardian_data.guardian_code)
data.save()
@shared_task()
def send_otp_email(recipient_email, otp):
"""Send otp on email with template"""
from_email = settings.EMAIL_FROM_ADDRESS
@ -127,20 +124,6 @@ def send_otp_email(recipient_email, otp):
)
return otp
@shared_task
def user_device_details(user, device_id):
"""
Used to store the device id of the user
user: user object
device_id: string
return
"""
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_details:
device_details.device_id = device_id
device_details.save()
def send_support_email(name, sender, subject, message):
"""Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS]
@ -251,9 +234,3 @@ def generate_code(value, user_id):
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
def get_user_full_name(user_obj):
"""
to get user's full name
"""
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name

View File

@ -1,12 +1,11 @@
"""Account view """
import threading
from notifications.utils import remove_fcm_token
# django imports
from datetime import datetime, timedelta
from rest_framework import viewsets, status, views
from rest_framework.decorators import action
import random
import logging
from django.utils import timezone
import jwt
@ -36,10 +35,10 @@ from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSeriali
AdminLoginSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ZOD, JUN, GRD, USER_TYPE_FLAG
from base.constants import NUMBER, ZOD, JUN, GRD
from guardian.tasks import generate_otp
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
generate_code, OTP_EXPIRY, user_device_details)
generate_code, OTP_EXPIRY)
from junior.serializers import JuniorProfileSerializer
from guardian.serializers import GuardianProfileSerializer
@ -281,48 +280,29 @@ class UserPhoneVerification(viewsets.ModelViewSet):
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
class UserLogin(viewsets.ViewSet):
"""User login"""
@action(methods=['post'], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user_type = request.data.get('user_type')
device_id = request.META.get('HTTP_DEVICE_ID')
user = authenticate(request, username=username, password=password)
try:
if user is not None:
login(request, user)
if str(user_type) == USER_TYPE_FLAG["TWO"]:
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
if guardian_data:
serializer = GuardianSerializer(
guardian_data, context={'user_type': user_type}
).data
else:
return custom_error_response(
ERROR_CODE["2070"],
response_status=status.HTTP_401_UNAUTHORIZED
)
elif str(user_type) == USER_TYPE_FLAG["FIRST"]:
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
if junior_data:
serializer = JuniorSerializer(
junior_data, context={'user_type': user_type}
).data
else:
return custom_error_response(
ERROR_CODE["2071"],
response_status=status.HTTP_401_UNAUTHORIZED
)
else:
return custom_error_response(
ERROR_CODE["2069"],
response_status=status.HTTP_401_UNAUTHORIZED
)
# storing device id in using thread so the time would be reduced
threading.Thread(target=user_device_details, args=(user, device_id))
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
if guardian_data:
serializer = GuardianSerializer(guardian_data).data
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
if junior_data:
serializer = JuniorSerializer(junior_data).data
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_details:
device_details.device_id = device_id
device_details.save()
return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
@ -332,12 +312,9 @@ class UserLogin(viewsets.ViewSet):
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {
"auth_token":access_token,
"refresh_token":refresh_token,
"is_profile_complete": False,
"user_type": user_type,
}
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False,
"user_type": email_verified.user_type,
}
is_verified = False
if email_verified:
is_verified = email_verified.is_verified
@ -346,18 +323,11 @@ class UserLogin(viewsets.ViewSet):
email_verified.otp = otp
email_verified.save()
data.update({"email_otp":otp})
send_otp_email.delay(username, otp)
return custom_response(
ERROR_CODE['2024'],
{"email_otp": otp, "is_email_verified": is_verified},
response_status=status.HTTP_200_OK
)
send_otp_email(username, otp)
return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified},
response_status=status.HTTP_200_OK)
data.update({"is_email_verified": is_verified})
return custom_response(
SUCCESS_CODE['3003'],
data,
response_status=status.HTTP_200_OK
)
return custom_response(SUCCESS_CODE['3003'], data, response_status=status.HTTP_200_OK)
@action(methods=['post'], detail=False)
def admin_login(self, request):
@ -401,13 +371,12 @@ class AdminLoginViewSet(viewsets.GenericViewSet):
class UserEmailVerification(viewsets.ModelViewSet):
"""User Email verification"""
serializer_class = EmailVerificationSerializer
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
def list(self, request, *args, **kwargs):
try:
user_obj = User.objects.filter(username=self.request.data.get('email')).last()
email_data = UserEmailOtp.objects.filter(email=self.request.data.get('email'),
otp=self.request.data.get('otp')).last()
user_obj = User.objects.filter(username=self.request.GET.get('email')).last()
email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'),
otp=self.request.GET.get('otp')).last()
if email_data:
input_datetime_str = str(email_data.expired_at)
input_format = "%Y-%m-%d %H:%M:%S.%f%z"
@ -421,12 +390,12 @@ class UserEmailVerification(viewsets.ModelViewSet):
email_data.is_verified = True
email_data.save()
if email_data.user_type == '1':
junior_data = Junior.objects.filter(auth__email=self.request.data.get('email')).last()
junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last()
if junior_data:
junior_data.is_verified = True
junior_data.save()
else:
guardian_data = Guardian.objects.filter(user__email=self.request.data.get('email')).last()
guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last()
if guardian_data:
guardian_data.is_verified = True
guardian_data.save()
@ -536,7 +505,7 @@ class UserNotificationAPIViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
queryset = UserNotification.objects.filter(user=request.user)
queryset = self.queryset.filter(user=request.user)
serializer = UserNotificationSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)

View File

@ -50,13 +50,6 @@ USER_TYPE = (
('2', 'guardian'),
('3', 'superuser')
)
USER_TYPE_FLAG = {
"FIRST" : "1",
"TWO" : "2",
"THREE": "3"
}
"""gender"""
GENDERS = (
('1', 'Male'),

View File

@ -94,10 +94,7 @@ ERROR_CODE = {
"2065": "Passwords do not match. Please try again.",
"2066": "Task does not exist or not in expired state",
"2067": "Action not allowed. User type missing.",
"2068": "No guardian associated with this junior",
"2069": "Invalid user type",
"2070": "You did not find as a guardian",
"2071": "You did not find as a junior"
"2068": "No guardian associated with this junior"
}
"""Success message code"""

Binary file not shown.

View File

@ -29,7 +29,7 @@ from .utils import real_time, convert_timedelta_into_datetime, update_referral_p
# notification's constant
from notifications.constants import TASK_POINTS, TASK_REJECTED
# send notification function
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
# In this serializer file
@ -383,8 +383,7 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update complete time of task
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_POINTS, None, junior_details.auth.id,
{'task_id': instance.id})
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
@ -392,8 +391,7 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update reject time of task
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_REJECTED, None, junior_details.auth.id,
{'task_id': instance.id})
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {})
instance.save()
junior_data.save()
return junior_details

View File

@ -21,9 +21,7 @@ from zod_bank.celery import app
# notification's constant
from notifications.constants import REFERRAL_POINTS
# send notification function
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
# Define upload image on
# ali baba cloud
# firstly save image
@ -94,7 +92,7 @@ def update_referral_points(referral_code, referral_code_used):
junior_query.total_points = junior_query.total_points + NUMBER['five']
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
junior_query.save()
send_notification_to_junior.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
send_notification.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})

View File

@ -38,8 +38,8 @@ from account.utils import custom_response, custom_error_response, OTP_EXPIRY, se
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, GUARDIAN_CODE_STATUS
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_ASSIGNED, LEADERBOARD_RANKING
from notifications.utils import send_notification_to_junior
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING
from notifications.utils import send_notification
""" Define APIs """
# Define Signup API,
@ -72,6 +72,8 @@ class SignupViewset(viewsets.ModelViewSet):
user_type=str(request.data['user_type']), expired_at=expiry)
"""Send email to the register user"""
send_otp_email(request.data['email'], otp)
# send push notification for registration
send_notification.delay(REGISTRATION, None, user.id, {})
return custom_response(SUCCESS_CODE['3001'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
@ -184,10 +186,9 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
if serializer.is_valid():
# save serializer
task = serializer.save()
serializer.save()
junior_id = Junior.objects.filter(id=junior).last()
send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'],
junior_id.auth.id, {'task_id': task.id})
send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
@ -240,7 +241,7 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save()
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)

View File

@ -21,7 +21,7 @@ from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian
from notifications.utils import send_notification
from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION,
TASK_SUBMITTED)
@ -98,7 +98,7 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
junior.guardian_code_status = str(NUMBER['three'])
junior_approval_mail(user.email, user.first_name)
send_notification_to_guardian.delay(APPROVED_JUNIOR, junior.auth.id, guardian_data.user.id, {})
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name)
@ -146,8 +146,8 @@ class JuniorDetailSerializer(serializers.ModelSerializer):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile',
'created_at', 'image', 'updated_at']
'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
'image', 'updated_at']
class JuniorDetailListSerializer(serializers.ModelSerializer):
"""junior serializer"""
@ -305,7 +305,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email(email, full_name, email, password)
# push notification
send_notification_to_junior.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
return junior_data
@ -320,7 +320,7 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
if instance:
instance.is_invited = False
instance.guardian_code = '{}'
instance.guardian_code_status = str(NUMBER['one'])
instance.guardian_code_status = str(NUMBER['1'])
instance.save()
return instance
@ -337,10 +337,8 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification_to_junior.delay(TASK_SUBMITTED, instance.guardian.user.id,
instance.junior.auth.id, {'task_id': instance.id})
send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id,
instance.guardian.user.id, {'task_id': instance.id})
send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {})
send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
@ -450,8 +448,8 @@ class AddGuardianSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(email, full_name)
send_notification_to_junior.delay(INVITED_GUARDIAN, guardian_data.user.id, junior_data.auth.id, {})
send_notification_to_guardian.delay(APPROVED_JUNIOR, junior_data.auth.id, guardian_data.user.id, {})
send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {})
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):
@ -504,7 +502,6 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
model = Junior
fields = ('id', )
def update(self, instance, validated_data):
instance.guardian_code = None
instance.guardian_code_status = str(NUMBER['one'])
instance.save()
return instance

View File

@ -42,7 +42,7 @@ from base.constants import NUMBER, ARTICLE_STATUS
from account.utils import custom_response, custom_error_response
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
from notifications.constants import REMOVE_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
@ -269,7 +269,7 @@ class RemoveJuniorAPIView(views.APIView):
if serializer.is_valid():
# save serializer
serializer.save()
send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
send_notification.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
@ -334,8 +334,7 @@ class CompleteJuniorTaskAPIView(views.APIView):
image_url = upload_image_to_alibaba(image, filename)
# fetch junior query
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user
).select_related('guardian', 'junior').last()
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
if task_queryset:
# use CompleteTaskSerializer serializer
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:

View File

@ -3,7 +3,7 @@ notification constants file
"""
from base.constants import NUMBER
REGISTRATION = NUMBER['one']
TASK_ASSIGNED = NUMBER['two']
TASK_CREATED = NUMBER['two']
INVITED_GUARDIAN = NUMBER['three']
APPROVED_JUNIOR = NUMBER['four']
REFERRAL_POINTS = NUMBER['five']
@ -21,17 +21,17 @@ NOTIFICATION_DICT = {
"title": "Successfully registered!",
"body": "You have registered successfully. Now login and complete your profile."
},
TASK_ASSIGNED: {
"title": "New task assigned !!",
"body": "{from_user} has assigned you a new task."
TASK_CREATED: {
"title": "Task created!",
"body": "Task created successfully."
},
INVITED_GUARDIAN: {
"title": "Invite guardian",
"body": "Invite guardian successfully"
},
APPROVED_JUNIOR: {
"title": "Approve junior !",
"body": "You have request from {from_user} to associate with you."
"title": "Approve junior",
"body": "You have request from junior to associate with you"
},
REFERRAL_POINTS: {
"title": "Earn Referral points",
@ -47,15 +47,15 @@ NOTIFICATION_DICT = {
},
SKIPPED_PROFILE_SETUP: {
"title": "Skipped profile setup!",
"body": "Your guardian {from_user} has setup your profile."
"body": "Your guardian has been setup your profile."
},
TASK_SUBMITTED: {
"title": "Task submitted!",
"body": "Your task has been submitted successfully."
},
TASK_ACTION: {
"title": "Task completion approval!",
"body": "You have request from {from_user} for task approval."
"title": "Task approval!",
"body": "You have request for task approval."
},
LEADERBOARD_RANKING: {
"title": "Leader board rank!",
@ -67,6 +67,6 @@ NOTIFICATION_DICT = {
},
TEST_NOTIFICATION: {
"title": "Test Notification",
"body": "This notification is for testing purpose from {from_user}."
"body": "This notification is for testing purpose"
}
}

View File

@ -35,8 +35,7 @@ class NotificationListSerializer(serializers.ModelSerializer):
class Meta(object):
"""meta info"""
model = Notification
fields = ['id', 'data', 'is_read', 'created_at']
fields = ['id', 'data', 'is_read']
class ReadNotificationSerializer(serializers.ModelSerializer):
"""User task Serializer"""

View File

@ -10,9 +10,6 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati
from django.contrib.auth import get_user_model
from account.models import UserNotification
from account.utils import get_user_full_name
from guardian.models import Guardian
from junior.models import Junior
from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification
@ -42,80 +39,30 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
print(e)
def get_basic_detail(from_user_id, to_user_id):
"""
used to get the basic details
"""
def get_basic_detail(notification_type, from_user_id, to_user_id):
""" used to get the basic details """
notification_data = NOTIFICATION_DICT[notification_type]
from_user = User.objects.get(id=from_user_id) if from_user_id else None
to_user = User.objects.get(id=to_user_id)
return from_user, to_user
return notification_data, from_user, to_user
def get_notification_data(notification_type, from_user, to_user, extra_data):
"""
get notification and push data
:param notification_type: notification_type
:param from_user: from_user obj
:param to_user: to_user obj
:param extra_data: any extra data provided
:return: notification and push data
"""
push_data = NOTIFICATION_DICT[notification_type].copy()
notification_data = push_data.copy()
notification_data['to_user'] = get_user_full_name(to_user)
if from_user:
from_user_name = get_user_full_name(from_user)
push_data['body'] = push_data['body'].format(from_user=from_user_name)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name)
notification_data['from_user'] = from_user_name
notification_data.update(extra_data)
return notification_data, push_data
def send_notification(notification_type, from_user, to_user, extra_data):
"""
used to send the push for the given notification type
"""
# (from_user, to_user) = get_basic_detail(from_user_id, to_user_id)
notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data)
@shared_task()
def send_notification(notification_type, from_user_id, to_user_id, extra_data):
""" used to send the push for the given notification type """
(notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id)
user_notification_type = UserNotification.objects.filter(user=to_user).first()
notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
data = notification_data
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
notification_to=to_user, data=notification_data)
notification_to=to_user, data=data)
if user_notification_type.push_notification:
send_push(to_user, push_data)
data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
send_push(to_user, data)
def send_push(user, data):
""" used to send push notification to specific user """
notification_data = data.pop('data', None)
user.fcmdevice_set.filter(active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
Message(notification=FirebaseNotification(data['title'], data['body']), data=notification_data)
)
@shared_task()
def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data):
from_user = None
if from_user_id:
from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.auth
to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.user, extra_data)
@shared_task()
def send_notification_to_junior(notification_type, from_user_id, to_user_id, extra_data):
from_user = None
if from_user_id:
from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.user
to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.auth, extra_data)

View File

@ -11,10 +11,8 @@ from rest_framework import viewsets, status, views
from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE
from notifications.constants import TEST_NOTIFICATION
# Import serializer
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior
# Import model
from notifications.utils import send_notification
from notifications.models import Notification
@ -56,10 +54,7 @@ class NotificationViewSet(viewsets.GenericViewSet):
to send test notification
:return:
"""
send_notification_to_guardian(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
send_notification_to_junior(TEST_NOTIFICATION, request.auth.payload['user_id'], None,
{'task_id': None})
send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {})
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['get'], detail=False, url_path='list', url_name='list',

View File

@ -99,3 +99,6 @@ uritemplate==4.1.1
urllib3==1.26.16
vine==5.0.0
wcwidth==0.2.6
pandas==2.0.3
XlsxWriter==3.1.2

View File

@ -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']

View File

@ -1,12 +1,24 @@
"""
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
USER = get_user_model()
class JuniorLeaderboardSerializer(serializers.ModelSerializer):
"""
junior leaderboard serializer
"""
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
@ -44,9 +56,75 @@ class JuniorLeaderboardSerializer(serializers.ModelSerializer):
class LeaderboardSerializer(serializers.ModelSerializer):
"""
leaderboard serializer
"""
junior = JuniorLeaderboardSerializer()
rank = serializers.IntegerField()
class Meta:
"""
meta class
"""
model = JuniorPoints
fields = ('total_points', 'rank', 'junior')
class UserCSVReportSerializer(serializers.ModelSerializer):
"""
user csv/xls report serializer
"""
phone_number = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
is_active = serializers.SerializerMethodField()
date_joined = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('first_name', 'last_name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
@staticmethod
def get_phone_number(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
return f"+{profile.country_code}{profile.phone}" \
if profile.country_code and profile.phone else profile.phone
else:
return None
@staticmethod
def get_user_type(obj):
"""
:param obj: user object
:return: user type
"""
if obj.guardian_profile.all().first():
return dict(USER_TYPE).get('2').capitalize()
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1').capitalize()
else:
return None
@staticmethod
def get_is_active(obj):
"""
:param obj: user object
:return: user type
"""
if profile := (obj.guardian_profile.all().first() or 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

View File

@ -224,7 +224,7 @@ class ArticleListSerializer(serializers.ModelSerializer):
total_points = serializers.SerializerMethodField('get_total_points')
is_completed = serializers.SerializerMethodField('get_is_completed')
class Meta(object):
class Meta:
"""
meta class
"""
@ -238,8 +238,7 @@ class ArticleListSerializer(serializers.ModelSerializer):
def get_is_completed(self, obj):
"""complete all question"""
context_data = self.context.get('user')
junior_article = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
junior_article = JuniorArticle.objects.filter(article=obj).last()
if junior_article:
return junior_article.is_completed
return False
@ -279,7 +278,7 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
return junior_article_obj.submitted_answer.id
return None
class Meta(object):
class Meta:
"""
meta class
"""
@ -300,7 +299,7 @@ class StartAssessmentSerializer(serializers.ModelSerializer):
if data:
return data.current_que_page
return NUMBER['zero']
class Meta(object):
class Meta:
"""
meta class
"""
@ -326,7 +325,7 @@ class ArticleCardlistSerializer(serializers.ModelSerializer):
return data.current_card_page
return NUMBER['zero']
class Meta(object):
class Meta:
"""
meta class
"""

View File

@ -5,7 +5,7 @@ web_admin user_management serializers file
from rest_framework import serializers
from django.contrib.auth import get_user_model
from base.constants import USER_TYPE
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
# local imports
from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.models import Guardian
@ -210,10 +210,10 @@ class JuniorSerializer(serializers.ModelSerializer):
"""
instance.auth.email = self.validated_data.get('email', instance.auth.email)
instance.auth.username = self.validated_data.get('email', instance.auth.username)
instance.auth.save()
instance.auth.save(update_fields=['email', 'username'])
instance.country_code = validated_data.get('country_code', instance.country_code)
instance.phone = validated_data.get('phone', instance.phone)
instance.save()
instance.save(update_fields=['country_code', 'phone'])
return instance
@staticmethod
@ -265,33 +265,30 @@ class UserManagementDetailSerializer(serializers.ModelSerializer):
model = USER
fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users')
@staticmethod
def get_user_type(obj):
def get_user_type(self, obj):
"""
:param obj: user object
:return: user type
"""
if obj.guardian_profile.all().first():
return dict(USER_TYPE).get('2')
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1')
else:
return None
return GUARDIAN if self.context['user_type'] == GUARDIAN else JUNIOR
@staticmethod
def get_associated_users(obj):
def get_associated_users(self, obj):
"""
:param obj: user object
:return: associated user
"""
if profile := obj.guardian_profile.all().first():
if self.context['user_type'] == GUARDIAN:
profile = obj.guardian_profile.all().only('user_id', 'guardian_code').first()
if profile.guardian_code:
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True)
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code],
is_verified=True).select_related('auth')
serializer = JuniorSerializer(junior, many=True)
return serializer.data
elif profile := obj.junior_profile.all().first():
elif self.context['user_type'] == JUNIOR:
profile = obj.junior_profile.all().only('auth_id', 'guardian_code').first()
if profile.guardian_code:
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True)
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code,
is_verified=True).select_related('user')
serializer = GuardianSerializer(guardian, many=True)
return serializer.data
else:

View File

@ -3,6 +3,12 @@ web_admin analytics view file
"""
# python imports
import datetime
import io
import pandas as pd
import xlsxwriter
import tempfile
import oss2
from django.conf import settings
# third party imports
from rest_framework.viewsets import GenericViewSet
@ -16,15 +22,17 @@ from django.db.models import Count
from django.db.models.functions import TruncDate
from django.db.models import F, Window
from django.db.models.functions.window import Rank
from django.http import HttpResponse
# local imports
from account.utils import custom_response
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS
from guardian.models import JuniorTask
from junior.models import JuniorPoints
from web_admin.pagination import CustomPageNumberPagination
from web_admin.permission import AdminPermission
from web_admin.serializers.analytics_serializer import LeaderboardSerializer
from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer
from web_admin.serializers.user_management_serializer import UserManagementListSerializer
USER = get_user_model()
@ -141,3 +149,121 @@ class AnalyticsViewSet(GenericViewSet):
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data)
@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"'
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)
# Use BytesIO for binary data
buffer = io.BytesIO()
# Create an XlsxWriter Workbook object
workbook = xlsxwriter.Workbook(buffer)
# Add sheets
sheets = ['Users', 'Assign Tasks', 'Juniors leaderboard']
for sheet_name in sheets:
worksheet = workbook.add_worksheet(name=sheet_name)
# sheet 1 for Total Users
if sheet_name == '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)
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):
# Write header
worksheet.write(0, idx, col)
for row_num, row in enumerate(df_users.values, start=1):
for col_num, value in enumerate(row):
worksheet.write(row_num, col_num, value)
# sheet 2 for Assign Task
elif sheet_name == 'Assign Tasks':
assign_tasks = JuniorTask.objects.filter(
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
).exclude(task_status__in=[PENDING, EXPIRED])
df_tasks = pd.DataFrame([
{'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()}
for task in assign_tasks
])
for idx, col in enumerate(df_tasks.columns):
# Write header
worksheet.write(0, idx, col)
for row_num, row in enumerate(df_tasks.values, start=1):
for col_num, value in enumerate(row):
worksheet.write(row_num, col_num, value)
# sheet 3 for Juniors Leaderboard and rank
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']
)).order_by('-total_points', 'junior__created_at')
df_leaderboard = pd.DataFrame([
{
'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}"
if junior.junior.auth.last_name else junior.junior.auth.first_name,
'Points': junior.total_points,
'Rank': junior.rank
}
for junior in queryset
])
for idx, col in enumerate(df_leaderboard.columns):
# Write header
worksheet.write(0, idx, col)
for row_num, row in enumerate(df_leaderboard.values, start=1):
for col_num, value in enumerate(row):
worksheet.write(row_num, col_num, value)
# Close the workbook to save the content
workbook.close()
# Reset the buffer position and write the content to the response
buffer.seek(0)
response.write(buffer.getvalue())
buffer.close()
filename = f"{'analytics'}/{'ZOD_Bank_Analytics.xlsx'}"
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
"""write image in temporary file"""
temp_file.write(response.content)
"""auth of bucket"""
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
"""fetch bucket details"""
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
# Upload the temporary file to Alibaba OSS
bucket.put_object_from_file(filename, temp_file.name)
"""create perfect url for image"""
new_filename = filename.replace(' ', '%20')
link = f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
print(link)
return custom_response(None, link)

View File

@ -221,7 +221,10 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
:return: list of article
"""
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, context={"user": request.user}, many=True)
count = queryset.count()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, data=serializer.data)
class ArticleCardListViewSet(viewsets.ModelViewSet):

View File

@ -14,6 +14,8 @@ from django.db.models import Q
from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE
from base.messages import SUCCESS_CODE, ERROR_CODE
from guardian.models import Guardian
from junior.models import Junior
from web_admin.permission import AdminPermission
from web_admin.serializers.user_management_serializer import (UserManagementListSerializer,
UserManagementDetailSerializer, GuardianSerializer,
@ -70,12 +72,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
"""
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
queryset = self.queryset
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
queryset = queryset.filter(guardian_profile__user__id=kwargs['pk'])
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
queryset = queryset.filter(junior_profile__auth__id=kwargs['pk'])
serializer = UserManagementDetailSerializer(queryset, many=True)
queryset = queryset.filter(id=kwargs['pk'])
serializer = UserManagementDetailSerializer(
queryset, many=True,
context={'user_type': self.request.query_params.get('user_type')}
)
return custom_response(None, data=serializer.data)
def partial_update(self, request, *args, **kwargs):
@ -87,15 +91,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
"""
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
queryset = self.queryset
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first()
serializer = GuardianSerializer(user_obj.guardian_profile.all().first(),
guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).first()
serializer = GuardianSerializer(guardian,
request.data, context={'user_id': kwargs['pk']})
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first()
serializer = JuniorSerializer(user_obj.junior_profile.all().first(),
junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first()
serializer = JuniorSerializer(junior,
request.data, context={'user_id': kwargs['pk']})
serializer.is_valid(raise_exception=True)
@ -114,12 +117,12 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
queryset = self.queryset
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first()
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.guardian_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first()
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.junior_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()

View File

@ -125,6 +125,7 @@ SIMPLE_JWT = {
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
# default db setting
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME':os.getenv('DB_NAME'),
@ -177,6 +178,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 +197,7 @@ LOGGING = {
"class": "logging.StreamHandler"
}
},
# database logger
"loggers": {
"django.db.backends": {
"level": "DEBUG",
@ -242,6 +247,7 @@ CORS_ALLOW_HEADERS = (
'x-requested-with',
)
# CORS header settings
CORS_EXPOSE_HEADERS = (
'Access-Control-Allow-Origin: *',
)
@ -297,5 +303,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')