Merge pull request #201 from KiwiTechLLC/qa

Qa
This commit is contained in:
Ankitajain-kiwi
2023-08-11 17:08:10 +05:30
committed by GitHub
42 changed files with 1904 additions and 285 deletions

View File

@ -137,6 +137,37 @@ class ForgotPasswordSerializer(serializers.Serializer):
"""Forget password serializer"""
email = serializers.EmailField()
class AdminLoginSerializer(serializers.ModelSerializer):
"""admin login serializer"""
email = serializers.EmailField(required=True)
password = serializers.CharField(required=True)
class Meta:
"""
meta class
"""
model = User
fields = ('email', 'password')
def validate(self, attrs):
user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True
).only('id', 'first_name', 'last_name', 'email',
'username', 'is_active', 'is_superuser').first()
if not user or not user.check_password(attrs['password']):
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
self.context.update({'user': user})
return attrs
def create(self, validated_data):
"""
used to return the user object after validation
"""
return self.context['user']
class SuperUserSerializer(serializers.ModelSerializer):
"""Super admin serializer"""
user_type = serializers.SerializerMethodField('get_user_type')

View File

@ -28,14 +28,15 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView)
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView,
AdminLoginViewSet)
"""Router"""
router = routers.SimpleRouter()
"""API End points with router"""
router.register('user', UserLogin, basename='user')
"""super admin login"""
router.register('admin', UserLogin, basename='admin')
router.register('admin', AdminLoginViewSet, basename='admin')
"""google login end point"""
router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')

View File

@ -23,7 +23,8 @@ from guardian.models import Guardian
from account.models import UserDelete
from base.messages import ERROR_CODE
from django.utils import timezone
from base.constants import NUMBER
from junior.models import JuniorPoints
# Define delete
# user account condition,
# Define delete
@ -91,7 +92,9 @@ def junior_account_update(user_tb):
junior_data.is_active = False
junior_data.is_verified = False
junior_data.guardian_code = '{}'
junior_data.guardian_code_status = str(NUMBER['one'])
junior_data.save()
JuniorPoints.objects.filter(junior=junior_data).delete()
def guardian_account_update(user_tb):
"""update guardian account after delete the user account"""
@ -138,12 +141,14 @@ def send_support_email(name, sender, subject, message):
}
)
return name
def custom_response(detail, data=None, response_status=status.HTTP_200_OK):
def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None):
"""Custom response code"""
if not data:
"""when data is none"""
data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status})
return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count})
def custom_error_response(detail, response_status):

View File

@ -1,7 +1,7 @@
"""Account view """
from notifications.utils import remove_fcm_token
"""Django import"""
# django imports
from datetime import datetime, timedelta
from rest_framework import viewsets, status, views
from rest_framework.decorators import action
@ -18,19 +18,21 @@ import google.auth.transport.requests
from rest_framework import status
import requests
from rest_framework.response import Response
from rest_framework import mixins
from django.conf import settings
"""App Import"""
# local imports
from guardian.models import Guardian
from junior.models import Junior
from junior.models import Junior, JuniorPoints
from guardian.utils import upload_image_to_alibaba
from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
from django.contrib.auth.models import User
"""Account serializer"""
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer)
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer,
AdminLoginSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ZOD, JUN, GRD
@ -95,6 +97,8 @@ class GoogleLoginMixin(object):
referral_code=generate_code(ZOD, user_obj.id)
)
serializer = JuniorSerializer(junior_query)
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_query, position=position)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
image=profile_picture,signup_method='2',
@ -145,6 +149,8 @@ class SigninWithApple(views.APIView):
junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
serializer = JuniorSerializer(junior_query)
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_query, position=position)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
signup_method='3',
@ -243,7 +249,6 @@ class ForgotPasswordAPIView(views.APIView):
class SendPhoneOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def create(self, request, *args, **kwargs):
otp = generate_otp()
@ -260,7 +265,6 @@ class SendPhoneOtp(viewsets.ModelViewSet):
class UserPhoneVerification(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def list(self, request, *args, **kwargs):
try:
@ -327,29 +331,46 @@ class UserLogin(viewsets.ViewSet):
@action(methods=['post'], detail=False)
def admin_login(self, request):
username = request.data.get('username')
email = request.data.get('email')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
try:
if user is not None:
login(request, user)
if user.is_superuser:
serializer = SuperUserSerializer(user)
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
except Exception as e:
logging.error(e)
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'}
return custom_response(None, data, response_status=status.HTTP_200_OK)
user = User.objects.filter(email__iexact=email, is_superuser=True
).only('id', 'first_name', 'last_name', 'email',
'username', 'is_active', 'is_superuser').first()
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)
class AdminLoginViewSet(viewsets.GenericViewSet):
"""
admin login api
"""
serializer_class = AdminLoginSerializer
@action(methods=['post'], url_name='login', url_path='login', detail=False)
def admin_login(self, request, *args, **kwargs):
"""
:param request:
:return:
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {"auth_token": access_token, "refresh_token": refresh_token, "username": user.username,
"email": user.email, "first_name": user.first_name, "last_name": user.last_name,
"is_active": user.is_active, "user_type": '3', "is_superuser": user.is_superuser}
return custom_response(None, data)
class UserEmailVerification(viewsets.ModelViewSet):
"""User Email verification"""
serializer_class = EmailVerificationSerializer
queryset = UserEmailOtp.objects.all()
def list(self, request, *args, **kwargs):
try:
@ -392,7 +413,6 @@ class UserEmailVerification(viewsets.ModelViewSet):
class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserEmailOtp.objects.all()
serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
@ -415,7 +435,6 @@ class ReSendEmailOtp(viewsets.ModelViewSet):
class ProfileAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = User.objects.all()
serializer_class = JuniorProfileSerializer
permission_classes = [IsAuthenticated]
@ -434,7 +453,6 @@ class ProfileAPIViewSet(viewsets.ModelViewSet):
class UploadImageAPIViewSet(viewsets.ModelViewSet):
"""upload task image"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesSerializer
def create(self, request, *args, **kwargs):
"""upload images"""
@ -453,7 +471,6 @@ class UploadImageAPIViewSet(viewsets.ModelViewSet):
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesDetailsSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -484,7 +501,6 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
"""notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UserNotificationSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -496,7 +512,6 @@ class UserNotificationAPIViewSet(viewsets.ModelViewSet):
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
"""Update notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UpdateUserNotificationSerializer
permission_classes = [IsAuthenticated]

View File

