Compare commits

..

40 Commits

Author SHA1 Message Date
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
23 changed files with 386 additions and 126 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

@ -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
@ -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):
@ -288,3 +288,36 @@ 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"
def make_special_password(length=10):
# Define character sets
lowercase_letters = string.ascii_lowercase
uppercase_letters = string.ascii_uppercase
digits = string.digits
special_characters = '!@#$%^&*()_-+=<>?/[]{}|'
# Combine character sets
all_characters = lowercase_letters + uppercase_letters + digits + special_characters
# Create a password with random characters
password = (
random.choice(lowercase_letters) +
random.choice(uppercase_letters) +
random.choice(digits) +
random.choice(special_characters) +
''.join(random.choice(all_characters) for _ in range(length - 4))
)
# Shuffle the characters to make it more random
password_list = list(password)
random.shuffle(password_list)
return ''.join(password_list)
def task_status_fun(status_value):
"""task status"""
task_status_value = ['1']
if str(status_value) == '2':
task_status_value = ['2', '4']
elif str(status_value) == '3':
task_status_value = ['3', '5', '6']
return task_status_value

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

@ -111,7 +111,10 @@ ERROR_CODE = {
"2080": "Can not add App version",
"2081": "A junior can only be associated with a maximum of 3 guardian",
# guardian code not exist
"2082": "Guardian code does not exist"
"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"""
@ -147,8 +150,8 @@ SUCCESS_CODE = {
"3018": "Task created successfully",
"3019": "Support Email sent successfully",
"3020": "Logged out successfully.",
"3021": "Add junior successfully",
"3022": "Remove junior successfully",
"3021": "Added junior successfully",
"3022": "Removed junior successfully",
"3023": "Junior is approved successfully",
"3024": "Junior request is rejected successfully",
"3025": "Task is approved successfully",

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,
}

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

@ -28,7 +28,7 @@ from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIA
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
from django.core.exceptions import ValidationError
@ -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):

View File

@ -1,4 +1,5 @@
"""Views of Guardian"""
import math
# django imports
# Import IsAuthenticated
@ -16,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,
@ -36,7 +38,7 @@ 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, GUARDIAN
from .utils import upload_image_to_alibaba
@ -135,7 +137,8 @@ class TaskListAPIView(viewsets.ModelViewSet):
Params
status
search
page"""
page
junior"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
@ -145,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
@ -154,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):
@ -177,45 +184,48 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
"""
try:
image = request.data['default_image']
junior = request.data['junior']
junior_id = Junior.objects.filter(id=junior).last()
if junior_id:
guardian_data = Guardian.objects.filter(user=request.user).last()
index = junior_id.guardian_code.index(guardian_data.guardian_code)
status_index = junior_id.guardian_code_status[index]
if status_index == str(NUMBER['three']):
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):
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')
# use TaskSerializer serializer
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
if serializer.is_valid():
# save serializer
task = serializer.save()
send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN,
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)
junior_ids = request.data['junior'].split(',')
# if not junior.isnumeric():
# """junior value must be 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 'https' in str(image):
image_data = image
else:
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
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_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).select_related('auth')
for junior in junior_data:
if junior:
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={"guardian": guardian, "image": image_data,
"junior_data": junior_data}, data=request.data)
if serializer.is_valid():
# save serializer
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
@ -344,6 +354,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

View File

@ -11,7 +11,7 @@ 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
@ -297,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,
@ -321,7 +321,7 @@ 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.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {})
return junior_data

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
@ -43,7 +47,7 @@ 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, GUARDIAN
from account.utils import custom_response, custom_error_response
from account.utils import custom_response, custom_error_response, task_status_fun
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification
@ -211,19 +215,24 @@ 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.append(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()
jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior)
if jun_data:
@ -341,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
@ -351,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)
@ -401,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)
@ -509,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():
@ -593,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

View File

@ -22,7 +22,7 @@ ARTICLE_REWARD_POINTS = 17
REMOVE_JUNIOR = 18
TEST_NOTIFICATION = 99
# notification dictionary
NOTIFICATION_DICT = {
REGISTRATION: {
"title": "Successfully registered!",

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

@ -11,6 +11,7 @@ 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
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
@ -33,10 +34,10 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
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, count=queryset.count())
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):

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

@ -81,7 +81,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):
"""
@ -306,8 +306,9 @@ 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):
"""

View File

@ -23,11 +23,11 @@ from django.http import HttpResponse
# local imports
from account.utils import custom_response, get_user_full_name
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS
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

View File

@ -130,6 +130,22 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
return custom_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='status-change', url_path='status-change',
detail=True)
def article_status_change(self, request, *args, **kwargs):
"""
article un-publish or publish api method
:param request: article id
:return: success message
"""
try:
article = Article.objects.filter(id=kwargs['pk']).first()
article.is_published = False if article.is_published else True
article.save(update_fields=['is_published'])
return custom_response(SUCCESS_CODE["3038"])
except AttributeError:
return custom_error_response(ERROR_CODE["2041"], response_status=status.HTTP_400_BAD_REQUEST)
@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

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