Compare commits

..

85 Commits

Author SHA1 Message Date
f3478c972e modification in create task api 2023-09-07 15:10:18 +05:30
bc18c67527 modified article publish and un-publish api, sonar issues, modification in create task api 2023-09-07 13:49:06 +05:30
ffb99f5099 modified some success messages. 2023-09-06 15:26:10 +05:30
8183edf319 Merge pull request #305 from KiwiTechLLC/ZBKBCK-51
modified create task api, added badge count in notification list
2023-09-05 19:36:09 +05:30
8f214d11a7 modified create task api, added badge count in notification list 2023-09-05 19:29:57 +05:30
b5a89df59a Merge pull request #304 from KiwiTechLLC/ZBKBCK-50
modified pagination in task list and junior task list api
2023-09-05 14:02:16 +05:30
5524eeed64 modified pagination in task list and junior task list api 2023-09-05 13:57:28 +05:30
aeaa7d7ab8 Merge pull request #303 from KiwiTechLLC/sprint6-bugs
handle scenerio for task after disassociate
2023-09-05 13:14:01 +05:30
69be3bb2ac Merge pull request #302 from KiwiTechLLC/ZBKBCK-50
modified cors allowed origin values, added yml file for qa stage and …
2023-09-04 18:55:12 +05:30
be9f600bcc modified cord allowed origin values, added yml file for qa stage and prod 2023-09-04 18:52:23 +05:30
65d0932893 sonar issues 2023-09-04 16:52:50 +05:30
9b14eedb18 handle scenerio for task after disassociate 2023-09-04 16:49:18 +05:30
6c96ea0820 Merge pull request #301 from KiwiTechLLC/ZBKBCK-49
added cors Allow specific origins setting, unpublish article api, pag…
2023-09-04 16:08:22 +05:30
a211baa10a added cors Allow specific origins setting, unpublish article api, pagination in notification list 2023-09-04 15:46:36 +05:30
a93dc83bd1 Merge pull request #300 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-09-04 15:27:42 +05:30
116fb00358 optimized task status code 2023-09-04 15:22:15 +05:30
20fa6e43da changes in task list API and add special character in password for added junior 2023-09-04 15:02:47 +05:30
2cffc4e128 Merge pull request #299 from KiwiTechLLC/ZBKBCK-49
added and modifid pagination
2023-09-04 13:16:15 +05:30
ec585d35f3 added and modifid pagination 2023-09-04 13:11:25 +05:30
d62efa2139 added and modifid pagination 2023-09-04 13:09:54 +05:30
7b75a3233c add current page and total page in task list API 2023-09-04 12:52:04 +05:30
e1ef289c69 Merge pull request #298 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-09-01 17:05:50 +05:30
4f79a690c1 unrestrict logout and refresh token api while login in multiple device 2023-09-01 16:25:05 +05:30
0af2a35206 task assign to multiple junior 2023-09-01 12:14:15 +05:30
a262b03292 Merge pull request #295 from KiwiTechLLC/sprint6-bugs
answer api
2023-08-29 21:32:11 +05:30
d24f075110 answer api 2023-08-29 21:28:36 +05:30
f7624bc1e7 Merge pull request #292 from KiwiTechLLC/sprint6-bugs
answer api
2023-08-29 19:47:55 +05:30
dc12b35842 answer api 2023-08-29 19:38:14 +05:30
a80f9db557 Merge pull request #291 from KiwiTechLLC/sprint6-bugs
otp expiry
2023-08-29 19:05:18 +05:30
3ad29e677d article page 2023-08-29 18:56:51 +05:30
8b0a5d9a8e otp expiry 2023-08-29 18:33:47 +05:30
16d823f97d Merge pull request #290 from KiwiTechLLC/sprint6-bugs
elif for guardian code
2023-08-29 17:51:42 +05:30
d4008d6cc2 elif for guardian code 2023-08-29 17:50:58 +05:30
3dae22a870 Merge pull request #287 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-08-29 11:56:58 +05:30
a8d291474a search junior task 2023-08-29 11:52:47 +05:30
e6482167ae Merge branch 'sprint6-bugs' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-29 11:51:11 +05:30
9a932d31b5 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-29 11:50:44 +05:30
e2c84eb83d search junior task 2023-08-29 11:49:59 +05:30
5bfc3966b3 Merge branch 'dev' into sprint6-bugs 2023-08-29 10:43:34 +05:30
37d191eef8 add message for blank search 2023-08-29 10:41:44 +05:30
e9ee8ec8b2 Merge pull request #285 from KiwiTechLLC/sprint6-bugs
condition in add junior
2023-08-28 20:14:43 +05:30
cc5ecc0647 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-28 20:10:36 +05:30
63dfd731fc condition in add junior 2023-08-28 20:09:39 +05:30
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
219bae792e added notification for existing junior add, modified leaderboard method at every places 2023-08-28 19:39:39 +05:30
df3cab99a5 Merge pull request #283 from KiwiTechLLC/sprint6-bugs
remove guardian request and resend otp
2023-08-28 18:58:23 +05:30
d2242d4c64 remove guardian request and resend otp 2023-08-28 18:39:29 +05:30
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
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
b6fe943bfc Merge pull request #281 from KiwiTechLLC/sprint6-bugs
add non in delete API
2023-08-28 13:46:28 +05:30
8e529b292d add non in delete API 2023-08-28 13:38:35 +05:30
61ca2e7d2d Merge pull request #279 from KiwiTechLLC/sprint6-bugs
change message
2023-08-28 12:53:24 +05:30
966f94eb1e change message 2023-08-28 12:41:29 +05:30
f424c99478 Merge pull request #278 from KiwiTechLLC/sprint6-bugs
guardian_status update
2023-08-28 12:13:10 +05:30
34359360e0 guardian_status update 2023-08-28 12:10:13 +05:30
685a822e52 Merge pull request #277 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-08-28 10:48:48 +05:30
e57344e54f Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint6-bugs 2023-08-28 10:38:14 +05:30
235fc9baac guardian code status in junior list API 2023-08-28 10:37:52 +05:30
3dc8268b07 Merge pull request #276 from KiwiTechLLC/ZBKBCK-48
fixed approve junior notification issue
2023-08-26 23:00:39 +05:30
8be8d56036 fixed approve junior notification issue 2023-08-26 21:36:01 +05:30
c79a891c89 Merge pull request #275 from KiwiTechLLC/sprint6-bugs
Sprint6 bugs
2023-08-26 18:24:09 +05:30
eb4e09964d changes of guardian code status in necessary API 2023-08-26 17:48:13 +05:30
cf9376663c list of guardian code status 2023-08-26 17:09:10 +05:30
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
5b2ab2275e added notification for getting points after reading article 2023-08-25 19:45:24 +05:30
9b6a84c7d1 Merge pull request #270 from KiwiTechLLC/sprint5
Sprint5
2023-08-25 18:02:13 +05:30
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
21b92f8c74 added notification when admin adds a new article 2023-08-25 17:05:01 +05:30
b82902081f Merge pull request #268 from KiwiTechLLC/sprint5
remove code
2023-08-25 12:58:09 +05:30
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
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
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
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
1a2fd2d3a8 Merge pull request #263 from KiwiTechLLC/sprint5
assessment answer
2023-08-24 18:59:38 +05:30
3d06139c4f Merge pull request #262 from KiwiTechLLC/sprint5
assement id
2023-08-24 18:27:17 +05:30
226ddfa7e6 Merge pull request #261 from KiwiTechLLC/sprint5
article-list optimization
2023-08-24 18:05:32 +05:30
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
624e7a4edb Merge pull request #258 from KiwiTechLLC/ZBKBCK-47
added optional name as user
2023-08-24 14:56:52 +05:30
3072bd5cdb added optional name as user 2023-08-24 14:54:46 +05:30
930b58cf05 added optional name as user 2023-08-24 14:51:24 +05:30
339c49577e Merge pull request #257 from KiwiTechLLC/sprint5
Sprint5
2023-08-24 14:10:17 +05:30
09f006eb13 Merge pull request #256 from KiwiTechLLC/ZBKBCK-47
added optional user as name
2023-08-24 13:41:50 +05:30
cd3b385756 added optional user as name 2023-08-24 13:33:34 +05:30
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
f541608656 changed notification method, added celery task to notify expiring task 2023-08-24 12:10:18 +05:30
36 changed files with 979 additions and 348 deletions

