Compare commits

..

13 Commits

Author SHA1 Message Date
c46b2ef52c sonar fixes 2023-08-25 16:59:17 +05:30
c47f6222d9 requested task not expired 2023-08-25 16:36:58 +05:30
e9315beab9 remove code 2023-08-25 12:57:15 +05:30
a65eb2f77d assessment answer 2023-08-24 18:58:31 +05:30
f5a03e2fdf assement id 2023-08-24 18:26:15 +05:30
1028822908 article-list optimization 2023-08-24 18:00:14 +05:30
e9aa2dfda9 limit for 3 guardian code and article list API optimization 2023-08-24 16:08:20 +05:30
ab1a2be679 remove junior by guardian 2023-08-24 13:32:08 +05:30
11605540d7 remove guardian code request 2023-08-24 13:18:23 +05:30
8cd4864748 add three guardian at a time 2023-08-24 13:13:25 +05:30
b1b7c42438 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint5 2023-08-24 11:27:13 +05:30
8f9450b743 set guardian code is None 2023-08-24 11:10:20 +05:30
bb3441e4ed remove guardian code in both case 2023-08-23 19:23:31 +05:30
23 changed files with 197 additions and 268 deletions

View File

@ -17,6 +17,8 @@ from guardian.models import Guardian
# multiple devices only
# user can login in single
# device at a time"""
# force update
# use 308 status code for force update
def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND):
"""custom response"""
@ -45,7 +47,11 @@ class CustomMiddleware(object):
api_endpoint = request.path
if request.user.is_authenticated:
# device details
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if device_id:
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if not device_details and api_endpoint != '/api/v1/user/login/':
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']):
junior = Junior.objects.filter(auth=request.user, is_active=False).last()
if junior:
@ -56,13 +62,11 @@ class CustomMiddleware(object):
if guardian:
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error)
if device_id and not device_details and api_endpoint != '/api/v1/user/login/':
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error)
force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last()
api_endpoint_checks = not any(endpoint in api_endpoint for endpoint in ['/admin/', '/api/v1/admin/'])
if not force_update and version and device_type:
custom_error = custom_error_response(ERROR_CODE['2079'],
response_status=status.HTTP_308_PERMANENT_REDIRECT)
response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT)
if version and device_type:
force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last()
if not force_update:
custom_error = custom_error_response(ERROR_CODE['2079'],
response_status=status.HTTP_308_PERMANENT_REDIRECT)
response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT)
return response

View File

@ -123,7 +123,7 @@ class ChangePasswordSerializer(serializers.Serializer):
def create(self, validated_data):
"""
change password
"""
new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password')
@ -392,7 +392,8 @@ class UserPhoneOtpSerializer(serializers.ModelSerializer):
fields = '__all__'
class ForceUpdateSerializer(serializers.ModelSerializer):
# ForceUpdate Serializer
""" ForceUpdate Serializer
"""
class Meta(object):
""" meta info """

View File

@ -93,7 +93,7 @@ def junior_account_update(user_tb):
# Update junior account
junior_data.is_active = False
junior_data.is_verified = False
junior_data.guardian_code = '{}'
junior_data.guardian_code = None
junior_data.guardian_code_status = str(NUMBER['one'])
junior_data.is_deleted = True
junior_data.save()
@ -282,9 +282,8 @@ 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.first_name or user_obj.last_name else "User"
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name

View File