@ -70,6 +70,18 @@ SIGNUP_METHODS = (
('2', 'google'),
('3', 'apple')
)
# guardian code status
GUARDIAN_CODE_STATUS = (
('1', 'no guardian code'),
('2', 'exist guardian code'),
('3', 'request for guardian code')
)
# article status
ARTICLE_STATUS = (
('1', 'read'),
('2', 'in_progress'),
('3', 'completed')
)
# relationship
RELATIONSHIP = (
('1', 'parent'),
@ -83,6 +95,7 @@ IN_PROGRESS = 2
REJECTED = 3
REQUESTED = 4
COMPLETED = 5
EXPIRED = 6
TASK_POINTS = 5
# duplicate name used defined in constant PROJECT_NAME
PROJECT_NAME = 'Zod Bank'
@ -105,7 +118,9 @@ MAX_ARTICLE_CARD = 6
MIN_ARTICLE_SURVEY = 5
MAX_ARTICLE_SURVEY = 10
# real time url
time_url = "http://worldtimeapi.org/api/timezone/Asia/Riyadh"
# already register
Already_register_user = "duplicate key value violates unique constraint"
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'
DATE_FORMAT = '%Y-%m-%d'

View File

@ -23,15 +23,15 @@ ERROR_CODE_REQUIRED = {
# Error code
ERROR_CODE = {
"2000": "Email not found.",
"2000": "Invalid email address. Please enter a registered email.",
"2001": "This is your existing password. Please choose other one",
"2002": "Invalid login credentials.",
"2002": "Invalid username or password.",
"2003": "An account already exists with this email address.",
"2004": "User not found.",
"2005": "Your account has been activated.",
"2006": "Your account is not activated.",
"2007": "Your account already activated.",
"2008": "Invalid OTP.",
"2008": "The OTP entered is not correct.",
"2009": "The user provided cannot be found or the reset password token has become invalid/timed out.",
"2010": "Invalid Link.",
"2011": "Your profile has not been completed yet.",
@ -54,7 +54,7 @@ ERROR_CODE = {
"2026": "New password should not same as old password",
"2027": "data should contain `identityToken`",
"2028": "You are not authorized person to sign up on this platform",
"2029": "Validity of otp verification is expired",
"2029": "Validity of otp verification has expired. Please request a new one.",
"2030": "Use correct user type and token",
# invalid password
"2031": "Invalid password",
@ -91,7 +91,11 @@ ERROR_CODE = {
"2062": "Please enter email address",
"2063": "Unauthorized access.",
"2064": "To change your password first request an OTP and get it verify then change your password.",
"2065": "Passwords do not match. Please try again."
"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"
}
"""Success message code"""
SUCCESS_CODE = {
@ -107,7 +111,7 @@ SUCCESS_CODE = {
# Success code for link verified
"3005": "Your account is deleted successfully.",
# Success code for password reset
"3006": "Your password has been reset successfully.",
"3006": "Password reset successful. You can now log in with your new password.",
# Success code for password update
"3007": "Your password has been changed successfully.",
# Success code for valid link
@ -120,8 +124,8 @@ SUCCESS_CODE = {
"3012": "Phone OTP Verified successfully",
"3013": "Valid Guardian code",
"3014": "Password has been updated successfully.",
"3015": "Verification code sent on your email.",
"3016": "Send otp on your Email successfully",
"3015": "Verification code has been sent on your email.",
"3016": "An OTP has been sent on your email.",
"3017": "Profile image update successfully",
"3018": "Task created successfully",
"3019": "Support Email sent successfully",
@ -140,7 +144,23 @@ SUCCESS_CODE = {
"3032": "Task request sent successfully",
"3033": "Valid Referral code",
"3034": "Invite guardian successfully",
"3035": "Task started successfully"
"3035": "Task started successfully",
"3036": "Task reassign successfully",
"3037": "Profile has been updated successfully.",
"3038": "Status has been changed successfully.",
# notification read
"3039": "Notification read successfully",
# start article
"3040": "Start article successfully",
# complete article
"3041": "Article completed successfully",
# submit assessment successfully
"3042": "Assessment completed successfully",
# read article
"3043": "Read article card successfully",
# remove guardian code request
"3044": "Remove guardian code request successfully",
}
"""status code error"""
STATUS_CODE_ERROR = {

Binary file not shown.

View File

@ -1,5 +1,5 @@
"""Serializer of Guardian"""
"""Third party Django app"""
# third party imports
import logging
from rest_framework import serializers
# Import Refresh token of jwt
@ -7,7 +7,8 @@ from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction
from django.contrib.auth.models import User
from datetime import datetime, time
"""Import Django app"""
import pytz
from django.utils import timezone
# Import guardian's model,
# Import junior's model,
# Import account's model,
@ -16,14 +17,14 @@ from datetime import datetime, time
# Import messages from
# base package,
# Import some functions
# from utils file"""
# local imports
from .models import Guardian, JuniorTask
from account.models import UserProfile, UserEmailOtp, UserNotification
from account.utils import generate_code
from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD
from junior.models import Junior, JuniorPoints
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant
from notifications.constants import TASK_POINTS, TASK_REJECTED
@ -67,8 +68,10 @@ class UserSerializer(serializers.ModelSerializer):
UserNotification.objects.get_or_create(user=user)
if user_type == str(NUMBER['one']):
# create junior profile
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
junior = Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior, position=position)
if user_type == str(NUMBER['two']):
# create guardian profile
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
@ -81,7 +84,7 @@ class UserSerializer(serializers.ModelSerializer):
otp_verified = False
if otp and otp.is_verified:
otp_verified = True
raise serializers.ValidationError({"details":ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
raise serializers.ValidationError({"details": ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
"code": 400, "status":"failed",
})
@ -211,7 +214,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer):
"""Meta info"""
model = Guardian
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at']
class TaskDetailsSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
@ -224,7 +227,10 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
""" remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
current_datetime = real_time()
# current_datetime = real_time()
# new code
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
current_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime
@ -244,6 +250,34 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
'requested_on', 'rejected_on', 'completed_on', 'is_expired',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
guardian = GuardianDetailSerializer()
remaining_time = serializers.SerializerMethodField('get_remaining_time')
def get_remaining_time(self, obj):
""" remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
# current_datetime = real_time()
# new code
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
current_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer"""
@ -320,7 +354,8 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
"""update guardian code"""
instance = self.context['junior']
instance.guardian_code = [self.context['guardian_code']]
instance.is_invited = True
instance.guardian_code_approved = True
instance.guardian_code_status = str(NUMBER['two'])
instance.save()
return instance
@ -346,18 +381,84 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update total task point
junior_data.total_points = junior_data.total_points + instance.points
# update complete time of task
instance.completed_on = real_time()
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
instance.is_approved = False
# update total task point
junior_data.total_points = junior_data.total_points - instance.points
# update reject time of task
instance.rejected_on = real_time()
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {})
instance.save()
junior_data.save()
return junior_details
class GuardianDetailListSerializer(serializers.ModelSerializer):
"""Guardian serializer"""
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
email = serializers.SerializerMethodField('get_email')
image = serializers.SerializerMethodField('get_image')
guardian_id = serializers.SerializerMethodField('get_guardian_id')
guardian_code = serializers.SerializerMethodField('get_guardian_code')
gender = serializers.SerializerMethodField('get_gender')
phone = serializers.SerializerMethodField('get_phone')
country_name = serializers.SerializerMethodField('get_country_name')
dob = serializers.SerializerMethodField('get_dob')
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
# code info
class Meta(object):
"""Meta info"""
model = JuniorGuardianRelationship
fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'dob',
'guardian_code', 'gender', 'phone', 'country_name', 'created_at', 'guardian_code_status',
'updated_at']
def get_guardian_id(self,obj):
"""first name of guardian"""
return obj.guardian.id
def get_first_name(self,obj):
"""first name of guardian"""
return obj.guardian.user.first_name
def get_last_name(self,obj):
"""last name of guardian"""
return obj.guardian.user.last_name
def get_email(self,obj):
"""email of guardian"""
return obj.guardian.user.email
def get_image(self,obj):
"""guardian image"""
return obj.guardian.image
def get_guardian_code(self,obj):
""" guardian code"""
return obj.guardian.guardian_code
def get_gender(self,obj):
""" guardian gender"""
return obj.guardian.gender
def get_phone(self,obj):
"""guardian phone"""
return obj.guardian.phone
def get_country_name(self,obj):
""" guardian country name """
return obj.guardian.country_name
def get_dob(self,obj):
"""guardian dob """
return obj.guardian.dob
def get_guardian_code_status(self,obj):
"""guardian code status"""
return obj.junior.guardian_code_status

View File

@ -2,7 +2,8 @@
"""Django import"""
from django.urls import path, include
from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView)
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
GuardianListAPIView)
"""Third party import"""
from rest_framework import routers
@ -36,6 +37,8 @@ router.register('filter-task', SearchTaskListAPIView, basename='filter-task')
router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior')
# Approve junior API"""
router.register('approve-task', ApproveTaskAPIView, basename='approve-task')
# guardian list API"""
router.register('guardian-list', GuardianListAPIView, basename='guardian-list')
# Define Url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),

View File

@ -11,7 +11,7 @@ import tempfile
# Import date time module's function
from datetime import datetime, time
# import Number constant
from base.constants import NUMBER, time_url
from base.constants import NUMBER
# Import Junior's model
from junior.models import Junior, JuniorPoints
# Import guardian's model
@ -41,7 +41,10 @@ def upload_image_to_alibaba(image, filename):
# Save the image object to a temporary file
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
"""write image in temporary file"""
temp_file.write(image.read())
if type(image) == bytes:
temp_file.write(image)
else:
temp_file.write(image.read())
"""auth of bucket"""
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
"""fetch bucket details"""

View File

@ -10,6 +10,8 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.utils import timezone
# Import guardian's model,
@ -26,14 +28,15 @@ from django.utils import timezone
# Import notification constant
# Import send_notification function
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer)
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer,
GuardianDetailListSerializer)
from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification
from .tasks import generate_otp
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
from base.constants import NUMBER, GUARDIAN_CODE_STATUS
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING
from notifications.utils import send_notification
@ -56,33 +59,29 @@ class SignupViewset(viewsets.ModelViewSet):
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
"""Create user profile"""
try:
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
if serializer.is_valid():
user = serializer.save()
"""Generate otp"""
otp = generate_otp()
# expire otp after 1 day
expiry = OTP_EXPIRY
# create user email otp object
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
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)
else:
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
if serializer.is_valid():
user = serializer.save()
"""Generate otp"""
otp = generate_otp()
# expire otp after 1 day
expiry = OTP_EXPIRY
# create user email otp object
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
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)
else:
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
class UpdateGuardianProfile(viewsets.ViewSet):
"""Update guardian profile"""
queryset = Guardian.objects.all()
serializer_class = CreateGuardianSerializer
permission_classes = [IsAuthenticated]
@ -116,7 +115,6 @@ class UpdateGuardianProfile(viewsets.ViewSet):
class AllTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
queryset = JuniorTask.objects.all()
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -126,38 +124,37 @@ class AllTaskListAPIView(viewsets.ModelViewSet):
serializer = TaskDetailsSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class TaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',)
def get_queryset(self):
queryset = JuniorTask.objects.filter(guardian__user=self.request.user
).prefetch_related('junior', 'junior__auth'
).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
try:
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
elif search and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search,
task_status=status_value).order_by('due_date', 'created_at')
if search is None and str(status_value) == '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_status=status_value).order_by('due_date','created_at')
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
status_value = self.request.GET.get('status')
queryset = self.get_queryset()
if status_value and status_value != '0':
queryset = queryset.filter(task_status=status_value)
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class CreateTaskAPIView(viewsets.ModelViewSet):
"""create task for junior"""
@ -227,9 +224,9 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Top juniors list"""
queryset = JuniorPoints.objects.all()
serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated]
queryset = JuniorPoints.objects.all()
def get_serializer_context(self):
# context list
@ -241,14 +238,12 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Fetch junior list of those who complete their tasks"""
try:
junior_total_points = self.get_queryset().order_by('-total_points')
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save()
serializer = self.get_serializer(junior_total_points, many=True)
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -282,6 +277,7 @@ class ApproveJuniorAPIView(viewsets.ViewSet):
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
queryset[1].guardian_code = None
queryset[1].guardian_code_status = str(NUMBER['one'])
queryset[1].save()
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
except Exception as e:
@ -329,3 +325,22 @@ class ApproveTaskAPIView(viewsets.ViewSet):
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class GuardianListAPIView(viewsets.ModelViewSet):
"""Guardian list of assosicated junior"""
serializer_class = GuardianDetailListSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
""" junior list"""
try:
guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user)
# fetch junior object
if guardian_data:
# use GuardianDetailListSerializer serializer
serializer = GuardianDetailListSerializer(guardian_data, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_response({"status": GUARDIAN_CODE_STATUS[1][0]}, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)

View File

@ -2,8 +2,26 @@
"""Third party Django app"""
from django.contrib import admin
"""Import Django app"""
from .models import Junior, JuniorPoints, JuniorGuardianRelationship
from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
JuniorArticleCard)
# Register your models here.
@admin.register(JuniorArticle)
class JuniorArticleAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'status', 'is_completed']
def __str__(self):
"""Return email id"""
return self.junior__auth__email
@admin.register(JuniorArticleCard)
class JuniorArticleCardAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'article_card', 'is_read']
def __str__(self):
"""Return email id"""
return self.junior__auth__email
@admin.register(Junior)
class JuniorAdmin(admin.ModelAdmin):
"""Junior Admin"""
@ -27,3 +45,7 @@ class JuniorGuardianRelationshipAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['guardian', 'junior', 'relationship']
@admin.register(JuniorArticlePoints)
class JuniorArticlePointsAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'question', 'submitted_answer', 'is_answer_correct']

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.2 on 2023-08-02 11:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0017_juniorguardianrelationship'),
]
operations = [
migrations.RemoveField(
model_name='junior',
name='relationship',
),
migrations.AddField(
model_name='junior',
name='guardian_code_approved',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 4.2.2 on 2023-08-07 13:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0018_remove_junior_relationship_and_more'),
]
operations = [
migrations.CreateModel(
name='JuniorArticlePoints',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('earn_points', models.IntegerField(blank=True, default=5, null=True)),
('is_attempt', models.BooleanField(default=False)),
('is_answer_correct', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles', to='web_admin.article')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_details', to='junior.junior', verbose_name='Junior')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='web_admin.articlesurvey')),
('submitted_answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submitted_answer', to='web_admin.surveyoption')),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-08-08 05:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0019_juniorarticlepoints'),
]
operations = [
migrations.AddField(
model_name='junior',
name='guardian_code_status',
field=models.CharField(blank=True, choices=[('1', 'no guardian code'), ('2', 'exist guardian code'), ('3', 'request for guardian code')], default='1', max_length=31, null=True),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 4.2.2 on 2023-08-08 09:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0020_junior_guardian_code_status'),
]
operations = [
migrations.AlterField(
model_name='juniorarticlepoints',
name='submitted_answer',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submitted_answer', to='web_admin.surveyoption'),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 4.2.2 on 2023-08-09 09:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0021_alter_juniorarticlepoints_submitted_answer'),
]
operations = [
migrations.CreateModel(
name='JuniorArticle',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_completed', models.BooleanField(default=False)),
('status', models.CharField(blank=True, choices=[('1', 'read'), ('2', 'in_progress'), ('3', 'completed')], default='1', max_length=10, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_details', to='web_admin.article')),
('junior', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior')),
],
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 4.2.2 on 2023-08-09 10:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0022_juniorarticle'),
]
operations = [
migrations.CreateModel(
name='JuniorArticleCard',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_read', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_detail', to='web_admin.article')),
('article_card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_article_card', to='web_admin.articlecard')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article_card', to='junior.junior', verbose_name='Junior')),
],
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.2 on 2023-08-10 08:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0023_juniorarticlecard'),
]
operations = [
migrations.AddField(
model_name='juniorarticle',
name='current_card_page',
field=models.IntegerField(blank=True, default=0, null=True),
),
migrations.AddField(
model_name='juniorarticle',
name='current_que_page',
field=models.IntegerField(blank=True, default=0, null=True),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.2 on 2023-08-10 14:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('junior', '0024_juniorarticle_current_card_page_and_more'),
]
operations = [
migrations.AlterField(
model_name='juniorarticle',
name='junior',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior'),
),
]

View File

@ -6,10 +6,11 @@ from django.contrib.auth import get_user_model
"""Import ArrayField"""
from django.contrib.postgres.fields import ArrayField
"""Import django app"""
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP, GUARDIAN_CODE_STATUS, ARTICLE_STATUS
# Import guardian's model
from guardian.models import Guardian
# Import web admin's model
from web_admin.models import SurveyOption, ArticleSurvey, Article, ArticleCard
"""Define User model"""
User = get_user_model()
# Create your models here.
@ -71,6 +72,11 @@ class Junior(models.Model):
passcode = models.IntegerField(null=True, blank=True, default=None)
# junior is verified or not"""
is_verified = models.BooleanField(default=False)
"""guardian code is approved or not"""
guardian_code_approved = models.BooleanField(default=False)
# guardian code status"""
guardian_code_status = models.CharField(max_length=31, choices=GUARDIAN_CODE_STATUS, default='1',
null=True, blank=True)
# Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@ -135,3 +141,62 @@ class JuniorGuardianRelationship(models.Model):
return f'{self.guardian.user}'
class JuniorArticlePoints(models.Model):
"""
Survey Options model
"""
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_details', verbose_name='Junior')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles')
question = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='questions')
submitted_answer = models.ForeignKey(SurveyOption, on_delete=models.SET_NULL, null=True,
related_name='submitted_answer')
# earn points"""
earn_points = models.IntegerField(blank=True, null=True, default=5)
is_attempt = models.BooleanField(default=False)
is_answer_correct = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.question}'
class JuniorArticle(models.Model):
"""
Survey Options model
"""
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article',
verbose_name='Junior')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_details')
# article completed"""
is_completed = models.BooleanField(default=False)
status = models.CharField(max_length=10, choices=ARTICLE_STATUS, null=True, blank=True, default='1')
current_card_page = models.IntegerField(blank=True, null=True, default=0)
current_que_page = models.IntegerField(blank=True, null=True, default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'
class JuniorArticleCard(models.Model):
"""
Survey Options model
"""
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article_card',
verbose_name='Junior')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_detail')
article_card = models.ForeignKey(ArticleCard, on_delete=models.CASCADE, related_name='junior_article_card')
# article card read"""
is_read = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'