View File

@ -45,11 +45,12 @@ class CustomMiddleware(object):
device_type = str(request.META.get('HTTP_TYPE'))
api_endpoint = request.path
unrestricted_api = ('/api/v1/user/login/', '/api/v1/logout/', '/api/v1/generate-token/')
if request.user.is_authenticated:
# device details
if device_id:
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if not device_details and api_endpoint != '/api/v1/user/login/':
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']):

View File

@ -1,7 +1,7 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
{{subject}}
Support Mail
{% endblock %}
{% block plain %}

View File

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

View File

@ -1,6 +1,6 @@
"""Account utils"""
from celery import shared_task
import random
"""Import django"""
from django.conf import settings
from rest_framework import viewsets, status
@ -94,7 +94,7 @@ def junior_account_update(user_tb):
junior_data.is_active = False
junior_data.is_verified = False
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.save()
JuniorPoints.objects.filter(junior=junior_data).delete()
@ -167,7 +167,7 @@ def user_device_details(user, device_id):
return False
def send_support_email(name, sender, subject, message):
def send_support_email(name, sender, message):
"""Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS]
from_email = settings.DEFAULT_ADDRESS
@ -179,7 +179,6 @@ def send_support_email(name, sender, subject, message):
context={
'name': name.title(),
'sender': sender,
'subject': subject,
'message': message
}
)
@ -191,7 +190,8 @@ def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count
if not data:
"""when data is none"""
data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count})
return Response({"data": data, "message": detail, "status": "success",
"code": response_status, "count": count})
def custom_error_response(detail, response_status):
@ -282,8 +282,48 @@ def generate_code(value, user_id):
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
def get_user_full_name(user_obj):
"""
to get user's full name
"""
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name
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):
"""
to make secured password
:param length:
:return:
"""
# 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 = (
secrets.choice(lowercase_letters) +
secrets.choice(uppercase_letters) +
secrets.choice(digits) +
secrets.choice(special_characters) +
''.join(secrets.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

View File

@ -322,7 +322,7 @@ class ForgotPasswordAPIView(views.APIView):
send_all_email.delay(
'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(
email=email
)
@ -546,7 +546,6 @@ class UserEmailVerification(viewsets.ModelViewSet):
class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
"""Param
@ -690,11 +689,10 @@ class SendSupportEmail(views.APIView):
def post(self, request):
name = request.data.get('name')
sender = request.data.get('email')
subject = request.data.get('subject')
message = request.data.get('message')
if name and sender and subject and message:
if name and sender and message:
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)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)

View File