@ -43,7 +43,8 @@ FILE_SIZE = 5 * 1024 * 1024
# String constant for configurable date for allocation lock period
ALLOCATION_LOCK_DATE = 1
# guardian code status tuple
guardian_code_tuple = ('1','3')
"""user type"""
USER_TYPE = (
('1', 'junior'),

View File

@ -101,12 +101,17 @@ ERROR_CODE = {
"2072": "You can not approve or reject this task because junior does not exist in the system",
"2073": "You can not approve or reject this junior because junior does not exist in the system",
"2074": "You can not complete this task because you does not exist in the system",
# deactivate account
"2075": "Your account is deactivated. Please contact with admin",
"2076": "This junior already associate with you",
"2077": "You can not add guardian",
"2078": "This junior is not associate with you",
# force update
"2079": "Please update your app version for enjoying uninterrupted services",
"2080": "Can not add App version"
"2080": "Can not add App version",
"2081": "You can not add more than 3 guardian",
# guardian code not exist
"2082": "Guardian code does not exist"
}
"""Success message code"""

View File

@ -1,8 +1,6 @@
"""
web_admin tasks file
"""
import datetime
# third party imports
from celery import shared_task
from templated_email import send_templated_mail
@ -10,12 +8,6 @@ from templated_email import send_templated_mail
# django imports
from django.conf import settings
# local imports
from base.constants import PENDING, IN_PROGRESS, JUNIOR
from guardian.models import JuniorTask
from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING
from notifications.utils import send_notification
@shared_task
def send_email_otp(email, verification_code):
@ -35,45 +27,3 @@ def send_email_otp(email, verification_code):
}
)
return True
@shared_task
def send_mail(recipient_list, template, context: dict = None):
"""
used to send otp on email
: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 = recipient_list
send_templated_mail(
template_name=template,
from_email=from_email,
recipient_list=recipient_list,
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

Binary file not shown.

View File

@ -24,18 +24,19 @@ 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, GUARDIAN
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant
from notifications.constants import TASK_APPROVED, TASK_REJECTED
# send notification function
from notifications.utils import send_notification
from notifications.utils import send_notification, send_notification_to_junior
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
# In this serializer file
# define user serializer,
# define password validation
# create guardian serializer,
# task serializer,
# guardian serializer,
@ -47,6 +48,7 @@ from django.utils.translation import gettext as _
from rest_framework import serializers
class PasswordValidator:
"""Password validation"""
def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True):
self.min_length = min_length
self.max_length = max_length
@ -57,6 +59,7 @@ class PasswordValidator:
self.enforce_password_policy(value)
def enforce_password_policy(self, password):
# add validation for password
special_characters = "!@#$%^&*()_-+=<>?/[]{}|"
if len(password) < self.min_length:
raise serializers.ValidationError(
@ -64,16 +67,20 @@ class PasswordValidator:
)
if self.max_length is not None and len(password) > self.max_length:
# must be 8 character
raise serializers.ValidationError(
_("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length}
)
if self.require_uppercase and not any(char.isupper() for char in password):
# must contain upper case letter
raise serializers.ValidationError(_("Password must contain at least one uppercase letter."))
if self.require_numbers and not any(char.isdigit() for char in password):
# must contain digit
raise serializers.ValidationError(_("Password must contain at least one digit."))
if self.require_numbers and not any(char in special_characters for char in password):
# must contain special character
raise serializers.ValidationError(_("Password must contain at least one special character."))
@ -420,8 +427,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.delay(TASK_APPROVED, instance.guardian.user.id, GUARDIAN,
junior_details.auth.id, {'task_id': instance.id})
send_notification_to_junior.delay(TASK_APPROVED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
@ -429,8 +436,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.delay(TASK_REJECTED, instance.guardian.user.id, GUARDIAN,
junior_details.auth.id, {'task_id': instance.id})
send_notification_to_junior.delay(TASK_REJECTED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
instance.save()
junior_data.save()
return junior_details

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
from notifications.utils import send_notification, send_notification_to_junior
# 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.delay(REFERRAL_POINTS, None, None, junior_queryset.auth.id, {})
send_notification_to_junior.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
@ -127,7 +127,7 @@ def update_expired_task_status(data=None):
Update task of the status if due date is in past
"""
try:
task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])]
task_status = [str(NUMBER['one']), str(NUMBER['two'])]
JuniorTask.objects.filter(due_date__lt=datetime.today().date(),
task_status__in=task_status).update(task_status=str(NUMBER['six']))
except ObjectDoesNotExist as e:

View File

