Merge pull request #306 from KiwiTechLLC/dev

Dev
This commit is contained in:
Abu Talib
2023-09-06 12:33:22 +05:30
committed by GitHub
23 changed files with 360 additions and 103 deletions

View File

@ -45,11 +45,12 @@ class CustomMiddleware(object):
device_type = str(request.META.get('HTTP_TYPE')) device_type = str(request.META.get('HTTP_TYPE'))
api_endpoint = request.path api_endpoint = request.path
unrestricted_api = ('/api/v1/user/login/', '/api/v1/logout/', '/api/v1/generate-token/')
if request.user.is_authenticated: if request.user.is_authenticated:
# device details # device details
if device_id: if device_id:
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() 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) custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error) response = custom_response(custom_error)
if user_type and str(user_type) == str(NUMBER['one']): if user_type and str(user_type) == str(NUMBER['one']):

View File

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

View File

@ -1,6 +1,6 @@
"""Account utils""" """Account utils"""
from celery import shared_task from celery import shared_task
import random
"""Import django""" """Import django"""
from django.conf import settings from django.conf import settings
from rest_framework import viewsets, status from rest_framework import viewsets, status
@ -167,7 +167,7 @@ def user_device_details(user, device_id):
return False return False
def send_support_email(name, sender, subject, message): def send_support_email(name, sender, message):
"""Send otp on email with template""" """Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS] to_email = [settings.EMAIL_FROM_ADDRESS]
from_email = settings.DEFAULT_ADDRESS from_email = settings.DEFAULT_ADDRESS
@ -179,7 +179,6 @@ def send_support_email(name, sender, subject, message):
context={ context={
'name': name.title(), 'name': name.title(),
'sender': sender, 'sender': sender,
'subject': subject,
'message': message 'message': message
} }
) )
@ -191,7 +190,8 @@ def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count
if not data: if not data:
"""when data is none""" """when data is none"""
data = None data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count}) return Response({"data": data, "message": detail, "status": "success",
"code": response_status, "count": count})
def custom_error_response(detail, response_status): def custom_error_response(detail, response_status):
@ -288,3 +288,36 @@ def get_user_full_name(user_obj):
to get user's full name to get user's full name
""" """
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User" return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User"
def make_special_password(length=10):
# Define character sets
lowercase_letters = string.ascii_lowercase
uppercase_letters = string.ascii_uppercase
digits = string.digits
special_characters = '!@#$%^&*()_-+=<>?/[]{}|'
# Combine character sets
all_characters = lowercase_letters + uppercase_letters + digits + special_characters
# Create a password with random characters
password = (
random.choice(lowercase_letters) +
random.choice(uppercase_letters) +
random.choice(digits) +
random.choice(special_characters) +
''.join(random.choice(all_characters) for _ in range(length - 4))
)
# Shuffle the characters to make it more random
password_list = list(password)
random.shuffle(password_list)
return ''.join(password_list)
def task_status_fun(status_value):
"""task status"""
task_status_value = ['1']
if str(status_value) == '2':
task_status_value = ['2', '4']
elif str(status_value) == '3':
task_status_value = ['3', '5', '6']
return task_status_value

View File

@ -689,11 +689,10 @@ class SendSupportEmail(views.APIView):
def post(self, request): def post(self, request):
name = request.data.get('name') name = request.data.get('name')
sender = request.data.get('email') sender = request.data.get('email')
subject = request.data.get('subject')
message = request.data.get('message') message = request.data.get('message')
if name and sender and subject and message: if name and sender and message:
try: try:
send_support_email(name, sender, subject, message) send_support_email(name, sender, message)
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)

View File

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

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 junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant # notification's constant
from notifications.constants import TASK_APPROVED, TASK_REJECTED from notifications.constants import TASK_APPROVED, TASK_REJECTED, TASK_ASSIGNED
# send notification function # send notification function
from notifications.utils import send_notification from notifications.utils import send_notification
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -218,7 +218,7 @@ class TaskSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = JuniorTask model = JuniorTask
fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image'] fields = ['id', 'task_name','task_description','points', 'due_date','default_image']
def validate_due_date(self, value): def validate_due_date(self, value):
"""validation on due date""" """validation on due date"""
@ -229,11 +229,22 @@ class TaskSerializer(serializers.ModelSerializer):
return value return value
def create(self, validated_data): def create(self, validated_data):
"""create default task image data""" """create default task image data"""
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last() guardian = self.context['guardian']
# update image of the task # update image of the task
images = self.context['image'] images = self.context['image']
validated_data['default_image'] = images junior_data = self.context['junior_data']
instance = JuniorTask.objects.create(**validated_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 return instance
class GuardianDetailSerializer(serializers.ModelSerializer): class GuardianDetailSerializer(serializers.ModelSerializer):

View File

@ -1,4 +1,5 @@
"""Views of Guardian""" """Views of Guardian"""
import math
# django imports # django imports
# Import IsAuthenticated # Import IsAuthenticated
@ -16,6 +17,7 @@ from base.constants import guardian_code_tuple
from rest_framework.filters import SearchFilter from rest_framework.filters import SearchFilter
from django.utils import timezone from django.utils import timezone
from base.pagination import CustomPageNumberPagination
# Import guardian's model, # Import guardian's model,
# Import junior's model, # Import junior's model,
# Import account's model, # Import account's model,
@ -36,7 +38,7 @@ from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification, UserDeviceDetails from account.models import UserEmailOtp, UserNotification, UserDeviceDetails
from .tasks import generate_otp from .tasks import generate_otp
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email from account.utils import custom_response, custom_error_response, send_otp_email, task_status_fun
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
from .utils import upload_image_to_alibaba from .utils import upload_image_to_alibaba
@ -135,7 +137,8 @@ class TaskListAPIView(viewsets.ModelViewSet):
Params Params
status status
search search
page""" page
junior"""
serializer_class = TaskDetailsSerializer serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,) filter_backends = (SearchFilter,)
@ -145,7 +148,7 @@ class TaskListAPIView(viewsets.ModelViewSet):
def get_queryset(self): def get_queryset(self):
queryset = JuniorTask.objects.filter(guardian__user=self.request.user queryset = JuniorTask.objects.filter(guardian__user=self.request.user
).prefetch_related('junior', 'junior__auth' ).select_related('junior', 'junior__auth'
).order_by('due_date', 'created_at') ).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset) queryset = self.filter_queryset(queryset)
@ -154,15 +157,19 @@ class TaskListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""Create guardian profile""" """Create guardian profile"""
status_value = self.request.GET.get('status') status_value = self.request.GET.get('status')
junior = self.request.GET.get('junior')
queryset = self.get_queryset() queryset = self.get_queryset()
if status_value and status_value != '0': task_status = task_status_fun(status_value)
queryset = queryset.filter(task_status=status_value) if status_value:
paginator = self.pagination_class() queryset = queryset.filter(task_status__in=task_status)
if junior:
queryset = queryset.filter(junior=int(junior))
paginator = CustomPageNumberPagination()
# use Pagination # use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer # use TaskDetailsSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return paginator.get_paginated_response(serializer.data)
class CreateTaskAPIView(viewsets.ModelViewSet): class CreateTaskAPIView(viewsets.ModelViewSet):
@ -177,45 +184,48 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
""" """
try: try:
image = request.data['default_image'] image = request.data['default_image']
junior = request.data['junior'] junior_ids = request.data['junior'].split(',')
junior_id = Junior.objects.filter(id=junior).last() # if not junior.isnumeric():
if junior_id: # """junior value must be integer"""
guardian_data = Guardian.objects.filter(user=request.user).last() # return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
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'] allowed_extensions = ['.jpg', '.jpeg', '.png']
if not any(extension in str(image) for extension in allowed_extensions): 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) 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): if 'https' in str(image):
image_data = image image_data = image
else: else:
filename = f"images/{image}" filename = f"images/{image}"
if image and image.size == NUMBER['zero']: if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2035'],
image_url = upload_image_to_alibaba(image, filename) response_status=status.HTTP_400_BAD_REQUEST)
image_data = image_url image_data = upload_image_to_alibaba(image, filename)
data.pop('default_image') request.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, guardian = Guardian.objects.filter(user=request.user).select_related('user').last()
junior_id.auth.id, {'task_id': task.id}) junior_data = Junior.objects.filter(id__in=junior_ids).select_related('auth')
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) 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: else:
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
# 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: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class SearchTaskListAPIView(viewsets.ModelViewSet): class SearchTaskListAPIView(viewsets.ModelViewSet):
"""Filter task""" """Filter task"""
serializer_class = TaskDetailsSerializer serializer_class = TaskDetailsSerializer
@ -344,6 +354,8 @@ class ApproveTaskAPIView(viewsets.ModelViewSet):
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'), task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian, guardian=guardian,
junior=self.request.data.get('junior_id')).last() junior=self.request.data.get('junior_id')).last()
if task_queryset and guardian.guardian_code not in task_queryset.junior.guardian_code:
return custom_error_response(ERROR_CODE['2084'], response_status=status.HTTP_400_BAD_REQUEST)
if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active): if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active):
return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST)
# use ApproveJuniorSerializer serializer # use ApproveJuniorSerializer serializer