@ -109,9 +109,12 @@ ERROR_CODE = {
# force update
"2079": "Please update your app version for enjoying uninterrupted services",
"2080": "Can not add App version",
"2081": "You can not add more than 3 guardian",
"2081": "A junior can only be associated with a maximum of 3 guardian",
# guardian code not exist
"2082": "Guardian code does 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"""
@ -122,11 +125,11 @@ SUCCESS_CODE = {
# Success code for Thank you
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
# Success code for account activation
"3003": "Log in successful",
"3003": "Log in successful.",
# Success code for password reset
"3004": "Password reset link has been sent to your email address",
"3004": "Password reset link has been sent to your email address.",
# Success code for link verified
"3005": "Your account is deleted successfully.",
"3005": "Your account has been deleted successfully.",
# Success code for password reset
"3006": "Password reset successful. You can now log in with your new password.",
# Success code for password update
@ -134,11 +137,11 @@ SUCCESS_CODE = {
# Success code for valid link
"3008": "You have a valid link.",
# Success code for logged out
"3009": "You have successfully logged out!",
"3009": "You have successfully logged out.",
# Success code for check all fields
"3010": "All fields are valid",
"3011": "Email OTP Verified successfully",
"3012": "Phone OTP Verified successfully",
"3010": "All fields are valid.",
"3011": "Email OTP has been verified successfully.",
"3012": "Phone OTP has been verified successfully.",
"3013": "Valid Guardian code",
"3014": "Password has been updated successfully.",
"3015": "Verification code has been sent on your email.",
@ -147,39 +150,39 @@ SUCCESS_CODE = {
"3018": "Task created successfully",
"3019": "Support Email sent successfully",
"3020": "Logged out successfully.",
"3021": "Add junior successfully",
"3022": "Remove junior successfully",
"3023": "Junior is approved successfully",
"3024": "Junior request is rejected successfully",
"3025": "Task is approved successfully",
"3026": "Task is rejected successfully",
"3021": "Junior has been added successfully.",
"3022": "Junior has been removed successfully.",
"3023": "Junior has been approved successfully.",
"3024": "Junior request is rejected successfully.",
"3025": "Task is approved successfully.",
"3026": "Task is rejected successfully.",
"3027": "Article has been created successfully.",
"3028": "Article has been updated successfully.",
"3029": "Article has been deleted successfully.",
"3030": "Article Card has been removed successfully.",
"3031": "Article Survey has been removed successfully.",
"3032": "Task request sent successfully",
"3033": "Valid Referral code",
"3034": "Invite guardian successfully",
"3035": "Task started successfully",
"3036": "Task reassign successfully",
"3032": "Task request sent successfully.",
"3033": "Valid Referral code.",
"3034": "Invite guardian successfully.",
"3035": "Task started successfully.",
"3036": "Task reassign successfully.",
"3037": "Profile has been updated successfully.",
"3038": "Status has been changed successfully.",
# notification read
"3039": "Notification read successfully",
"3039": "Notification read successfully.",
# start article
"3040": "Start article successfully",
"3040": "Start article successfully.",
# complete article
"3041": "Article completed successfully",
"3041": "Article completed successfully.",
# submit assessment successfully
"3042": "Assessment completed successfully",
"3042": "Assessment completed successfully.",
# read article
"3043": "Read article card successfully",
"3043": "Read article card successfully.",
# remove guardian code request
"3044": "Remove guardian code request successfully",
"3044": "Remove guardian code request successfully.",
# create faq
"3045": "Create FAQ data",
"3046": "Add App version successfully"
"3045": "Create FAQ data.",
"3046": "Add App version successfully."
}
"""status code error"""

46
base/pagination.py Normal file
View File

@ -0,0 +1,46 @@
"""
web_admin pagination file
"""
# third party imports
from collections import OrderedDict
from rest_framework.pagination import PageNumberPagination
from account.utils import custom_response
from base.constants import NUMBER
class CustomPageNumberPagination(PageNumberPagination):
"""
custom paginator class
"""
# Set the desired page size
page_size = NUMBER['ten']
page_size_query_param = 'page_size'
# Set a maximum page size if needed
max_page_size = NUMBER['hundred']
def get_paginated_response(self, data):
"""
:param data: queryset to be paginated
:return: return a OrderedDict
"""
return custom_response(None, OrderedDict([
('count', self.page.paginator.count),
('data', data),
('current_page', self.page.number),
('total_pages', self.page.paginator.num_pages),
]))
def get_paginated_dict_response(self, data):
"""
:param data: queryset to be paginated
:return: return a simple dict obj
"""
return {
'count': self.page.paginator.count,
'data': data,
'current_page': self.page.number,
'total_pages': self.page.paginator.num_pages,
}

View File

@ -1,29 +1,88 @@
"""
web_admin tasks file
"""
import datetime
# third party imports
from celery import shared_task
from templated_email import send_templated_mail
# django imports
from django.conf import settings
from django.db.models import F, Window
from django.db.models.functions.window import Rank
# local imports
from base.constants import PENDING, IN_PROGRESS, JUNIOR
from guardian.models import JuniorTask
from junior.models import JuniorPoints
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
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
:param context:
:param recipient_list: e-mail list
:param template: email template
"""
if context is None:
context = {}
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
template_name=template,
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
context=context
)
return True
@shared_task()
def notify_task_expiry():
"""
task to send notification for those task which expiring soon
:return:
"""
all_pending_tasks = JuniorTask.objects.filter(
task_status__in=[PENDING, IN_PROGRESS],
due_date__range=[datetime.datetime.now().date(),
(datetime.datetime.now().date() + datetime.timedelta(days=1))])
if pending_tasks := all_pending_tasks.filter(task_status=PENDING):
for task in pending_tasks:
send_notification(PENDING_TASK_EXPIRING, None, None, task.junior.auth.id,
{'task_id': task.id})
if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS):
for task in in_progress_tasks:
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id,
{'task_id': task.id})
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.

39
docker-compose-prod.yml Normal file
View File

@ -0,0 +1,39 @@
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8000:8000"
volumes:
- ./nginx:/etc/nginx/conf.d
- .:/usr/src/app
depends_on:
- web
web:
build: .
container_name: prod_django
restart: always
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
volumes:
- .:/usr/src/app
broker:
image: rabbitmq:3.7
container_name: prod_rabbitmq
volumes:
- .:/usr/src/app
ports:
- 5673:5673
worker:
build: .
image: celery
container_name: prod_celery
restart: "always"
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
volumes:
- .:/usr/src/app
depends_on:
- broker

39
docker-compose-qa.yml Normal file
View File

@ -0,0 +1,39 @@
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8000:8000"
volumes:
- ./nginx:/etc/nginx/conf.d
- .:/usr/src/app
depends_on:
- web
web:
build: .
container_name: qa_django
restart: always
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
volumes:
- .:/usr/src/app
broker:
image: rabbitmq:3.7
container_name: qa_rabbitmq
volumes:
- .:/usr/src/app
ports:
- 5673:5673
worker:
build: .
image: celery
container_name: qa_celery
restart: "always"
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
volumes:
- .:/usr/src/app
depends_on:
- broker

39
docker-compose-stage.yml Normal file
View File

@ -0,0 +1,39 @@
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8000:8000"
volumes:
- ./nginx:/etc/nginx/conf.d
- .:/usr/src/app
depends_on:
- web
web:
build: .
container_name: stage_django
restart: always
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
volumes:
- .:/usr/src/app
broker:
image: rabbitmq:3.7
container_name: stage_rabbitmq
volumes:
- .:/usr/src/app
ports:
- 5673:5673
worker:
build: .
image: celery
container_name: stage_celery
restart: "always"
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
volumes:
- .:/usr/src/app
depends_on:
- broker

View File

@ -24,13 +24,13 @@ from account.models import UserProfile, UserEmailOtp, UserNotification
from account.utils import generate_code
from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIAN
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant
from notifications.constants import TASK_APPROVED, TASK_REJECTED
from notifications.constants import TASK_APPROVED, TASK_REJECTED, TASK_ASSIGNED
# send notification function
from notifications.utils import send_notification, send_notification_to_junior
from notifications.utils import send_notification
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
@ -218,7 +218,7 @@ class TaskSerializer(serializers.ModelSerializer):
class Meta(object):
"""Meta info"""
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):
"""validation on due date"""
@ -229,11 +229,22 @@ class TaskSerializer(serializers.ModelSerializer):
return value
def create(self, validated_data):
"""create default task image data"""
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last()
guardian = self.context['guardian']
# update image of the task
images = self.context['image']
validated_data['default_image'] = images
instance = JuniorTask.objects.create(**validated_data)
junior_data = self.context['junior_data']
tasks_created = []
for junior in junior_data:
# create task
task_data = validated_data.copy()
task_data['guardian'] = guardian
task_data['default_image'] = images
task_data['junior'] = junior
instance = JuniorTask.objects.create(**task_data)
tasks_created.append(instance)
send_notification.delay(TASK_ASSIGNED, guardian.user.id, GUARDIAN,
junior.auth.id, {'task_id': instance.id})
return instance
class GuardianDetailSerializer(serializers.ModelSerializer):
@ -323,10 +334,11 @@ class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer"""
junior = JuniorDetailSerializer()
position = serializers.IntegerField()
position = serializers.SerializerMethodField()
class Meta(object):
"""Meta info"""
@ -336,9 +348,13 @@ class TopJuniorSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
"""Convert instance to representation"""
representation = super().to_representation(instance)
representation['position'] = instance.position
return representation
@staticmethod
def get_position(obj):
""" get position/rank """
return obj.rank
class GuardianProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
@ -397,9 +413,9 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
def create(self, validated_data):
"""update guardian code"""
instance = self.context['junior']
instance.guardian_code = [self.context['guardian_code']]
instance.guardian_code_approved = True
instance.guardian_code_status = str(NUMBER['two'])
guardian_code = self.context['guardian_code']
index = instance.guardian_code.index(guardian_code)
instance.guardian_code_status[index] = str(NUMBER['two'])
instance.save()
return instance
@ -427,8 +443,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update complete time of task
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_APPROVED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
send_notification.delay(TASK_APPROVED, instance.guardian.user.id, GUARDIAN,
junior_details.auth.id, {'task_id': instance.id})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
@ -436,8 +452,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update reject time of task
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_REJECTED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
send_notification.delay(TASK_REJECTED, instance.guardian.user.id, GUARDIAN,
junior_details.auth.id, {'task_id': instance.id})
instance.save()
junior_data.save()
return junior_details
@ -507,4 +523,7 @@ class GuardianDetailListSerializer(serializers.ModelSerializer):
def get_guardian_code_status(self,obj):
"""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

View File

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

View File

@ -1,4 +1,5 @@
"""Views of Guardian"""
import math
# django imports
# Import IsAuthenticated
@ -6,6 +7,8 @@
# Import PageNumberPagination
# Import User
# 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 import viewsets, status
from rest_framework.pagination import PageNumberPagination
@ -14,6 +17,7 @@ from base.constants import guardian_code_tuple
from rest_framework.filters import SearchFilter
from django.utils import timezone
from base.pagination import CustomPageNumberPagination
# Import guardian's model,
# Import junior's model,
# Import account's model,
@ -34,12 +38,12 @@ from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification, UserDeviceDetails
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.constants import NUMBER, GUARDIAN_CODE_STATUS
from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED
from notifications.utils import send_notification_to_junior
from notifications.utils import send_notification
""" Define APIs """
# Define Signup API,
@ -133,7 +137,8 @@ class TaskListAPIView(viewsets.ModelViewSet):
Params
status
search
page"""
page
junior"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
@ -143,8 +148,8 @@ class TaskListAPIView(viewsets.ModelViewSet):
def get_queryset(self):
queryset = JuniorTask.objects.filter(guardian__user=self.request.user
).prefetch_related('junior', 'junior__auth'
).order_by('due_date', 'created_at')
).select_related('junior', 'junior__auth'
).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset)
return queryset
@ -152,15 +157,19 @@ class TaskListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
status_value = self.request.GET.get('status')
junior = self.request.GET.get('junior')
queryset = self.get_queryset()
if status_value and status_value != '0':
queryset = queryset.filter(task_status=status_value)
paginator = self.pagination_class()
task_status = task_status_fun(status_value)
if status_value:
queryset = queryset.filter(task_status__in=task_status)
if junior:
queryset = queryset.filter(junior=int(junior))
paginator = CustomPageNumberPagination()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return paginator.get_paginated_response(serializer.data)
class CreateTaskAPIView(viewsets.ModelViewSet):
@ -175,41 +184,52 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
"""
try:
image = request.data['default_image']
junior = request.data['junior']
junior_id = Junior.objects.filter(id=junior).last()
guardian_data = Guardian.objects.filter(user=request.user).last()
if (guardian_data.guardian_code not in junior_id.guardian_code or
junior_id.guardian_code_status in guardian_code_tuple):
return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST)
junior_ids = request.data['junior'].split(',')
invalid_junior_ids = [junior_id for junior_id in junior_ids if not junior_id.isnumeric()]
if invalid_junior_ids:
# At least one junior value is not an integer
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
allowed_extensions = ['.jpg', '.jpeg', '.png']
if not any(extension in str(image) for extension in allowed_extensions):
return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST)
if not junior.isnumeric():
"""junior value must be integer"""
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
data = request.data
if 'https' in str(image):
image_data = image
else:
filename = f"images/{image}"
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
image_url = upload_image_to_alibaba(image, filename)
image_data = image_url
data.pop('default_image')
return custom_error_response(ERROR_CODE['2035'],
response_status=status.HTTP_400_BAD_REQUEST)
image_data = upload_image_to_alibaba(image, filename)
request.data.pop('default_image')
guardian = Guardian.objects.filter(user=request.user).select_related('user').last()
junior_data = Junior.objects.filter(id__in=junior_ids,
guardian_code__contains=[guardian.guardian_code]
).select_related('auth')
if junior_data:
for junior in junior_data:
index = junior.guardian_code.index(guardian.guardian_code)
status_index = junior.guardian_code_status[index]
if status_index == str(NUMBER['three']):
return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
# use TaskSerializer serializer
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
serializer = TaskSerializer(context={"guardian": guardian, "image": image_data,
"junior_data": junior_data}, data=request.data)
if serializer.is_valid():
# save serializer
task = serializer.save()
send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'],
junior_id.auth.id, {'task_id': task.id})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
serializer.save()
# removed send notification method and used in serializer
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)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class SearchTaskListAPIView(viewsets.ModelViewSet):
"""Filter task"""
serializer_class = TaskDetailsSerializer
@ -242,7 +262,6 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Top juniors list
No Params"""
queryset = JuniorPoints.objects.all()
serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
@ -253,15 +272,22 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
context.update({'view': self})
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):
"""Fetch junior list of those who complete their tasks"""
try:
junior_total_points = self.get_queryset().order_by('-total_points')
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
junior.save()
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True)
junior_total_points = self.get_queryset()[:15]
serializer = self.get_serializer(junior_total_points, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -294,13 +320,19 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
if serializer.is_valid():
# save serializer
serializer.save()
send_notification_to_junior.delay(ASSOCIATE_APPROVED, guardian.user.id, junior_queryset.auth.id)
send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN,
junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
junior_queryset.guardian_code = None
junior_queryset.guardian_code_status = str(NUMBER['one'])
if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code):
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()
send_notification_to_junior.delay(ASSOCIATE_REJECTED, guardian.user.id, 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)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -326,6 +358,8 @@ class ApproveTaskAPIView(viewsets.ModelViewSet):
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian,
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):
return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST)
# use ApproveJuniorSerializer serializer
@ -349,7 +383,7 @@ class ApproveTaskAPIView(viewsets.ModelViewSet):
return custom_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
#
class GuardianListAPIView(viewsets.ModelViewSet):
"""Guardian list of assosicated junior"""