View File

@ -1,5 +1,8 @@
"""Serializer file for junior"""
"""Import Django 3rd party app"""
# third party imports
import pytz
# django imports
from rest_framework import serializers
from django.contrib.auth.models import User
from django.db import transaction
@ -7,12 +10,13 @@ from datetime import datetime
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
"""Import django app"""
# local imports
from account.utils import send_otp_email, generate_code
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints
from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD
from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED,
GUARDIAN_CODE_STATUS)
from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
@ -89,7 +93,13 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
"""condition for guardian code"""
if guardian_code:
junior.guardian_code = guardian_code
junior.dob = validated_data.get('dob',junior.dob)
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
if guardian_data:
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.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)
"""Update country code and phone number"""
@ -136,7 +146,7 @@ class JuniorDetailSerializer(serializers.ModelSerializer):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
'image', 'updated_at']
class JuniorDetailListSerializer(serializers.ModelSerializer):
@ -172,10 +182,11 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.position
return 99999
def get_points(self, obj):
data = sum(JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).values_list('points', flat=True))
return data
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.total_points
return NUMBER['zero']
def get_in_progress_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
@ -200,10 +211,10 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'country_name', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task', 'position', 'is_invited']
'requested_task', 'rejected_task', 'position', 'is_invited', 'guardian_code_status']
class JuniorProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
@ -246,15 +257,14 @@ class JuniorProfileSerializer(serializers.ModelSerializer):
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
'is_invited', 'passcode']
'is_invited', 'passcode', 'guardian_code_approved']
class AddJuniorSerializer(serializers.ModelSerializer):
"""Add junior serializer"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'gender','dob', 'is_invited']
fields = ['id', 'gender', 'dob', 'is_invited']
def create(self, validated_data):
@ -263,6 +273,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
email = self.context['email']
guardian = self.context['user']
relationship = self.context['relationship']
profile_image = self.context['image']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
guardian_data = Guardian.objects.filter(user__username=guardian).last()
user_data = User.objects.create(username=email, email=email,
@ -272,14 +283,18 @@ class AddJuniorSerializer(serializers.ModelSerializer):
user_data.set_password(password)
user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
image=profile_image,
dob=validated_data.get('dob'), is_invited=True,
guardian_code=[guardian_data.guardian_code],
junior_code=generate_code(JUN, user_data.id),
referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True)
is_password_set=False, is_verified=True,
guardian_code_status=GUARDIAN_CODE_STATUS[1][0])
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
total_junior = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
@ -289,7 +304,6 @@ class AddJuniorSerializer(serializers.ModelSerializer):
UserNotification.objects.get_or_create(user=user_data)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(guardian, full_name)
# push notification
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
return junior_data
@ -306,6 +320,7 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
if instance:
instance.is_invited = False
instance.guardian_code = '{}'
instance.guardian_code_status = str(NUMBER['1'])
instance.save()
return instance
@ -318,7 +333,7 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
fields = ('id', 'image')
def update(self, instance, validated_data):
instance.image = validated_data.get('image', instance.image)
instance.requested_on = real_time()
instance.requested_on = timezone.now().astimezone(pytz.utc)
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
@ -335,6 +350,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
expired_task = serializers.SerializerMethodField('get_expired_task')
position = serializers.SerializerMethodField('get_position')
def get_junior_id(self, obj):
@ -370,11 +386,14 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
def get_pending_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=PENDING).count()
def get_expired_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=EXPIRED).count()
class Meta(object):
"""Meta info"""
model = Junior
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task']
'requested_task', 'rejected_task', 'expired_task']
class AddGuardianSerializer(serializers.ModelSerializer):
"""Add guardian serializer"""
@ -393,13 +412,15 @@ class AddGuardianSerializer(serializers.ModelSerializer):
relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
junior_data = Junior.objects.filter(auth__username=junior).last()
junior_data.guardian_code_status = GUARDIAN_CODE_STATUS[2][0]
junior_data.save()
instance = User.objects.filter(username=email).last()
if instance:
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
referral_code=generate_code(ZOD,
referral_code=generate_code(ZOD,
instance.id),
referral_code_used=junior_data.referral_code,
is_verified=True)
is_verified=True)
UserNotification.objects.get_or_create(user=instance)
return guardian_data
else:
@ -439,7 +460,10 @@ class StartTaskSerializer(serializers.ModelSerializer):
""" remaining time to complete task"""
due_date = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
real_datetime = real_time()
# real_datetime = real_time()
# new code
due_date = due_date.astimezone(pytz.utc)
real_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date > real_datetime:
time_difference = due_date - real_datetime
@ -454,3 +478,31 @@ class StartTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['two'])
instance.save()
return instance
class ReAssignTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'due_date')
def update(self, instance, validated_data):
instance.task_status = str(NUMBER['one'])
instance.due_date = validated_data.get('due_date')
instance.is_approved = False
instance.requested_on = None
instance.save()
return instance
class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
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

@ -4,7 +4,9 @@ from django.urls import path, include
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
InviteGuardianAPIView, StartTaskAPIView)
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
CreateArticleCardAPIView, RemoveGuardianCodeAPIView)
"""Third party import"""
from rest_framework import routers
@ -41,10 +43,22 @@ router.register('junior-points', JuniorPointsListAPIView, basename='junior-point
router.register('validate-referral-code', ValidateReferralCode, basename='validate-referral-code')
# invite guardian API"""
router.register('invite-guardian', InviteGuardianAPIView, basename='invite-guardian')
# start article"""
router.register('start-article', StartArticleAPIView, basename='start-article')
# start assessment api"""
router.register('start-assessment', StartAssessmentAPIView, basename='start-assessment')
# check answer api"""
router.register('check-answer', CheckAnswerAPIView, basename='check-answer')
# start article"""
router.register('create-article-card', CreateArticleCardAPIView, basename='create-article-card')
# Define url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()),
path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()),
path('api/v1/start-task/', StartTaskAPIView.as_view())
path('api/v1/start-task/', StartTaskAPIView.as_view()),
path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()),
path('api/v1/complete-article/', CompleteArticleAPIView.as_view()),
path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()),
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view()),
]