@ -10,7 +10,7 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
from base.constants import guardian_code_tuple
from rest_framework.filters import SearchFilter
from django.utils import timezone
@ -36,10 +36,10 @@ 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 base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
from base.constants import NUMBER, GUARDIAN_CODE_STATUS
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED
from notifications.utils import send_notification
from notifications.utils import send_notification_to_junior
""" Define APIs """
# Define Signup API,
@ -178,7 +178,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
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:
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)
allowed_extensions = ['.jpg', '.jpeg', '.png']
if not any(extension in str(image) for extension in allowed_extensions):
@ -202,8 +203,8 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
# save serializer
task = serializer.save()
send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN,
junior_id.auth.id, {'task_id': task.id})
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)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
@ -287,19 +288,19 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
if request.data['action'] == '1':
# use ApproveJuniorSerializer serializer
serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code,
"junior": junior_queryset, "action": request.data['action']},
"junior": junior_queryset,
"action": request.data['action']},
data=request.data)
if serializer.is_valid():
# save serializer
serializer.save()
send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN,
junior_queryset.auth.id)
send_notification_to_junior.delay(ASSOCIATE_APPROVED, guardian.user.id, 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'])
junior_queryset.save()
send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id)
send_notification_to_junior.delay(ASSOCIATE_REJECTED, guardian.user.id, 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)

View File

@ -16,12 +16,12 @@ from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, Juni
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, JUNIOR)
GUARDIAN_CODE_STATUS)
from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification
from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian
from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION,
)
from web_admin.models import ArticleCard
@ -88,17 +88,22 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
if junior:
"""update details according to the data get from request"""
junior.gender = validated_data.get('gender',junior.gender)
"""Update guardian code"""
junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code)
"""condition for guardian code"""
# Update guardian code"""
# condition for guardian code
if guardian_code:
junior.guardian_code = guardian_code
if not junior.guardian_code:
junior.guardian_code = []
junior.guardian_code.extend(guardian_code)
elif len(junior.guardian_code) < 3 and len(guardian_code) < 3:
junior.guardian_code.extend(guardian_code)
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'])
junior_approval_mail.delay(user.email, user.first_name)
send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {})
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)
@ -307,7 +312,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email.delay(email, full_name, email, password)
# push notification
send_notification.delay(JUNIOR_ADDED, None, None, junior_data.auth.id, {})
send_notification_to_junior.delay(JUNIOR_ADDED, None, junior_data.auth.id, {})
return junior_data
@ -320,9 +325,11 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
fields = ('id', 'is_invited')
def update(self, instance, validated_data):
if instance:
guardian_code = self.context['guardian_code']
instance.is_invited = False
instance.guardian_code = '{}'
instance.guardian_code_status = str(NUMBER['one'])
instance.guardian_code.remove(guardian_code)
if not instance.guardian_code:
instance.guardian_code_status = str(NUMBER['one'])
instance.save()
return instance
@ -339,8 +346,8 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification.delay(TASK_ACTION, instance.junior.auth.id, JUNIOR,
instance.guardian.user.id, {'task_id': instance.id})
send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id,
instance.guardian.user.id, {'task_id': instance.id})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
@ -450,7 +457,7 @@ class AddGuardianSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail.delay(email, full_name)
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth.id, JUNIOR, guardian_data.user.id, {})
send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):
@ -494,8 +501,6 @@ class ReAssignTaskSerializer(serializers.ModelSerializer):
instance.save()
return instance
class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
@ -503,24 +508,33 @@ class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
model = Junior
fields = ('id', )
def update(self, instance, validated_data):
instance.guardian_code = None
instance.guardian_code_status = str(NUMBER['one'])
guardian_code = self.context['guardian_code']
if guardian_code in instance.guardian_code:
instance.guardian_code.remove(guardian_code)
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
class FAQSerializer(serializers.ModelSerializer):
# FAQ Serializer
"""FAQ Serializer"""
class Meta(object):
# meta info
"""meta info"""
model = FAQ
fields = ('id', 'question', 'description')
class CreateArticleCardSerializer(serializers.ModelSerializer):
# Article card Serializer
"""Article card Serializer"""
class Meta(object):
# meta info
"""meta info"""
model = ArticleCard
fields = ('id', 'article')

View File

@ -62,5 +62,5 @@ urlpatterns = [
path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()),
path('api/v1/complete-article/', CompleteArticleAPIView.as_view()),
path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()),
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view()),
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view())
]

View File