View File

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

View File

@ -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),
),
]

View File

@ -76,9 +76,9 @@ class Junior(models.Model):
is_verified = models.BooleanField(default=False)
"""guardian code is approved or not"""
guardian_code_approved = models.BooleanField(default=False)
# guardian code status"""
guardian_code_status = models.CharField(max_length=31, choices=GUARDIAN_CODE_STATUS, default='1',
null=True, blank=True)
# # guardian code status"""
guardian_code_status = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None), null=True,
)
# Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@ -11,18 +11,18 @@ from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
# 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 guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED,
GUARDIAN_CODE_STATUS)
GUARDIAN_CODE_STATUS, JUNIOR)
from guardian.models import Guardian, JuniorTask
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 notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian
from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION,
from notifications.utils import send_notification
from notifications.constants import (ASSOCIATE_REQUEST, ASSOCIATE_JUNIOR, TASK_ACTION,
)
from web_admin.models import ArticleCard
@ -91,20 +91,24 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
# Update guardian code"""
# condition for guardian code
if 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()
if guardian_data:
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)
send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior.auth.id, guardian_data.user.id, {})
junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name)
@ -169,6 +173,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
position = serializers.SerializerMethodField('get_position')
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
def get_auth(self, obj):
@ -185,9 +190,8 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
return data
def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.position
return get_junior_leaderboard_rank(obj)
def get_points(self, obj):
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
@ -214,6 +218,13 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
def get_pending_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
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):
"""Meta info"""
model = Junior
@ -286,8 +297,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
user_data = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user_data.set_password(password)
special_password = make_special_password()
user_data.set_password(special_password)
user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
image=profile_image,
@ -297,8 +308,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True,
guardian_code_status=GUARDIAN_CODE_STATUS[1][0])
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data,
guardian_code_status=[str(NUMBER['two'])])
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
total_junior = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
@ -310,9 +321,9 @@ class AddJuniorSerializer(serializers.ModelSerializer):
# add push notification
UserNotification.objects.get_or_create(user=user_data)
"""Notification email"""
junior_notification_email.delay(email, full_name, email, password)
junior_notification_email.delay(email, full_name, email, special_password)
# push notification
send_notification_to_junior.delay(JUNIOR_ADDED, None, junior_data.auth.id, {})
send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {})
return junior_data
@ -327,9 +338,11 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
if instance:
guardian_code = self.context['guardian_code']
instance.is_invited = False
if instance.guardian_code and ('-' in instance.guardian_code):
instance.guardian_code.remove('-')
index = instance.guardian_code.index(guardian_code)
instance.guardian_code.remove(guardian_code)
if not instance.guardian_code:
instance.guardian_code_status = str(NUMBER['one'])
instance.guardian_code_status.pop(index)
instance.save()
return instance
@ -346,8 +359,8 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id,
instance.guardian.user.id, {'task_id': instance.id})
send_notification.delay(TASK_ACTION, instance.junior.auth.id, JUNIOR,
instance.guardian.user.id, {'task_id': instance.id})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
@ -367,10 +380,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
return obj.junior.id
def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj.junior).last()
if data:
return data.position
return 99999
return get_junior_leaderboard_rank(obj.junior)
def get_points(self, obj):
"""total points"""
points = JuniorPoints.objects.filter(junior=obj.junior).last()
@ -451,13 +461,13 @@ class AddGuardianSerializer(serializers.ModelSerializer):
user_type=str(NUMBER['two']), expired_at=expiry_time,
is_verified=True)
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)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail.delay(email, full_name)
send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {})
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth.id, JUNIOR, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):
@ -510,15 +520,15 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
guardian_code = self.context['guardian_code']
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"})
if not instance.guardian_code:
instance.guardian_code_status = str(NUMBER['one'])
elif instance.guardian_code and (len(instance.guardian_code) == 1 and '-' in instance.guardian_code):
instance.guardian_code_status = str(NUMBER['one'])
else:
instance.guardian_code_status = str(NUMBER['two'])
instance.save()
return instance

