mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-25 13:49:40 +00:00
@ -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"""
|
||||
|
@ -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 """
|
||||
|
@ -101,13 +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",
|
||||
"2081": "You can not add more than 3 guardian"
|
||||
"2081": "You can not add more than 3 guardian",
|
||||
# guardian code not exist
|
||||
"2082": "Guardian code does not exist"
|
||||
|
||||
}
|
||||
"""Success message code"""
|
||||
|
@ -68,7 +68,7 @@ def notify_top_junior():
|
||||
task to send notification for top leaderboard junior to all junior's
|
||||
:return:
|
||||
"""
|
||||
junior_points_qs = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
|
||||
junior_points_qs = JuniorPoints.objects.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')
|
||||
@ -76,9 +76,8 @@ def notify_top_junior():
|
||||
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:
|
||||
to_user_list = [junior_point.junior.auth for junior_point in junior_points_qs]
|
||||
send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR,
|
||||
to_user_list, {'points': new_top_position.total_points})
|
||||
{'points': new_top_position.total_points})
|
||||
for junior_point in junior_points_qs:
|
||||
junior_point.position = junior_point.rank
|
||||
junior_point.save()
|
||||
|
Binary file not shown.
@ -36,6 +36,7 @@ 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."))
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -255,7 +255,7 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
|
||||
queryset = JuniorPoints.objects.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')
|
||||
@ -292,7 +292,8 @@ 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
|
||||
|
@ -504,24 +504,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')
|
||||
|
||||
|
@ -70,7 +70,7 @@ def get_junior_leaderboard_rank(junior_obj):
|
||||
:param junior_obj:
|
||||
:return: junior's position/rank
|
||||
"""
|
||||
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
|
||||
queryset = JuniorPoints.objects.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')
|
||||
|
@ -207,13 +207,15 @@ 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 not junior.guardian_code:
|
||||
junior.guardian_code = [guardian.guardian_code]
|
||||
if type(junior.guardian_code) is list and len(junior.guardian_code) < 4:
|
||||
if type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
|
||||
junior.guardian_code.append(guardian.guardian_code)
|
||||
else:
|
||||
return "Max"
|
||||
@ -264,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
|
||||
]
|
||||
@ -724,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()
|
||||
@ -743,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,
|
||||
|
@ -14,10 +14,10 @@ TASK_REJECTED = 10
|
||||
TASK_APPROVED = 11
|
||||
PENDING_TASK_EXPIRING = 12
|
||||
IN_PROGRESS_TASK_EXPIRING = 13
|
||||
|
||||
TOP_JUNIOR = 14
|
||||
|
||||
REMOVE_JUNIOR = 15
|
||||
NEW_ARTICLE_PUBLISHED = 15
|
||||
REMOVE_JUNIOR = 16
|
||||
|
||||
TEST_NOTIFICATION = 99
|
||||
|
||||
@ -68,7 +68,8 @@ NOTIFICATION_DICT = {
|
||||
"title": "Task completion approval!",
|
||||
"body": "{from_user} completed her 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}."
|
||||
@ -92,11 +93,18 @@ NOTIFICATION_DICT = {
|
||||
"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
|
||||
# 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 as soon as their custodians remove them from account
|
||||
REMOVE_JUNIOR: {
|
||||
"title": "Disassociate by guardian!",
|
||||
|
@ -8,6 +8,7 @@ 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
|
||||
@ -79,6 +80,7 @@ def get_notification_data(notification_type, from_user_id, from_user_type, to_us
|
||||
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)
|
||||
@ -124,10 +126,13 @@ def send_multiple_push(queryset, data):
|
||||
|
||||
@shared_task()
|
||||
def send_notification_multiple_user(notification_type, from_user_id, from_user_type,
|
||||
to_user_list: list = [], extra_data: dict = {}):
|
||||
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)
|
||||
|
||||
push_data = NOTIFICATION_DICT[notification_type].copy()
|
||||
notification_data = push_data.copy()
|
||||
points = extra_data.get('points', None)
|
||||
@ -144,7 +149,7 @@ def send_notification_multiple_user(notification_type, from_user_id, from_user_t
|
||||
notification_from=from_user,
|
||||
data=notification_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)
|
||||
|
||||
|
||||
|
@ -10,6 +10,8 @@ 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
|
||||
@ -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
|
||||
"""
|
||||
|
@ -27,6 +27,7 @@ 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",
|
||||
@ -34,11 +35,11 @@ app.conf.beat_schedule = {
|
||||
},
|
||||
'notify_task_expiry': {
|
||||
'task': 'base.tasks.notify_task_expiry',
|
||||
'schedule': crontab(minute='0', hour='13'),
|
||||
'schedule': crontab(minute='5', hour='12'),
|
||||
},
|
||||
'notify_top_junior': {
|
||||
'task': 'base.tasks.notify_top_junior',
|
||||
'schedule': crontab(minute='0', hour='*/1'),
|
||||
'schedule': crontab(minute='*/5',),
|
||||
},
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user