mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-25 13:49:40 +00:00
@ -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']):
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% extends "templated_email/email_base.email" %}
|
||||
|
||||
{% block subject %}
|
||||
{{subject}}
|
||||
Support Mail
|
||||
{% endblock %}
|
||||
|
||||
{% block plain %}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
46
base/pagination.py
Normal 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
39
docker-compose-prod.yml
Normal 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
39
docker-compose-qa.yml
Normal 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
39
docker-compose-stage.yml
Normal 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
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -353,8 +357,8 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = JuniorTask.objects.filter(junior__auth=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
|
||||
@ -367,14 +371,15 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
|
||||
try:
|
||||
status_value = self.request.GET.get('status')
|
||||
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)
|
||||
paginator = CustomPageNumberPagination()
|
||||
# use Pagination
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
# use TaskDetails juniorSerializer 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)
|
||||
except Exception as e:
|
||||
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
|
||||
).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)
|
||||
@ -511,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():
|
||||
|
@ -22,7 +22,7 @@ ARTICLE_REWARD_POINTS = 17
|
||||
REMOVE_JUNIOR = 18
|
||||
|
||||
TEST_NOTIFICATION = 99
|
||||
|
||||
# notification dictionary
|
||||
NOTIFICATION_DICT = {
|
||||
REGISTRATION: {
|
||||
"title": "Successfully registered!",
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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']
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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'))]
|
||||
|
Reference in New Issue
Block a user