View File

@ -5,7 +5,8 @@ from django.conf import settings
from templated_email import send_templated_mail
from .models import JuniorPoints
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
# email for sending email
# when guardian create junior profile
@ -61,3 +62,21 @@ def update_positions_based_on_points():
junior_point.position = position
junior_point.save()
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

View File

@ -12,6 +12,10 @@ import datetime
import requests
from rest_framework.viewsets import GenericViewSet, mixins
import math
from base.pagination import CustomPageNumberPagination
"""Django app import"""
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
@ -42,12 +46,12 @@ from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, Ad
from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ARTICLE_STATUS, none
from account.utils import custom_response, custom_error_response
from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN
from account.utils import custom_response, custom_error_response, task_status_fun
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification, send_notification_to_junior
from notifications.constants import REMOVE_JUNIOR
from notifications.utils import send_notification
from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS, ASSOCIATE_EXISTING_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
StartAssessmentSerializer)
@ -99,7 +103,10 @@ class UpdateJuniorProfile(viewsets.ModelViewSet):
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)
except Exception as e:
error_detail = e.detail.get('error', None)
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):
@ -142,14 +149,15 @@ class JuniorListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
""" junior list"""
try:
update_positions_based_on_points()
# update_positions_based_on_points, function removed
guardian_data = Guardian.objects.filter(user__email=request.user).last()
# fetch junior object
if guardian_data:
queryset = self.get_queryset()
queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code))
# 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_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK)
except Exception as e:
@ -191,7 +199,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST)
elif not data:
return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST)
if data == "Max":
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)
# use AddJuniorSerializer serializer
@ -207,22 +215,30 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
def associate_guardian(self, user):
junior = Junior.objects.filter(auth__email=self.request.data['email']).first()
guardian = Guardian.objects.filter(user=self.request.user).first()
if junior.guardian_code and ('-' in junior.guardian_code):
junior.guardian_code.remove('-')
if not junior:
if junior is None:
return none
if junior.guardian_code and (guardian.guardian_code in junior.guardian_code):
return False
if junior.guardian_code and ('-' in junior.guardian_code):
junior.guardian_code.remove('-')
if not junior.guardian_code:
junior.guardian_code = [guardian.guardian_code]
if type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
elif type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
junior.guardian_code.append(guardian.guardian_code)
else:
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()
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior,
relationship=str(self.request.data['relationship']))
jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior)
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
@ -320,7 +336,8 @@ class RemoveJuniorAPIView(views.APIView):
if serializer.is_valid():
# save serializer
serializer.save()
send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
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_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
@ -333,9 +350,19 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Junior task list"""
serializer_class = TaskDetailsjuniorSerializer
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination
http_method_names = ('get',)
def get_queryset(self):
queryset = JuniorTask.objects.filter(junior__auth=self.request.user
).select_related('junior', 'junior__auth'
).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
"""Junior task list
status=0
@ -343,28 +370,16 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
page=1"""
try:
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
# search with title and for all task list
queryset = JuniorTask.objects.filter(junior__auth=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
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()
queryset = self.get_queryset()
task_status = task_status_fun(status_value)
if status_value:
queryset = queryset.filter(task_status__in=task_status)
paginator = CustomPageNumberPagination()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsjuniorSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
# use TaskDetails juniorSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True)
return paginator.get_paginated_response(serializer.data)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -393,10 +408,12 @@ class CompleteJuniorTaskAPIView(views.APIView):
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user
).select_related('guardian', 'junior').last()
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)
# 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 """
return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST)
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
@ -420,7 +437,7 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet):
"""Junior Points
No Params"""
try:
update_positions_based_on_points()
# update_positions_based_on_points, function removed
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
# update position of junior
serializer = JuniorPointsSerializer(queryset)
@ -501,7 +518,10 @@ class StartTaskAPIView(views.APIView):
try:
task_id = self.request.data.get('task_id')
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.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
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid():
@ -585,8 +605,8 @@ class StartAssessmentAPIView(viewsets.ModelViewSet):
article_id = self.request.GET.get('article_id')
# if referral_code:
article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
'article_survey'
)
return article
def list(self, request, *args, **kwargs):
"""Params
@ -666,6 +686,8 @@ class CompleteArticleAPIView(views.APIView):
is_answer_correct=True).aggregate(
total_earn_points=Sum('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)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -736,6 +758,7 @@ class RemoveGuardianCodeAPIView(views.APIView):
def put(self, request, format=None):
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()
if junior_queryset:
# use RemoveGuardianCodeSerializer serializer
@ -744,6 +767,7 @@ class RemoveGuardianCodeAPIView(views.APIView):
if serializer.is_valid():
# save serializer
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_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:

View File

@ -6,57 +6,76 @@ ASSOCIATE_REQUEST = 3
ASSOCIATE_REJECTED = 4
ASSOCIATE_APPROVED = 5
REFERRAL_POINTS = 6
JUNIOR_ADDED = 7
ASSOCIATE_JUNIOR = 7
ASSOCIATE_EXISTING_JUNIOR = 8
TASK_ASSIGNED = 8
TASK_ACTION = 9
TASK_REJECTED = 10
TASK_APPROVED = 11
TASK_ASSIGNED = 9
TASK_ACTION = 10
TASK_REJECTED = 11
TASK_APPROVED = 12
PENDING_TASK_EXPIRING = 13
IN_PROGRESS_TASK_EXPIRING = 14
TOP_JUNIOR = 15
REMOVE_JUNIOR = 13
NEW_ARTICLE_PUBLISHED = 16
ARTICLE_REWARD_POINTS = 17
REMOVE_JUNIOR = 18
TEST_NOTIFICATION = 99
# notification dictionary
NOTIFICATION_DICT = {
REGISTRATION: {
"title": "Successfully registered!",
"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 for association
ASSOCIATE_REQUEST: {
"title": "Associate request!",
"body": "You have request from {from_user} to associate with you."
},
# Juniors will receive notification when
# custodians reject their request for associate
ASSOCIATE_REJECTED: {
"title": "Associate request rejected!",
"body": "Your request to associate has been rejected by {from_user}."
},
# Juniors will receive notification when
# custodians approve their request for associate
ASSOCIATE_APPROVED: {
"title": "Associate request approved!",
"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: {
"title": "Earn Referral points!",
"body": "You earn 5 points for referral."
},
# Juniors will receive notification once any custodians add them in their account
JUNIOR_ADDED: {
# Juniors will receive notification
# once any custodians add them in their account
ASSOCIATE_JUNIOR: {
"title": "Profile already setup!",
"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: {
"title": "New task assigned!",
"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: {
"title": "Task completion approval!",
"body": "You have request from {from_user} for task completion."
"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: {
"title": "Task completion rejected!",
"body": "Your task completion request has been rejected by {from_user}."
@ -68,11 +87,41 @@ NOTIFICATION_DICT = {
"body": "Your task completion request has been approved by {from_user}. "
"Also you earned 5 points for successful completion."
},
# Juniors will receive notification when their task end date about to end
PENDING_TASK_EXPIRING: {
"title": "Task expiring soon!",
"body": "Your task {task_name} is expiring soon. Please complete it."
},
# User will receive notification when their assigned task is about to end
# and juniors have not performed any action
IN_PROGRESS_TASK_EXPIRING: {
"title": "Task 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."
},
# 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
REMOVE_JUNIOR: {
"title": "Disassociate by guardian!",
"body": "Your guardian has disassociated you."
},
# Test notification
TEST_NOTIFICATION: {
"title": "Test Notification",
"body": "This notification is for testing purpose from {from_user}."

View File

@ -31,11 +31,16 @@ class RegisterDevice(serializers.Serializer):
class NotificationListSerializer(serializers.ModelSerializer):
"""List of notification"""
badge = serializers.SerializerMethodField()
class Meta(object):
"""meta info"""
model = Notification
fields = ['id', 'data', 'is_read', 'created_at']
fields = ['id', 'data', 'badge', 'is_read', 'created_at']
@staticmethod
def get_badge(obj):
return Notification.objects.filter(notification_to=obj.notification_to, is_read=False).count()
class ReadNotificationSerializer(serializers.ModelSerializer):

View File

@ -8,24 +8,24 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati
# django imports
from django.contrib.auth import get_user_model
from django.db.models import Q
# local imports
from account.models import UserNotification
from account.utils import get_user_full_name
from guardian.models import Guardian
from base.constants import GUARDIAN, JUNIOR
from guardian.models import Guardian, JuniorTask
from junior.models import Junior
from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification
# local imports
User = get_user_model()
def register_fcm_token(user_id, registration_id, device_id, device_type):
""" used to register the fcm device token"""
device, _ = FCMDevice.objects.update_or_create(device_id=device_id,
defaults={'user_id': user_id, 'type': device_type,
device, _ = FCMDevice.objects.update_or_create(user_id=user_id,
defaults={'device_id': device_id, 'type': device_type,
'active': True,
'registration_id': registration_id})
return device
@ -42,49 +42,72 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
print(e)
def get_basic_detail(from_user_id, to_user_id):
def get_from_user_details(from_user_id, from_user_type):
"""
used to get the basic details
used to get from user details
"""
from_user = User.objects.get(id=from_user_id) if from_user_id else None
to_user = User.objects.get(id=to_user_id)
return from_user, to_user
from_user = None
from_user_name = None
from_user_image = None
if from_user_id:
if from_user_type == GUARDIAN:
guardian = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
from_user = guardian.user
from_user_name = get_user_full_name(from_user)
from_user_image = guardian.image
elif from_user_type == JUNIOR:
junior = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
from_user = junior.auth
from_user_name = get_user_full_name(from_user)
from_user_image = junior.image
return from_user_name, from_user_image, from_user
def get_notification_data(notification_type, from_user, to_user, extra_data):
def get_notification_data(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
"""
get notification and push data
:param from_user_type: GUARDIAN or JUNIOR
:param notification_type: notification_type
:param from_user: from_user obj
:param to_user: to_user obj
:param from_user_id: from_user obj
:param to_user_id: to_user obj
:param extra_data: any extra data provided
:return: notification and push data
"""
push_data = NOTIFICATION_DICT[notification_type].copy()
notification_data = push_data.copy()
notification_data['to_user'] = get_user_full_name(to_user)
if from_user:
from_user_name = get_user_full_name(from_user)
push_data['body'] = push_data['body'].format(from_user=from_user_name)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name)
notification_data['from_user'] = from_user_name
task_name = None
points = extra_data.get('points', None)
if 'task_id' in extra_data:
task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first()
task_name = task.task_name
extra_data['task_name'] = task_name
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)
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, points=points)
notification_data['from_user'] = from_user_name
notification_data['from_user_image'] = from_user_image
notification_data.update(extra_data)
return notification_data, push_data
to_user = User.objects.filter(id=to_user_id).first()
return notification_data, push_data, from_user, to_user
def send_notification(notification_type, from_user, to_user, extra_data):
@shared_task()
def send_notification(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
"""
used to send the push for the given notification type
"""
# (from_user, to_user) = get_basic_detail(from_user_id, to_user_id)
notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data)
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
from_user_type, to_user_id, extra_data)
user_notification_type = UserNotification.objects.filter(user=to_user).first()
notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
notification_to=to_user, data=notification_data)
if user_notification_type.push_notification:
if user_notification_type and user_notification_type.push_notification:
send_push(to_user, push_data)
@ -95,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()
def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data):
"""
@ -104,14 +160,10 @@ def send_notification_to_guardian(notification_type, from_user_id, to_user_id, e
:param extra_data:
:return:
"""
from_user = None
if from_user_id:
from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
from_user = Junior.objects.filter(auth_id=from_user_id).first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.auth
to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.user, extra_data)
send_notification(notification_type, from_user_id, to_user_id, extra_data)
@shared_task()
@ -123,13 +175,7 @@ def send_notification_to_junior(notification_type, from_user_id, to_user_id, ext
:param extra_data:
:return:
"""
from_user = None
if from_user_id:
from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
from_user = Guardian.objects.filter(user_id=from_user_id).first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.user
to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.auth, extra_data)
send_notification(notification_type, from_user_id, to_user_id, extra_data)