View File

@ -4,6 +4,7 @@ from django.conf import settings
"""Third party Django app"""
from templated_email import send_templated_mail
from .models import JuniorPoints
from base.constants import NUMBER
from django.db.models import F
# junior notification
# email for sending email
@ -50,11 +51,11 @@ def junior_approval_mail(guardian, full_name):
def update_positions_based_on_points():
"""Update position of the junior"""
# First, retrieve all the JuniorPoints instances ordered by total_points in descending order.
juniors_points = JuniorPoints.objects.order_by('-total_points')
juniors_points = JuniorPoints.objects.order_by('-total_points', 'created_at')
# Now, iterate through the queryset and update the position field based on the order.
position = 1
position = NUMBER['one']
for junior_point in juniors_points:
junior_point.position = position
junior_point.save()
position += 1
position += NUMBER['one']

View File

@ -6,6 +6,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.db.models import F
import datetime
import requests
@ -27,20 +28,25 @@ import requests
# Import upload_image_to_alibaba
# Import custom_response, custom_error_response
# Import constants
from junior.models import Junior, JuniorPoints
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
from django.db.models import Sum
from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
JuniorArticleCard)
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer,
AddGuardianSerializer, StartTaskSerializer)
AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer,
RemoveGuardianCodeSerializer)
from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer
from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
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
from notifications.constants import REMOVE_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
StartAssessmentSerializer)
""" Define APIs """
# Define validate guardian code API,
# update junior profile,
@ -59,7 +65,6 @@ from notifications.constants import REMOVE_JUNIOR
# Create your views here.
class UpdateJuniorProfile(viewsets.ViewSet):
"""Update junior profile"""
queryset = Junior.objects.all()
serializer_class = CreateJuniorSerializer
permission_classes = [IsAuthenticated]
@ -93,7 +98,6 @@ class UpdateJuniorProfile(viewsets.ViewSet):
class ValidateGuardianCode(viewsets.ViewSet):
"""Check guardian code exist or not"""
queryset = Guardian.objects.all()
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -144,7 +148,6 @@ class JuniorListAPIView(viewsets.ModelViewSet):
class AddJuniorAPIView(viewsets.ModelViewSet):
"""Add Junior by guardian"""
queryset = Junior.objects.all()
serializer_class = AddJuniorSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
@ -152,8 +155,19 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
""" junior list"""
try:
info_data = {'user': request.user, 'relationship': str(request.data['relationship']), 'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name']}
info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name'], 'image':None}
profile_image = request.data.get('image')
if profile_image:
# check image size
if profile_image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
# convert into file
filename = f"images/{profile_image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(profile_image, filename)
info_data.update({"image": image_url})
if user := User.objects.filter(username=request.data['email']).first():
self.associate_guardian(user)
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
@ -163,7 +177,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -171,7 +185,10 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
junior = Junior.objects.filter(auth=user).first()
guardian = Guardian.objects.filter(user=self.request.user).first()
junior.guardian_code = [guardian.guardian_code]
junior.guardian_code_status = str(NUMBER['two'])
junior.save()
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior,
relationship=str(self.request.data['relationship']))
return True
@ -179,7 +196,6 @@ class InvitedJuniorAPIView(viewsets.ModelViewSet):
"""Junior list of assosicated guardian"""
serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all()
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
http_method_names = ('get',)
@ -209,7 +225,6 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
serializer_class = JuniorDetailListSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = Junior.objects.all()
http_method_names = ('get',)
def get_queryset(self):
@ -246,8 +261,8 @@ class RemoveJuniorAPIView(views.APIView):
junior_id = self.request.GET.get('id')
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code),
is_invited=True).last()
junior_queryset = Junior.objects.filter(id=junior_id,
guardian_code__icontains=str(guardian.guardian_code)).last()
if junior_queryset:
# use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
@ -265,10 +280,9 @@ class RemoveJuniorAPIView(views.APIView):
class JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
serializer_class = TaskDetailsjuniorSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
@ -295,7 +309,7 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
serializer = TaskDetailsjuniorSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -343,16 +357,12 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
"""get queryset"""
return JuniorTask.objects.filter(junior__auth__email=self.request.user).last()
def list(self, request, *args, **kwargs):
"""profile view"""
try:
queryset = self.get_queryset()
# update position of junior
update_positions_based_on_points()
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
# update position of junior
serializer = JuniorPointsSerializer(queryset)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
@ -434,3 +444,208 @@ class StartTaskAPIView(views.APIView):
return custom_error_response(ERROR_CODE['2060'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ReAssignJuniorTaskAPIView(views.APIView):
"""Update junior task API"""
serializer_class = ReAssignTaskSerializer
model = JuniorTask
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
task_id = self.request.data.get('task_id')
task_queryset = JuniorTask.objects.filter(id=task_id, guardian__user__email=self.request.user).last()
if task_queryset and task_queryset.task_status == str(NUMBER['six']):
# use StartTaskSerializer serializer
serializer = ReAssignTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3036'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
# task in another state
return custom_error_response(ERROR_CODE['2066'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class StartArticleAPIView(viewsets.ModelViewSet):
"""Start article"""
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
junior_instance = Junior.objects.filter(auth=self.request.user).last()
article_id = request.data.get('article_id')
article_data = Article.objects.filter(id=article_id).last()
if not JuniorArticle.objects.filter(junior=junior_instance, article=article_data).last():
JuniorArticle.objects.create(junior=junior_instance, article=article_data, status=str(NUMBER['two']),
current_card_page=NUMBER['zero'], current_que_page=NUMBER['zero'])
if article_data:
question_query = ArticleSurvey.objects.filter(article=article_id)
for question in question_query:
if not JuniorArticlePoints.objects.filter(junior=junior_instance,
article=article_data,
question=question):
JuniorArticlePoints.objects.create(junior=junior_instance,
article=article_data,
question=question)
return custom_response(SUCCESS_CODE['3040'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class StartAssessmentAPIView(viewsets.ModelViewSet):
"""Junior Points viewset"""
serializer_class = StartAssessmentSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
article_id = self.request.GET.get('article_id')
# if referral_code:
article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
return article
def list(self, request, *args, **kwargs):
"""profile view"""
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, context={"user":request.user}, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CheckAnswerAPIView(viewsets.ModelViewSet):
"""Junior Points viewset"""
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
question_id = self.request.GET.get('question_id')
article = ArticleSurvey.objects.filter(id=question_id).last()
return article
def list(self, request, *args, **kwargs):
"""profile view"""
try:
answer_id = self.request.GET.get('answer_id')
current_page = self.request.GET.get('current_page')
queryset = self.get_queryset()
submit_ans = SurveyOption.objects.filter(id=answer_id, is_answer=True).last()
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
question=queryset)
if submit_ans:
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, is_answer_correct=True)
JuniorPoints.objects.filter(junior__auth=self.request.user).update(total_points=
F('total_points') + queryset.points)
else:
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, earn_points=0,
is_answer_correct=False)
JuniorArticle.objects.filter(junior__auth=self.request.user,
article=queryset.article).update(
current_que_page=int(current_page) + NUMBER['one'])
return custom_response(None, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CompleteArticleAPIView(views.APIView):
"""Remove junior API"""
permission_classes = [IsAuthenticated]
http_method_names = ('put', 'get',)
def put(self, request, format=None):
try:
article_id = self.request.data.get('article_id')
JuniorArticle.objects.filter(junior__auth=request.user, article__id=article_id).update(
is_completed=True, status=str(NUMBER['three'])
)
return custom_response(SUCCESS_CODE['3041'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
def get(self, request, *args, **kwargs):
""" junior list"""
try:
article_id = self.request.GET.get('article_id')
total_earn_points = JuniorArticlePoints.objects.filter(junior__auth=request.user,
article__id=article_id,
is_answer_correct=True).aggregate(
total_earn_points=Sum('earn_points'))['total_earn_points']
data = {"total_earn_points":total_earn_points}
return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ReadArticleCardAPIView(views.APIView):
"""Remove junior API"""
permission_classes = [IsAuthenticated]
http_method_names = ('put',)
def put(self, request, *args, **kwargs):
""" junior list"""
try:
junior_instance = Junior.objects.filter(auth=self.request.user).last()
article = self.request.data.get('article_id')
article_card = self.request.data.get('article_card')
current_page = self.request.data.get('current_page')
JuniorArticleCard.objects.filter(junior=junior_instance,
article__id=article,
article_card__id=article_card).update(is_read=True)
JuniorArticle.objects.filter(junior=junior_instance,
article__id=article).update(current_card_page=int(current_page)+NUMBER['one'])
return custom_response(SUCCESS_CODE['3043'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CreateArticleCardAPIView(viewsets.ModelViewSet):
"""Start article"""
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
junior_instance = Junior.objects.filter(auth=self.request.user).last()
article_id = request.data.get('article_id')
article_data = Article.objects.filter(id=article_id).last()
if article_data:
article_cards = ArticleCard.objects.filter(article=article_id)
for article_card in article_cards:
if not JuniorArticleCard.objects.filter(junior=junior_instance,
article=article_data,
article_card=article_card):
JuniorArticleCard.objects.create(junior=junior_instance,
article=article_data,
article_card=article_card)
return custom_response(None, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class RemoveGuardianCodeAPIView(views.APIView):
"""Update junior task API"""
serializer_class = RemoveGuardianCodeSerializer
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
junior_queryset = Junior.objects.filter(auth=self.request.user).last()
if junior_queryset:
# use RemoveGuardianCodeSerializer serializer
serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
# task in another state
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)

View File

@ -6,6 +6,7 @@ from rest_framework import serializers
# local imports
from notifications.utils import register_fcm_token
from notifications.models import Notification
class RegisterDevice(serializers.Serializer):
@ -26,3 +27,19 @@ class RegisterDevice(serializers.Serializer):
device_type = validated_data['type']
return register_fcm_token(self.context['user_id'], registration_id,
validated_data['device_id'], device_type)
class NotificationListSerializer(serializers.ModelSerializer):
"""List of notification"""
class Meta(object):
"""meta info"""
model = Notification
fields = ['id', 'data', 'is_read']
class ReadNotificationSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = Notification
fields = ('id',)

View File

@ -6,7 +6,7 @@ from django.urls import path, include
from rest_framework import routers
# local imports
from notifications.views import NotificationViewSet
from notifications.views import NotificationViewSet, ReadNotification
# initiate router
router = routers.SimpleRouter()
@ -15,4 +15,5 @@ router.register('notifications', NotificationViewSet, basename='notifications')
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/read-notification/', ReadNotification.as_view()),
]

View File

@ -3,24 +3,40 @@ notifications views file
"""
# django imports
from django.db.models import Q
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import viewsets, status, views
# local imports
from account.utils import custom_response
from base.messages import SUCCESS_CODE
from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE
from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification
from notifications.models import Notification
class NotificationViewSet(viewsets.GenericViewSet):
""" used to do the notification actions """
serializer_class = RegisterDevice
serializer_class = NotificationListSerializer
permission_classes = [IsAuthenticated, ]
def list(self, request, *args, **kwargs) -> Response:
""" list the notifications """
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id')
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
self.mark_notifications_as_read(serializer.data)
return custom_response(None, serializer.data)
@staticmethod
def mark_notifications_as_read(data):
""" used to mark notification queryset as read """
ids = [obj['id'] for obj in data]
Notification.objects.filter(id__in=ids).update(is_read=True)
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request):
"""
@ -40,3 +56,33 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
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',
serializer_class=NotificationListSerializer)
def notification_list(self, request):
"""
notification list
"""
try:
queryset = Notification.objects.filter(notification_to=request.user)
serializer = NotificationListSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ReadNotification(views.APIView):
"""Update notification API"""
serializer_class = ReadNotificationSerializer
model = Notification
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
notification_id = self.request.data.get('notification_id')
notification_queryset = Notification.objects.filter(id__in=notification_id,
notification_to=self.request.user).update(is_read=True)
if notification_queryset:
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.2 on 2023-08-01 07:35
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0003_defaultarticlecardimage_and_more'),
]
operations = [
migrations.AlterField(
model_name='surveyoption',
name='survey',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='web_admin.articlesurvey'),
),
]

View File

@ -49,14 +49,14 @@ class ArticleSurvey(models.Model):
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'
return f'{self.id} | {self.question}'
class SurveyOption(models.Model):
"""
Survey Options model
"""
survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='survey_options')
survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='options')
option = models.CharField(max_length=255)
is_answer = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
@ -64,7 +64,7 @@ class SurveyOption(models.Model):
def __str__(self):
"""Return title"""
return f'{self.id} | {self.survey}'
return f'{self.id} | {self.option}'
class DefaultArticleCardImage(models.Model):

13
web_admin/pagination.py Normal file
View File

@ -0,0 +1,13 @@
"""
web_admin pagination file
"""
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
"""
custom paginator class
"""
page_size = 10 # Set the desired page size
page_size_query_param = 'page_size'
max_page_size = 100 # Set a maximum page size if needed

View File

@ -0,0 +1,52 @@
"""
web_admin analytics serializer file
"""
from rest_framework import serializers
from junior.models import JuniorPoints, Junior
class JuniorLeaderboardSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = Junior
fields = ('id', 'name', 'first_name', 'last_name', 'is_active', 'image')
@staticmethod
def get_name(obj):
"""
:param obj: junior object
:return: full name
"""
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
@staticmethod
def get_first_name(obj):
"""
:param obj: junior object
:return: first name
"""
return obj.auth.first_name
@staticmethod
def get_last_name(obj):
"""
:param obj: junior object
:return: last name
"""
return obj.auth.last_name
class LeaderboardSerializer(serializers.ModelSerializer):
junior = JuniorLeaderboardSerializer()
rank = serializers.IntegerField()
class Meta:
model = JuniorPoints
fields = ('total_points', 'rank', 'junior')

View File

@ -11,8 +11,8 @@ from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE
from base.messages import ERROR_CODE
from guardian.utils import upload_image_to_alibaba
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
from web_admin.utils import pop_id
from web_admin.utils import pop_id, get_image_url
from junior.models import JuniorArticlePoints, JuniorArticle
USER = get_user_model()
@ -21,23 +21,20 @@ class ArticleCardSerializer(serializers.ModelSerializer):
Article Card serializer
"""
id = serializers.IntegerField(required=False)
image = serializers.FileField(required=False)
image_url = serializers.URLField(required=False)
image_name = serializers.CharField(required=False)
image_url = serializers.CharField(required=False)
class Meta:
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image', 'image_url')
fields = ('id', 'title', 'description', 'image_name', 'image_url')
def create(self, validated_data):
if 'image' in validated_data and validated_data['image'] is not None:
image = validated_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
validated_data['image_url'] = upload_image_to_alibaba(image, filename)
article_card = ArticleCard.objects.create(article_id='1', **validated_data)
validated_data['image_url'] = get_image_url(validated_data)
article = Article.objects.all().first()
article_card = ArticleCard.objects.create(article=article, **validated_data)
return article_card
@ -111,11 +108,7 @@ class ArticleSerializer(serializers.ModelSerializer):
for card in article_cards:
card = pop_id(card)
if 'image' in card and card['image'] is not None:
image = card.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card['image_url'] = upload_image_to_alibaba(image, filename)
card['image_url'] = get_image_url(card)
ArticleCard.objects.create(article=article, **card)
for survey in article_survey:
@ -139,40 +132,36 @@ class ArticleSerializer(serializers.ModelSerializer):
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description)
instance.save()
prev_card = list(ArticleCard.objects.filter(article=instance).values_list('id', flat=True))
# Update or create cards
for card_data in article_cards:
card_id = card_data.get('id', None)
if card_id:
prev_card.remove(card_id)
card = ArticleCard.objects.get(id=card_id, article=instance)
card.title = card_data.get('title', card.title)
card.description = card_data.get('description', card.description)
if 'image' in card_data and card_data['image'] is not None:
image = card_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card.image_url = upload_image_to_alibaba(image, filename)
card.image_url = get_image_url(card_data)
card.save()
else:
card_data = pop_id(card_data)
if 'image' in card_data and card_data['image'] is not None:
image = card_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card_data['image_url'] = upload_image_to_alibaba(image, filename)
card_data['image_url'] = get_image_url(card_data)
ArticleCard.objects.create(article=instance, **card_data)
ArticleCard.objects.filter(id__in=prev_card, article=instance).delete()
prev_survey = list(ArticleSurvey.objects.filter(article=instance).values_list('id', flat=True))
# Update or create survey sections
for survey_data in article_survey:
survey_id = survey_data.get('id', None)
options_data = survey_data.pop('options')
if survey_id:
prev_survey.remove(survey_id)
survey = ArticleSurvey.objects.get(id=survey_id, article=instance)
survey.question = survey_data.get('question', survey.question)
survey.save()
else:
survey_data = pop_id(survey_data)
survey = ArticleSurvey.objects.create(article=instance, **survey_data)
survey = ArticleSurvey.objects.create(article=instance, points=ARTICLE_SURVEY_POINTS, **survey_data)
# Update or create survey options
for option_data in options_data:
@ -185,6 +174,7 @@ class ArticleSerializer(serializers.ModelSerializer):
else:
option_data = pop_id(option_data)
SurveyOption.objects.create(survey=survey, **option_data)
ArticleSurvey.objects.filter(id__in=prev_survey, article=instance).delete()
return instance
@ -210,6 +200,9 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
"""
if 'image' not in attrs and attrs.get('image') is None:
raise serializers.ValidationError({'details': ERROR_CODE['2061']})
image = attrs.get('image')
if image and image.size == NUMBER['zero']:
raise serializers.ValidationError(ERROR_CODE['2035'])
return attrs
def create(self, validated_data):
@ -217,62 +210,124 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
to create and upload image
:return: card_image object
"""
image = validated_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
if image and image.size == NUMBER['zero']:
raise serializers.ValidationError(ERROR_CODE['2035'])
# upload image on ali baba
validated_data['image_url'] = upload_image_to_alibaba(image, filename)
validated_data['image_url'] = get_image_url(validated_data)
card_image = DefaultArticleCardImage.objects.create(**validated_data)
return card_image
class UserManagementListSerializer(serializers.ModelSerializer):
class ArticleListSerializer(serializers.ModelSerializer):
"""
user management serializer
serializer for article API
"""
name = serializers.SerializerMethodField()
phone_number = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
article_cards = ArticleCardSerializer(many=True)
article_survey = ArticleSurveySerializer(many=True)
total_points = serializers.SerializerMethodField('get_total_points')
is_completed = serializers.SerializerMethodField('get_is_completed')
class Meta:
"""
meta class
"""
model = USER
fields = ('name', 'email', 'phone_number', 'user_type', 'is_active')
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed')
@staticmethod
def get_name(obj):
"""
:param obj: user object
:return: full name
"""
return (obj.first_name + obj.last_name) if obj.last_name else obj.first_name
def get_total_points(self, obj):
"""total points of article"""
total_question = ArticleSurvey.objects.filter(article=obj).count()
return total_question * NUMBER['five']
@staticmethod
def get_phone_number(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := obj.guardian_profile.all().first():
return profile.phone
elif profile := obj.junior_profile.all().first():
return profile.phone
else:
return None
def get_is_completed(self, obj):
"""complete all question"""
junior_article = JuniorArticle.objects.filter(article=obj).last()
if junior_article:
return junior_article.is_completed
return False
@staticmethod
def get_user_type(obj):
class ArticleQuestionSerializer(serializers.ModelSerializer):
"""
article survey serializer
"""
id = serializers.IntegerField(required=False)
options = SurveyOptionSerializer(many=True)
is_attempt = serializers.SerializerMethodField('get_is_attempt')
correct_answer = serializers.SerializerMethodField('get_correct_answer')
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
def get_is_attempt(self, obj):
"""attempt question or not"""
context_data = self.context.get('user')
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, question=obj).last()
if junior_article_obj:
return junior_article_obj.is_attempt
return False
def get_correct_answer(self, obj):
"""attempt question or not"""
ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last()
if ans_obj:
return ans_obj.id
return str("None")
def get_attempted_answer(self, obj):
"""attempt question or not"""
context_data = self.context.get('user')
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data,
question=obj, is_answer_correct=True).last()
if junior_article_obj:
return junior_article_obj.submitted_answer.id
return None
class Meta:
"""
:param obj: user object
:return: user type
meta class
"""
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
model = ArticleSurvey
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
class StartAssessmentSerializer(serializers.ModelSerializer):
"""
serializer for article API
"""
article_survey = ArticleQuestionSerializer(many=True)
current_page = serializers.SerializerMethodField('get_current_page')
def get_current_page(self, obj):
"""current page"""
context_data = self.context.get('user')
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
if data:
return data.current_que_page
return NUMBER['zero']
class Meta:
"""
meta class
"""
model = Article
fields = ('article_survey', 'current_page')
class ArticleCardlistSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
id = serializers.IntegerField(required=False)
image_name = serializers.CharField(required=False)
image_url = serializers.CharField(required=False)
current_page = serializers.SerializerMethodField('get_current_page')
def get_current_page(self, obj):
"""current page"""
context_data = self.context.get('user')
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj.article).last()
if data:
return data.current_card_page
return NUMBER['zero']
class Meta:
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image_name', 'image_url', 'current_page')

View File

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

View File

@ -0,0 +1,298 @@
"""
web_admin user_management serializers file
"""
# django imports
from rest_framework import serializers
from django.contrib.auth import get_user_model
from base.constants import USER_TYPE
# local imports
from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.models import Guardian
from junior.models import Junior
USER = get_user_model()
class UserManagementListSerializer(serializers.ModelSerializer):
"""
user management serializer
"""
name = serializers.SerializerMethodField()
country_code = serializers.SerializerMethodField()
phone = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
is_active = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('id', 'name', 'email', 'country_code', 'phone', 'user_type', 'is_active')
@staticmethod
def get_name(obj):
"""
:param obj: user object
:return: full name
"""
return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name
@staticmethod
def get_country_code(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := obj.guardian_profile.all().first():
return profile.country_code if profile.country_code else None
elif profile := obj.junior_profile.all().first():
return profile.country_code if profile.country_code else None
else:
return None
@staticmethod
def get_phone(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := obj.guardian_profile.all().first():
return profile.phone if profile.phone else None
elif profile := obj.junior_profile.all().first():
return profile.phone if profile.phone else None
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')
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1')
else:
return None
@staticmethod
def get_is_active(obj):
"""
:param obj: user object
:return: user type
"""
if profile := obj.guardian_profile.all().first():
return profile.is_active
elif profile := obj.junior_profile.all().first():
return profile.is_active
else:
return obj.is_active
class GuardianSerializer(serializers.ModelSerializer):
"""
guardian serializer
"""
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
username = serializers.SerializerMethodField()
email = serializers.EmailField(required=False)
class Meta:
"""
meta class
"""
model = Guardian
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
'is_active', 'country_name', 'image', 'email')
def validate(self, attrs):
"""
to validate request data
:return: validated attrs
"""
email = attrs.get('email')
phone = attrs.get('phone')
if USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2003']})
if Guardian.objects.filter(phone=phone).exclude(user__id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2012']})
return attrs
def update(self, instance, validated_data):
"""
to update user and its related profile
:param instance: user's guardian object
:param validated_data:
:return: guardian object
"""
instance.user.email = self.validated_data.get('email', instance.user.email)
instance.user.username = self.validated_data.get('email', instance.user.username)
instance.user.save()
instance.country_code = validated_data.get('country_code', instance.country_code)
instance.phone = validated_data.get('phone', instance.phone)
instance.save()
return instance
@staticmethod
def get_name(obj):
"""
:param obj: guardian object
:return: full name
"""
return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name
@staticmethod
def get_first_name(obj):
"""
:param obj: guardian object
:return: first name
"""
return obj.user.first_name
@staticmethod
def get_last_name(obj):
"""
:param obj: guardian object
:return: last name
"""
return obj.user.last_name
@staticmethod
def get_username(obj):
"""
:param obj: guardian object
:return: email
"""
return obj.user.username
class JuniorSerializer(serializers.ModelSerializer):
"""
junior serializer
"""
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
username = serializers.SerializerMethodField()
email = serializers.EmailField(required=False)
class Meta:
"""
meta class
"""
model = Junior
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
'is_active', 'country_name', 'image', 'email')
def validate(self, attrs):
"""
to validate request data
:return: validated attrs
"""
email = attrs.get('email')
phone = attrs.get('phone')
if email and USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2003']})
if phone and Junior.objects.filter(phone=phone).exclude(auth__id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2012']})
return attrs
def update(self, instance, validated_data):
"""
:param instance: user's junior object
:param validated_data: validated data
:return: instance
"""
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.country_code = validated_data.get('country_code', instance.country_code)
instance.phone = validated_data.get('phone', instance.phone)
instance.save()
return instance
@staticmethod
def get_name(obj):
"""
:param obj: junior object
:return: full name
"""
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
@staticmethod
def get_first_name(obj):
"""
:param obj: junior object
:return: first name
"""
return obj.auth.first_name
@staticmethod
def get_last_name(obj):
"""
:param obj: junior object
:return: last name
"""
return obj.auth.last_name
@staticmethod
def get_username(obj):
"""
:param obj: junior object
:return: email
"""
return obj.auth.username
class UserManagementDetailSerializer(serializers.ModelSerializer):
"""
user management details serializer
"""
user_type = serializers.SerializerMethodField()
guardian_profile = GuardianSerializer(many=True)
junior_profile = JuniorSerializer(many=True)
associated_users = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users')
@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')
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1')
else:
return None
@staticmethod
def get_associated_users(obj):
"""
:param obj: user object
:return: associated user
"""
if profile := obj.guardian_profile.all().first():
if profile.guardian_code:
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True)
serializer = JuniorSerializer(junior, many=True)
return serializer.data
elif profile := obj.junior_profile.all().first():
if profile.guardian_code:
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True)
serializer = GuardianSerializer(guardian, many=True)
return serializer.data
else:
return None

View File

@ -6,15 +6,22 @@ from django.urls import path, include
from rest_framework import routers
# local imports
from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, UserManagementViewSet
from web_admin.views.analytics import AnalyticsViewSet
from web_admin.views.article import (ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet,
ArticleCardListViewSet)
from web_admin.views.auth import ForgotAndResetPasswordViewSet
from web_admin.views.user_management import UserManagementViewSet
# initiate router
router = routers.SimpleRouter()
router.register('article', ArticleViewSet, basename='article')
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
router.register('user', UserManagementViewSet, basename='user')
router.register('user-management', UserManagementViewSet, basename='user')
router.register('analytics', AnalyticsViewSet, basename='user-analytics')
router.register('article-list', ArticleListViewSet, basename='article-list')
router.register('article-card-list', ArticleCardListViewSet, basename='article-card-list')
# forgot and reset password api for admin
router.register('admin', ForgotAndResetPasswordViewSet, basename='admin')

View File

@ -1,6 +1,10 @@
"""
web_utils file
"""
import base64
from base.constants import ARTICLE_CARD_IMAGE_FOLDER
from guardian.utils import upload_image_to_alibaba
def pop_id(data):
@ -11,3 +15,28 @@ def pop_id(data):
"""
data.pop('id') if 'id' in data else data
return data
def get_image_url(data):
"""
to get image url
:param data:
:return: image url
"""
if 'image_url' in data and 'http' in data['image_url']:
if 'image_name' in data:
data.pop('image_name')
return data['image_url']
elif 'image_url' in data and type(data['image_url']) == str and data['image_url'].startswith('data:image'):
base64_image = base64.b64decode(data.get('image_url').split(',')[1])
image_name = f"{data['title']} {data.pop('image_name')}" if 'image_name' in data else data['title']
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(base64_image, filename)
return image_url
elif 'image' in data and data['image'] is not None:
image = data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
return image_url

View File

@ -0,0 +1,143 @@
"""
web_admin analytics view file
"""
# python imports
import datetime
# third party imports
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
# django imports
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 django.db.models import F, Window
from django.db.models.functions.window import Rank
# local imports
from account.utils import custom_response
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT
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
USER = get_user_model()
class AnalyticsViewSet(GenericViewSet):
"""
analytics api view
to get user report (active users, guardians and juniors counts)
to get new user sign up report
to get task report (completed, in-progress, requested and rejected tasks count)
to get junior leaderboard and ranking
"""
serializer_class = None
permission_classes = [IsAuthenticated, AdminPermission]
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: start_date: date format (yyyy-mm-dd)
:param request: 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'), DATE_FORMAT)
end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT)
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):
"""
api method to get new signups
:param request: start_date: date format (yyyy-mm-dd)
:param request: 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'), DATE_FORMAT)
end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT)
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):
"""
api method to get assign tasks count for (completed, in-progress, requested and rejected) task
:param request: start_date: date format (yyyy-mm-dd)
:param request: 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'), DATE_FORMAT)
end_date = datetime.datetime.strptime(request.query_params.get('end_date'), DATE_FORMAT)
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)
@action(methods=['get'], url_name='junior-leaderboard', url_path='junior-leaderboard', detail=False,
serializer_class=LeaderboardSerializer)
def junior_leaderboard(self, request):
"""
to get junior leaderboard and rank
:param request:
:return:
"""
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')
paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data)

