Compare commits

...
72 Commits
Author SHA1 Message Date
jain 65d0932893 sonar issues 2023-09-04 16:52:50 +05:30
jain 9b14eedb18 handle scenerio for task after disassociate 2023-09-04 16:49:18 +05:30
jain 116fb00358 optimized task status code 2023-09-04 15:22:15 +05:30
jain 20fa6e43da changes in task list API and add special character in password for added junior 2023-09-04 15:02:47 +05:30
jain 7b75a3233c add current page and total page in task list API 2023-09-04 12:52:04 +05:30
jain 4f79a690c1 unrestrict logout and refresh token api while login in multiple device 2023-09-01 16:25:05 +05:30
jain 0af2a35206 task assign to multiple junior 2023-09-01 12:14:15 +05:30
jain d24f075110 answer api 2023-08-29 21:28:36 +05:30
jain dc12b35842 answer api 2023-08-29 19:38:14 +05:30
jain 3ad29e677d article page 2023-08-29 18:56:51 +05:30
jain 8b0a5d9a8e otp expiry 2023-08-29 18:33:47 +05:30
jain d4008d6cc2 elif for guardian code 2023-08-29 17:50:58 +05:30
jain a8d291474a search junior task 2023-08-29 11:52:47 +05:30
jain e6482167ae Merge branch 'sprint6-bugs' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-29 11:51:11 +05:30
jain 9a932d31b5 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-29 11:50:44 +05:30
jain e2c84eb83d search junior task 2023-08-29 11:49:59 +05:30
Ankitajain-kiwi 5bfc3966b3 Merge branch 'dev' into sprint6-bugs 2023-08-29 10:43:34 +05:30
jain 37d191eef8 add message for blank search 2023-08-29 10:41:44 +05:30
Abu Talib e9ee8ec8b2 Merge pull request #285 from KiwiTechLLC/sprint6-bugs
condition in add junior
2023-08-28 20:14:43 +05:30
jain cc5ecc0647 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-28 20:10:36 +05:30
jain 63dfd731fc condition in add junior 2023-08-28 20:09:39 +05:30
dilipshrivastwa-kiwi e9d4d7091e Merge pull request #284 from KiwiTechLLC/ZBKBCK-48
added notification for existing junior add, modified leaderboard meth…
2023-08-28 19:51:20 +05:30
abutalib-kiwi 219bae792e added notification for existing junior add, modified leaderboard method at every places 2023-08-28 19:39:39 +05:30
dilipshrivastwa-kiwi df3cab99a5 Merge pull request #283 from KiwiTechLLC/sprint6-bugs
remove guardian request and resend otp
2023-08-28 18:58:23 +05:30
jain d2242d4c64 remove guardian request and resend otp 2023-08-28 18:39:29 +05:30
dilipshrivastwa-kiwi b7d5916f8e Merge pull request #282 from KiwiTechLLC/ZBKBCK-48
fixed add junior multiple device notification issue, changed send mul…
2023-08-28 15:56:43 +05:30
abutalib-kiwi 5e17edcf3f fixed add junior multiple device notification issue, changed send multiple user notification method, modified add fcm token method 2023-08-28 15:42:30 +05:30
Ankitajain-kiwi b6fe943bfc Merge pull request #281 from KiwiTechLLC/sprint6-bugs
add non in delete API
2023-08-28 13:46:28 +05:30
jain 8e529b292d add non in delete API 2023-08-28 13:38:35 +05:30
Ankitajain-kiwi 61ca2e7d2d Merge pull request #279 from KiwiTechLLC/sprint6-bugs
change message
2023-08-28 12:53:24 +05:30
jain 966f94eb1e change message 2023-08-28 12:41:29 +05:30
dilipshrivastwa-kiwi f424c99478 Merge pull request #278 from KiwiTechLLC/sprint6-bugs
guardian_status update
2023-08-28 12:13:10 +05:30
jain 34359360e0 guardian_status update 2023-08-28 12:10:13 +05:30
dilipshrivastwa-kiwi 685a822e52 Merge pull request #277 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-08-28 10:48:48 +05:30
jain e57344e54f Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-28 10:38:14 +05:30
jain 235fc9baac guardian code status in junior list API 2023-08-28 10:37:52 +05:30
dilipshrivastwa-kiwi 3dc8268b07 Merge pull request #276 from KiwiTechLLC/ZBKBCK-48
fixed approve junior notification issue
2023-08-26 23:00:39 +05:30
abutalib-kiwi 8be8d56036 fixed approve junior notification issue 2023-08-26 21:36:01 +05:30
dilipshrivastwa-kiwi c79a891c89 Merge pull request #275 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-08-26 18:24:09 +05:30
jain eb4e09964d changes of guardian code status in necessary API 2023-08-26 17:48:13 +05:30
jain cf9376663c list of guardian code status 2023-08-26 17:09:10 +05:30
dilipshrivastwa-kiwi 9891060ee3 Merge pull request #273 from KiwiTechLLC/ZBKBCK-48
added notification for getting points after reading article
2023-08-25 20:10:46 +05:30
abutalib-kiwi 5b2ab2275e added notification for getting points after reading article 2023-08-25 19:45:24 +05:30
dilipshrivastwa-kiwi 9b6a84c7d1 Merge pull request #270 from KiwiTechLLC/sprint5
Sprint5
2023-08-25 18:02:13 +05:30
dilipshrivastwa-kiwi e9d4fbe373 Merge pull request #271 from KiwiTechLLC/ZBKBCK-48
added notification when admin adds a new article
2023-08-25 17:12:27 +05:30
abutalib-kiwi 21b92f8c74 added notification when admin adds a new article 2023-08-25 17:05:01 +05:30
jain c46b2ef52c sonar fixes 2023-08-25 16:59:17 +05:30
jain c47f6222d9 requested task not expired 2023-08-25 16:36:58 +05:30
Ankitajain-kiwi b82902081f Merge pull request #268 from KiwiTechLLC/sprint5
remove code
2023-08-25 12:58:09 +05:30
jain e9315beab9 remove code 2023-08-25 12:57:15 +05:30
dilipshrivastwa-kiwi 3a938720dd Merge pull request #266 from KiwiTechLLC/ZBKBCK-48
added notification for top leaderboard junior, added related celery t…
2023-08-25 12:30:01 +05:30
abutalib-kiwi 464899f7d3 added notification for top leaderboard junior, added related celery task, created method to send notification to multiple users 2023-08-25 12:26:38 +05:30
dilipshrivastwa-kiwi 8a436bb79f Merge pull request #265 from KiwiTechLLC/ZBKBCK-48
modified top-list api, junior-list api and junior-points api, nodifie…
2023-08-24 20:08:07 +05:30
abutalib-kiwi 2e0ceb8c92 modified top-list api, junior-list api and junior-points api, nodified rank method, added mail for deactivating user from admin 2023-08-24 19:34:59 +05:30
Ankitajain-kiwi 1a2fd2d3a8 Merge pull request #263 from KiwiTechLLC/sprint5
assessment answer
2023-08-24 18:59:38 +05:30
jain a65eb2f77d assessment answer 2023-08-24 18:58:31 +05:30
Ankitajain-kiwi 3d06139c4f Merge pull request #262 from KiwiTechLLC/sprint5
assement id
2023-08-24 18:27:17 +05:30
jain f5a03e2fdf assement id 2023-08-24 18:26:15 +05:30
dilipshrivastwa-kiwi 226ddfa7e6 Merge pull request #261 from KiwiTechLLC/sprint5
article-list optimization
2023-08-24 18:05:32 +05:30
jain 1028822908 article-list optimization 2023-08-24 18:00:14 +05:30
dilipshrivastwa-kiwi 342f27fc62 Merge pull request #260 from KiwiTechLLC/sprint5
limit for 3 guardian code and article list API optimization
2023-08-24 16:15:30 +05:30
jain e9aa2dfda9 limit for 3 guardian code and article list API optimization 2023-08-24 16:08:20 +05:30
dilipshrivastwa-kiwi 624e7a4edb Merge pull request #258 from KiwiTechLLC/ZBKBCK-47
added optional name as user
2023-08-24 14:56:52 +05:30
dilipshrivastwa-kiwi 339c49577e Merge pull request #257 from KiwiTechLLC/sprint5
Sprint5
2023-08-24 14:10:17 +05:30
dilipshrivastwa-kiwi 09f006eb13 Merge pull request #256 from KiwiTechLLC/ZBKBCK-47
added optional user as name
2023-08-24 13:41:50 +05:30
jain ab1a2be679 remove junior by guardian 2023-08-24 13:32:08 +05:30
jain 11605540d7 remove guardian code request 2023-08-24 13:18:23 +05:30
jain 8cd4864748 add three guardian at a time 2023-08-24 13:13:25 +05:30
dilipshrivastwa-kiwi ad0b5bfc75 Merge pull request #255 from KiwiTechLLC/ZBKBCK-47
changed notification method, added celery task to notify expiring task
2023-08-24 12:29:25 +05:30
jain b1b7c42438 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint5 2023-08-24 11:27:13 +05:30
jain 8f9450b743 set guardian code is None 2023-08-24 11:10:20 +05:30
jain bb3441e4ed remove guardian code in both case 2023-08-23 19:23:31 +05:30
30 changed files with 630 additions and 289 deletions
+15 -10
View File
@@ -17,6 +17,8 @@ from guardian.models import Guardian
# multiple devices only # multiple devices only
# user can login in single # user can login in single
# device at a time""" # device at a time"""
# force update
# use 308 status code for force update
def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND): def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND):
"""custom response""" """custom response"""
@@ -43,9 +45,14 @@ class CustomMiddleware(object):
device_type = str(request.META.get('HTTP_TYPE')) device_type = str(request.META.get('HTTP_TYPE'))
api_endpoint = request.path api_endpoint = request.path
unrestricted_api = ('/api/v1/user/login/', '/api/v1/logout/', '/api/v1/generate-token/')
if request.user.is_authenticated: if request.user.is_authenticated:
# device details # device details
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() if device_id:
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if not device_details and api_endpoint not in unrestricted_api:
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error)
if user_type and str(user_type) == str(NUMBER['one']): if user_type and str(user_type) == str(NUMBER['one']):
junior = Junior.objects.filter(auth=request.user, is_active=False).last() junior = Junior.objects.filter(auth=request.user, is_active=False).last()
if junior: if junior:
@@ -56,13 +63,11 @@ class CustomMiddleware(object):
if guardian: if guardian:
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND) custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error) response = custom_response(custom_error)
if device_id and not device_details and api_endpoint != '/api/v1/user/login/':
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) if version and device_type:
response = custom_response(custom_error) force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last()
force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last() if not force_update:
api_endpoint_checks = not any(endpoint in api_endpoint for endpoint in ['/admin/', '/api/v1/admin/']) custom_error = custom_error_response(ERROR_CODE['2079'],
if not force_update and version and device_type: response_status=status.HTTP_308_PERMANENT_REDIRECT)
custom_error = custom_error_response(ERROR_CODE['2079'], response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT)
response_status=status.HTTP_308_PERMANENT_REDIRECT)
response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT)
return response return response
+3 -2
View File
@@ -123,7 +123,7 @@ class ChangePasswordSerializer(serializers.Serializer):
def create(self, validated_data): def create(self, validated_data):
""" """
change password
""" """
new_password = validated_data.pop('new_password') new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password') current_password = validated_data.pop('current_password')
@@ -392,7 +392,8 @@ class UserPhoneOtpSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class ForceUpdateSerializer(serializers.ModelSerializer): class ForceUpdateSerializer(serializers.ModelSerializer):
# ForceUpdate Serializer """ ForceUpdate Serializer
"""
class Meta(object): class Meta(object):
""" meta info """ """ meta info """
@@ -1,7 +1,7 @@
{% extends "templated_email/email_base.email" %} {% extends "templated_email/email_base.email" %}
{% block subject %} {% block subject %}
{{subject}} Support Mail
{% endblock %} {% endblock %}
{% block plain %} {% block plain %}
@@ -0,0 +1,22 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Account Deactivated
{% endblock %}
{% block plain %}
<tr>
<td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi User,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
Your account has been deactivated by admin. Please reach out to the admin for assistance.
</p>
</td>
</tr>
{% endblock %}
+40 -7
View File
@@ -1,6 +1,6 @@
"""Account utils""" """Account utils"""
from celery import shared_task from celery import shared_task
import random
"""Import django""" """Import django"""
from django.conf import settings from django.conf import settings
from rest_framework import viewsets, status from rest_framework import viewsets, status
@@ -93,8 +93,8 @@ def junior_account_update(user_tb):
# Update junior account # Update junior account
junior_data.is_active = False junior_data.is_active = False
junior_data.is_verified = False junior_data.is_verified = False
junior_data.guardian_code = '{}' junior_data.guardian_code = None
junior_data.guardian_code_status = str(NUMBER['one']) junior_data.guardian_code_status = None
junior_data.is_deleted = True junior_data.is_deleted = True
junior_data.save() junior_data.save()
JuniorPoints.objects.filter(junior=junior_data).delete() JuniorPoints.objects.filter(junior=junior_data).delete()
@@ -167,7 +167,7 @@ def user_device_details(user, device_id):
return False return False
def send_support_email(name, sender, subject, message): def send_support_email(name, sender, message):
"""Send otp on email with template""" """Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS] to_email = [settings.EMAIL_FROM_ADDRESS]
from_email = settings.DEFAULT_ADDRESS from_email = settings.DEFAULT_ADDRESS
@@ -179,19 +179,19 @@ def send_support_email(name, sender, subject, message):
context={ context={
'name': name.title(), 'name': name.title(),
'sender': sender, 'sender': sender,
'subject': subject,
'message': message 'message': message
} }
) )
return name return name
def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None): def custom_response(detail, data=None, total_pages=None, current_page=None, response_status=status.HTTP_200_OK, count=None):
"""Custom response code""" """Custom response code"""
if not data: if not data:
"""when data is none""" """when data is none"""
data = None data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count}) return Response({"data": data, "message": detail, "total_pages":total_pages, "current_page":current_page,
"status": "success", "code": response_status, "count": count})
def custom_error_response(detail, response_status): def custom_error_response(detail, response_status):
@@ -288,3 +288,36 @@ def get_user_full_name(user_obj):
to get user's full name to get user's full name
""" """
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User" return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User"
def make_special_password(length=10):
# Define character sets
lowercase_letters = string.ascii_lowercase
uppercase_letters = string.ascii_uppercase
digits = string.digits
special_characters = '!@#$%^&*()_-+=<>?/[]{}|'
# Combine character sets
all_characters = lowercase_letters + uppercase_letters + digits + special_characters
# Create a password with random characters
password = (
random.choice(lowercase_letters) +
random.choice(uppercase_letters) +
random.choice(digits) +
random.choice(special_characters) +
''.join(random.choice(all_characters) for _ in range(length - 4))
)
# Shuffle the characters to make it more random
password_list = list(password)
random.shuffle(password_list)
return ''.join(password_list)
def task_status_fun(status_value):
"""task status"""
task_status_value = ['1']
if str(status_value) == '2':
task_status_value = ['2', '4']
elif str(status_value) == '3':
task_status_value = ['3', '5', '6']
return task_status_value
+3 -5
View File
@@ -322,7 +322,7 @@ class ForgotPasswordAPIView(views.APIView):
send_all_email.delay( send_all_email.delay(
'email_reset_verification.email', email, verification_code 'email_reset_verification.email', email, verification_code
) )
expiry = OTP_EXPIRY expiry = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.get_or_create( user_data, created = UserEmailOtp.objects.get_or_create(
email=email email=email
) )
@@ -546,7 +546,6 @@ class UserEmailVerification(viewsets.ModelViewSet):
class ReSendEmailOtp(viewsets.ModelViewSet): class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
serializer_class = EmailVerificationSerializer serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',) http_method_names = ('post',)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""Param """Param
@@ -690,11 +689,10 @@ class SendSupportEmail(views.APIView):
def post(self, request): def post(self, request):
name = request.data.get('name') name = request.data.get('name')
sender = request.data.get('email') sender = request.data.get('email')
subject = request.data.get('subject')
message = request.data.get('message') message = request.data.get('message')
if name and sender and subject and message: if name and sender and message:
try: try:
send_support_email(name, sender, subject, message) send_support_email(name, sender, message)
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
+2 -1
View File
@@ -43,7 +43,8 @@ FILE_SIZE = 5 * 1024 * 1024
# String constant for configurable date for allocation lock period # String constant for configurable date for allocation lock period
ALLOCATION_LOCK_DATE = 1 ALLOCATION_LOCK_DATE = 1
# guardian code status tuple
guardian_code_tuple = ('1','3')
"""user type""" """user type"""
USER_TYPE = ( USER_TYPE = (
('1', 'junior'), ('1', 'junior'),
+11 -3
View File
@@ -101,12 +101,20 @@ ERROR_CODE = {
"2072": "You can not approve or reject this task because junior does not exist in the system", "2072": "You can not approve or reject this task because junior does not exist in the system",
"2073": "You can not approve or reject this junior because junior does not exist in the system", "2073": "You can not approve or reject this junior because junior does not exist in the system",
"2074": "You can not complete this task because you does not exist in the system", "2074": "You can not complete this task because you does not exist in the system",
# deactivate account
"2075": "Your account is deactivated. Please contact with admin", "2075": "Your account is deactivated. Please contact with admin",
"2076": "This junior already associate with you", "2076": "This junior already associate with you",
"2077": "You can not add guardian", "2077": "You can not add guardian",
"2078": "This junior is not associate with you", "2078": "This junior is not associate with you",
# force update
"2079": "Please update your app version for enjoying uninterrupted services", "2079": "Please update your app version for enjoying uninterrupted services",
"2080": "Can not add App version" "2080": "Can not add App version",
"2081": "A junior can only be associated with a maximum of 3 guardian",
# guardian code not exist
"2082": "Guardian code does not exist",
"2083": "You can not start this task because guardian is not associate with you",
"2084": "You can not complete this task because guardian is not associate with you",
"2085": "You can not take action on this task because junior is not associate with you"
} }
"""Success message code""" """Success message code"""
@@ -142,8 +150,8 @@ SUCCESS_CODE = {
"3018": "Task created successfully", "3018": "Task created successfully",
"3019": "Support Email sent successfully", "3019": "Support Email sent successfully",
"3020": "Logged out successfully.", "3020": "Logged out successfully.",
"3021": "Add junior successfully", "3021": "Added junior successfully",
"3022": "Remove junior successfully", "3022": "Removed junior successfully",
"3023": "Junior is approved successfully", "3023": "Junior is approved successfully",
"3024": "Junior request is rejected successfully", "3024": "Junior request is rejected successfully",
"3025": "Task is approved successfully", "3025": "Task is approved successfully",
+33 -24
View File
@@ -9,36 +9,20 @@ from templated_email import send_templated_mail
# django imports # django imports
from django.conf import settings from django.conf import settings
from django.db.models import F, Window
from django.db.models.functions.window import Rank
# local imports # local imports
from base.constants import PENDING, IN_PROGRESS, JUNIOR from base.constants import PENDING, IN_PROGRESS, JUNIOR
from guardian.models import JuniorTask from guardian.models import JuniorTask
from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING from junior.models import JuniorPoints
from notifications.utils import send_notification from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING, NOTIFICATION_DICT, TOP_JUNIOR
from notifications.models import Notification
from notifications.utils import send_notification, get_from_user_details, send_notification_multiple_user
@shared_task @shared_task
def send_email_otp(email, verification_code): def send_email(recipient_list, template, context: dict = None):
"""
used to send otp on email
:param email: e-mail
:param verification_code: otp
"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
)
return True
@shared_task
def send_mail(recipient_list, template, context: dict = None):
""" """
used to send otp on email used to send otp on email
:param context: :param context:
@@ -48,7 +32,6 @@ def send_mail(recipient_list, template, context: dict = None):
if context is None: if context is None:
context = {} context = {}
from_email = settings.EMAIL_FROM_ADDRESS from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = recipient_list
send_templated_mail( send_templated_mail(
template_name=template, template_name=template,
from_email=from_email, from_email=from_email,
@@ -77,3 +60,29 @@ def notify_task_expiry():
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id, send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id,
{'task_id': task.id}) {'task_id': task.id})
return True return True
@shared_task()
def notify_top_junior():
"""
task to send notification for top leaderboard junior to all junior's
:return:
"""
junior_points_qs = JuniorPoints.objects.filter(
junior__is_verified=True
).select_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')
prev_top_position = junior_points_qs.filter(position=1).first()
new_top_position = junior_points_qs.filter(rank=1).first()
if prev_top_position != new_top_position:
send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR,
{'points': new_top_position.total_points})
for junior_point in junior_points_qs:
junior_point.position = junior_point.rank
junior_point.save()
return True
Binary file not shown.
+35 -10
View File
@@ -36,6 +36,7 @@ from django.utils.translation import gettext as _
# In this serializer file # In this serializer file
# define user serializer, # define user serializer,
# define password validation
# create guardian serializer, # create guardian serializer,
# task serializer, # task serializer,
# guardian serializer, # guardian serializer,
@@ -47,6 +48,7 @@ from django.utils.translation import gettext as _
from rest_framework import serializers from rest_framework import serializers
class PasswordValidator: class PasswordValidator:
"""Password validation"""
def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True): def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True):
self.min_length = min_length self.min_length = min_length
self.max_length = max_length self.max_length = max_length
@@ -57,6 +59,7 @@ class PasswordValidator:
self.enforce_password_policy(value) self.enforce_password_policy(value)
def enforce_password_policy(self, password): def enforce_password_policy(self, password):
# add validation for password
special_characters = "!@#$%^&*()_-+=<>?/[]{}|" special_characters = "!@#$%^&*()_-+=<>?/[]{}|"
if len(password) < self.min_length: if len(password) < self.min_length:
raise serializers.ValidationError( raise serializers.ValidationError(
@@ -64,16 +67,20 @@ class PasswordValidator:
) )
if self.max_length is not None and len(password) > self.max_length: if self.max_length is not None and len(password) > self.max_length:
# must be 8 character
raise serializers.ValidationError( raise serializers.ValidationError(
_("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length} _("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length}
) )
if self.require_uppercase and not any(char.isupper() for char in password): if self.require_uppercase and not any(char.isupper() for char in password):
# must contain upper case letter
raise serializers.ValidationError(_("Password must contain at least one uppercase letter.")) raise serializers.ValidationError(_("Password must contain at least one uppercase letter."))
if self.require_numbers and not any(char.isdigit() for char in password): if self.require_numbers and not any(char.isdigit() for char in password):
# must contain digit
raise serializers.ValidationError(_("Password must contain at least one digit.")) raise serializers.ValidationError(_("Password must contain at least one digit."))
if self.require_numbers and not any(char in special_characters for char in password): if self.require_numbers and not any(char in special_characters for char in password):
# must contain special character
raise serializers.ValidationError(_("Password must contain at least one special character.")) raise serializers.ValidationError(_("Password must contain at least one special character."))
@@ -211,7 +218,7 @@ class TaskSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = JuniorTask model = JuniorTask
fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image'] fields = ['id', 'task_name','task_description','points', 'due_date','default_image']
def validate_due_date(self, value): def validate_due_date(self, value):
"""validation on due date""" """validation on due date"""
@@ -222,11 +229,21 @@ class TaskSerializer(serializers.ModelSerializer):
return value return value
def create(self, validated_data): def create(self, validated_data):
"""create default task image data""" """create default task image data"""
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last() guardian = Guardian.objects.filter(user=self.context['user']).last()
# update image of the task # update image of the task
images = self.context['image'] images = self.context['image']
validated_data['default_image'] = images junior_ids = self.context['junior_data']
instance = JuniorTask.objects.create(**validated_data) junior_data = junior_ids[0].split(',')
tasks_created = []
for junior_id in junior_data:
# create task
task_data = validated_data.copy()
task_data['guardian'] = guardian
task_data['default_image'] = images
task_data['junior'] = Junior.objects.filter(id=junior_id).last()
instance = JuniorTask.objects.create(**task_data)
tasks_created.append(instance)
return instance return instance
class GuardianDetailSerializer(serializers.ModelSerializer): class GuardianDetailSerializer(serializers.ModelSerializer):
@@ -316,10 +333,11 @@ class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
'requested_on', 'rejected_on', 'completed_on', 'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at'] 'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer): class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer""" """Top junior serializer"""
junior = JuniorDetailSerializer() junior = JuniorDetailSerializer()
position = serializers.IntegerField() position = serializers.SerializerMethodField()
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
@@ -329,9 +347,13 @@ class TopJuniorSerializer(serializers.ModelSerializer):
def to_representation(self, instance): def to_representation(self, instance):
"""Convert instance to representation""" """Convert instance to representation"""
representation = super().to_representation(instance) representation = super().to_representation(instance)
representation['position'] = instance.position
return representation return representation
@staticmethod
def get_position(obj):
""" get position/rank """
return obj.rank
class GuardianProfileSerializer(serializers.ModelSerializer): class GuardianProfileSerializer(serializers.ModelSerializer):
"""junior serializer""" """junior serializer"""
@@ -390,9 +412,9 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
"""update guardian code""" """update guardian code"""
instance = self.context['junior'] instance = self.context['junior']
instance.guardian_code = [self.context['guardian_code']] guardian_code = self.context['guardian_code']
instance.guardian_code_approved = True index = instance.guardian_code.index(guardian_code)
instance.guardian_code_status = str(NUMBER['two']) instance.guardian_code_status[index] = str(NUMBER['two'])
instance.save() instance.save()
return instance return instance
@@ -500,4 +522,7 @@ class GuardianDetailListSerializer(serializers.ModelSerializer):
def get_guardian_code_status(self,obj): def get_guardian_code_status(self,obj):
"""guardian code status""" """guardian code status"""
return obj.junior.guardian_code_status if obj.guardian.guardian_code in obj.junior.guardian_code:
index = obj.junior.guardian_code.index(obj.guardian.guardian_code)
data = obj.junior.guardian_code_status[index]
return data
+1 -1
View File
@@ -127,7 +127,7 @@ def update_expired_task_status(data=None):
Update task of the status if due date is in past Update task of the status if due date is in past
""" """
try: try:
task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])] task_status = [str(NUMBER['one']), str(NUMBER['two'])]
JuniorTask.objects.filter(due_date__lt=datetime.today().date(), JuniorTask.objects.filter(due_date__lt=datetime.today().date(),
task_status__in=task_status).update(task_status=str(NUMBER['six'])) task_status__in=task_status).update(task_status=str(NUMBER['six']))
except ObjectDoesNotExist as e: except ObjectDoesNotExist as e:
+84 -49
View File
@@ -1,4 +1,5 @@
"""Views of Guardian""" """Views of Guardian"""
import math
# django imports # django imports
# Import IsAuthenticated # Import IsAuthenticated
@@ -6,11 +7,13 @@
# Import PageNumberPagination # Import PageNumberPagination
# Import User # Import User
# Import timezone # Import timezone
from django.db.models import F, Window
from django.db.models.functions.window import Rank
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status from rest_framework import viewsets, status
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User from django.contrib.auth.models import User
from base.constants import guardian_code_tuple
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from django.utils import timezone from django.utils import timezone
@@ -34,7 +37,7 @@ from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification, UserDeviceDetails from account.models import UserEmailOtp, UserNotification, UserDeviceDetails
from .tasks import generate_otp from .tasks import generate_otp
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email from account.utils import custom_response, custom_error_response, send_otp_email, task_status_fun
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
from .utils import upload_image_to_alibaba from .utils import upload_image_to_alibaba
@@ -133,7 +136,8 @@ class TaskListAPIView(viewsets.ModelViewSet):
Params Params
status status
search search
page""" page
junior"""
serializer_class = TaskDetailsSerializer serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,) filter_backends = (SearchFilter,)
@@ -152,15 +156,23 @@ class TaskListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""Create guardian profile""" """Create guardian profile"""
status_value = self.request.GET.get('status') status_value = self.request.GET.get('status')
current_page = self.request.GET.get('page')
junior = self.request.GET.get('junior')
queryset = self.get_queryset() queryset = self.get_queryset()
if status_value and status_value != '0': task_status = task_status_fun(status_value)
queryset = queryset.filter(task_status=status_value) if status_value and not junior:
queryset = queryset.filter(task_status__in=task_status)
elif status_value and junior:
queryset = queryset.filter(task_status__in=task_status,junior=int(junior))
paginator = self.pagination_class() paginator = self.pagination_class()
total_count = len(queryset)
total_pages = math.ceil(total_count/10)
# use Pagination # use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer # use TaskDetailsSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, total_pages=total_pages, current_page=current_page,
response_status=status.HTTP_200_OK)
class CreateTaskAPIView(viewsets.ModelViewSet): class CreateTaskAPIView(viewsets.ModelViewSet):
@@ -175,37 +187,46 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
""" """
try: try:
image = request.data['default_image'] image = request.data['default_image']
junior = request.data['junior'] juniors = request.data['junior'].split(',')
junior_id = Junior.objects.filter(id=junior).last() for junior in juniors:
guardian_data = Guardian.objects.filter(user=request.user).last() junior_id = Junior.objects.filter(id=junior).last()
if guardian_data.guardian_code not in junior_id.guardian_code: if junior_id:
return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST) guardian_data = Guardian.objects.filter(user=request.user).last()
allowed_extensions = ['.jpg', '.jpeg', '.png'] index = junior_id.guardian_code.index(guardian_data.guardian_code)
if not any(extension in str(image) for extension in allowed_extensions): status_index = junior_id.guardian_code_status[index]
return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST) if status_index == str(NUMBER['three']):
if not junior.isnumeric(): return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST)
"""junior value must be integer""" allowed_extensions = ['.jpg', '.jpeg', '.png']
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) if not any(extension in str(image) for extension in allowed_extensions):
data = request.data return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST)
if 'https' in str(image): if not junior.isnumeric():
image_data = image """junior value must be integer"""
else: return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image}" data = request.data
if image and image.size == NUMBER['zero']: if 'https' in str(image):
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST) image_data = image
image_url = upload_image_to_alibaba(image, filename) else:
image_data = image_url filename = f"images/{image}"
data.pop('default_image') if image and image.size == NUMBER['zero']:
# use TaskSerializer serializer return custom_error_response(ERROR_CODE['2035'],
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data) response_status=status.HTTP_400_BAD_REQUEST)
if serializer.is_valid(): image_url = upload_image_to_alibaba(image, filename)
# save serializer image_data = image_url
task = serializer.save() data.pop('default_image')
junior_data = data.pop('junior')
# use TaskSerializer serializer
serializer = TaskSerializer(context={"user":request.user, "image":image_data,
"junior_data":junior_data}, data=data)
if serializer.is_valid():
# save serializer
task = serializer.save()
send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN, send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN,
junior_id.auth.id, {'task_id': task.id}) junior_id.auth.id, {'task_id': task.id})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3018'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -241,7 +262,6 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
class TopJuniorListAPIView(viewsets.ModelViewSet): class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Top juniors list """Top juniors list
No Params""" No Params"""
queryset = JuniorPoints.objects.all()
serializer_class = TopJuniorSerializer serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
http_method_names = ('get',) http_method_names = ('get',)
@@ -252,15 +272,22 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
context.update({'view': self}) context.update({'view': self})
return context return context
def get_queryset(self):
queryset = JuniorPoints.objects.filter(
junior__is_verified=True
).select_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')
return queryset
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""Fetch junior list of those who complete their tasks""" """Fetch junior list of those who complete their tasks"""
try: try:
junior_total_points = self.get_queryset().order_by('-total_points') junior_total_points = self.get_queryset()[:15]
# Update the position field for each JuniorPoints object serializer = self.get_serializer(junior_total_points, many=True)
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
junior.save()
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -287,19 +314,25 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
if request.data['action'] == '1': if request.data['action'] == '1':
# use ApproveJuniorSerializer serializer # use ApproveJuniorSerializer serializer
serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code, serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code,
"junior": junior_queryset, "action": request.data['action']}, "junior": junior_queryset,
"action": request.data['action']},
data=request.data) data=request.data)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() serializer.save()
send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN, send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN,
junior_queryset.auth.id) junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else: else:
junior_queryset.guardian_code = None if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code):
junior_queryset.guardian_code_status = str(NUMBER['one']) junior_queryset.guardian_code.remove('-')
if junior_queryset.guardian_code_status and ('-' in junior_queryset.guardian_code_status):
junior_queryset.guardian_code_status.remove('-')
index = junior_queryset.guardian_code.index(guardian.guardian_code)
junior_queryset.guardian_code.remove(guardian.guardian_code)
junior_queryset.guardian_code_status.pop(index)
junior_queryset.save() junior_queryset.save()
send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id) send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -325,6 +358,8 @@ class ApproveTaskAPIView(viewsets.ModelViewSet):
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'), task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian, guardian=guardian,
junior=self.request.data.get('junior_id')).last() junior=self.request.data.get('junior_id')).last()
if task_queryset and guardian.guardian_code not in task_queryset.junior.guardian_code:
return custom_error_response(ERROR_CODE['2084'], response_status=status.HTTP_400_BAD_REQUEST)
if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active): if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active):
return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST)
# use ApproveJuniorSerializer serializer # use ApproveJuniorSerializer serializer
@@ -348,7 +383,7 @@ class ApproveTaskAPIView(viewsets.ModelViewSet):
return custom_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
#
class GuardianListAPIView(viewsets.ModelViewSet): class GuardianListAPIView(viewsets.ModelViewSet):
"""Guardian list of assosicated junior""" """Guardian list of assosicated junior"""
@@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2023-08-26 08:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('junior', '0029_junior_is_deleted'),
]
operations = [
migrations.RemoveField(
model_name='junior',
name='guardian_code_status',
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.2 on 2023-08-26 08:59
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0030_remove_junior_guardian_code_status'),
]
operations = [
migrations.AddField(
model_name='junior',
name='guardian_code_status',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None),
),
]
+3 -3
View File
@@ -76,9 +76,9 @@ class Junior(models.Model):
is_verified = models.BooleanField(default=False) is_verified = models.BooleanField(default=False)
"""guardian code is approved or not""" """guardian code is approved or not"""
guardian_code_approved = models.BooleanField(default=False) guardian_code_approved = models.BooleanField(default=False)
# guardian code status""" # # guardian code status"""
guardian_code_status = models.CharField(max_length=31, choices=GUARDIAN_CODE_STATUS, default='1', guardian_code_status = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None), null=True,
null=True, blank=True) )
# Profile created and updated time""" # Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
+58 -34
View File
@@ -11,7 +11,7 @@ from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
# local imports # local imports
from account.utils import send_otp_email, generate_code from account.utils import send_otp_email, generate_code, make_special_password
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ
from guardian.tasks import generate_otp from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
@@ -19,10 +19,10 @@ from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED
GUARDIAN_CODE_STATUS, JUNIOR) GUARDIAN_CODE_STATUS, JUNIOR)
from guardian.models import Guardian, JuniorTask from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail from junior.utils import junior_notification_email, junior_approval_mail, get_junior_leaderboard_rank
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification from notifications.utils import send_notification
from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION, from notifications.constants import (ASSOCIATE_REQUEST, ASSOCIATE_JUNIOR, TASK_ACTION,
) )
from web_admin.models import ArticleCard from web_admin.models import ArticleCard
@@ -88,18 +88,27 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
if junior: if junior:
"""update details according to the data get from request""" """update details according to the data get from request"""
junior.gender = validated_data.get('gender',junior.gender) junior.gender = validated_data.get('gender',junior.gender)
"""Update guardian code""" # Update guardian code"""
junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code) # condition for guardian code
"""condition for guardian code"""
if guardian_code: if guardian_code:
junior.guardian_code = guardian_code if junior.guardian_code and guardian_code:
if guardian_code[0] in junior.guardian_code:
raise serializers.ValidationError({"error":ERROR_CODE['2076'],"code":"400", "status":"failed"})
if not junior.guardian_code:
junior.guardian_code = []
junior.guardian_code_status = []
junior.guardian_code.extend(guardian_code)
junior.guardian_code_status.extend(str(NUMBER['three']))
elif len(junior.guardian_code) < 3 and len(guardian_code) < 3:
junior.guardian_code.extend(guardian_code)
junior.guardian_code_status.extend(str(NUMBER['three']))
else:
raise serializers.ValidationError({"error":ERROR_CODE['2081'],"code":"400", "status":"failed"})
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last() guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
if guardian_data: if guardian_data:
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior) JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
junior.guardian_code_status = str(NUMBER['three']) send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {})
junior_approval_mail.delay(user.email, user.first_name) junior_approval_mail.delay(user.email, user.first_name)
send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {})
junior.dob = validated_data.get('dob', junior.dob) junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode) junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name) junior.country_name = validated_data.get('country_name', junior.country_name)
@@ -164,6 +173,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
rejected_task = serializers.SerializerMethodField('get_rejected_task') rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task') pending_task = serializers.SerializerMethodField('get_pending_task')
position = serializers.SerializerMethodField('get_position') position = serializers.SerializerMethodField('get_position')
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
def get_auth(self, obj): def get_auth(self, obj):
@@ -180,9 +190,8 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
return data return data
def get_position(self, obj): def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj).last() return get_junior_leaderboard_rank(obj)
if data:
return data.position
def get_points(self, obj): def get_points(self, obj):
data = JuniorPoints.objects.filter(junior=obj).last() data = JuniorPoints.objects.filter(junior=obj).last()
if data: if data:
@@ -209,6 +218,13 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
def get_pending_task(self, obj): def get_pending_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count() data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
return data return data
def get_guardian_code_status(self, obj):
if self.context['guardian_code'] in obj.guardian_code:
index = obj.guardian_code.index(self.context['guardian_code'])
if obj.guardian_code_status:
data = obj.guardian_code_status[index]
return data
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Junior model = Junior
@@ -281,8 +297,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
user_data = User.objects.create(username=email, email=email, user_data = User.objects.create(username=email, email=email,
first_name=self.context['first_name'], first_name=self.context['first_name'],
last_name=self.context['last_name']) last_name=self.context['last_name'])
password = User.objects.make_random_password() special_password = make_special_password()
user_data.set_password(password) user_data.set_password(special_password)
user_data.save() user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'), junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
image=profile_image, image=profile_image,
@@ -292,8 +308,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
referral_code=generate_code(ZOD, user_data.id), referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code, 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]) guardian_code_status=[str(NUMBER['two'])])
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship) relationship=relationship)
total_junior = Junior.objects.all().count() total_junior = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_data, position=total_junior) JuniorPoints.objects.create(junior=junior_data, position=total_junior)
@@ -305,9 +321,9 @@ class AddJuniorSerializer(serializers.ModelSerializer):
# add push notification # add push notification
UserNotification.objects.get_or_create(user=user_data) UserNotification.objects.get_or_create(user=user_data)
"""Notification email""" """Notification email"""
junior_notification_email.delay(email, full_name, email, password) junior_notification_email.delay(email, full_name, email, special_password)
# push notification # push notification
send_notification.delay(JUNIOR_ADDED, None, None, junior_data.auth.id, {}) send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {})
return junior_data return junior_data
@@ -320,9 +336,13 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
fields = ('id', 'is_invited') fields = ('id', 'is_invited')
def update(self, instance, validated_data): def update(self, instance, validated_data):
if instance: if instance:
guardian_code = self.context['guardian_code']
instance.is_invited = False instance.is_invited = False
instance.guardian_code = '{}' if instance.guardian_code and ('-' in instance.guardian_code):
instance.guardian_code_status = str(NUMBER['one']) instance.guardian_code.remove('-')
index = instance.guardian_code.index(guardian_code)
instance.guardian_code.remove(guardian_code)
instance.guardian_code_status.pop(index)
instance.save() instance.save()
return instance return instance
@@ -360,10 +380,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
return obj.junior.id return obj.junior.id
def get_position(self, obj): def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj.junior).last() return get_junior_leaderboard_rank(obj.junior)
if data:
return data.position
return 99999
def get_points(self, obj): def get_points(self, obj):
"""total points""" """total points"""
points = JuniorPoints.objects.filter(junior=obj.junior).last() points = JuniorPoints.objects.filter(junior=obj.junior).last()
@@ -444,7 +461,7 @@ class AddGuardianSerializer(serializers.ModelSerializer):
user_type=str(NUMBER['two']), expired_at=expiry_time, user_type=str(NUMBER['two']), expired_at=expiry_time,
is_verified=True) is_verified=True)
UserNotification.objects.get_or_create(user=user) UserNotification.objects.get_or_create(user=user)
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data, JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship) relationship=relationship)
"""Notification email""" """Notification email"""
@@ -494,8 +511,6 @@ class ReAssignTaskSerializer(serializers.ModelSerializer):
instance.save() instance.save()
return instance return instance
class RemoveGuardianCodeSerializer(serializers.ModelSerializer): class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
"""User task Serializer""" """User task Serializer"""
class Meta(object): class Meta(object):
@@ -503,24 +518,33 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
model = Junior model = Junior
fields = ('id', ) fields = ('id', )
def update(self, instance, validated_data): def update(self, instance, validated_data):
instance.guardian_code = None guardian_code = self.context['guardian_code']
instance.guardian_code_status = str(NUMBER['one']) if guardian_code in instance.guardian_code:
if instance.guardian_code and ('-' in instance.guardian_code):
instance.guardian_code.remove('-')
if instance.guardian_code_status and ('-' in instance.guardian_code_status):
instance.guardian_code_status.remove('-')
index = instance.guardian_code.index(guardian_code)
instance.guardian_code.remove(guardian_code)
instance.guardian_code_status.pop(index)
else:
raise serializers.ValidationError({"error":ERROR_CODE['2082'],"code":"400", "status":"failed"})
instance.save() instance.save()
return instance return instance
class FAQSerializer(serializers.ModelSerializer): class FAQSerializer(serializers.ModelSerializer):
# FAQ Serializer """FAQ Serializer"""
class Meta(object): class Meta(object):
# meta info """meta info"""
model = FAQ model = FAQ
fields = ('id', 'question', 'description') fields = ('id', 'question', 'description')
class CreateArticleCardSerializer(serializers.ModelSerializer): class CreateArticleCardSerializer(serializers.ModelSerializer):
# Article card Serializer """Article card Serializer"""
class Meta(object): class Meta(object):
# meta info """meta info"""
model = ArticleCard model = ArticleCard
fields = ('id', 'article') fields = ('id', 'article')
+1 -1
View File
@@ -62,5 +62,5 @@ urlpatterns = [
path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()), path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()),
path('api/v1/complete-article/', CompleteArticleAPIView.as_view()), path('api/v1/complete-article/', CompleteArticleAPIView.as_view()),
path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()), path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()),
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view()), path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view())
] ]
+20 -1
View File
@@ -5,7 +5,8 @@ from django.conf import settings
from templated_email import send_templated_mail from templated_email import send_templated_mail
from .models import JuniorPoints from .models import JuniorPoints
from base.constants import NUMBER from base.constants import NUMBER
from django.db.models import F from django.db.models import F, Window
from django.db.models.functions.window import Rank
# junior notification # junior notification
# email for sending email # email for sending email
# when guardian create junior profile # when guardian create junior profile
@@ -61,3 +62,21 @@ def update_positions_based_on_points():
junior_point.position = position junior_point.position = position
junior_point.save() junior_point.save()
position += NUMBER['one'] position += NUMBER['one']
def get_junior_leaderboard_rank(junior_obj):
"""
to get junior's position/rank
:param junior_obj:
:return: junior's position/rank
"""
queryset = JuniorPoints.objects.filter(
junior__is_verified=True
).select_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')
junior = next((query for query in queryset if query.junior == junior_obj), None)
return junior.rank if junior else None
+83 -44
View File
@@ -12,6 +12,7 @@ import datetime
import requests import requests
from rest_framework.viewsets import GenericViewSet, mixins from rest_framework.viewsets import GenericViewSet, mixins
import math
"""Django app import""" """Django app import"""
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi from drf_yasg import openapi
@@ -42,12 +43,12 @@ from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, Ad
from guardian.models import Guardian, JuniorTask from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ARTICLE_STATUS, none from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response, task_status_fun
from guardian.utils import upload_image_to_alibaba from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points from .utils import update_positions_based_on_points
from notifications.utils import send_notification from notifications.utils import send_notification
from notifications.constants import REMOVE_JUNIOR from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS, ASSOCIATE_EXISTING_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer, from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
StartAssessmentSerializer) StartAssessmentSerializer)
@@ -99,7 +100,11 @@ class UpdateJuniorProfile(viewsets.ModelViewSet):
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) if e.detail:
error_detail = e.detail.get('error', None)
else:
error_detail = str(e)
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
class ValidateGuardianCode(viewsets.ModelViewSet): class ValidateGuardianCode(viewsets.ModelViewSet):
"""Check guardian code exist or not""" """Check guardian code exist or not"""
@@ -141,14 +146,15 @@ class JuniorListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
""" junior list""" """ junior list"""
try: try:
update_positions_based_on_points() # update_positions_based_on_points, function removed
guardian_data = Guardian.objects.filter(user__email=request.user).last() guardian_data = Guardian.objects.filter(user__email=request.user).last()
# fetch junior object # fetch junior object
if guardian_data: if guardian_data:
queryset = self.get_queryset() queryset = self.get_queryset()
queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code)) queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code))
# use JuniorDetailListSerializer serializer # use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(queryset, many=True) serializer = JuniorDetailListSerializer(queryset, context={"guardian_code":
guardian_data.guardian_code}, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK) return custom_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
@@ -190,6 +196,8 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST)
elif not data: elif not data:
return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST)
elif data == "Max":
return custom_error_response(ERROR_CODE['2081'], response_status=status.HTTP_400_BAD_REQUEST)
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
# use AddJuniorSerializer serializer # use AddJuniorSerializer serializer
serializer = AddJuniorSerializer(data=request.data, context=info_data) serializer = AddJuniorSerializer(data=request.data, context=info_data)
@@ -204,18 +212,30 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
def associate_guardian(self, user): def associate_guardian(self, user):
junior = Junior.objects.filter(auth__email=self.request.data['email']).first() junior = Junior.objects.filter(auth__email=self.request.data['email']).first()
guardian = Guardian.objects.filter(user=self.request.user).first() guardian = Guardian.objects.filter(user=self.request.user).first()
if not junior: if junior is None:
return none return none
if junior.guardian_code and (guardian.guardian_code in junior.guardian_code): if junior.guardian_code and (guardian.guardian_code in junior.guardian_code):
return False return False
if type(junior.guardian_code) is list: if junior.guardian_code and ('-' in junior.guardian_code):
junior.guardian_code.remove('-')
if not junior.guardian_code:
junior.guardian_code = [guardian.guardian_code]
elif type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
junior.guardian_code.append(guardian.guardian_code) junior.guardian_code.append(guardian.guardian_code)
else: else:
junior.guardian_code = [guardian.guardian_code] return "Max"
junior.guardian_code_status = str(NUMBER['two']) if junior.guardian_code_status and ('-' in junior.guardian_code_status):
junior.guardian_code_status.remove('-')
if not junior.guardian_code_status:
junior.guardian_code_status = [str(NUMBER['two'])]
else:
junior.guardian_code_status.append(str(NUMBER['two']))
junior.save() junior.save()
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior, jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior)
relationship=str(self.request.data['relationship'])) if jun_data:
jun_data.relationship = str(self.request.data['relationship'])
jun_data.save()
send_notification.delay(ASSOCIATE_EXISTING_JUNIOR, self.request.user.id, GUARDIAN, junior.auth.id, {})
return True return True
@@ -259,10 +279,10 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
manual_parameters=[ manual_parameters=[
# Example of a query parameter # Example of a query parameter
openapi.Parameter( openapi.Parameter(
'title', # Query parameter name 'title',
openapi.IN_QUERY, # Parameter location openapi.IN_QUERY,
description='title of the name', description='title of the name',
type=openapi.TYPE_STRING, # Parameter type type=openapi.TYPE_STRING,
), ),
# Add more parameters as needed # Add more parameters as needed
] ]
@@ -308,10 +328,12 @@ class RemoveJuniorAPIView(views.APIView):
guardian_code__icontains=str(guardian.guardian_code)).last() guardian_code__icontains=str(guardian.guardian_code)).last()
if junior_queryset: if junior_queryset:
# use RemoveJuniorSerializer serializer # use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True) serializer = RemoveJuniorSerializer(junior_queryset, context={"guardian_code":guardian.guardian_code},
data=request.data, partial=True)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() serializer.save()
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {}) send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
@@ -325,9 +347,19 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Junior task list""" """Junior task list"""
serializer_class = TaskDetailsjuniorSerializer serializer_class = TaskDetailsjuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination pagination_class = PageNumberPagination
http_method_names = ('get',) http_method_names = ('get',)
def get_queryset(self):
queryset = JuniorTask.objects.filter(junior__auth=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): def list(self, request, *args, **kwargs):
"""Junior task list """Junior task list
status=0 status=0
@@ -335,28 +367,20 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
page=1""" page=1"""
try: try:
status_value = self.request.GET.get('status') status_value = self.request.GET.get('status')
search = self.request.GET.get('search') current_page = self.request.GET.get('page')
if search and str(status_value) == '0': queryset = self.get_queryset()
# search with title and for all task list task_status = task_status_fun(status_value)
queryset = JuniorTask.objects.filter(junior__auth=request.user, if status_value:
task_name__icontains=search).order_by('due_date', 'created_at') queryset = queryset.filter(task_status__in=task_status)
elif search and str(status_value) != '0':
# search with title and fetch task list with status wise
queryset = JuniorTask.objects.filter(junior__auth=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':
# fetch all task list
queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
# fetch task list with status wise
queryset = JuniorTask.objects.filter(junior__auth=request.user,
task_status=status_value).order_by('due_date','created_at')
paginator = self.pagination_class() paginator = self.pagination_class()
total_count = len(queryset)
total_pages = math.ceil(total_count / 10)
# use Pagination # use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer # use TaskDetails juniorSerializer serializer
serializer = TaskDetailsjuniorSerializer(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, total_pages=total_pages, current_page=current_page,
response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -385,10 +409,12 @@ class CompleteJuniorTaskAPIView(views.APIView):
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user
).select_related('guardian', 'junior').last() ).select_related('guardian', 'junior').last()
if task_queryset: if task_queryset:
if task_queryset.junior.is_deleted or not task_queryset.junior.is_active: if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
return custom_error_response(ERROR_CODE['2085'], response_status=status.HTTP_400_BAD_REQUEST)
elif task_queryset.junior.is_deleted or not task_queryset.junior.is_active:
return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST)
# use CompleteTaskSerializer serializer # use CompleteTaskSerializer serializer
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: elif task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
"""Already request send """ """Already request send """
return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST)
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True) serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
@@ -412,7 +438,7 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet):
"""Junior Points """Junior Points
No Params""" No Params"""
try: try:
update_positions_based_on_points() # update_positions_based_on_points, function removed
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last() queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
# update position of junior # update position of junior
serializer = JuniorPointsSerializer(queryset) serializer = JuniorPointsSerializer(queryset)
@@ -493,7 +519,10 @@ class StartTaskAPIView(views.APIView):
try: try:
task_id = self.request.data.get('task_id') task_id = self.request.data.get('task_id')
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last() task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
print("task_queryset==>",task_queryset)
if task_queryset and task_queryset.task_status == str(NUMBER['one']): if task_queryset and task_queryset.task_status == str(NUMBER['one']):
if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST)
# use StartTaskSerializer serializer # use StartTaskSerializer serializer
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True) serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid(): if serializer.is_valid():
@@ -577,8 +606,8 @@ class StartAssessmentAPIView(viewsets.ModelViewSet):
article_id = self.request.GET.get('article_id') article_id = self.request.GET.get('article_id')
# if referral_code: # if referral_code:
article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related( article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options' 'article_survey'
).order_by('-created_at') )
return article return article
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""Params """Params
@@ -615,7 +644,7 @@ class CheckAnswerAPIView(viewsets.ModelViewSet):
answer_id = self.request.GET.get('answer_id') answer_id = self.request.GET.get('answer_id')
current_page = self.request.GET.get('current_page') current_page = self.request.GET.get('current_page')
queryset = self.get_queryset() queryset = self.get_queryset()
submit_ans = SurveyOption.objects.filter(id=answer_id, is_answer=True).last() submit_ans = SurveyOption.objects.filter(id=answer_id).last()
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user, junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
question=queryset) question=queryset)
if submit_ans: if submit_ans:
@@ -658,6 +687,8 @@ class CompleteArticleAPIView(views.APIView):
is_answer_correct=True).aggregate( is_answer_correct=True).aggregate(
total_earn_points=Sum('earn_points'))['total_earn_points'] total_earn_points=Sum('earn_points'))['total_earn_points']
data = {"total_earn_points":total_earn_points} data = {"total_earn_points":total_earn_points}
send_notification.delay(ARTICLE_REWARD_POINTS, None, None,
request.user.id, {'points': total_earn_points})
return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -718,26 +749,34 @@ class CreateArticleCardAPIView(viewsets.ModelViewSet):
class RemoveGuardianCodeAPIView(views.APIView): class RemoveGuardianCodeAPIView(views.APIView):
"""Remove guardian code request API """Remove guardian code request API
No Payload""" Payload
{"guardian_code"
:"GRD037"
}"""
serializer_class = RemoveGuardianCodeSerializer serializer_class = RemoveGuardianCodeSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def put(self, request, format=None): def put(self, request, format=None):
try: try:
guardian_code = self.request.data.get("guardian_code")
guardian_data = Guardian.objects.filter(guardian_code=guardian_code).last()
junior_queryset = Junior.objects.filter(auth=self.request.user).last() junior_queryset = Junior.objects.filter(auth=self.request.user).last()
if junior_queryset: if junior_queryset:
# use RemoveGuardianCodeSerializer serializer # use RemoveGuardianCodeSerializer serializer
serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True) serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code},
data=request.data, partial=True)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() serializer.save()
JuniorGuardianRelationship.objects.filter(guardian=guardian_data, junior=junior_queryset).delete()
return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK) 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) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else: else:
# task in another state # task in another state
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) error_detail = e.detail.get('error', None)
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
class FAQViewSet(GenericViewSet, mixins.CreateModelMixin, class FAQViewSet(GenericViewSet, mixins.CreateModelMixin,
+53 -20
View File
@@ -6,60 +6,76 @@ ASSOCIATE_REQUEST = 3
ASSOCIATE_REJECTED = 4 ASSOCIATE_REJECTED = 4
ASSOCIATE_APPROVED = 5 ASSOCIATE_APPROVED = 5
REFERRAL_POINTS = 6 REFERRAL_POINTS = 6
JUNIOR_ADDED = 7 ASSOCIATE_JUNIOR = 7
ASSOCIATE_EXISTING_JUNIOR = 8
TASK_ASSIGNED = 8 TASK_ASSIGNED = 9
TASK_ACTION = 9 TASK_ACTION = 10
TASK_REJECTED = 10 TASK_REJECTED = 11
TASK_APPROVED = 11 TASK_APPROVED = 12
PENDING_TASK_EXPIRING = 12 PENDING_TASK_EXPIRING = 13
IN_PROGRESS_TASK_EXPIRING = 13 IN_PROGRESS_TASK_EXPIRING = 14
REMOVE_JUNIOR = 15 TOP_JUNIOR = 15
NEW_ARTICLE_PUBLISHED = 16
ARTICLE_REWARD_POINTS = 17
REMOVE_JUNIOR = 18
TEST_NOTIFICATION = 99 TEST_NOTIFICATION = 99
# notification dictionary
NOTIFICATION_DICT = { NOTIFICATION_DICT = {
REGISTRATION: { REGISTRATION: {
"title": "Successfully registered!", "title": "Successfully registered!",
"body": "You have registered successfully. Now login and complete your profile." "body": "You have registered successfully. Now login and complete your profile."
}, },
# user will receive notification as soon junior sign up application using their guardian code # user will receive notification as soon junior
# for association # sign up application using their guardian code for association
ASSOCIATE_REQUEST: { ASSOCIATE_REQUEST: {
"title": "Associate request!", "title": "Associate request!",
"body": "You have request from {from_user} to associate with you." "body": "You have request from {from_user} to associate with you."
}, },
# Juniors will receive notification when custodians reject their request for associate # Juniors will receive notification when
# custodians reject their request for associate
ASSOCIATE_REJECTED: { ASSOCIATE_REJECTED: {
"title": "Associate request rejected!", "title": "Associate request rejected!",
"body": "Your request to associate has been rejected by {from_user}." "body": "Your request to associate has been rejected by {from_user}."
}, },
# Juniors will receive notification when custodians approve their request for associate # Juniors will receive notification when
# custodians approve their request for associate
ASSOCIATE_APPROVED: { ASSOCIATE_APPROVED: {
"title": "Associate request approved!", "title": "Associate request approved!",
"body": "Your request to associate has been approved by {from_user}." "body": "Your request to associate has been approved by {from_user}."
}, },
# Juniors will receive Notifications for every Points earned by referrals # Juniors will receive Notifications
# for every Points earned by referrals
REFERRAL_POINTS: { REFERRAL_POINTS: {
"title": "Earn Referral points!", "title": "Earn Referral points!",
"body": "You earn 5 points for referral." "body": "You earn 5 points for referral."
}, },
# Juniors will receive notification once any custodians add them in their account # Juniors will receive notification
JUNIOR_ADDED: { # once any custodians add them in their account
ASSOCIATE_JUNIOR: {
"title": "Profile already setup!", "title": "Profile already setup!",
"body": "Your guardian has already setup your profile." "body": "Your guardian has already setup your profile."
}, },
# Juniors will receive Notification for every Task Assign by Custodians ASSOCIATE_EXISTING_JUNIOR: {
"title": "Associated to guardian",
"body": "Your are associated to your guardian {from_user}."
},
# Juniors will receive Notification
# for every Task Assign by Custodians
TASK_ASSIGNED: { TASK_ASSIGNED: {
"title": "New task assigned!", "title": "New task assigned!",
"body": "{from_user} has assigned you a new task." "body": "{from_user} has assigned you a new task."
}, },
# Guardian will receive notification as soon as junior send task for approval # Guardian will receive notification as soon
# as junior send task for approval
TASK_ACTION: { TASK_ACTION: {
"title": "Task completion approval!", "title": "Task completion approval!",
"body": "{from_user} completed her task {task_name}." "body": "{from_user} completed their task {task_name}."
}, },
# Juniors will receive notification as soon as their task is rejected by custodians # Juniors will receive notification as soon
# as their task is rejected by custodians
TASK_REJECTED: { TASK_REJECTED: {
"title": "Task completion rejected!", "title": "Task completion rejected!",
"body": "Your task completion request has been rejected by {from_user}." "body": "Your task completion request has been rejected by {from_user}."
@@ -83,6 +99,23 @@ NOTIFICATION_DICT = {
"body": "{from_user} didn't take any action on assigned task {task_name} and it's expiring soon. " "body": "{from_user} didn't take any action on assigned task {task_name} and it's expiring soon. "
"Please assist to complete it." "Please assist to complete it."
}, },
# Juniors will receive Notification
# related to Leaderboard progress
TOP_JUNIOR: {
"title": "Leaderboard topper!",
"body": "{from_user} is on top in leaderboard with {points} points."
},
# Juniors will receive notification
# when admin add any new financial learnings
NEW_ARTICLE_PUBLISHED: {
"title": "Time to read!",
"body": "A new article has been published."
},
# Juniors will receive notification when they earn points by reading financial Learning
ARTICLE_REWARD_POINTS: {
"title": "Article reward points!",
"body": "You are rewarded with {points} points for reading article and answering questions. "
},
# Juniors will receive notification as soon as their custodians remove them from account # Juniors will receive notification as soon as their custodians remove them from account
REMOVE_JUNIOR: { REMOVE_JUNIOR: {
"title": "Disassociate by guardian!", "title": "Disassociate by guardian!",
+42 -8
View File
@@ -8,6 +8,7 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati
# django imports # django imports
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db.models import Q
# local imports # local imports
from account.models import UserNotification from account.models import UserNotification
@@ -18,14 +19,13 @@ from junior.models import Junior
from notifications.constants import NOTIFICATION_DICT from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification from notifications.models import Notification
User = get_user_model() User = get_user_model()
def register_fcm_token(user_id, registration_id, device_id, device_type): def register_fcm_token(user_id, registration_id, device_id, device_type):
""" used to register the fcm device token""" """ used to register the fcm device token"""
device, _ = FCMDevice.objects.update_or_create(device_id=device_id, device, _ = FCMDevice.objects.update_or_create(user_id=user_id,
defaults={'user_id': user_id, 'type': device_type, defaults={'device_id': device_id, 'type': device_type,
'active': True, 'active': True,
'registration_id': registration_id}) 'registration_id': registration_id})
return device return device
@@ -76,20 +76,23 @@ def get_notification_data(notification_type, from_user_id, from_user_type, to_us
push_data = NOTIFICATION_DICT[notification_type].copy() push_data = NOTIFICATION_DICT[notification_type].copy()
notification_data = push_data.copy() notification_data = push_data.copy()
task_name = None task_name = None
points = extra_data.get('points', None)
if 'task_id' in extra_data: if 'task_id' in extra_data:
task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first() task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first()
task_name = task.task_name task_name = task.task_name
extra_data['task_name'] = task_name
extra_data['task_image'] = task.image if task.image else task.default_image extra_data['task_image'] = task.image if task.image else task.default_image
from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type) from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type)
push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name) push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name, points=points)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name, task_name=task_name) notification_data['body'] = notification_data['body'].format(from_user=from_user_name,
task_name=task_name, points=points)
notification_data['from_user'] = from_user_name notification_data['from_user'] = from_user_name
notification_data['from_user_image'] = from_user_image notification_data['from_user_image'] = from_user_image
notification_data.update(extra_data) notification_data.update(extra_data)
to_user = User.objects.get(id=to_user_id) to_user = User.objects.filter(id=to_user_id).first()
return notification_data, push_data, from_user, to_user return notification_data, push_data, from_user, to_user
@@ -115,6 +118,39 @@ def send_push(user, data):
) )
def send_multiple_push(queryset, data):
""" used to send same notification to multiple users """
FCMDevice.objects.filter(user__in=queryset, active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
)
@shared_task()
def send_notification_multiple_user(notification_type, from_user_id, from_user_type,
extra_data: dict = {}):
"""
used to send notification to multiple user for the given notification type
"""
to_user_list = User.objects.filter(junior_profile__is_verified=True, is_superuser=False
).exclude(junior_profile__isnull=True, guardian_profile__isnull=True)
notification_data, push_data, from_user, _ = get_notification_data(notification_type, from_user_id,
from_user_type, None, extra_data)
notification_list = []
for user in to_user_list:
notification_copy_data = notification_data.copy()
notification_copy_data.update(
{'badge': Notification.objects.filter(notification_to=user, is_read=False).count()})
notification_list.append(Notification(notification_type=notification_type,
notification_to=user,
notification_from=from_user,
data=notification_copy_data))
Notification.objects.bulk_create(notification_list)
to_user_list = to_user_list.filter(user_notification__push_notification=True)
send_multiple_push(to_user_list, push_data)
@shared_task() @shared_task()
def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data): def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data):
""" """
@@ -143,5 +179,3 @@ def send_notification_to_junior(notification_type, from_user_id, to_user_id, ext
from_user = Guardian.objects.filter(user_id=from_user_id).first() from_user = Guardian.objects.filter(user_id=from_user_id).first()
extra_data['from_user_image'] = from_user.image extra_data['from_user_image'] = from_user.image
send_notification(notification_type, from_user_id, to_user_id, extra_data) send_notification(notification_type, from_user_id, to_user_id, extra_data)
+5 -12
View File
@@ -11,7 +11,7 @@ from rest_framework import viewsets, status, views
# local imports # local imports
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE from base.messages import SUCCESS_CODE, ERROR_CODE
from base.tasks import notify_task_expiry from base.tasks import notify_task_expiry, notify_top_junior
from notifications.constants import TEST_NOTIFICATION from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification from notifications.utils import send_notification
@@ -36,7 +36,7 @@ class NotificationViewSet(viewsets.GenericViewSet):
paginator = self.pagination_class() paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data) return custom_response(None, serializer.data, count=queryset.count())
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice) @action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request): def fcm_registration(self, request):
@@ -52,9 +52,11 @@ class NotificationViewSet(viewsets.GenericViewSet):
@action(methods=['get'], detail=False, url_path='test', url_name='test') @action(methods=['get'], detail=False, url_path='test', url_name='test')
def send_test_notification(self, request): def send_test_notification(self, request):
""" """
to send test notification to test send notification, task expiry, top junior
:return: :return:
""" """
notify_task_expiry()
notify_top_junior()
send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'], send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'],
{}) {})
return custom_response(SUCCESS_CODE["3000"]) return custom_response(SUCCESS_CODE["3000"])
@@ -67,12 +69,3 @@ class NotificationViewSet(viewsets.GenericViewSet):
""" """
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True) Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
@action(methods=['get'], url_path='task', url_name='task', detail=False,
permission_classes=[AllowAny])
def task(self, request, *args, **kwargs):
"""
notification list
"""
notify_task_expiry()
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
+13 -3
View File
@@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model
from account.utils import get_user_full_name from account.utils import get_user_full_name
# local imports # local imports
from base.constants import USER_TYPE from base.constants import USER_TYPE, JUNIOR
from junior.models import JuniorPoints, Junior from junior.models import JuniorPoints, Junior
@@ -37,7 +37,7 @@ class JuniorLeaderboardSerializer(serializers.ModelSerializer):
:param obj: junior object :param obj: junior object
:return: full name :return: full name
""" """
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name return get_user_full_name(obj.auth)
@staticmethod @staticmethod
def get_first_name(obj): def get_first_name(obj):
@@ -60,6 +60,8 @@ class LeaderboardSerializer(serializers.ModelSerializer):
""" """
leaderboard serializer leaderboard serializer
""" """
user_id = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
junior = JuniorLeaderboardSerializer() junior = JuniorLeaderboardSerializer()
rank = serializers.IntegerField() rank = serializers.IntegerField()
@@ -68,7 +70,15 @@ class LeaderboardSerializer(serializers.ModelSerializer):
meta class meta class
""" """
model = JuniorPoints model = JuniorPoints
fields = ('total_points', 'rank', 'junior') fields = ('user_id', 'user_type', 'total_points', 'rank', 'junior')
@staticmethod
def get_user_id(obj):
return obj.junior.auth.id
@staticmethod
def get_user_type(obj):
return JUNIOR
class UserCSVReportSerializer(serializers.ModelSerializer): class UserCSVReportSerializer(serializers.ModelSerializer):
+19 -9
View File
@@ -10,6 +10,8 @@ from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE
# local imports # local imports
from base.messages import ERROR_CODE from base.messages import ERROR_CODE
from guardian.utils import upload_image_to_alibaba from guardian.utils import upload_image_to_alibaba
from notifications.constants import NEW_ARTICLE_PUBLISHED
from notifications.utils import send_notification_multiple_user
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
from web_admin.utils import pop_id, get_image_url from web_admin.utils import pop_id, get_image_url
from junior.models import JuniorArticlePoints, JuniorArticle from junior.models import JuniorArticlePoints, JuniorArticle
@@ -119,11 +121,15 @@ class ArticleSerializer(serializers.ModelSerializer):
option = pop_id(option) option = pop_id(option)
SurveyOption.objects.create(survey=survey_obj, **option) SurveyOption.objects.create(survey=survey_obj, **option)
# All juniors will receive notification when admin add any new financial learnings/article
send_notification_multiple_user.delay(NEW_ARTICLE_PUBLISHED, None, None, {})
return article return article
def update(self, instance, validated_data): def update(self, instance, validated_data):
""" """
to update article and related table to update article and related table
:param validated_data:
:param instance: article object, :param instance: article object,
:return: article object :return: article object
""" """
@@ -219,8 +225,7 @@ class ArticleListSerializer(serializers.ModelSerializer):
""" """
serializer for article API serializer for article API
""" """
article_cards = ArticleCardSerializer(many=True) image = serializers.SerializerMethodField('get_image')
article_survey = ArticleSurveySerializer(many=True)
total_points = serializers.SerializerMethodField('get_total_points') total_points = serializers.SerializerMethodField('get_total_points')
is_completed = serializers.SerializerMethodField('get_is_completed') is_completed = serializers.SerializerMethodField('get_is_completed')
@@ -229,12 +234,16 @@ class ArticleListSerializer(serializers.ModelSerializer):
meta class meta class
""" """
model = Article model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed') fields = ('id', 'title', 'description','image', 'total_points', 'is_completed')
def get_image(self, obj):
"""article image"""
if obj.article_cards.first():
return obj.article_cards.first().image_url
return None
def get_total_points(self, obj): def get_total_points(self, obj):
"""total points of article""" """total points of article"""
total_question = ArticleSurvey.objects.filter(article=obj).count() return obj.article_survey.all().count() * NUMBER['five']
return total_question * NUMBER['five']
def get_is_completed(self, obj): def get_is_completed(self, obj):
"""complete all question""" """complete all question"""
@@ -268,14 +277,14 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last() ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last()
if ans_obj: if ans_obj:
return ans_obj.id return ans_obj.id
return str("None") return None
def get_attempted_answer(self, obj): def get_attempted_answer(self, obj):
"""attempt question or not""" """attempt question or not"""
context_data = self.context.get('user') context_data = self.context.get('user')
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data,
question=obj, is_answer_correct=True).last() question=obj).last()
if junior_article_obj: if junior_article_obj and junior_article_obj.submitted_answer:
return junior_article_obj.submitted_answer.id return junior_article_obj.submitted_answer.id
return None return None
@@ -297,8 +306,9 @@ class StartAssessmentSerializer(serializers.ModelSerializer):
"""current page""" """current page"""
context_data = self.context.get('user') context_data = self.context.get('user')
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last() data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
total_count = obj.article_survey.all().count()
if data: if data:
return data.current_que_page return data.current_que_page if data.current_que_page < total_count else data.current_que_page - 1
return NUMBER['zero'] return NUMBER['zero']
class Meta(object): class Meta(object):
""" """
+2 -2
View File
@@ -14,7 +14,7 @@ from account.models import UserEmailOtp
from base.constants import USER_TYPE from base.constants import USER_TYPE
from base.messages import ERROR_CODE from base.messages import ERROR_CODE
from guardian.tasks import generate_otp from guardian.tasks import generate_otp
from base.tasks import send_mail from base.tasks import send_email
USER = get_user_model() USER = get_user_model()
@@ -54,7 +54,7 @@ class AdminOTPSerializer(serializers.ModelSerializer):
data = { data = {
"verification_code": verification_code "verification_code": verification_code
} }
send_mail.delay([email], template, data) send_email.delay([email], template, data)
expiry = timezone.now() + timezone.timedelta(days=1) expiry = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.update_or_create(email=email, user_data, created = UserEmailOtp.objects.update_or_create(email=email,
+12 -8
View File
@@ -128,10 +128,12 @@ class AnalyticsViewSet(GenericViewSet):
:param request: :param request:
:return: :return:
""" """
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( queryset = JuniorPoints.objects.filter(
expression=Rank(), junior__is_verified=True
order_by=[F('total_points').desc(), 'junior__created_at'] ).select_related('junior', 'junior__auth').annotate(rank=Window(
)).order_by('-total_points', 'junior__created_at') expression=Rank(),
order_by=[F('total_points').desc(), 'junior__created_at']
)).order_by('-total_points', 'junior__created_at')
paginator = CustomPageNumberPagination() paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
@@ -199,10 +201,12 @@ class AnalyticsViewSet(GenericViewSet):
# sheet 3 for Juniors Leaderboard and rank # sheet 3 for Juniors Leaderboard and rank
elif sheet_name == 'Juniors Leaderboard': elif sheet_name == 'Juniors Leaderboard':
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window( queryset = JuniorPoints.objects.filter(
expression=Rank(), junior__is_verified=True
order_by=[F('total_points').desc(), 'junior__created_at'] ).select_related('junior', 'junior__auth').annotate(rank=Window(
)).order_by('-total_points', 'junior__created_at')[:15] expression=Rank(),
order_by=[F('total_points').desc(), 'junior__created_at']
)).order_by('-total_points', 'junior__created_at')[:15]
df_leaderboard = pd.DataFrame([ df_leaderboard = pd.DataFrame([
{ {
'Name': get_user_full_name(junior.junior.auth), 'Name': get_user_full_name(junior.junior.auth),
+1 -4
View File
@@ -229,10 +229,7 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
http_method_names = ['get',] http_method_names = ['get',]
def get_queryset(self): def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False, is_published=True).prefetch_related( queryset = self.queryset.objects.filter(is_deleted=False, is_published=True).order_by('-created_at')
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
queryset = self.filter_queryset(article)
return queryset return queryset
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
+22 -15
View File
@@ -12,8 +12,9 @@ from django.db.models import Q
# local imports # local imports
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE from base.constants import USER_TYPE, GUARDIAN, JUNIOR
from base.messages import SUCCESS_CODE, ERROR_CODE from base.messages import SUCCESS_CODE, ERROR_CODE
from base.tasks import send_email
from guardian.models import Guardian from guardian.models import Guardian
from junior.models import Junior from junior.models import Junior
from web_admin.permission import AdminPermission from web_admin.permission import AdminPermission
@@ -92,12 +93,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: 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) return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).first() guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True
).select_related('user').first()
serializer = GuardianSerializer(guardian, serializer = GuardianSerializer(guardian,
request.data, context={'user_id': kwargs['pk']}) request.data, context={'user_id': kwargs['pk']})
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first() junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True
).select_related('auth').first()
serializer = JuniorSerializer(junior, serializer = JuniorSerializer(junior,
request.data, context={'user_id': kwargs['pk']}) request.data, context={'user_id': kwargs['pk']})
@@ -113,17 +116,21 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: success message :return: success message
""" """
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]: user_type = self.request.query_params.get('user_type')
if user_type not in [GUARDIAN, JUNIOR]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST) 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'): email_template = 'user_deactivate.email'
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.guardian_profile.all().first() if user_type == GUARDIAN:
obj.is_active = False if obj.is_active else True obj = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).select_related('user').first()
obj.save() elif user_type == JUNIOR:
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): obj = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first()
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.junior_profile.all().first() if obj.is_active:
obj.is_active = False if obj.is_active else True obj.is_active = False
obj.save() send_email([obj.user.email if user_type == GUARDIAN else obj.auth.email], email_template)
else:
obj.is_active = True
obj.save()
return custom_response(SUCCESS_CODE['3038']) return custom_response(SUCCESS_CODE['3038'])
+10 -12
View File
@@ -27,10 +27,19 @@ app.config_from_object('django.conf:settings')
# Load task modules from all registered Django apps. # Load task modules from all registered Django apps.
app.autodiscover_tasks() app.autodiscover_tasks()
# scheduled task
app.conf.beat_schedule = { app.conf.beat_schedule = {
"expired_task": {
"task": "guardian.utils.update_expired_task_status",
"schedule": crontab(minute=0, hour=0),
},
'notify_task_expiry': { 'notify_task_expiry': {
'task': 'base.tasks.notify_task_expiry', 'task': 'base.tasks.notify_task_expiry',
'schedule': crontab(minute='0', hour='*/1'), 'schedule': crontab(minute='0', hour='18'),
},
'notify_top_junior': {
'task': 'base.tasks.notify_top_junior',
'schedule': crontab(minute='0', hour='*/2'),
}, },
} }
@@ -39,14 +48,3 @@ app.conf.beat_schedule = {
def debug_task(self): def debug_task(self):
""" celery debug task """ """ celery debug task """
print(f'Request: {self.request!r}') print(f'Request: {self.request!r}')
"""cron task"""
app.conf.beat_schedule = {
"expired_task": {
"task": "guardian.utils.update_expired_task_status",
"schedule": crontab(minute=0, hour=0),
},
}