View File

@ -4,33 +4,40 @@ notifications views file
# django imports
from django.db.models import Q
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework import viewsets, status, views
# local imports
from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE
from base.pagination import CustomPageNumberPagination
from base.tasks import notify_task_expiry, notify_top_junior
from notifications.constants import TEST_NOTIFICATION
# Import serializer
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior
# Import model
from notifications.utils import send_notification
from notifications.models import Notification
class NotificationViewSet(viewsets.GenericViewSet):
""" used to do the notification actions """
"""
used to do the notification actions
"""
serializer_class = NotificationListSerializer
permission_classes = [IsAuthenticated, ]
def list(self, request, *args, **kwargs) -> Response:
""" list the notifications """
"""
to list user's notifications
:param request:
:return:
"""
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id')
paginator = self.pagination_class()
paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data)
return paginator.get_paginated_response(serializer.data)
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request):
@ -46,13 +53,13 @@ class NotificationViewSet(viewsets.GenericViewSet):
@action(methods=['get'], detail=False, url_path='test', url_name='test')
def send_test_notification(self, request):
"""
to send test notification
to test send notification, task expiry, top junior
:return:
"""
send_notification_to_guardian.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
send_notification_to_junior.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
notify_task_expiry()
notify_top_junior()
send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'],
{})
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False,

