mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-26 06:09:41 +00:00
Compare commits
61 Commits
sprint6-bu
...
ZBKBCK-52
Author | SHA1 | Date | |
---|---|---|---|
3afd7fecf3 | |||
d1a4b86b09 | |||
e157e98a17 | |||
a653518cfd | |||
eaf67b682f | |||
085607128b | |||
fbdef7c0c4 | |||
535e2e7f56 | |||
08f54f28a4 | |||
de710c8a7b | |||
af06dddbeb | |||
0a9dde2038 | |||
f11959d6bc | |||
7aee372606 | |||
1dec5ace3f | |||
2444b6f55e | |||
3ecbc1d303 | |||
ca0041caa6 | |||
c800853e9b | |||
373c1b70ab | |||
3a84b1ea75 | |||
bf1004696a | |||
d937c1bb92 | |||
0a877b410e | |||
9d4e7b05e4 | |||
b20e1cf516 | |||
6b0ea91742 | |||
859d26d073 | |||
154b9de32b | |||
4a554272a0 | |||
8533a27cb7 | |||
7db6502a89 | |||
a8e9a09d3f | |||
1e97d7bd6b | |||
b084b255dc | |||
441842df74 | |||
78fb5f5650 | |||
f3478c972e | |||
f63d9ddea0 | |||
bc18c67527 | |||
0471a3d588 | |||
ffb99f5099 | |||
8183edf319 | |||
8f214d11a7 | |||
b5a89df59a | |||
5524eeed64 | |||
aeaa7d7ab8 | |||
69be3bb2ac | |||
be9f600bcc | |||
6c96ea0820 | |||
a211baa10a | |||
a93dc83bd1 | |||
2cffc4e128 | |||
ec585d35f3 | |||
d62efa2139 | |||
e1ef289c69 | |||
a262b03292 | |||
f7624bc1e7 | |||
a80f9db557 | |||
16d823f97d | |||
3dae22a870 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,7 +6,6 @@ media/
|
|||||||
*.name
|
*.name
|
||||||
*.iml
|
*.iml
|
||||||
*.log
|
*.log
|
||||||
*.xml
|
|
||||||
*.pyo
|
*.pyo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
|
@ -1,5 +1,60 @@
|
|||||||
"""Test cases file of account"""
|
"""
|
||||||
"""Django import"""
|
test cases file of account
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
|
||||||
|
|
||||||
|
class UserLoginTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
test cases for login
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
set up data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client = APIClient()
|
||||||
|
self.user_email = 'user@example.com'
|
||||||
|
self.user = User.objects.create_superuser(username=self.user_email, email=self.user_email)
|
||||||
|
self.user.set_password('user@1234')
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
def test_admin_login_success(self):
|
||||||
|
"""
|
||||||
|
test admin login with valid credentials
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('account:admin-login')
|
||||||
|
data = {
|
||||||
|
'email': self.user_email,
|
||||||
|
'password': 'user@1234',
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertIn('auth_token', response.data['data'])
|
||||||
|
self.assertIn('refresh_token', response.data['data'])
|
||||||
|
self.assertEqual(response.data['data']['username'], data['email'])
|
||||||
|
|
||||||
|
def test_admin_login_invalid_credentials(self):
|
||||||
|
"""
|
||||||
|
test admin login with invalid credentials
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('account:admin-login')
|
||||||
|
data = {
|
||||||
|
'email': self.user_email,
|
||||||
|
'password': 'user@1235',
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertNotIn('auth_token', response.data)
|
||||||
|
self.assertNotIn('refresh_token', response.data)
|
||||||
|
|
||||||
|
# Add more test cases as needed
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
@ -185,13 +185,13 @@ def send_support_email(name, sender, message):
|
|||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def custom_response(detail, data=None, total_pages=None, current_page=None, response_status=status.HTTP_200_OK, count=None):
|
def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None):
|
||||||
"""Custom response code"""
|
"""Custom response code"""
|
||||||
if not data:
|
if not data:
|
||||||
"""when data is none"""
|
"""when data is none"""
|
||||||
data = None
|
data = None
|
||||||
return Response({"data": data, "message": detail, "total_pages":total_pages, "current_page":current_page,
|
return Response({"data": data, "message": detail, "status": "success",
|
||||||
"status": "success", "code": response_status, "count": count})
|
"code": response_status, "count": count})
|
||||||
|
|
||||||
|
|
||||||
def custom_error_response(detail, response_status):
|
def custom_error_response(detail, response_status):
|
||||||
@ -289,29 +289,32 @@ def get_user_full_name(user_obj):
|
|||||||
"""
|
"""
|
||||||
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):
|
def make_special_password(length=10):
|
||||||
|
"""
|
||||||
|
to make secured password
|
||||||
|
:param length:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
# Define character sets
|
# Define character sets
|
||||||
lowercase_letters = string.ascii_lowercase
|
lowercase_letters = string.ascii_lowercase
|
||||||
uppercase_letters = string.ascii_uppercase
|
uppercase_letters = string.ascii_uppercase
|
||||||
digits = string.digits
|
digits = string.digits
|
||||||
special_characters = '!@#$%^&*()_-+=<>?/[]{}|'
|
special_characters = '@#$%&*?'
|
||||||
|
|
||||||
# Combine character sets
|
# Combine character sets
|
||||||
all_characters = lowercase_letters + uppercase_letters + digits + special_characters
|
alphabets = lowercase_letters + uppercase_letters
|
||||||
|
|
||||||
# Create a password with random characters
|
# Create a password with random characters
|
||||||
password = (
|
password = [
|
||||||
random.choice(lowercase_letters) +
|
secrets.choice(uppercase_letters) +
|
||||||
random.choice(uppercase_letters) +
|
secrets.choice(lowercase_letters) +
|
||||||
random.choice(digits) +
|
secrets.choice(digits) +
|
||||||
random.choice(special_characters) +
|
secrets.choice(special_characters) +
|
||||||
''.join(random.choice(all_characters) for _ in range(length - 4))
|
''.join(secrets.choice(alphabets) for _ in range(length - 4))
|
||||||
)
|
]
|
||||||
|
|
||||||
# Shuffle the characters to make it more random
|
return ''.join(password)
|
||||||
password_list = list(password)
|
|
||||||
random.shuffle(password_list)
|
|
||||||
return ''.join(password_list)
|
|
||||||
|
|
||||||
def task_status_fun(status_value):
|
def task_status_fun(status_value):
|
||||||
"""task status"""
|
"""task status"""
|
||||||
|
@ -44,7 +44,7 @@ ERROR_CODE = {
|
|||||||
"2018": "Attached File not found",
|
"2018": "Attached File not found",
|
||||||
"2019": "Invalid Referral code",
|
"2019": "Invalid Referral code",
|
||||||
"2020": "Enter valid mobile number",
|
"2020": "Enter valid mobile number",
|
||||||
"2021": "Already register",
|
"2021": "User registered",
|
||||||
"2022": "Invalid Guardian code",
|
"2022": "Invalid Guardian code",
|
||||||
"2023": "Invalid user",
|
"2023": "Invalid user",
|
||||||
# email not verified
|
# email not verified
|
||||||
@ -103,9 +103,9 @@ ERROR_CODE = {
|
|||||||
"2074": "You can not complete this task because you does not exist in the system",
|
"2074": "You can not complete this task because you does not exist in the system",
|
||||||
# deactivate account
|
# deactivate account
|
||||||
"2075": "Your account is deactivated. Please contact with admin",
|
"2075": "Your account is deactivated. Please contact with admin",
|
||||||
"2076": "This junior already associate with you",
|
"2076": "This junior already associated with you",
|
||||||
"2077": "You can not add guardian",
|
"2077": "You can not add guardian",
|
||||||
"2078": "This junior is not associate with you",
|
"2078": "This junior is not associated with you",
|
||||||
# force update
|
# force update
|
||||||
"2079": "Please update your app version for enjoying uninterrupted services",
|
"2079": "Please update your app version for enjoying uninterrupted services",
|
||||||
"2080": "Can not add App version",
|
"2080": "Can not add App version",
|
||||||
@ -125,11 +125,11 @@ SUCCESS_CODE = {
|
|||||||
# Success code for Thank you
|
# Success code for Thank you
|
||||||
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
|
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
|
||||||
# Success code for account activation
|
# Success code for account activation
|
||||||
"3003": "Log in successful",
|
"3003": "Log in successful.",
|
||||||
# Success code for password reset
|
# Success code for password reset
|
||||||
"3004": "Password reset link has been sent to your email address",
|
"3004": "Password reset link has been sent to your email address.",
|
||||||
# Success code for link verified
|
# Success code for link verified
|
||||||
"3005": "Your account is deleted successfully.",
|
"3005": "Your account has been deleted successfully.",
|
||||||
# Success code for password reset
|
# Success code for password reset
|
||||||
"3006": "Password reset successful. You can now log in with your new password.",
|
"3006": "Password reset successful. You can now log in with your new password.",
|
||||||
# Success code for password update
|
# Success code for password update
|
||||||
@ -137,11 +137,11 @@ SUCCESS_CODE = {
|
|||||||
# Success code for valid link
|
# Success code for valid link
|
||||||
"3008": "You have a valid link.",
|
"3008": "You have a valid link.",
|
||||||
# Success code for logged out
|
# Success code for logged out
|
||||||
"3009": "You have successfully logged out!",
|
"3009": "You have successfully logged out.",
|
||||||
# Success code for check all fields
|
# Success code for check all fields
|
||||||
"3010": "All fields are valid",
|
"3010": "All fields are valid.",
|
||||||
"3011": "Email OTP Verified successfully",
|
"3011": "Email OTP has been verified successfully.",
|
||||||
"3012": "Phone OTP Verified successfully",
|
"3012": "Phone OTP has been verified successfully.",
|
||||||
"3013": "Valid Guardian code",
|
"3013": "Valid Guardian code",
|
||||||
"3014": "Password has been updated successfully.",
|
"3014": "Password has been updated successfully.",
|
||||||
"3015": "Verification code has been sent on your email.",
|
"3015": "Verification code has been sent on your email.",
|
||||||
@ -150,39 +150,39 @@ 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": "Added junior successfully",
|
"3021": "Junior has been added successfully.",
|
||||||
"3022": "Removed junior successfully",
|
"3022": "Junior has been removed successfully.",
|
||||||
"3023": "Junior is approved successfully",
|
"3023": "Junior has been approved successfully.",
|
||||||
"3024": "Junior request is rejected successfully",
|
"3024": "Junior request is rejected successfully.",
|
||||||
"3025": "Task is approved successfully",
|
"3025": "Task is approved successfully.",
|
||||||
"3026": "Task is rejected successfully",
|
"3026": "Task is rejected successfully.",
|
||||||
"3027": "Article has been created successfully.",
|
"3027": "Article has been created successfully.",
|
||||||
"3028": "Article has been updated successfully.",
|
"3028": "Article has been updated successfully.",
|
||||||
"3029": "Article has been deleted successfully.",
|
"3029": "Article has been deleted successfully.",
|
||||||
"3030": "Article Card has been removed successfully.",
|
"3030": "Article Card has been removed successfully.",
|
||||||
"3031": "Article Survey has been removed successfully.",
|
"3031": "Article Survey has been removed successfully.",
|
||||||
"3032": "Task request sent successfully",
|
"3032": "Task request sent successfully.",
|
||||||
"3033": "Valid Referral code",
|
"3033": "Valid Referral code.",
|
||||||
"3034": "Invite guardian successfully",
|
"3034": "Invite guardian successfully.",
|
||||||
"3035": "Task started successfully",
|
"3035": "Task started successfully.",
|
||||||
"3036": "Task reassign successfully",
|
"3036": "Task reassign successfully.",
|
||||||
"3037": "Profile has been updated successfully.",
|
"3037": "Profile has been updated successfully.",
|
||||||
"3038": "Status has been changed successfully.",
|
"3038": "Status has been changed successfully.",
|
||||||
# notification read
|
# notification read
|
||||||
"3039": "Notification read successfully",
|
"3039": "Notification read successfully.",
|
||||||
# start article
|
# start article
|
||||||
"3040": "Start article successfully",
|
"3040": "Start article successfully.",
|
||||||
# complete article
|
# complete article
|
||||||
"3041": "Article completed successfully",
|
"3041": "Article completed successfully.",
|
||||||
# submit assessment successfully
|
# submit assessment successfully
|
||||||
"3042": "Assessment completed successfully",
|
"3042": "Assessment completed successfully.",
|
||||||
# read article
|
# read article
|
||||||
"3043": "Read article card successfully",
|
"3043": "Read article card successfully.",
|
||||||
# remove guardian code request
|
# remove guardian code request
|
||||||
"3044": "Remove guardian code request successfully",
|
"3044": "Remove guardian code request successfully.",
|
||||||
# create faq
|
# create faq
|
||||||
"3045": "Create FAQ data",
|
"3045": "Create FAQ data.",
|
||||||
"3046": "Add App version successfully"
|
"3046": "Add App version successfully."
|
||||||
|
|
||||||
}
|
}
|
||||||
"""status code error"""
|
"""status code error"""
|
||||||
|
34
base/pagination.py
Normal file
34
base/pagination.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
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),
|
||||||
|
|
||||||
|
|
||||||
|
]))
|
@ -53,11 +53,11 @@ def notify_task_expiry():
|
|||||||
(datetime.datetime.now().date() + datetime.timedelta(days=1))])
|
(datetime.datetime.now().date() + datetime.timedelta(days=1))])
|
||||||
if pending_tasks := all_pending_tasks.filter(task_status=PENDING):
|
if pending_tasks := all_pending_tasks.filter(task_status=PENDING):
|
||||||
for task in pending_tasks:
|
for task in pending_tasks:
|
||||||
send_notification(PENDING_TASK_EXPIRING, None, None, task.junior.auth.id,
|
send_notification(PENDING_TASK_EXPIRING, None, None, task.junior.auth_id,
|
||||||
{'task_id': task.id})
|
{'task_id': task.id})
|
||||||
if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS):
|
if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS):
|
||||||
for task in in_progress_tasks:
|
for task in in_progress_tasks:
|
||||||
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id,
|
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth_id, JUNIOR, task.guardian.user_id,
|
||||||
{'task_id': task.id})
|
{'task_id': task.id})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ def notify_top_junior():
|
|||||||
prev_top_position = junior_points_qs.filter(position=1).first()
|
prev_top_position = junior_points_qs.filter(position=1).first()
|
||||||
new_top_position = junior_points_qs.filter(rank=1).first()
|
new_top_position = junior_points_qs.filter(rank=1).first()
|
||||||
if prev_top_position != new_top_position:
|
if prev_top_position != new_top_position:
|
||||||
send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR,
|
send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth_id, JUNIOR,
|
||||||
{'points': new_top_position.total_points})
|
{'points': new_top_position.total_points})
|
||||||
for junior_point in junior_points_qs:
|
for junior_point in junior_points_qs:
|
||||||
junior_point.position = junior_point.rank
|
junior_point.position = junior_point.rank
|
||||||
|
Binary file not shown.
5792
coverage-reports/coverage.xml
Normal file
5792
coverage-reports/coverage.xml
Normal file
File diff suppressed because it is too large
Load Diff
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
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-09-08 10:41
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('guardian', '0021_guardian_is_deleted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='juniortask',
|
||||||
|
name='task_description',
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -97,7 +97,7 @@ class JuniorTask(models.Model):
|
|||||||
"""task details"""
|
"""task details"""
|
||||||
task_name = models.CharField(max_length=100)
|
task_name = models.CharField(max_length=100)
|
||||||
"""task description"""
|
"""task description"""
|
||||||
task_description = models.CharField(max_length=500)
|
task_description = models.CharField(max_length=500, null=True, blank=True)
|
||||||
"""points of the task"""
|
"""points of the task"""
|
||||||
points = models.IntegerField(default=TASK_POINTS)
|
points = models.IntegerField(default=TASK_POINTS)
|
||||||
"""last date of the task"""
|
"""last date of the task"""
|
||||||
|
@ -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
|
||||||
@ -229,21 +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"""
|
||||||
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']
|
||||||
junior_ids = self.context['junior_data']
|
junior_data = self.context['junior_data']
|
||||||
junior_data = junior_ids[0].split(',')
|
|
||||||
tasks_created = []
|
tasks_created = []
|
||||||
|
|
||||||
for junior_id in junior_data:
|
for junior in junior_data:
|
||||||
# create task
|
# create task
|
||||||
task_data = validated_data.copy()
|
task_data = validated_data.copy()
|
||||||
task_data['guardian'] = guardian
|
task_data['guardian'] = guardian
|
||||||
task_data['default_image'] = images
|
task_data['default_image'] = images
|
||||||
task_data['junior'] = Junior.objects.filter(id=junior_id).last()
|
task_data['junior'] = junior
|
||||||
instance = JuniorTask.objects.create(**task_data)
|
instance = JuniorTask.objects.create(**task_data)
|
||||||
tasks_created.append(instance)
|
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):
|
||||||
@ -442,8 +443,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
|
|||||||
# update complete time of task
|
# update complete time of task
|
||||||
# instance.completed_on = real_time()
|
# instance.completed_on = real_time()
|
||||||
instance.completed_on = timezone.now().astimezone(pytz.utc)
|
instance.completed_on = timezone.now().astimezone(pytz.utc)
|
||||||
send_notification.delay(TASK_APPROVED, instance.guardian.user.id, GUARDIAN,
|
send_notification.delay(TASK_APPROVED, instance.guardian.user_id, GUARDIAN,
|
||||||
junior_details.auth.id, {'task_id': instance.id})
|
junior_details.auth_id, {'task_id': instance.id})
|
||||||
else:
|
else:
|
||||||
# reject the task
|
# reject the task
|
||||||
instance.task_status = str(NUMBER['three'])
|
instance.task_status = str(NUMBER['three'])
|
||||||
@ -451,8 +452,8 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
|
|||||||
# update reject time of task
|
# update reject time of task
|
||||||
# instance.rejected_on = real_time()
|
# instance.rejected_on = real_time()
|
||||||
instance.rejected_on = timezone.now().astimezone(pytz.utc)
|
instance.rejected_on = timezone.now().astimezone(pytz.utc)
|
||||||
send_notification.delay(TASK_REJECTED, instance.guardian.user.id, GUARDIAN,
|
send_notification.delay(TASK_REJECTED, instance.guardian.user_id, GUARDIAN,
|
||||||
junior_details.auth.id, {'task_id': instance.id})
|
junior_details.auth_id, {'task_id': instance.id})
|
||||||
instance.save()
|
instance.save()
|
||||||
junior_data.save()
|
junior_data.save()
|
||||||
return junior_details
|
return junior_details
|
||||||
|
@ -117,7 +117,7 @@ def update_referral_points(referral_code, referral_code_used):
|
|||||||
junior_query.total_points = junior_query.total_points + NUMBER['five']
|
junior_query.total_points = junior_query.total_points + NUMBER['five']
|
||||||
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
|
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
|
||||||
junior_query.save()
|
junior_query.save()
|
||||||
send_notification.delay(REFERRAL_POINTS, None, None, junior_queryset.auth.id, {})
|
send_notification.delay(REFERRAL_POINTS, None, None, junior_queryset.auth_id, {})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,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,
|
||||||
@ -147,8 +148,8 @@ 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('-created_at')
|
||||||
|
|
||||||
queryset = self.filter_queryset(queryset)
|
queryset = self.filter_queryset(queryset)
|
||||||
return queryset
|
return queryset
|
||||||
@ -156,23 +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')
|
||||||
current_page = self.request.GET.get('page')
|
|
||||||
junior = self.request.GET.get('junior')
|
junior = self.request.GET.get('junior')
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
task_status = task_status_fun(status_value)
|
task_status = task_status_fun(status_value)
|
||||||
if status_value and not junior:
|
if status_value:
|
||||||
queryset = queryset.filter(task_status__in=task_status)
|
queryset = queryset.filter(task_status__in=task_status)
|
||||||
elif status_value and junior:
|
if junior:
|
||||||
queryset = queryset.filter(task_status__in=task_status,junior=int(junior))
|
queryset = queryset.filter(junior=int(junior))
|
||||||
paginator = self.pagination_class()
|
paginator = CustomPageNumberPagination()
|
||||||
total_count = len(queryset)
|
|
||||||
total_pages = math.ceil(total_count/10)
|
|
||||||
# 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, total_pages=total_pages, current_page=current_page,
|
return paginator.get_paginated_response(serializer.data)
|
||||||
response_status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateTaskAPIView(viewsets.ModelViewSet):
|
class CreateTaskAPIView(viewsets.ModelViewSet):
|
||||||
@ -187,49 +184,51 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
image = request.data['default_image']
|
image = request.data['default_image']
|
||||||
juniors = request.data['junior'].split(',')
|
junior_ids = request.data['junior'].split(',')
|
||||||
for junior in juniors:
|
|
||||||
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')
|
|
||||||
junior_data = data.pop('junior')
|
|
||||||
# use TaskSerializer serializer
|
|
||||||
serializer = TaskSerializer(context={"user":request.user, "image":image_data,
|
|
||||||
"junior_data":junior_data}, data=data)
|
|
||||||
if serializer.is_valid():
|
|
||||||
# save serializer
|
|
||||||
task = serializer.save()
|
|
||||||
|
|
||||||
send_notification.delay(TASK_ASSIGNED, request.auth.payload['user_id'], GUARDIAN,
|
invalid_junior_ids = [junior_id for junior_id in junior_ids if not junior_id.isnumeric()]
|
||||||
junior_id.auth.id, {'task_id': task.id})
|
if invalid_junior_ids:
|
||||||
return custom_response(SUCCESS_CODE['3018'], response_status=status.HTTP_200_OK)
|
# At least one junior value is not an integer
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
|
||||||
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:
|
||||||
|
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,
|
||||||
|
guardian_code__contains=[guardian.guardian_code]
|
||||||
|
).select_related('auth')
|
||||||
|
if not junior_data:
|
||||||
|
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
for junior in junior_data:
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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
|
||||||
@ -305,9 +304,13 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
|
|||||||
"action":"1"}
|
"action":"1"}
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
relation_obj = JuniorGuardianRelationship.objects.filter(
|
||||||
|
guardian__user__email=self.request.user,
|
||||||
|
junior__id=self.request.data.get('junior_id')
|
||||||
|
).select_related('guardian', 'junior').first()
|
||||||
|
guardian = relation_obj.guardian
|
||||||
# fetch junior query
|
# fetch junior query
|
||||||
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
|
junior_queryset = relation_obj.junior
|
||||||
if junior_queryset and (junior_queryset.is_deleted or not junior_queryset.is_active):
|
if junior_queryset and (junior_queryset.is_deleted or not junior_queryset.is_active):
|
||||||
return custom_error_response(ERROR_CODE['2073'], response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE['2073'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
# action 1 is use for approve and 2 for reject
|
# action 1 is use for approve and 2 for reject
|
||||||
@ -320,8 +323,8 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
|
|||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
# save serializer
|
# save serializer
|
||||||
serializer.save()
|
serializer.save()
|
||||||
send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN,
|
send_notification.delay(ASSOCIATE_APPROVED, guardian.user_id, GUARDIAN,
|
||||||
junior_queryset.auth.id, {})
|
junior_queryset.auth_id, {})
|
||||||
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
else:
|
else:
|
||||||
if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code):
|
if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code):
|
||||||
@ -332,7 +335,8 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
|
|||||||
junior_queryset.guardian_code.remove(guardian.guardian_code)
|
junior_queryset.guardian_code.remove(guardian.guardian_code)
|
||||||
junior_queryset.guardian_code_status.pop(index)
|
junior_queryset.guardian_code_status.pop(index)
|
||||||
junior_queryset.save()
|
junior_queryset.save()
|
||||||
send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id, {})
|
send_notification.delay(ASSOCIATE_REJECTED, guardian.user_id, GUARDIAN, junior_queryset.auth_id, {})
|
||||||
|
relation_obj.delete()
|
||||||
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3024'], 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)
|
||||||
|
@ -107,8 +107,8 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
|
|||||||
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
|
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
|
||||||
if guardian_data:
|
if guardian_data:
|
||||||
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
|
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
|
||||||
send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {})
|
send_notification.delay(ASSOCIATE_REQUEST, junior.auth_id, JUNIOR, guardian_data.user_id, {})
|
||||||
junior_approval_mail.delay(user.email, user.first_name)
|
# junior_approval_mail.delay(user.email, user.first_name) removed as per changes
|
||||||
junior.dob = validated_data.get('dob', junior.dob)
|
junior.dob = validated_data.get('dob', junior.dob)
|
||||||
junior.passcode = validated_data.get('passcode', junior.passcode)
|
junior.passcode = validated_data.get('passcode', junior.passcode)
|
||||||
junior.country_name = validated_data.get('country_name', junior.country_name)
|
junior.country_name = validated_data.get('country_name', junior.country_name)
|
||||||
@ -323,7 +323,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
|
|||||||
"""Notification email"""
|
"""Notification email"""
|
||||||
junior_notification_email.delay(email, full_name, email, special_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
|
||||||
|
|
||||||
|
|
||||||
@ -359,8 +359,8 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
|
|||||||
instance.task_status = str(NUMBER['four'])
|
instance.task_status = str(NUMBER['four'])
|
||||||
instance.is_approved = False
|
instance.is_approved = False
|
||||||
instance.save()
|
instance.save()
|
||||||
send_notification.delay(TASK_ACTION, instance.junior.auth.id, JUNIOR,
|
send_notification.delay(TASK_ACTION, instance.junior.auth_id, JUNIOR,
|
||||||
instance.guardian.user.id, {'task_id': instance.id})
|
instance.guardian.user_id, {'task_id': instance.id})
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
class JuniorPointsSerializer(serializers.ModelSerializer):
|
class JuniorPointsSerializer(serializers.ModelSerializer):
|
||||||
@ -466,8 +466,8 @@ class AddGuardianSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
"""Notification email"""
|
"""Notification email"""
|
||||||
junior_notification_email(email, full_name, email, password)
|
junior_notification_email(email, full_name, email, password)
|
||||||
junior_approval_mail.delay(email, full_name)
|
# junior_approval_mail.delay(email, full_name) removed as per changes
|
||||||
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth.id, JUNIOR, guardian_data.user.id, {})
|
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth_id, JUNIOR, guardian_data.user_id, {})
|
||||||
return guardian_data
|
return guardian_data
|
||||||
|
|
||||||
class StartTaskSerializer(serializers.ModelSerializer):
|
class StartTaskSerializer(serializers.ModelSerializer):
|
||||||
|
@ -6,7 +6,7 @@ from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView
|
|||||||
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
|
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
|
||||||
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
|
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
|
||||||
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
|
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
|
||||||
CreateArticleCardAPIView, RemoveGuardianCodeAPIView, FAQViewSet)
|
CreateArticleCardAPIView, RemoveGuardianCodeAPIView, FAQViewSet, CheckJuniorApiViewSet)
|
||||||
"""Third party import"""
|
"""Third party import"""
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
@ -29,6 +29,8 @@ router.register('create-junior-profile', UpdateJuniorProfile, basename='profile-
|
|||||||
router.register('validate-guardian-code', ValidateGuardianCode, basename='validate-guardian-code')
|
router.register('validate-guardian-code', ValidateGuardianCode, basename='validate-guardian-code')
|
||||||
# junior list API"""
|
# junior list API"""
|
||||||
router.register('junior-list', JuniorListAPIView, basename='junior-list')
|
router.register('junior-list', JuniorListAPIView, basename='junior-list')
|
||||||
|
|
||||||
|
router.register('check-junior', CheckJuniorApiViewSet, basename='check-junior')
|
||||||
# Add junior list API"""
|
# Add junior list API"""
|
||||||
router.register('add-junior', AddJuniorAPIView, basename='add-junior')
|
router.register('add-junior', AddJuniorAPIView, basename='add-junior')
|
||||||
# Invited junior list API"""
|
# Invited junior list API"""
|
||||||
|
@ -13,6 +13,9 @@ import requests
|
|||||||
|
|
||||||
from rest_framework.viewsets import GenericViewSet, mixins
|
from rest_framework.viewsets import GenericViewSet, mixins
|
||||||
import math
|
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
|
||||||
@ -161,6 +164,29 @@ class JuniorListAPIView(viewsets.ModelViewSet):
|
|||||||
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 CheckJuniorApiViewSet(viewsets.GenericViewSet):
|
||||||
|
"""
|
||||||
|
api to check whether given user exist or not
|
||||||
|
"""
|
||||||
|
serializer_class = None
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
junior = Junior.objects.filter(auth__email=self.request.data.get('email')).first()
|
||||||
|
return junior
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
junior = self.get_queryset()
|
||||||
|
data = {
|
||||||
|
'junior_exist': True if junior else False
|
||||||
|
}
|
||||||
|
return custom_response(None, data)
|
||||||
|
|
||||||
|
|
||||||
class AddJuniorAPIView(viewsets.ModelViewSet):
|
class AddJuniorAPIView(viewsets.ModelViewSet):
|
||||||
"""Add Junior by guardian"""
|
"""Add Junior by guardian"""
|
||||||
serializer_class = AddJuniorSerializer
|
serializer_class = AddJuniorSerializer
|
||||||
@ -177,6 +203,16 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
|
|||||||
"email":"abc@yopmail.com"
|
"email":"abc@yopmail.com"
|
||||||
}"""
|
}"""
|
||||||
try:
|
try:
|
||||||
|
if user := User.objects.filter(username=request.data['email']).first():
|
||||||
|
data = self.associate_guardian(user)
|
||||||
|
if data == none:
|
||||||
|
return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
elif not data:
|
||||||
|
return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
elif data == "Max":
|
||||||
|
return custom_error_response(ERROR_CODE['2081'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
|
info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
|
||||||
'email': request.data['email'], 'first_name': request.data['first_name'],
|
'email': request.data['email'], 'first_name': request.data['first_name'],
|
||||||
'last_name': request.data['last_name'], 'image':None}
|
'last_name': request.data['last_name'], 'image':None}
|
||||||
@ -190,15 +226,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
|
|||||||
# upload image on ali baba
|
# upload image on ali baba
|
||||||
image_url = upload_image_to_alibaba(profile_image, filename)
|
image_url = upload_image_to_alibaba(profile_image, filename)
|
||||||
info_data.update({"image": image_url})
|
info_data.update({"image": image_url})
|
||||||
if user := User.objects.filter(username=request.data['email']).first():
|
|
||||||
data = self.associate_guardian(user)
|
|
||||||
if data == none:
|
|
||||||
return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
elif not data:
|
|
||||||
return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
elif data == "Max":
|
|
||||||
return custom_error_response(ERROR_CODE['2081'], response_status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
|
|
||||||
# use AddJuniorSerializer serializer
|
# use AddJuniorSerializer serializer
|
||||||
serializer = AddJuniorSerializer(data=request.data, context=info_data)
|
serializer = AddJuniorSerializer(data=request.data, context=info_data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
@ -235,7 +263,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
|
|||||||
if jun_data:
|
if jun_data:
|
||||||
jun_data.relationship = str(self.request.data['relationship'])
|
jun_data.relationship = str(self.request.data['relationship'])
|
||||||
jun_data.save()
|
jun_data.save()
|
||||||
send_notification.delay(ASSOCIATE_EXISTING_JUNIOR, self.request.user.id, GUARDIAN, junior.auth.id, {})
|
send_notification.delay(ASSOCIATE_EXISTING_JUNIOR, guardian.user_id, GUARDIAN, junior.auth_id, {})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -334,7 +362,7 @@ class RemoveJuniorAPIView(views.APIView):
|
|||||||
# save serializer
|
# save serializer
|
||||||
serializer.save()
|
serializer.save()
|
||||||
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
|
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
|
||||||
send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth.id, {})
|
send_notification.delay(REMOVE_JUNIOR, None, None, junior_queryset.auth_id, {})
|
||||||
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
@ -354,8 +382,8 @@ 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('-created_at')
|
||||||
|
|
||||||
queryset = self.filter_queryset(queryset)
|
queryset = self.filter_queryset(queryset)
|
||||||
return queryset
|
return queryset
|
||||||
@ -367,20 +395,16 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
|
|||||||
page=1"""
|
page=1"""
|
||||||
try:
|
try:
|
||||||
status_value = self.request.GET.get('status')
|
status_value = self.request.GET.get('status')
|
||||||
current_page = self.request.GET.get('page')
|
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
task_status = task_status_fun(status_value)
|
task_status = task_status_fun(status_value)
|
||||||
if status_value:
|
if status_value:
|
||||||
queryset = queryset.filter(task_status__in=task_status)
|
queryset = queryset.filter(task_status__in=task_status)
|
||||||
paginator = self.pagination_class()
|
paginator = CustomPageNumberPagination()
|
||||||
total_count = len(queryset)
|
|
||||||
total_pages = math.ceil(total_count / 10)
|
|
||||||
# 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, total_pages=total_pages, current_page=current_page,
|
return paginator.get_paginated_response(serializer.data)
|
||||||
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)
|
||||||
|
|
||||||
@ -647,7 +671,7 @@ class CheckAnswerAPIView(viewsets.ModelViewSet):
|
|||||||
submit_ans = SurveyOption.objects.filter(id=answer_id).last()
|
submit_ans = SurveyOption.objects.filter(id=answer_id).last()
|
||||||
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
|
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
|
||||||
question=queryset)
|
question=queryset)
|
||||||
if submit_ans:
|
if submit_ans.is_answer:
|
||||||
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, is_answer_correct=True)
|
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, is_answer_correct=True)
|
||||||
JuniorPoints.objects.filter(junior__auth=self.request.user).update(total_points=
|
JuniorPoints.objects.filter(junior__auth=self.request.user).update(total_points=
|
||||||
F('total_points') + queryset.points)
|
F('total_points') + queryset.points)
|
||||||
@ -687,8 +711,9 @@ class CompleteArticleAPIView(views.APIView):
|
|||||||
is_answer_correct=True).aggregate(
|
is_answer_correct=True).aggregate(
|
||||||
total_earn_points=Sum('earn_points'))['total_earn_points']
|
total_earn_points=Sum('earn_points'))['total_earn_points']
|
||||||
data = {"total_earn_points":total_earn_points}
|
data = {"total_earn_points":total_earn_points}
|
||||||
send_notification.delay(ARTICLE_REWARD_POINTS, None, None,
|
if total_earn_points:
|
||||||
request.user.id, {'points': total_earn_points})
|
send_notification.delay(ARTICLE_REWARD_POINTS, None, None,
|
||||||
|
request.user.id, {'points': total_earn_points})
|
||||||
return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3042'], data, 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)
|
||||||
|
@ -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):
|
||||||
|
@ -1,6 +1,98 @@
|
|||||||
"""
|
"""
|
||||||
notification test file
|
notification test file
|
||||||
"""
|
"""
|
||||||
from django.test import TestCase
|
# third party imports
|
||||||
|
from fcm_django.models import FCMDevice
|
||||||
|
|
||||||
# Create your tests here.
|
# django imports
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from account.models import UserNotification
|
||||||
|
# local imports
|
||||||
|
from account.serializers import GuardianSerializer
|
||||||
|
from notifications.models import Notification
|
||||||
|
from web_admin.tests.test_set_up import AnalyticsSetUp
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationTestCase(AnalyticsSetUp):
|
||||||
|
"""
|
||||||
|
test notification
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
test data up
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(NotificationTestCase, self).setUp()
|
||||||
|
|
||||||
|
# notification settings create
|
||||||
|
UserNotification.objects.create(user=self.user)
|
||||||
|
|
||||||
|
# notification create
|
||||||
|
self.notification = Notification.objects.create(notification_to=self.user, notification_from=self.user_3)
|
||||||
|
|
||||||
|
# to get guardian/user auth token
|
||||||
|
self.guardian_data = GuardianSerializer(
|
||||||
|
self.guardian, context={'user_type': 2}
|
||||||
|
).data
|
||||||
|
self.auth_token = self.guardian_data['auth_token']
|
||||||
|
|
||||||
|
# api header
|
||||||
|
self.header = {
|
||||||
|
'HTTP_AUTHORIZATION': f'Bearer {self.auth_token}',
|
||||||
|
'Content-Type': "Application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_notification_list(self):
|
||||||
|
"""
|
||||||
|
test notification list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = reverse('notifications:notifications-list')
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one notification exists in the database
|
||||||
|
self.assertEqual(Notification.objects.filter(notification_to=self.user).count(), 1)
|
||||||
|
|
||||||
|
def test_fcm_register(self):
|
||||||
|
"""
|
||||||
|
test fcm register
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('notifications:notifications-device')
|
||||||
|
data = {
|
||||||
|
'registration_id': 'registration_id',
|
||||||
|
'device_id': 'device_id',
|
||||||
|
'type': 'ios'
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, **self.header)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# device created for user
|
||||||
|
self.assertEqual(FCMDevice.objects.count(), 1)
|
||||||
|
|
||||||
|
def test_send_test_notification(self):
|
||||||
|
"""
|
||||||
|
test send test notification
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('notifications:notifications-test')
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming one notification exists in the database and two created after api run
|
||||||
|
self.assertEqual(Notification.objects.filter(notification_to=self.user).count(), 3)
|
||||||
|
|
||||||
|
def test_mark_as_read(self):
|
||||||
|
"""
|
||||||
|
test mark as read
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('notifications:notifications-mark-as-read')
|
||||||
|
data = {
|
||||||
|
'id': [self.notification.id]
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, **self.header)
|
||||||
|
self.notification.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.notification.is_read, True)
|
||||||
|
@ -104,7 +104,6 @@ def send_notification(notification_type, from_user_id, from_user_type, to_user_i
|
|||||||
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
|
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
|
||||||
from_user_type, to_user_id, extra_data)
|
from_user_type, to_user_id, extra_data)
|
||||||
user_notification_type = UserNotification.objects.filter(user=to_user).first()
|
user_notification_type = UserNotification.objects.filter(user=to_user).first()
|
||||||
notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
|
|
||||||
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
|
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
|
||||||
notification_to=to_user, data=notification_data)
|
notification_to=to_user, data=notification_data)
|
||||||
if user_notification_type and user_notification_type.push_notification:
|
if user_notification_type and user_notification_type.push_notification:
|
||||||
@ -139,43 +138,10 @@ def send_notification_multiple_user(notification_type, from_user_id, from_user_t
|
|||||||
|
|
||||||
notification_list = []
|
notification_list = []
|
||||||
for user in to_user_list:
|
for user in to_user_list:
|
||||||
notification_copy_data = notification_data.copy()
|
|
||||||
notification_copy_data.update(
|
|
||||||
{'badge': Notification.objects.filter(notification_to=user, is_read=False).count()})
|
|
||||||
notification_list.append(Notification(notification_type=notification_type,
|
notification_list.append(Notification(notification_type=notification_type,
|
||||||
notification_to=user,
|
notification_to=user,
|
||||||
notification_from=from_user,
|
notification_from=from_user,
|
||||||
data=notification_copy_data))
|
data=notification_data))
|
||||||
Notification.objects.bulk_create(notification_list)
|
Notification.objects.bulk_create(notification_list)
|
||||||
to_user_list = to_user_list.filter(user_notification__push_notification=True)
|
to_user_list = to_user_list.filter(user_notification__push_notification=True)
|
||||||
send_multiple_push(to_user_list, push_data)
|
send_multiple_push(to_user_list, push_data)
|
||||||
|
|
||||||
|
|
||||||
@shared_task()
|
|
||||||
def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data):
|
|
||||||
"""
|
|
||||||
:param notification_type:
|
|
||||||
:param from_user_id:
|
|
||||||
:param to_user_id:
|
|
||||||
:param extra_data:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if from_user_id:
|
|
||||||
from_user = Junior.objects.filter(auth_id=from_user_id).first()
|
|
||||||
extra_data['from_user_image'] = from_user.image
|
|
||||||
send_notification(notification_type, from_user_id, to_user_id, extra_data)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task()
|
|
||||||
def send_notification_to_junior(notification_type, from_user_id, to_user_id, extra_data):
|
|
||||||
"""
|
|
||||||
:param notification_type:
|
|
||||||
:param from_user_id:
|
|
||||||
:param to_user_id:
|
|
||||||
:param extra_data:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if from_user_id:
|
|
||||||
from_user = Guardian.objects.filter(user_id=from_user_id).first()
|
|
||||||
extra_data['from_user_image'] = from_user.image
|
|
||||||
send_notification(notification_type, from_user_id, to_user_id, extra_data)
|
|
||||||
|
@ -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):
|
||||||
@ -67,5 +68,8 @@ class NotificationViewSet(viewsets.GenericViewSet):
|
|||||||
"""
|
"""
|
||||||
notification list
|
notification list
|
||||||
"""
|
"""
|
||||||
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
|
if request.query_params.get('all'):
|
||||||
|
Notification.objects.filter(notification_to_id=request.auth.payload['user_id']).update(is_read=True)
|
||||||
|
elif request.data.get('id'):
|
||||||
|
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
|
||||||
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
|
||||||
|
@ -101,4 +101,5 @@ vine==5.0.0
|
|||||||
wcwidth==0.2.6
|
wcwidth==0.2.6
|
||||||
|
|
||||||
pandas==2.0.3
|
pandas==2.0.3
|
||||||
XlsxWriter==3.1.2
|
XlsxWriter==3.1.2
|
||||||
|
coverage==7.3.1
|
||||||
|
@ -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']
|
|
@ -115,8 +115,6 @@ class UserCSVReportSerializer(serializers.ModelSerializer):
|
|||||||
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
|
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
|
||||||
return f"+{profile.country_code}{profile.phone}" \
|
return f"+{profile.country_code}{profile.phone}" \
|
||||||
if profile.country_code and profile.phone else profile.phone
|
if profile.country_code and profile.phone else profile.phone
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_type(obj):
|
def get_user_type(obj):
|
||||||
@ -128,8 +126,6 @@ class UserCSVReportSerializer(serializers.ModelSerializer):
|
|||||||
return dict(USER_TYPE).get('2').capitalize()
|
return dict(USER_TYPE).get('2').capitalize()
|
||||||
elif obj.junior_profile.all().first():
|
elif obj.junior_profile.all().first():
|
||||||
return dict(USER_TYPE).get('1').capitalize()
|
return dict(USER_TYPE).get('1').capitalize()
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_is_active(obj):
|
def get_is_active(obj):
|
||||||
|
@ -15,6 +15,7 @@ from notifications.utils import send_notification_multiple_user
|
|||||||
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
|
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
|
||||||
from web_admin.utils import pop_id, get_image_url
|
from web_admin.utils import pop_id, get_image_url
|
||||||
from junior.models import JuniorArticlePoints, JuniorArticle
|
from junior.models import JuniorArticlePoints, JuniorArticle
|
||||||
|
|
||||||
USER = get_user_model()
|
USER = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +82,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):
|
||||||
"""
|
"""
|
||||||
@ -90,10 +91,9 @@ class ArticleSerializer(serializers.ModelSerializer):
|
|||||||
"""
|
"""
|
||||||
article_cards = attrs.get('article_cards', None)
|
article_cards = attrs.get('article_cards', None)
|
||||||
article_survey = attrs.get('article_survey', None)
|
article_survey = attrs.get('article_survey', None)
|
||||||
if article_cards is None or len(article_cards) > int(MAX_ARTICLE_CARD):
|
if not 0 < len(article_cards) <= int(MAX_ARTICLE_CARD):
|
||||||
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
|
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
|
||||||
if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int(
|
if not int(MIN_ARTICLE_SURVEY) <= len(article_survey) <= int(MAX_ARTICLE_SURVEY):
|
||||||
MAX_ARTICLE_SURVEY) < len(article_survey):
|
|
||||||
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
|
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@ -185,6 +185,28 @@ class ArticleSerializer(serializers.ModelSerializer):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleStatusChangeSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Article status change serializer
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Article
|
||||||
|
fields = ('is_published', )
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
"""
|
||||||
|
:param instance: article object
|
||||||
|
:param validated_data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
instance.is_published = validated_data['is_published']
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
|
class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Article Card serializer
|
Article Card serializer
|
||||||
@ -221,6 +243,7 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
|
|||||||
card_image = DefaultArticleCardImage.objects.create(**validated_data)
|
card_image = DefaultArticleCardImage.objects.create(**validated_data)
|
||||||
return card_image
|
return card_image
|
||||||
|
|
||||||
|
|
||||||
class ArticleListSerializer(serializers.ModelSerializer):
|
class ArticleListSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
serializer for article API
|
serializer for article API
|
||||||
@ -234,13 +257,14 @@ class ArticleListSerializer(serializers.ModelSerializer):
|
|||||||
meta class
|
meta class
|
||||||
"""
|
"""
|
||||||
model = Article
|
model = Article
|
||||||
fields = ('id', 'title', 'description','image', 'total_points', 'is_completed')
|
fields = ('id', 'title', 'description', 'image', 'total_points', 'is_completed')
|
||||||
|
|
||||||
def get_image(self, obj):
|
def get_image(self, obj):
|
||||||
"""article image"""
|
"""article image"""
|
||||||
if obj.article_cards.first():
|
if obj.article_cards.first():
|
||||||
return obj.article_cards.first().image_url
|
return obj.article_cards.first().image_url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_total_points(self, obj):
|
def get_total_points(self, obj):
|
||||||
"""total points of article"""
|
"""total points of article"""
|
||||||
return obj.article_survey.all().count() * NUMBER['five']
|
return obj.article_survey.all().count() * NUMBER['five']
|
||||||
@ -253,6 +277,7 @@ class ArticleListSerializer(serializers.ModelSerializer):
|
|||||||
return junior_article.is_completed
|
return junior_article.is_completed
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ArticleQuestionSerializer(serializers.ModelSerializer):
|
class ArticleQuestionSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
article survey serializer
|
article survey serializer
|
||||||
@ -263,7 +288,6 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
|
|||||||
correct_answer = serializers.SerializerMethodField('get_correct_answer')
|
correct_answer = serializers.SerializerMethodField('get_correct_answer')
|
||||||
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
|
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
|
||||||
|
|
||||||
|
|
||||||
def get_is_attempt(self, obj):
|
def get_is_attempt(self, obj):
|
||||||
"""attempt question or not"""
|
"""attempt question or not"""
|
||||||
context_data = self.context.get('user')
|
context_data = self.context.get('user')
|
||||||
@ -295,6 +319,7 @@ class ArticleQuestionSerializer(serializers.ModelSerializer):
|
|||||||
model = ArticleSurvey
|
model = ArticleSurvey
|
||||||
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
|
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
|
||||||
|
|
||||||
|
|
||||||
class StartAssessmentSerializer(serializers.ModelSerializer):
|
class StartAssessmentSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
serializer for article API
|
serializer for article API
|
||||||
@ -310,6 +335,7 @@ class StartAssessmentSerializer(serializers.ModelSerializer):
|
|||||||
if data:
|
if data:
|
||||||
return data.current_que_page if data.current_que_page < total_count else data.current_que_page - 1
|
return data.current_que_page if data.current_que_page < total_count else data.current_que_page - 1
|
||||||
return NUMBER['zero']
|
return NUMBER['zero']
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""
|
"""
|
||||||
meta class
|
meta class
|
||||||
@ -318,7 +344,6 @@ class StartAssessmentSerializer(serializers.ModelSerializer):
|
|||||||
fields = ('article_survey', 'current_page')
|
fields = ('article_survey', 'current_page')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ArticleCardlistSerializer(serializers.ModelSerializer):
|
class ArticleCardlistSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Article Card serializer
|
Article Card serializer
|
||||||
@ -332,8 +357,9 @@ class ArticleCardlistSerializer(serializers.ModelSerializer):
|
|||||||
"""current page"""
|
"""current page"""
|
||||||
context_data = self.context.get('user')
|
context_data = self.context.get('user')
|
||||||
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj.article).last()
|
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj.article).last()
|
||||||
|
total_count = self.context.get('card_count')
|
||||||
if data:
|
if data:
|
||||||
return data.current_card_page
|
return data.current_card_page if data.current_card_page < total_count else data.current_card_page - 1
|
||||||
return NUMBER['zero']
|
return NUMBER['zero']
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
|
@ -50,8 +50,6 @@ class UserManagementListSerializer(serializers.ModelSerializer):
|
|||||||
return profile.country_code if profile.country_code else None
|
return profile.country_code if profile.country_code else None
|
||||||
elif profile := obj.junior_profile.all().first():
|
elif profile := obj.junior_profile.all().first():
|
||||||
return profile.country_code if profile.country_code else None
|
return profile.country_code if profile.country_code else None
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_phone(obj):
|
def get_phone(obj):
|
||||||
@ -63,8 +61,6 @@ class UserManagementListSerializer(serializers.ModelSerializer):
|
|||||||
return profile.phone if profile.phone else None
|
return profile.phone if profile.phone else None
|
||||||
elif profile := obj.junior_profile.all().first():
|
elif profile := obj.junior_profile.all().first():
|
||||||
return profile.phone if profile.phone else None
|
return profile.phone if profile.phone else None
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_type(obj):
|
def get_user_type(obj):
|
||||||
@ -76,8 +72,6 @@ class UserManagementListSerializer(serializers.ModelSerializer):
|
|||||||
return dict(USER_TYPE).get('2')
|
return dict(USER_TYPE).get('2')
|
||||||
elif obj.junior_profile.all().first():
|
elif obj.junior_profile.all().first():
|
||||||
return dict(USER_TYPE).get('1')
|
return dict(USER_TYPE).get('1')
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_is_active(obj):
|
def get_is_active(obj):
|
||||||
@ -89,8 +83,6 @@ class UserManagementListSerializer(serializers.ModelSerializer):
|
|||||||
return profile.is_active
|
return profile.is_active
|
||||||
elif profile := obj.junior_profile.all().first():
|
elif profile := obj.junior_profile.all().first():
|
||||||
return profile.is_active
|
return profile.is_active
|
||||||
else:
|
|
||||||
return obj.is_active
|
|
||||||
|
|
||||||
|
|
||||||
class GuardianSerializer(serializers.ModelSerializer):
|
class GuardianSerializer(serializers.ModelSerializer):
|
||||||
@ -292,5 +284,3 @@ class UserManagementDetailSerializer(serializers.ModelSerializer):
|
|||||||
is_verified=True).select_related('user')
|
is_verified=True).select_related('user')
|
||||||
serializer = GuardianSerializer(guardian, many=True)
|
serializer = GuardianSerializer(guardian, many=True)
|
||||||
return serializer.data
|
return serializer.data
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
"""
|
|
||||||
web_admin test file
|
|
||||||
"""
|
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
0
web_admin/tests/__init__.py
Normal file
0
web_admin/tests/__init__.py
Normal file
109
web_admin/tests/test_analytics.py
Normal file
109
web_admin/tests/test_analytics.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
web admin test analytics file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from web_admin.tests.test_set_up import AnalyticsSetUp
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsViewSetTestCase(AnalyticsSetUp):
|
||||||
|
"""
|
||||||
|
test cases for analytics, users count, new sign-ups,
|
||||||
|
assign tasks report, junior leaderboard, export excel
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
test data set up
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AnalyticsViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
def test_total_sign_up_count(self):
|
||||||
|
"""
|
||||||
|
test total sign up count
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-users-count')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four users exists in the database
|
||||||
|
self.assertEqual(response.data['data']['total_users'], 4)
|
||||||
|
# Assuming two guardians exists in the database
|
||||||
|
self.assertEqual(response.data['data']['total_guardians'], 2)
|
||||||
|
# Assuming two juniors exists in the database
|
||||||
|
self.assertEqual(response.data['data']['total_juniors'], 2)
|
||||||
|
|
||||||
|
def test_new_user_sign_ups(self):
|
||||||
|
"""
|
||||||
|
test new user sign-ups
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-new-signups')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four users exists in the database
|
||||||
|
self.assertEqual(response.data['data'][0]['signups'], 4)
|
||||||
|
|
||||||
|
def test_new_user_sign_ups_between_given_dates(self):
|
||||||
|
"""
|
||||||
|
test new user sign-ups
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-new-signups')
|
||||||
|
query_params = {
|
||||||
|
'start_date': '2023-09-12',
|
||||||
|
'end_date': '2023-09-13'
|
||||||
|
}
|
||||||
|
response = self.client.get(url, query_params)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four users exists in the database
|
||||||
|
self.assertEqual(response.data['data'][0]['signups'], 4)
|
||||||
|
|
||||||
|
def test_assign_tasks_report(self):
|
||||||
|
"""
|
||||||
|
test assign tasks report
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-assign-tasks')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming two completed tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_completed'], 2)
|
||||||
|
# Assuming two pending tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_pending'], 2)
|
||||||
|
# Assuming two in progress tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_in_progress'], 2)
|
||||||
|
# Assuming two requested tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_requested'], 2)
|
||||||
|
# Assuming two rejected tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_rejected'], 2)
|
||||||
|
# Assuming two expired tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_expired'], 2)
|
||||||
|
|
||||||
|
def test_junior_leaderboard(self):
|
||||||
|
"""
|
||||||
|
test junior leaderboard
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-junior-leaderboard')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_export_excel(self):
|
||||||
|
"""
|
||||||
|
test export excel
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-export-excel')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertURLEqual(response.data['data'], self.export_excel_url)
|
255
web_admin/tests/test_article.py
Normal file
255
web_admin/tests/test_article.py
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
"""
|
||||||
|
web_admin test article file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
|
||||||
|
from web_admin.tests.test_set_up import ArticleTestSetUp
|
||||||
|
|
||||||
|
# user model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleViewSetTestCase(ArticleTestSetUp):
|
||||||
|
"""
|
||||||
|
test cases for article create, update, list, retrieve, delete
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
|
||||||
|
def test_article_create_with_default_card_image(self):
|
||||||
|
"""
|
||||||
|
test article create with default card_image
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
response = self.client.post(url, self.article_data_with_default_card_image, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Check that a new article was created
|
||||||
|
self.assertEqual(Article.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_article_create_with_base64_card_image(self):
|
||||||
|
"""
|
||||||
|
test article create with base64 card image
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
response = self.client.post(url, self.article_data_with_base64_card_image, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Check that a new article was created
|
||||||
|
self.assertEqual(Article.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_article_update(self):
|
||||||
|
"""
|
||||||
|
test article update
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = reverse(self.article_detail_url, kwargs={'pk': self.article.id})
|
||||||
|
response = self.client.put(url, self.article_update_data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.article.refresh_from_db()
|
||||||
|
self.assertEqual(self.article.title, self.article_update_data['title'])
|
||||||
|
self.assertEqual(self.article.article_cards.count(), 1)
|
||||||
|
self.assertEqual(self.article.article_survey.count(), 6)
|
||||||
|
self.assertEqual(self.article.article_survey.first().options.count(), 3)
|
||||||
|
|
||||||
|
def test_articles_list(self):
|
||||||
|
"""
|
||||||
|
test articles list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
def test_article_retrieve(self):
|
||||||
|
"""
|
||||||
|
test article retrieve
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_detail_url, kwargs={'pk': self.article.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_article_delete(self):
|
||||||
|
"""
|
||||||
|
test article delete
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_detail_url, kwargs={'pk': self.article.id})
|
||||||
|
response = self.client.delete(url)
|
||||||
|
self.article.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.article.is_deleted, True)
|
||||||
|
|
||||||
|
def test_article_create_with_invalid_data(self):
|
||||||
|
"""
|
||||||
|
test article create with invalid data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
# Missing article_cards
|
||||||
|
invalid_data = {
|
||||||
|
"title": "Invalid Article",
|
||||||
|
"article_survey": [{"question": "Invalid Survey Question"}]
|
||||||
|
}
|
||||||
|
response = self.client.post(url, invalid_data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_article_status_change(self):
|
||||||
|
"""
|
||||||
|
test article status change (publish/un-publish)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-status-change', kwargs={'pk': self.article.id})
|
||||||
|
data = {
|
||||||
|
"is_published": False
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json')
|
||||||
|
self.article.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.article.is_published, False)
|
||||||
|
|
||||||
|
def test_article_card_remove(self):
|
||||||
|
"""
|
||||||
|
test article card remove
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-remove-card', kwargs={'pk': self.article_card.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(ArticleCard.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_article_survey_remove(self):
|
||||||
|
"""
|
||||||
|
test article survey remove
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-remove-survey', kwargs={'pk': self.article_survey.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(ArticleSurvey.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_article_card_create_with_default_card_image(self):
|
||||||
|
"""
|
||||||
|
test article card create with default card_image
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-test-add-card')
|
||||||
|
response = self.client.post(url, self.article_card_data_with_default_card_image, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Check that a new article card was created
|
||||||
|
self.assertEqual(ArticleCard.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_article_cards_list(self):
|
||||||
|
"""
|
||||||
|
test article cards list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-test-list-card')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultArticleCardImagesViewSetTestCase(APITestCase):
|
||||||
|
"""
|
||||||
|
test case for default article card image
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client = APIClient()
|
||||||
|
self.admin_user = User.objects.create_user(username='admin@example.com', email='admin@example.com',
|
||||||
|
password='admin@1234', is_staff=True, is_superuser=True)
|
||||||
|
self.default_image = DefaultArticleCardImage.objects.create(
|
||||||
|
image_name="card1.jpg",
|
||||||
|
image_url="https://example.com/updated_card1.jpg")
|
||||||
|
|
||||||
|
def test_default_article_card_image_list(self):
|
||||||
|
"""
|
||||||
|
test default article card image list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = reverse('web_admin:default-card-images-list')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one default article card image exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleListViewSetTestCase(ArticleTestSetUp):
|
||||||
|
"""
|
||||||
|
test cases for article list for junior
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleListViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.client.force_authenticate(user=self.user)
|
||||||
|
|
||||||
|
def test_article_list(self):
|
||||||
|
"""
|
||||||
|
test article list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-list-list')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleCardListViewSetTestCase(ArticleTestSetUp):
|
||||||
|
"""
|
||||||
|
test cases for article card list for junior
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleCardListViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.client.force_authenticate(user=self.user)
|
||||||
|
|
||||||
|
def test_article_cards_list(self):
|
||||||
|
"""
|
||||||
|
test article cards list for junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-card-list-list')
|
||||||
|
query_params = {
|
||||||
|
'article_id': self.article.id,
|
||||||
|
}
|
||||||
|
response = self.client.get(url, query_params)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
# Add more test cases for edge cases, permissions, etc.
|
160
web_admin/tests/test_auth.py
Normal file
160
web_admin/tests/test_auth.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
"""
|
||||||
|
web admin test auth file
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from rest_framework.test import APITestCase, APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from account.models import UserEmailOtp
|
||||||
|
from base.constants import USER_TYPE
|
||||||
|
from guardian.tasks import generate_otp
|
||||||
|
from web_admin.tests.test_set_up import BaseSetUp
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class AdminOTPTestCase(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test case to send otp to admin email
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AdminOTPTestCase, self).setUp()
|
||||||
|
self.url = reverse('web_admin:admin-otp')
|
||||||
|
|
||||||
|
def test_admin_otp_for_valid_email(self):
|
||||||
|
"""
|
||||||
|
test admin otp for valid email
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email
|
||||||
|
}
|
||||||
|
response = self.client.post(self.url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(UserEmailOtp.objects.count(), 1)
|
||||||
|
|
||||||
|
def test_admin_otp_for_invalid_email(self):
|
||||||
|
"""
|
||||||
|
test admin otp for invalid email
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': 'notadmin@example.com'
|
||||||
|
}
|
||||||
|
response = self.client.post(self.url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminVerifyOTPTestCase(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test case to verify otp for admin email
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AdminVerifyOTPTestCase, self).setUp()
|
||||||
|
self.verification_code = generate_otp()
|
||||||
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
self.user_email_otp = UserEmailOtp.objects.create(email=self.admin_email,
|
||||||
|
otp=self.verification_code,
|
||||||
|
expired_at=expiry,
|
||||||
|
user_type=dict(USER_TYPE).get('3'),
|
||||||
|
)
|
||||||
|
self.url = reverse('web_admin:admin-verify-otp')
|
||||||
|
|
||||||
|
def test_admin_verify_otp_with_valid_otp(self):
|
||||||
|
"""
|
||||||
|
test admin verify otp with valid otp
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"otp": self.verification_code
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.user_email_otp.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.user_email_otp.is_verified, True)
|
||||||
|
|
||||||
|
def test_admin_verify_otp_with_invalid_otp(self):
|
||||||
|
"""
|
||||||
|
test admin verify otp with invalid otp
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"otp": generate_otp()
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.user_email_otp.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(self.user_email_otp.is_verified, False)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCreateNewPassword(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test case to create new password for admin email
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AdminCreateNewPassword, self).setUp()
|
||||||
|
self.verification_code = generate_otp()
|
||||||
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
self.user_email_otp = UserEmailOtp.objects.create(email=self.admin_email,
|
||||||
|
otp=self.verification_code,
|
||||||
|
expired_at=expiry,
|
||||||
|
user_type=dict(USER_TYPE).get('3'),
|
||||||
|
)
|
||||||
|
self.url = reverse('web_admin:admin-create-password')
|
||||||
|
|
||||||
|
def test_admin_create_new_password_after_verification(self):
|
||||||
|
"""
|
||||||
|
test admin create new password
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.user_email_otp.is_verified = True
|
||||||
|
self.user_email_otp.save()
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"new_password": "New@1234",
|
||||||
|
"confirm_password": "New@1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(UserEmailOtp.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_admin_create_new_password_without_verification(self):
|
||||||
|
"""
|
||||||
|
test admin create new password
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"new_password": "Some@1234",
|
||||||
|
"confirm_password": "Some@1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(UserEmailOtp.objects.count(), 1)
|
407
web_admin/tests/test_set_up.py
Normal file
407
web_admin/tests/test_set_up.py
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
"""
|
||||||
|
web_admin test set up file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.conf import settings
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from guardian.models import Guardian, JuniorTask
|
||||||
|
from junior.models import Junior, JuniorPoints
|
||||||
|
from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption
|
||||||
|
|
||||||
|
# user model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
# image data in base 64 string
|
||||||
|
base64_image = ("data:image/jpeg;base64,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")
|
||||||
|
|
||||||
|
# export excel path and
|
||||||
|
# export excel url
|
||||||
|
export_excel_path = 'analytics/ZOD_Bank_Analytics.xlsx'
|
||||||
|
export_excel_url = f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{export_excel_path}"
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSetUp(APITestCase):
|
||||||
|
"""
|
||||||
|
basic setup
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
user data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# user and admin email
|
||||||
|
self.user_email = 'user@example.com'
|
||||||
|
self.admin_email = 'admin@example.com'
|
||||||
|
self.client = APIClient()
|
||||||
|
|
||||||
|
# create user
|
||||||
|
self.user = User.objects.create_user(username=self.user_email, email=self.user_email)
|
||||||
|
self.user.set_password('user@1234')
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
# create admin
|
||||||
|
self.admin_user = User.objects.create_user(username=self.admin_email, email=self.admin_email,
|
||||||
|
is_staff=True, is_superuser=True)
|
||||||
|
self.admin_user.set_password('admin@1234')
|
||||||
|
self.admin_user.save()
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleTestSetUp(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test cases data set up
|
||||||
|
for article create, update, list, retrieve and
|
||||||
|
remove card, survey and add test card, list test card and
|
||||||
|
default image upload and list
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
set up data for test
|
||||||
|
create user and admin
|
||||||
|
create article, article card and article survey and survey options
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleTestSetUp, self).setUp()
|
||||||
|
|
||||||
|
# create article
|
||||||
|
self.article = Article.objects.create(title="Existing Article", description="Existing Description",
|
||||||
|
is_published=True)
|
||||||
|
# create article card
|
||||||
|
self.article_card = ArticleCard.objects.create(article=self.article, title="Existing Card 1",
|
||||||
|
description="Existing Card 1 Description")
|
||||||
|
# create article survey
|
||||||
|
self.article_survey = ArticleSurvey.objects.create(article=self.article, points=5,
|
||||||
|
question="Existing Survey Question 1")
|
||||||
|
# create article survey options
|
||||||
|
SurveyOption.objects.create(survey=self.article_survey, option="Existing Option 1", is_answer=True)
|
||||||
|
SurveyOption.objects.create(survey=self.article_survey, option="Existing Option 2", is_answer=False)
|
||||||
|
|
||||||
|
# article api url used for get api
|
||||||
|
self.article_list_url = 'web_admin:article-list'
|
||||||
|
|
||||||
|
# article api url used for post api
|
||||||
|
self.article_detail_url = 'web_admin:article-detail'
|
||||||
|
|
||||||
|
# article card data with default card image
|
||||||
|
self.article_card_data_with_default_card_image = {
|
||||||
|
"title": "Card 1",
|
||||||
|
"description": "Card 1 Description",
|
||||||
|
"image_name": "card1.jpg",
|
||||||
|
"image_url": "https://example.com/card1.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
# article card data with base64 image
|
||||||
|
self.article_card_data_with_base64_image = {
|
||||||
|
"title": "Card base64",
|
||||||
|
"description": "Card base64 Description",
|
||||||
|
"image_name": "base64_image.jpg",
|
||||||
|
"image_url": base64_image
|
||||||
|
}
|
||||||
|
|
||||||
|
# article survey option data
|
||||||
|
self.article_survey_option_data = [
|
||||||
|
{"option": "Option 1", "is_answer": True},
|
||||||
|
{"option": "Option 2", "is_answer": False}
|
||||||
|
]
|
||||||
|
|
||||||
|
# article survey data
|
||||||
|
self.article_survey_data = [
|
||||||
|
{
|
||||||
|
"question": "Survey Question 1",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 2",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 3",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 4",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 5",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# article data with default card image
|
||||||
|
self.article_data_with_default_card_image = {
|
||||||
|
"title": "Test Article",
|
||||||
|
"description": "Test Description",
|
||||||
|
"article_cards": [
|
||||||
|
self.article_card_data_with_default_card_image
|
||||||
|
],
|
||||||
|
# minimum 5 article survey needed
|
||||||
|
"article_survey": self.article_survey_data
|
||||||
|
}
|
||||||
|
|
||||||
|
# article data with base64 card image
|
||||||
|
self.article_data_with_base64_card_image = {
|
||||||
|
"title": "Test Article",
|
||||||
|
"description": "Test Description",
|
||||||
|
"article_cards": [
|
||||||
|
self.article_card_data_with_base64_image
|
||||||
|
],
|
||||||
|
# minimum 5 article survey needed
|
||||||
|
"article_survey": self.article_survey_data
|
||||||
|
}
|
||||||
|
|
||||||
|
# article update data
|
||||||
|
self.article_update_data = {
|
||||||
|
"title": "Updated Article",
|
||||||
|
"description": "Updated Description",
|
||||||
|
|
||||||
|
# updated article card
|
||||||
|
"article_cards": [
|
||||||
|
{
|
||||||
|
"id": self.article_card.id,
|
||||||
|
"title": "Updated Card 1",
|
||||||
|
"description": "Updated Card 1 Description",
|
||||||
|
"image_name": "updated_card1.jpg",
|
||||||
|
"image_url": "https://example.com/updated_card1.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
# updated article survey
|
||||||
|
"article_survey": [
|
||||||
|
# updated article survey
|
||||||
|
{
|
||||||
|
"id": self.article_survey.id,
|
||||||
|
"question": "Updated Survey Question 1",
|
||||||
|
"options": [
|
||||||
|
{"id": self.article_survey.options.first().id,
|
||||||
|
"option": "Updated Option 1", "is_answer": False},
|
||||||
|
# New option
|
||||||
|
{"option": "New Option 3", "is_answer": True}
|
||||||
|
]
|
||||||
|
# added new articles
|
||||||
|
}] + self.article_survey_data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagementSetUp(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test cases for user management
|
||||||
|
users count, new sign-ups,
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
create new guardian and junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(UserManagementSetUp, self).setUp()
|
||||||
|
# guardian codes
|
||||||
|
self.guardian_code_1 = 'GRD123'
|
||||||
|
self.guardian_code_2 = 'GRD456'
|
||||||
|
|
||||||
|
# guardian 1
|
||||||
|
self.guardian = Guardian.objects.create(user=self.user, country_code=91, phone='8765876565',
|
||||||
|
country_name='India', gender=2, is_verified=True,
|
||||||
|
guardian_code=self.guardian_code_1)
|
||||||
|
|
||||||
|
# user 2 email
|
||||||
|
self.user_email_2 = 'user2@yopmail.com'
|
||||||
|
# create user 2
|
||||||
|
self.user_2 = User.objects.create_user(username=self.user_email_2, email=self.user_email_2)
|
||||||
|
self.user_2.set_password('user2@1234')
|
||||||
|
self.user_2.save()
|
||||||
|
|
||||||
|
# guardian 2
|
||||||
|
self.guardian_2 = Guardian.objects.create(user=self.user_2, country_code=92, phone='8765876575',
|
||||||
|
country_name='India', gender=1, is_verified=True,
|
||||||
|
guardian_code=self.guardian_code_2)
|
||||||
|
|
||||||
|
# user 3 email
|
||||||
|
self.user_email_3 = 'user3@yopmail.com'
|
||||||
|
# create user 3
|
||||||
|
self.user_3 = User.objects.create_user(username=self.user_email_3, email=self.user_email_3)
|
||||||
|
self.user_3.set_password('user3@1234')
|
||||||
|
self.user_3.save()
|
||||||
|
|
||||||
|
# junior 1
|
||||||
|
self.junior = Junior.objects.create(auth=self.user_3, country_name='India', gender=2,
|
||||||
|
is_verified=True, guardian_code=[self.guardian_code_1])
|
||||||
|
|
||||||
|
# user 4 email
|
||||||
|
self.user_email_4 = 'user4@yopmail.com'
|
||||||
|
# create user 4
|
||||||
|
self.user_4 = User.objects.create_user(username=self.user_email_4, email=self.user_email_4)
|
||||||
|
self.user_4.set_password('user4@1234')
|
||||||
|
self.user_4.save()
|
||||||
|
|
||||||
|
# junior 2
|
||||||
|
self.junior_2 = Junior.objects.create(auth=self.user_4, country_code=92, phone='8768763443',
|
||||||
|
country_name='India', gender=1, is_verified=True,
|
||||||
|
guardian_code=[self.guardian_code_2])
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsSetUp(UserManagementSetUp):
|
||||||
|
"""
|
||||||
|
test analytics
|
||||||
|
task assign report, junior leaderboard
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
test data set up
|
||||||
|
create task and assigned to junior
|
||||||
|
create junior points data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AnalyticsSetUp, self).setUp()
|
||||||
|
|
||||||
|
# pending tasks 1
|
||||||
|
self.pending_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Pending Task 1', task_status=1,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# pending tasks 2
|
||||||
|
self.pending_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Pending Task 2', task_status=1,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# in progress tasks 1
|
||||||
|
self.in_progress_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='In progress Task 1', task_status=2,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# in progress tasks 2
|
||||||
|
self.in_progress_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='In progress Task 2', task_status=2,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# rejected tasks 1
|
||||||
|
self.rejected_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Rejected Task 1', task_status=3,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# rejected tasks 2
|
||||||
|
self.rejected_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Rejected Task 2', task_status=3,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# requested task 1
|
||||||
|
self.requested_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Requested Task 1', task_status=4,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# requested task 2
|
||||||
|
self.requested_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Requested Task 2', task_status=4,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# completed task 1
|
||||||
|
self.completed_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Completed Task 1', task_status=5,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# completed task 2
|
||||||
|
self.completed_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Completed Task 2', task_status=5,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# expired task 1
|
||||||
|
self.expired_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Expired Task 1', task_status=6,
|
||||||
|
due_date='2023-09-11')
|
||||||
|
# expired task 2
|
||||||
|
self.expired_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Expired Task 2', task_status=6,
|
||||||
|
due_date='2023-09-11')
|
||||||
|
|
||||||
|
# junior point table data
|
||||||
|
JuniorPoints.objects.create(junior=self.junior_2, total_points=50)
|
||||||
|
JuniorPoints.objects.create(junior=self.junior, total_points=40)
|
||||||
|
|
||||||
|
# export excel url
|
||||||
|
self.export_excel_url = export_excel_url
|
255
web_admin/tests/test_user_management.py
Normal file
255
web_admin/tests/test_user_management.py
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
"""
|
||||||
|
web admin test user management file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from base.constants import GUARDIAN, JUNIOR
|
||||||
|
from web_admin.tests.test_set_up import UserManagementSetUp
|
||||||
|
|
||||||
|
# user model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagementViewSetTestCase(UserManagementSetUp):
|
||||||
|
"""
|
||||||
|
test cases for user management
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
super(UserManagementViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.update_data = {
|
||||||
|
'email': 'user5@yopmail.com',
|
||||||
|
'country_code': 93,
|
||||||
|
'phone': '8765454235'
|
||||||
|
}
|
||||||
|
self.user_management_endpoint = "/api/v1/user-management"
|
||||||
|
|
||||||
|
def test_user_management_list_all_users(self):
|
||||||
|
"""
|
||||||
|
test user management list all users
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/"
|
||||||
|
response = self.client.get(url, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four user exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 4)
|
||||||
|
|
||||||
|
def test_user_management_list_guardians(self):
|
||||||
|
"""
|
||||||
|
test user management list guardians
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming two guardians exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 2)
|
||||||
|
|
||||||
|
def test_user_management_list_juniors(self):
|
||||||
|
"""
|
||||||
|
test user management list juniors
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/?user_type={JUNIOR}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming two juniors exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 2)
|
||||||
|
|
||||||
|
def test_user_management_list_with_unauthorised_user(self):
|
||||||
|
"""
|
||||||
|
test user management list with unauthorised user
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# user unauthorised access
|
||||||
|
self.client.force_authenticate(user=self.user)
|
||||||
|
url = f"{self.user_management_endpoint}/"
|
||||||
|
response = self.client.get(url, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_user_management_retrieve_guardian(self):
|
||||||
|
"""
|
||||||
|
test user management retrieve guardian
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
def test_user_management_retrieve_junior(self):
|
||||||
|
"""
|
||||||
|
test user management retrieve junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
def test_user_management_retrieve_without_user_type(self):
|
||||||
|
"""
|
||||||
|
test user management retrieve without user type
|
||||||
|
user status is mandatory
|
||||||
|
API will throw error
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_guardian(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.patch(url, self.update_data, format='json',)
|
||||||
|
self.user.refresh_from_db()
|
||||||
|
self.guardian.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.user.email, self.update_data['email'])
|
||||||
|
self.assertEqual(self.guardian.country_code, self.update_data['country_code'])
|
||||||
|
self.assertEqual(self.guardian.phone, self.update_data['phone'])
|
||||||
|
|
||||||
|
def test_user_management_update_guardian_with_existing_email(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian with existing email
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
data = {
|
||||||
|
'email': self.user_email_2
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_guardian_with_existing_phone(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian with existing phone
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
data = {
|
||||||
|
'phone': self.guardian_2.phone
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_junior(self):
|
||||||
|
"""
|
||||||
|
test user management update junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
response = self.client.patch(url, self.update_data, format='json',)
|
||||||
|
self.user_3.refresh_from_db()
|
||||||
|
self.junior.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.user_3.email, self.update_data['email'])
|
||||||
|
self.assertEqual(self.junior.country_code, self.update_data['country_code'])
|
||||||
|
self.assertEqual(self.junior.phone, self.update_data['phone'])
|
||||||
|
|
||||||
|
def test_user_management_update_junior_with_existing_email(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian with existing phone
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
data = {
|
||||||
|
'email': self.user_email_4
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_junior_with_existing_phone(self):
|
||||||
|
"""
|
||||||
|
test user management update junior with existing phone
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
data = {
|
||||||
|
'phone': self.junior_2.phone
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_without_user_type(self):
|
||||||
|
"""
|
||||||
|
test user management update without user type
|
||||||
|
user status is mandatory
|
||||||
|
API will throw error
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/"
|
||||||
|
response = self.client.patch(url, self.update_data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_change_status_guardian(self):
|
||||||
|
"""
|
||||||
|
test user management change status guardian
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/change-status/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.guardian.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.guardian.is_active, False)
|
||||||
|
|
||||||
|
def test_user_management_change_status_junior(self):
|
||||||
|
"""
|
||||||
|
test user management change status junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/change-status/?user_type={JUNIOR}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.junior.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.junior.is_active, False)
|
||||||
|
|
||||||
|
def test_user_management_change_status_without_user_type(self):
|
||||||
|
"""
|
||||||
|
test user management change status without user type
|
||||||
|
user status is mandatory
|
||||||
|
API will throw error
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
@ -18,7 +18,7 @@ router = routers.SimpleRouter()
|
|||||||
router.register('article', ArticleViewSet, basename='article')
|
router.register('article', ArticleViewSet, basename='article')
|
||||||
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
|
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
|
||||||
router.register('user-management', UserManagementViewSet, basename='user')
|
router.register('user-management', UserManagementViewSet, basename='user')
|
||||||
router.register('analytics', AnalyticsViewSet, basename='user-analytics')
|
router.register('analytics', AnalyticsViewSet, basename='analytics')
|
||||||
|
|
||||||
router.register('article-list', ArticleListViewSet, basename='article-list')
|
router.register('article-list', ArticleListViewSet, basename='article-list')
|
||||||
router.register('article-card-list', ArticleCardListViewSet, basename='article-card-list')
|
router.register('article-card-list', ArticleCardListViewSet, basename='article-card-list')
|
||||||
|
@ -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
|
||||||
|
@ -17,7 +17,7 @@ from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticle
|
|||||||
from web_admin.permission import AdminPermission
|
from web_admin.permission import AdminPermission
|
||||||
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
|
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
|
||||||
DefaultArticleCardImageSerializer, ArticleListSerializer,
|
DefaultArticleCardImageSerializer, ArticleListSerializer,
|
||||||
ArticleCardlistSerializer)
|
ArticleCardlistSerializer, ArticleStatusChangeSerializer)
|
||||||
|
|
||||||
USER = get_user_model()
|
USER = get_user_model()
|
||||||
|
|
||||||
@ -32,7 +32,6 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
|||||||
queryset = Article
|
queryset = Article
|
||||||
filter_backends = (SearchFilter,)
|
filter_backends = (SearchFilter,)
|
||||||
search_fields = ['title']
|
search_fields = ['title']
|
||||||
http_method_names = ['get', 'post', 'put', 'delete']
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
|
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
|
||||||
@ -130,6 +129,23 @@ 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=['patch'], url_name='status-change', url_path='status-change',
|
||||||
|
detail=True, serializer_class=ArticleStatusChangeSerializer)
|
||||||
|
def article_status_change(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
article un-publish or publish api method
|
||||||
|
:param request: article id and
|
||||||
|
{
|
||||||
|
"is_published": true/false
|
||||||
|
}
|
||||||
|
:return: success message
|
||||||
|
"""
|
||||||
|
article = Article.objects.filter(id=kwargs['pk']).first()
|
||||||
|
serializer = self.serializer_class(article, data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
return custom_response(SUCCESS_CODE["3038"])
|
||||||
|
|
||||||
@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)
|
||||||
|
|
||||||
@ -263,7 +279,8 @@ class ArticleCardListViewSet(viewsets.ModelViewSet):
|
|||||||
try:
|
try:
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
# article card list
|
# article card list
|
||||||
serializer = ArticleCardlistSerializer(queryset, context={"user": self.request.user}, many=True)
|
serializer = ArticleCardlistSerializer(queryset, context={"user": self.request.user,
|
||||||
|
"card_count": queryset.count()}, many=True)
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(None, serializer.data, 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)
|
||||||
|
@ -4,7 +4,7 @@ web_admin auth views file
|
|||||||
# django imports
|
# django imports
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
|
@ -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',
|
||||||
|
@ -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'))]
|
||||||
|
Reference in New Issue
Block a user