View File

@ -11,7 +11,7 @@ from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
# local imports # local imports
from account.utils import send_otp_email, generate_code from account.utils import send_otp_email, generate_code, make_special_password
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ
from guardian.tasks import generate_otp from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
@ -297,8 +297,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
user_data = User.objects.create(username=email, email=email, user_data = User.objects.create(username=email, email=email,
first_name=self.context['first_name'], first_name=self.context['first_name'],
last_name=self.context['last_name']) last_name=self.context['last_name'])
password = User.objects.make_random_password() special_password = make_special_password()
user_data.set_password(password) user_data.set_password(special_password)
user_data.save() user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'), junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
image=profile_image, image=profile_image,
@ -321,7 +321,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
# add push notification # add push notification
UserNotification.objects.get_or_create(user=user_data) UserNotification.objects.get_or_create(user=user_data)
"""Notification email""" """Notification email"""
junior_notification_email.delay(email, full_name, email, password) junior_notification_email.delay(email, full_name, email, special_password)
# push notification # push notification
send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {}) send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {})
return junior_data return junior_data

View File

@ -12,6 +12,10 @@ import datetime
import requests import requests
from rest_framework.viewsets import GenericViewSet, mixins from rest_framework.viewsets import GenericViewSet, mixins
import math
from base.pagination import CustomPageNumberPagination
"""Django app import""" """Django app import"""
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi from drf_yasg import openapi
@ -43,7 +47,7 @@ from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response, task_status_fun
from guardian.utils import upload_image_to_alibaba from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points from .utils import update_positions_based_on_points
from notifications.utils import send_notification from notifications.utils import send_notification
@ -353,7 +357,7 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
def get_queryset(self): def get_queryset(self):
queryset = JuniorTask.objects.filter(junior__auth=self.request.user queryset = JuniorTask.objects.filter(junior__auth=self.request.user
).prefetch_related('junior', 'junior__auth' ).select_related('junior', 'junior__auth'
).order_by('due_date', 'created_at') ).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset) queryset = self.filter_queryset(queryset)
@ -367,14 +371,15 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
try: try:
status_value = self.request.GET.get('status') status_value = self.request.GET.get('status')
queryset = self.get_queryset() queryset = self.get_queryset()
if status_value and status_value != '0': task_status = task_status_fun(status_value)
queryset = queryset.filter(task_status=status_value) if status_value:
paginator = self.pagination_class() queryset = queryset.filter(task_status__in=task_status)
paginator = CustomPageNumberPagination()
# use Pagination # use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetails juniorSerializer serializer # use TaskDetails juniorSerializer serializer
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return paginator.get_paginated_response(serializer.data)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@ -403,10 +408,12 @@ class CompleteJuniorTaskAPIView(views.APIView):
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user
).select_related('guardian', 'junior').last() ).select_related('guardian', 'junior').last()
if task_queryset: if task_queryset:
if task_queryset.junior.is_deleted or not task_queryset.junior.is_active: if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
return custom_error_response(ERROR_CODE['2085'], response_status=status.HTTP_400_BAD_REQUEST)
elif task_queryset.junior.is_deleted or not task_queryset.junior.is_active:
return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST)
# use CompleteTaskSerializer serializer # use CompleteTaskSerializer serializer
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: elif task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
"""Already request send """ """Already request send """
return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST)
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True) serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
@ -511,7 +518,10 @@ class StartTaskAPIView(views.APIView):
try: try:
task_id = self.request.data.get('task_id') task_id = self.request.data.get('task_id')
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last() task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
print("task_queryset==>",task_queryset)
if task_queryset and task_queryset.task_status == str(NUMBER['one']): if task_queryset and task_queryset.task_status == str(NUMBER['one']):
if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST)
# use StartTaskSerializer serializer # use StartTaskSerializer serializer
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True) serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid(): if serializer.is_valid():