View File

@ -4,7 +4,7 @@ web_admin views file
# django imports
from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework import status
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import get_user_model
@ -16,8 +16,8 @@ from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
from web_admin.permission import AdminPermission
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
DefaultArticleCardImageSerializer,
UserManagementListSerializer)
DefaultArticleCardImageSerializer, ArticleListSerializer,
ArticleCardlistSerializer)
USER = get_user_model()
@ -30,13 +30,13 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticated, AdminPermission]
queryset = Article
filter_backends = (OrderingFilter, SearchFilter,)
filter_backends = (SearchFilter,)
search_fields = ['title']
http_method_names = ['get', 'post', 'put', 'delete']
def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__survey_options'
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
queryset = self.filter_queryset(article)
return queryset
@ -77,10 +77,11 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
:return: list of article
"""
queryset = self.get_queryset()
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)
return custom_response(None, data=serializer.data, count=count)
def retrieve(self, request, *args, **kwargs):
"""
@ -109,7 +110,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
return custom_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_card', url_path='remove_card',
@action(methods=['get'], url_name='remove-card', url_path='remove-card',
detail=True)
def remove_article_card(self, request, *args, **kwargs):
"""
@ -125,7 +126,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
except AttributeError:
return custom_error_response(ERROR_CODE["2042"], response_status=status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_survey', url_path='remove_survey',
@action(methods=['get'], url_name='remove-survey', url_path='remove-survey',
detail=True)
def remove_article_survey(self, request, *args, **kwargs):
"""
@ -195,30 +196,53 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
return custom_response(None, data=serializer.data)
class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin):
class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
"""
api to manage (list, view, edit) user
article api
"""
serializer_class = UserManagementListSerializer
permission_classes = []
queryset = USER.objects.prefetch_related(
'guardian_profile', 'junior_profile')
serializer_class = ArticleListSerializer
permission_classes = [IsAuthenticated,]
queryset = Article
http_method_names = ['get',]
def get_queryset(self):
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
return self.queryset.filter(junior_profile__isnull=True)
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
return self.queryset.filter(guardian_profile__isnull=True)
else:
return self.queryset
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
queryset = self.filter_queryset(article)
return queryset
def list(self, request, *args, **kwargs):
"""
api method to list all the user
article list api method
:param request:
:return:
:param args:
:param kwargs:
:return: list of article
"""
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, 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):
"""Junior Points viewset"""
serializer_class = ArticleCardlistSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
"""get queryset"""
return ArticleCard.objects.filter(article=self.request.GET.get('article_id'))
def list(self, request, *args, **kwargs):
"""profile view"""
try:
queryset = self.get_queryset()
# article card list
serializer = ArticleCardlistSerializer(queryset, context={"user": self.request.user}, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)