View File

@ -1,18 +0,0 @@
"""
web_admin pagination file
"""
# third party imports
from rest_framework.pagination import PageNumberPagination
from base.constants import NUMBER
class CustomPageNumberPagination(PageNumberPagination):
"""
custom paginator class
"""
# Set the desired page size
page_size = NUMBER['ten']
page_size_query_param = 'page_size'
# Set a maximum page size if needed
max_page_size = NUMBER['hundred']

View File

@ -7,8 +7,9 @@ from rest_framework import serializers
# django imports
from django.contrib.auth import get_user_model
from account.utils import get_user_full_name
# local imports
from base.constants import USER_TYPE
from base.constants import USER_TYPE, JUNIOR
from junior.models import JuniorPoints, Junior
@ -36,7 +37,7 @@ class JuniorLeaderboardSerializer(serializers.ModelSerializer):
:param obj: junior object
:return: full name
"""
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
return get_user_full_name(obj.auth)
@staticmethod
def get_first_name(obj):
@ -59,6 +60,8 @@ class LeaderboardSerializer(serializers.ModelSerializer):
"""
leaderboard serializer
"""
user_id = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
junior = JuniorLeaderboardSerializer()
rank = serializers.IntegerField()
@ -67,13 +70,22 @@ class LeaderboardSerializer(serializers.ModelSerializer):
meta class
"""
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):
"""
user csv/xls report serializer
"""
name = serializers.SerializerMethodField()
phone_number = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
is_active = serializers.SerializerMethodField()
@ -84,7 +96,15 @@ class UserCSVReportSerializer(serializers.ModelSerializer):
meta class
"""
model = USER
fields = ('first_name', 'last_name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
fields = ('name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
@staticmethod
def get_name(obj):
"""
:param obj: user object
:return: full name
"""
return get_user_full_name(obj)
@staticmethod
def get_phone_number(obj):

View File

@ -10,9 +10,12 @@ from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE
# local imports
from base.messages import ERROR_CODE
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.utils import pop_id, get_image_url
from junior.models import JuniorArticlePoints, JuniorArticle
USER = get_user_model()
@ -79,7 +82,7 @@ class ArticleSerializer(serializers.ModelSerializer):
meta class
"""
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey')
fields = ('id', 'title', 'description', 'is_published', 'article_cards', 'article_survey')
def validate(self, attrs):
"""
@ -88,10 +91,9 @@ class ArticleSerializer(serializers.ModelSerializer):
"""
article_cards = attrs.get('article_cards', None)
article_survey = attrs.get('article_survey', None)
if article_cards is None or len(article_cards) > int(MAX_ARTICLE_CARD):
if not 0 < len(article_cards) <= int(MAX_ARTICLE_CARD):
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int(
MAX_ARTICLE_SURVEY) < len(article_survey):
if not int(MIN_ARTICLE_SURVEY) <= len(article_survey) <= int(MAX_ARTICLE_SURVEY):
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
return attrs
@ -119,11 +121,15 @@ class ArticleSerializer(serializers.ModelSerializer):
option = pop_id(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
def update(self, instance, validated_data):
"""
to update article and related table
:param validated_data:
:param instance: article object,
:return: article object
"""
@ -179,6 +185,28 @@ class ArticleSerializer(serializers.ModelSerializer):
return instance
class ArticleStatusChangeSerializer(serializers.ModelSerializer):
"""
Article status change serializer
"""
class Meta:
"""
meta class
"""
model = Article
fields = ('is_published', )
def update(self, instance, validated_data):
"""
:param instance: article object
:param validated_data:
:return:
"""
instance.is_published = validated_data['is_published']
instance.save()
return instance
class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
"""
Article Card serializer
@ -215,6 +243,7 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
card_image = DefaultArticleCardImage.objects.create(**validated_data)
return card_image
class ArticleListSerializer(serializers.ModelSerializer):
"""
serializer for article API
@ -228,13 +257,14 @@ class ArticleListSerializer(serializers.ModelSerializer):
meta class
"""
model = Article
fields = ('id', 'title', 'description','image', '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):
"""total points of article"""
return obj.article_survey.all().count() * NUMBER['five']
@ -247,6 +277,7 @@ class ArticleListSerializer(serializers.ModelSerializer):
return junior_article.is_completed
return False
class ArticleQuestionSerializer(serializers.ModelSerializer):
"""
article survey serializer
@ -257,7 +288,6 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
correct_answer = serializers.SerializerMethodField('get_correct_answer')
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
def get_is_attempt(self, obj):
"""attempt question or not"""
context_data = self.context.get('user')
@ -289,6 +319,7 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
model = ArticleSurvey
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
class StartAssessmentSerializer(serializers.ModelSerializer):
"""
serializer for article API
@ -300,9 +331,11 @@ class StartAssessmentSerializer(serializers.ModelSerializer):
"""current page"""
context_data = self.context.get('user')
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
total_count = obj.article_survey.all().count()
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']
class Meta(object):
"""
meta class
@ -311,7 +344,6 @@ class StartAssessmentSerializer(serializers.ModelSerializer):
fields = ('article_survey', 'current_page')
class ArticleCardlistSerializer(serializers.ModelSerializer):
"""
Article Card serializer

View File

@ -14,7 +14,7 @@ from account.models import UserEmailOtp
from base.constants import USER_TYPE
from base.messages import ERROR_CODE
from guardian.tasks import generate_otp
from base.tasks import send_email_otp
from base.tasks import send_email
USER = get_user_model()
@ -48,11 +48,13 @@ class AdminOTPSerializer(serializers.ModelSerializer):
:return: user_data
"""
email = validated_data['email']
verification_code = generate_otp()
template = 'email_reset_verification.email'
# Send the verification code to the user's email
send_email_otp.delay(email, verification_code)
data = {
"verification_code": verification_code
}
send_email.delay([email], template, data)
expiry = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.update_or_create(email=email,

View File

@ -5,6 +5,7 @@ web_admin user_management serializers file
from rest_framework import serializers
from django.contrib.auth import get_user_model
from account.utils import get_user_full_name
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
# local imports
from base.messages import ERROR_CODE, SUCCESS_CODE
@ -37,7 +38,7 @@ class UserManagementListSerializer(serializers.ModelSerializer):
:param obj: user object
:return: full name
"""
return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name
return get_user_full_name(obj)
@staticmethod
def get_country_code(obj):
@ -144,7 +145,7 @@ class GuardianSerializer(serializers.ModelSerializer):
:param obj: guardian object
:return: full name
"""
return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name
return get_user_full_name(obj.user)
@staticmethod
def get_first_name(obj):
@ -222,7 +223,7 @@ class JuniorSerializer(serializers.ModelSerializer):
:param obj: junior object
:return: full name
"""
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
return get_user_full_name(obj.auth)
@staticmethod
def get_first_name(obj):

View File

@ -22,12 +22,12 @@ from django.db.models.functions.window import Rank
from django.http import HttpResponse
# local imports
from account.utils import custom_response
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS
from account.utils import custom_response, get_user_full_name
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, TASK_STATUS
from guardian.models import JuniorTask
from guardian.utils import upload_excel_file_to_alibaba
from junior.models import JuniorPoints
from web_admin.pagination import CustomPageNumberPagination
from base.pagination import CustomPageNumberPagination
from web_admin.permission import AdminPermission
from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer
from web_admin.utils import get_dates
@ -107,13 +107,15 @@ class AnalyticsViewSet(GenericViewSet):
assign_tasks = JuniorTask.objects.filter(
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
).exclude(task_status__in=[PENDING, EXPIRED])
)
data = {
'task_completed': assign_tasks.filter(task_status=COMPLETED).count(),
'task_pending': assign_tasks.filter(task_status=PENDING).count(),
'task_in_progress': assign_tasks.filter(task_status=IN_PROGRESS).count(),
'task_requested': assign_tasks.filter(task_status=REQUESTED).count(),
'task_rejected': assign_tasks.filter(task_status=REJECTED).count(),
'task_expired': assign_tasks.filter(task_status=EXPIRED).count(),
}
return custom_response(None, data)
@ -126,10 +128,12 @@ class AnalyticsViewSet(GenericViewSet):
:param request:
:return:
"""
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
expression=Rank(),
order_by=[F('total_points').desc(), 'junior__created_at']
)).order_by('-total_points', 'junior__created_at')
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')
paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
@ -169,10 +173,9 @@ class AnalyticsViewSet(GenericViewSet):
serializer = UserCSVReportSerializer(queryset, many=True)
df_users = pd.DataFrame([
{'Name': f"{user['first_name']} {user['last_name']}",
'Email': user['email'], 'Phone Number': user['phone_number'],
'User Type': user['user_type'], 'Status': user['is_active'],
'Date Joined': user['date_joined']}
{'Name': user['name'], 'Email': user['email'],
'Phone Number': user['phone_number'], 'User Type': user['user_type'],
'Status': user['is_active'], 'Date Joined': user['date_joined']}
for user in serializer.data
])
write_excel_worksheet(worksheet, df_users)
@ -181,10 +184,16 @@ class AnalyticsViewSet(GenericViewSet):
elif sheet_name == 'Assign Tasks':
assign_tasks = JuniorTask.objects.filter(
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
).exclude(task_status__in=[PENDING, EXPIRED])
).select_related('junior__auth', 'guardian__user').only('task_name', 'task_status',
'junior__auth__first_name',
'junior__auth__last_name',
'guardian__user__first_name',
'guardian__user__last_name',)
df_tasks = pd.DataFrame([
{'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()}
{'Task Name': task.task_name, 'Assign To': get_user_full_name(task.junior.auth),
'Assign By': get_user_full_name(task.guardian.user),
'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()}
for task in assign_tasks
])
@ -192,14 +201,15 @@ class AnalyticsViewSet(GenericViewSet):
# sheet 3 for Juniors Leaderboard and rank
elif sheet_name == 'Juniors Leaderboard':
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
expression=Rank(),
order_by=[F('total_points').desc(), 'junior__created_at']
)).order_by('-total_points', 'junior__created_at')[:15]
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')[:15]
df_leaderboard = pd.DataFrame([
{
'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}"
if junior.junior.auth.last_name else junior.junior.auth.first_name,
'Name': get_user_full_name(junior.junior.auth),
'Points': junior.total_points,
'Rank': junior.rank
}

View File

@ -17,7 +17,7 @@ from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticle
from web_admin.permission import AdminPermission
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
DefaultArticleCardImageSerializer, ArticleListSerializer,
ArticleCardlistSerializer)
ArticleCardlistSerializer, ArticleStatusChangeSerializer)
USER = get_user_model()
@ -32,7 +32,6 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
queryset = Article
filter_backends = (SearchFilter,)
search_fields = ['title']
http_method_names = ['get', 'post', 'put', 'delete']
def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
@ -130,6 +129,23 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
return custom_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
@action(methods=['patch'], url_name='status-change', url_path='status-change',
detail=True, serializer_class=ArticleStatusChangeSerializer)
def article_status_change(self, request, *args, **kwargs):
"""
article un-publish or publish api method
:param request: article id and
{
"is_published": true/false
}
:return: success message
"""
article = Article.objects.filter(id=kwargs['pk']).first()
serializer = self.serializer_class(article, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3038"])
@action(methods=['get'], url_name='remove-card', url_path='remove-card',
detail=True)
def remove_article_card(self, request, *args, **kwargs):
@ -214,7 +230,7 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
:param request:
:return: default article card images
"""
queryset = self.queryset
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True)
return custom_response(None, data=serializer.data)

View File

@ -12,8 +12,9 @@ from django.db.models import Q
# local imports
from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
from base.messages import SUCCESS_CODE, ERROR_CODE
from base.tasks import send_email
from guardian.models import Guardian
from junior.models import Junior
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')]:
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'):
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,
request.data, context={'user_id': kwargs['pk']})
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,
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
: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)
queryset = self.queryset
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.guardian_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.junior_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()
email_template = 'user_deactivate.email'
if user_type == GUARDIAN:
obj = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).select_related('user').first()
elif user_type == JUNIOR:
obj = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first()
if obj.is_active:
obj.is_active = False
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'])

View File

@ -27,19 +27,24 @@ app.config_from_object('django.conf:settings')
# Load task modules from all registered Django apps.
app.autodiscover_tasks()
# scheduled task
app.conf.beat_schedule = {
"expired_task": {
"task": "guardian.utils.update_expired_task_status",
"schedule": crontab(minute=0, hour=0),
},
'notify_task_expiry': {
'task': 'base.tasks.notify_task_expiry',
'schedule': crontab(minute='0', hour='18'),
},
'notify_top_junior': {
'task': 'base.tasks.notify_top_junior',
'schedule': crontab(minute='0', hour='*/2'),
},
}
@app.task(bind=True)
def debug_task(self):
""" celery debug task """
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),
},
}

View File

@ -35,9 +35,28 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = os.getenv('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG')
ENV = os.getenv('ENV')
# cors allow setting
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_ALLOW_ALL = False
# Allow specific origins
if ENV in ['dev', 'qa', 'stage']:
CORS_ALLOWED_ORIGINS = [
# backend base url
"https://dev-api.zodqaapp.com",
"https://qa-api.zodqaapp.com",
"https://stage-api.zodqaapp.com",
# frontend url
"http://localhost:3000",
"https://zod-dev.zodqaapp.com",
"https://zod-qa.zodqaapp.com",
"https://zod-stage.zodqaapp.com",
# Add more trusted origins as needed
]
if ENV == "prod":
CORS_ALLOWED_ORIGINS = []
# allow all host
ALLOWED_HOSTS = ['*']
@ -53,7 +72,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Add Django rest frame work apps here
# Add Django rest framework apps here
'django_extensions',
'storages',
'drf_yasg',

View File

@ -20,12 +20,11 @@ from django.urls import path, include
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from django.urls import path
from django.conf import settings
schema_view = get_schema_view(openapi.Info(title="Zod Bank API", default_version='v1'), public=True, )
urlpatterns = [
path('apidoc/', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'),
path('admin/', admin.site.urls),
path('', include(('account.urls', 'account'), namespace='account')),
path('', include('guardian.urls')),
@ -33,3 +32,6 @@ urlpatterns = [
path('', include(('notifications.urls', 'notifications'), namespace='notifications')),
path('', include(('web_admin.urls', 'web_admin'), namespace='web_admin')),
]
if settings.DEBUG:
urlpatterns += [(path('apidoc/', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'))]