View File

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

View File

@ -31,11 +31,16 @@ class RegisterDevice(serializers.Serializer):
class NotificationListSerializer(serializers.ModelSerializer): class NotificationListSerializer(serializers.ModelSerializer):
"""List of notification""" """List of notification"""
badge = serializers.SerializerMethodField()
class Meta(object): class Meta(object):
"""meta info""" """meta info"""
model = Notification 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): class ReadNotificationSerializer(serializers.ModelSerializer):

View File

@ -11,6 +11,7 @@ from rest_framework import viewsets, status, views
# local imports # local imports
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE from base.messages import SUCCESS_CODE, ERROR_CODE
from base.pagination import CustomPageNumberPagination
from base.tasks import notify_task_expiry, notify_top_junior from base.tasks import notify_task_expiry, notify_top_junior
from notifications.constants import TEST_NOTIFICATION from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
@ -33,10 +34,10 @@ class NotificationViewSet(viewsets.GenericViewSet):
""" """
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id'] queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id') ).select_related('notification_to').order_by('-id')
paginator = self.pagination_class() paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
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) @action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request): 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 meta class
""" """
model = Article model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey') fields = ('id', 'title', 'description', 'is_published', 'article_cards', 'article_survey')
def validate(self, attrs): def validate(self, attrs):
""" """

View File

@ -23,11 +23,11 @@ from django.http import HttpResponse
# local imports # local imports
from account.utils import custom_response, get_user_full_name 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.models import JuniorTask
from guardian.utils import upload_excel_file_to_alibaba from guardian.utils import upload_excel_file_to_alibaba
from junior.models import JuniorPoints 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.permission import AdminPermission
from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer
from web_admin.utils import get_dates 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_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST) 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', @action(methods=['get'], url_name='remove-card', url_path='remove-card',
detail=True) detail=True)
def remove_article_card(self, request, *args, **kwargs): def remove_article_card(self, request, *args, **kwargs):
@ -214,7 +230,7 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
:param request: :param request:
:return: default article card images :return: default article card images
""" """
queryset = self.queryset queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True) serializer = self.serializer_class(queryset, many=True)
return custom_response(None, data=serializer.data) 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') SECRET_KEY = os.getenv('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG') DEBUG = os.getenv('DEBUG')
ENV = os.getenv('ENV')
# cors allow setting # 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 # allow all host
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
@ -53,7 +72,7 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
# Add Django rest frame work apps here # Add Django rest framework apps here
'django_extensions', 'django_extensions',
'storages', 'storages',
'drf_yasg', 'drf_yasg',

View File

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