@ -46,7 +46,7 @@ from base.constants import NUMBER, ARTICLE_STATUS, none
from account.utils import custom_response, custom_error_response
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification
from notifications.utils import send_notification, send_notification_to_junior
from notifications.constants import REMOVE_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
@ -99,7 +99,8 @@ 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:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
error_detail = e.detail.get('error', None)
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
class ValidateGuardianCode(viewsets.ModelViewSet):
"""Check guardian code exist or not"""
@ -190,6 +191,8 @@ 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":
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
serializer = AddJuniorSerializer(data=request.data, context=info_data)
@ -204,14 +207,18 @@ 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:
return none
if junior.guardian_code and (guardian.guardian_code in junior.guardian_code):
return False
if type(junior.guardian_code) is list:
if not junior.guardian_code:
junior.guardian_code = [guardian.guardian_code]
if type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
junior.guardian_code.append(guardian.guardian_code)
else:
junior.guardian_code = [guardian.guardian_code]
return "Max"
junior.guardian_code_status = str(NUMBER['two'])
junior.save()
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior,
@ -259,10 +266,10 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
manual_parameters=[
# Example of a query parameter
openapi.Parameter(
'title', # Query parameter name
openapi.IN_QUERY, # Parameter location
'title',
openapi.IN_QUERY,
description='title of the name',
type=openapi.TYPE_STRING, # Parameter type
type=openapi.TYPE_STRING,
),
# Add more parameters as needed
]
@ -308,11 +315,12 @@ class RemoveJuniorAPIView(views.APIView):
guardian_code__icontains=str(guardian.guardian_code)).last()
if junior_queryset:
# use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
serializer = RemoveJuniorSerializer(junior_queryset, context={"guardian_code":guardian.guardian_code},
data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {})
send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
@ -615,7 +623,7 @@ class CheckAnswerAPIView(viewsets.ModelViewSet):
answer_id = self.request.GET.get('answer_id')
current_page = self.request.GET.get('current_page')
queryset = self.get_queryset()
submit_ans = SurveyOption.objects.filter(id=answer_id, is_answer=True).last()
submit_ans = SurveyOption.objects.filter(id=answer_id).last()
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
question=queryset)
if submit_ans:
@ -718,16 +726,21 @@ class CreateArticleCardAPIView(viewsets.ModelViewSet):
class RemoveGuardianCodeAPIView(views.APIView):
"""Remove guardian code request API
No Payload"""
Payload
{"guardian_code"
:"GRD037"
}"""
serializer_class = RemoveGuardianCodeSerializer
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
guardian_code = self.request.data.get("guardian_code")
junior_queryset = Junior.objects.filter(auth=self.request.user).last()
if junior_queryset:
# use RemoveGuardianCodeSerializer serializer
serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True)
serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code},
data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
@ -737,7 +750,8 @@ class RemoveGuardianCodeAPIView(views.APIView):
# task in another state
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
error_detail = e.detail.get('error', None)
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
class FAQViewSet(GenericViewSet, mixins.CreateModelMixin,

View File

@ -12,9 +12,8 @@ TASK_ASSIGNED = 8
TASK_ACTION = 9
TASK_REJECTED = 10
TASK_APPROVED = 11
PENDING_TASK_EXPIRING = 12
IN_PROGRESS_TASK_EXPIRING = 13
REMOVE_JUNIOR = 15
REMOVE_JUNIOR = 13
TEST_NOTIFICATION = 99
@ -23,18 +22,16 @@ NOTIFICATION_DICT = {
"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}."
@ -57,7 +54,7 @@ NOTIFICATION_DICT = {
# Guardian will receive notification as soon as junior send task for approval
TASK_ACTION: {
"title": "Task completion approval!",
"body": "{from_user} completed her task {task_name}."
"body": "You have request from {from_user} for task completion."
},
# Juniors will receive notification as soon as their task is rejected by custodians
TASK_REJECTED: {
@ -71,24 +68,11 @@ 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 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

@ -9,15 +9,15 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati
# django imports
from django.contrib.auth import get_user_model
# local imports
from account.models import UserNotification
from account.utils import get_user_full_name
from base.constants import GUARDIAN, JUNIOR
from guardian.models import Guardian, JuniorTask
from guardian.models import Guardian
from junior.models import Junior
from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification
# local imports
User = get_user_model()
@ -42,69 +42,49 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
print(e)
def get_from_user_details(from_user_id, from_user_type):
def get_basic_detail(from_user_id, to_user_id):
"""
used to get from user details
used to get the basic details
"""
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
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
def get_notification_data(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
def get_notification_data(notification_type, from_user, to_user, extra_data):
"""
get notification and push data
:param from_user_type: GUARDIAN or JUNIOR
:param notification_type: notification_type
:param from_user_id: from_user obj
:param to_user_id: to_user obj
:param from_user: from_user obj
:param to_user: to_user obj
:param extra_data: any extra data provided
:return: notification and push data
"""
push_data = NOTIFICATION_DICT[notification_type].copy()
notification_data = push_data.copy()
task_name = 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_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)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name, task_name=task_name)
notification_data['from_user'] = from_user_name
notification_data['from_user_image'] = from_user_image
notification_data['to_user'] = get_user_full_name(to_user)
if from_user:
from_user_name = get_user_full_name(from_user)
push_data['body'] = push_data['body'].format(from_user=from_user_name)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name)
notification_data['from_user'] = from_user_name
notification_data.update(extra_data)
to_user = User.objects.get(id=to_user_id)
return notification_data, push_data, from_user, to_user
return notification_data, push_data
@shared_task()
def send_notification(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
def send_notification(notification_type, from_user, to_user, extra_data):
"""
used to send the push for the given notification type
"""
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
from_user_type, to_user_id, extra_data)
# (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)
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 and user_notification_type.push_notification:
if user_notification_type.push_notification:
send_push(to_user, push_data)
@ -124,10 +104,14 @@ 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).first()
from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
extra_data['from_user_image'] = from_user.image
send_notification(notification_type, from_user_id, to_user_id, extra_data)
from_user = from_user.auth
to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.user, extra_data)
@shared_task()
@ -139,9 +123,13 @@ 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).first()
from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
extra_data['from_user_image'] = from_user.image
send_notification(notification_type, from_user_id, to_user_id, extra_data)
from_user = from_user.user
to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.auth, extra_data)

View File

@ -4,33 +4,27 @@ notifications views file
# django imports
from django.db.models import Q
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import viewsets, status, views
# local imports
from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE
from base.tasks import notify_task_expiry
from notifications.constants import TEST_NOTIFICATION
# Import serializer
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification
from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior
# Import model
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:
"""
to list user's notifications
:param request:
:return:
"""
""" list the notifications """
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id')
paginator = self.pagination_class()
@ -55,8 +49,10 @@ class NotificationViewSet(viewsets.GenericViewSet):
to send test notification
:return:
"""
send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'],
{})
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})
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False,
@ -67,12 +63,3 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
@action(methods=['get'], url_path='task', url_name='task', detail=False,
permission_classes=[AllowAny])
def task(self, request, *args, **kwargs):
"""
notification list
"""
notify_task_expiry()
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)