View File

@ -33,7 +33,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet):
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3015'])
return custom_error_response(ERROR_CODE['2063'], status.HTTP_400_BAD_REQUEST)
return custom_error_response(ERROR_CODE['2000'], status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='verify-otp', url_path='verify-otp',
detail=False, serializer_class=AdminVerifyOTPSerializer)
@ -45,7 +45,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
return custom_response(SUCCESS_CODE['3011'])
return custom_error_response(ERROR_CODE['2063'], status.HTTP_400_BAD_REQUEST)
return custom_error_response(ERROR_CODE['2008'], status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='create-password', url_path='create-password',
detail=False, serializer_class=AdminCreatePasswordSerializer)
@ -59,5 +59,5 @@ class ForgotAndResetPasswordViewSet(GenericViewSet):
user = USER.objects.filter(email=serializer.validated_data.get('email')).first()
user.set_password(serializer.validated_data.get('new_password'))
user.save()
return custom_response(SUCCESS_CODE['3007'])
return custom_response(SUCCESS_CODE['3006'])
return custom_error_response(ERROR_CODE['2064'], status.HTTP_400_BAD_REQUEST)

View File

@ -0,0 +1,126 @@
"""
web_admin user_management views file
"""
# django imports
from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import get_user_model
from django.db.models import Q
# local imports
from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE
from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.permission import AdminPermission
from web_admin.serializers.user_management_serializer import (UserManagementListSerializer,
UserManagementDetailSerializer, GuardianSerializer,
JuniorSerializer)
USER = get_user_model()
class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
mixins.RetrieveModelMixin, mixins.UpdateModelMixin):
"""
api to manage (list, view, edit) user
"""
serializer_class = UserManagementListSerializer
permission_classes = [IsAuthenticated, AdminPermission]
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']
def get_queryset(self):
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
queryset = self.queryset.filter(junior_profile__isnull=True)
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
queryset = self.queryset.filter(guardian_profile__isnull=True)
else:
queryset = self.queryset
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
"""
api method to list all the user
:param request: user_type {'guardian' for Guardian list, 'junior' for Junior list}
:return:
"""
queryset = self.get_queryset()
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, count=queryset.count())
def retrieve(self, request, *args, **kwargs):
"""
to get details of single user
:param request: user_id along with
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: user details
"""
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)
return custom_response(None, data=serializer.data)
def partial_update(self, request, *args, **kwargs):
"""
api method to update user detail (email, phone number)
:param request: user_id along with
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: success message
"""
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(),
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(),
request.data, context={'user_id': kwargs['pk']})
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE['3037'])
@action(methods=['get'], url_name='change-status', url_path='change-status', detail=True)
def change_status(self, request, *args, **kwargs):
"""
api to change user status (mark as active or inactive)
:param request: user_id along with
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: success message
"""
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()
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()
obj = user_obj.junior_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()
return custom_response(SUCCESS_CODE['3038'])

View File

@ -177,6 +177,31 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
LOGGING = {
"version": 1,
"filters": {
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue"
}
},
"handlers": {
"console": {
"level": "DEBUG",
"filters": [
"require_debug_true"
],
"class": "logging.StreamHandler"
}
},
"loggers": {
"django.db.backends": {
"level": "DEBUG",
"handlers": [
"console"
]
}
}
}
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/