View File

@ -7,7 +7,6 @@ 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
@ -75,7 +74,6 @@ class UserCSVReportSerializer(serializers.ModelSerializer):
"""
user csv/xls report serializer
"""
name = serializers.SerializerMethodField()
phone_number = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
is_active = serializers.SerializerMethodField()
@ -86,15 +84,7 @@ class UserCSVReportSerializer(serializers.ModelSerializer):
meta class
"""
model = USER
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)
fields = ('first_name', 'last_name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
@staticmethod
def get_phone_number(obj):

View File

@ -219,8 +219,7 @@ class ArticleListSerializer(serializers.ModelSerializer):
"""
serializer for article API
"""
article_cards = ArticleCardSerializer(many=True)
article_survey = ArticleSurveySerializer(many=True)
image = serializers.SerializerMethodField('get_image')
total_points = serializers.SerializerMethodField('get_total_points')
is_completed = serializers.SerializerMethodField('get_is_completed')
@ -229,12 +228,16 @@ class ArticleListSerializer(serializers.ModelSerializer):
meta class
"""
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed')
fields = ('id', 'title', 'description','image', 'total_points', 'is_completed')
def get_image(self, obj):
"""article image"""
if obj.article_cards.first():
return obj.article_cards.first().image_url
return None
def get_total_points(self, obj):
"""total points of article"""
total_question = ArticleSurvey.objects.filter(article=obj).count()
return total_question * NUMBER['five']
return obj.article_survey.all().count() * NUMBER['five']
def get_is_completed(self, obj):
"""complete all question"""
@ -268,14 +271,14 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last()
if ans_obj:
return ans_obj.id
return str("None")
return None
def get_attempted_answer(self, obj):
"""attempt question or not"""
context_data = self.context.get('user')
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data,
question=obj, is_answer_correct=True).last()
if junior_article_obj:
question=obj).last()
if junior_article_obj and junior_article_obj.submitted_answer:
return junior_article_obj.submitted_answer.id
return None

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_mail
from base.tasks import send_email_otp
USER = get_user_model()
@ -48,13 +48,11 @@ 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
data = {
"verification_code": verification_code
}
send_mail.delay([email], template, data)
send_email_otp.delay(email, verification_code)
expiry = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.update_or_create(email=email,

View File

@ -5,7 +5,6 @@ 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
@ -38,7 +37,7 @@ class UserManagementListSerializer(serializers.ModelSerializer):
:param obj: user object
:return: full name
"""
return get_user_full_name(obj)
return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name
@staticmethod
def get_country_code(obj):
@ -145,7 +144,7 @@ class GuardianSerializer(serializers.ModelSerializer):
:param obj: guardian object
:return: full name
"""
return get_user_full_name(obj.user)
return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name
@staticmethod
def get_first_name(obj):
@ -223,7 +222,7 @@ class JuniorSerializer(serializers.ModelSerializer):
:param obj: junior object
:return: full name
"""
return get_user_full_name(obj.auth)
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
@staticmethod
def get_first_name(obj):

View File

@ -22,7 +22,7 @@ from django.db.models.functions.window import Rank
from django.http import HttpResponse
# local imports
from account.utils import custom_response, get_user_full_name
from account.utils import custom_response
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS
from guardian.models import JuniorTask
from guardian.utils import upload_excel_file_to_alibaba
@ -107,15 +107,13 @@ 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)
@ -171,9 +169,10 @@ class AnalyticsViewSet(GenericViewSet):
serializer = UserCSVReportSerializer(queryset, many=True)
df_users = pd.DataFrame([
{'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']}
{'Name': f"{user['first_name']} {user['last_name']}",
'Email': user['email'], 'Phone Number': user['phone_number'],
'User Type': user['user_type'], 'Status': user['is_active'],
'Date Joined': user['date_joined']}
for user in serializer.data
])
write_excel_worksheet(worksheet, df_users)
@ -182,16 +181,10 @@ class AnalyticsViewSet(GenericViewSet):
elif sheet_name == 'Assign Tasks':
assign_tasks = JuniorTask.objects.filter(
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
).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',)
).exclude(task_status__in=[PENDING, EXPIRED])
df_tasks = pd.DataFrame([
{'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()}
{'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()}
for task in assign_tasks
])
@ -205,7 +198,8 @@ class AnalyticsViewSet(GenericViewSet):
)).order_by('-total_points', 'junior__created_at')[:15]
df_leaderboard = pd.DataFrame([
{
'Name': get_user_full_name(junior.junior.auth),
'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}"
if junior.junior.auth.last_name else junior.junior.auth.first_name,
'Points': junior.total_points,
'Rank': junior.rank
}

View File

@ -229,10 +229,7 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
http_method_names = ['get',]
def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False, is_published=True).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
queryset = self.filter_queryset(article)
queryset = self.queryset.objects.filter(is_deleted=False, is_published=True).order_by('-created_at')
return queryset
def list(self, request, *args, **kwargs):

View File

@ -27,13 +27,6 @@ app.config_from_object('django.conf:settings')
# Load task modules from all registered Django apps.
app.autodiscover_tasks()
app.conf.beat_schedule = {
'notify_task_expiry': {
'task': 'base.tasks.notify_task_expiry',
'schedule': crontab(minute='0', hour='*/1'),
},
}
@app.task(bind=True)
def debug_task(self):