mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-25 21:59:40 +00:00
Compare commits
302 Commits
Author | SHA1 | Date | |
---|---|---|---|
f3478c972e | |||
bc18c67527 | |||
ffb99f5099 | |||
8183edf319 | |||
8f214d11a7 | |||
b5a89df59a | |||
5524eeed64 | |||
aeaa7d7ab8 | |||
69be3bb2ac | |||
be9f600bcc | |||
65d0932893 | |||
9b14eedb18 | |||
6c96ea0820 | |||
a211baa10a | |||
a93dc83bd1 | |||
116fb00358 | |||
20fa6e43da | |||
2cffc4e128 | |||
ec585d35f3 | |||
d62efa2139 | |||
7b75a3233c | |||
e1ef289c69 | |||
4f79a690c1 | |||
0af2a35206 | |||
a262b03292 | |||
d24f075110 | |||
f7624bc1e7 | |||
dc12b35842 | |||
a80f9db557 | |||
3ad29e677d | |||
8b0a5d9a8e | |||
16d823f97d | |||
d4008d6cc2 | |||
3dae22a870 | |||
a8d291474a | |||
e6482167ae | |||
9a932d31b5 | |||
e2c84eb83d | |||
5bfc3966b3 | |||
37d191eef8 | |||
e9ee8ec8b2 | |||
cc5ecc0647 | |||
63dfd731fc | |||
e9d4d7091e | |||
219bae792e | |||
df3cab99a5 | |||
d2242d4c64 | |||
b7d5916f8e | |||
5e17edcf3f | |||
b6fe943bfc | |||
8e529b292d | |||
61ca2e7d2d | |||
966f94eb1e | |||
f424c99478 | |||
34359360e0 | |||
685a822e52 | |||
e57344e54f | |||
235fc9baac | |||
3dc8268b07 | |||
8be8d56036 | |||
c79a891c89 | |||
eb4e09964d | |||
cf9376663c | |||
9891060ee3 | |||
5b2ab2275e | |||
9b6a84c7d1 | |||
e9d4fbe373 | |||
21b92f8c74 | |||
c46b2ef52c | |||
c47f6222d9 | |||
b82902081f | |||
e9315beab9 | |||
3a938720dd | |||
464899f7d3 | |||
8a436bb79f | |||
2e0ceb8c92 | |||
1a2fd2d3a8 | |||
a65eb2f77d | |||
3d06139c4f | |||
f5a03e2fdf | |||
226ddfa7e6 | |||
1028822908 | |||
342f27fc62 | |||
e9aa2dfda9 | |||
624e7a4edb | |||
3072bd5cdb | |||
930b58cf05 | |||
339c49577e | |||
09f006eb13 | |||
cd3b385756 | |||
ab1a2be679 | |||
11605540d7 | |||
8cd4864748 | |||
ad0b5bfc75 | |||
f541608656 | |||
b1b7c42438 | |||
8f9450b743 | |||
bb3441e4ed | |||
5b9f7b1828 | |||
89dda61bff | |||
31f071a463 | |||
20644a6293 | |||
a8b01d4c3a | |||
56e1484b87 | |||
557a7d7a5c | |||
4e0c6a91f4 | |||
f931e20334 | |||
8d159ac3a4 | |||
95ce57ec7d | |||
7068551050 | |||
c23d65f67c | |||
73f05dc843 | |||
290089b9ef | |||
5f3a9c35fa | |||
b92115168d | |||
19acef10ef | |||
05fd76a50b | |||
60e6d96379 | |||
fad39d399f | |||
d573d56a35 | |||
08a08960ee | |||
f8e529600b | |||
9765c90b9b | |||
930c10b578 | |||
8ca74e4b03 | |||
95dad86b12 | |||
dfff643c71 | |||
214566ec8f | |||
210e1a39e9 | |||
e380f3f6ab | |||
99a59162a2 | |||
67a5f670f8 | |||
82189d3953 | |||
65ecd9a125 | |||
a7bce32993 | |||
4495d8999c | |||
61e94c9469 | |||
1c3d4731c1 | |||
46853011be | |||
2216411fd2 | |||
606c1fa6e6 | |||
092cbfad3e | |||
249a469d89 | |||
d202774df1 | |||
1adf0c2f70 | |||
b193166914 | |||
48b455e38a | |||
6d54a718b9 | |||
454f66b3a0 | |||
5045f5acda | |||
e84180a6c2 | |||
21e006ae2a | |||
92e5104e3f | |||
dfb73a7d36 | |||
3921f76f22 | |||
cdf1a7b74e | |||
b4026a4f11 | |||
71a3e36bf3 | |||
4bc91abebf | |||
5b236dfc81 | |||
150bb9250d | |||
51d3b77ff7 | |||
92c67dd455 | |||
9a5447bca2 | |||
392086760f | |||
ceaf584332 | |||
ef4c459229 | |||
dd2890bca6 | |||
b46109e487 | |||
3f6c9a2d99 | |||
f74302df04 | |||
9bdd324c4e | |||
04ed9c668c | |||
e1d092d663 | |||
4e8243c17e | |||
13ba311822 | |||
043c8c2f63 | |||
a5f239b9d9 | |||
36427b50c1 | |||
728d19da99 | |||
5da07002e0 | |||
3017b0ece0 | |||
e73113fcca | |||
ed1b5dd453 | |||
c278670c39 | |||
60af098e1e | |||
74328f37b2 | |||
d29a60558f | |||
451c3bdae7 | |||
d8f4467d98 | |||
7c809776b6 | |||
a9cc05c675 | |||
e46d649fdc | |||
adb827f5a0 | |||
a02dfd4e31 | |||
d3b0be953e | |||
11b9f00285 | |||
4bc16c56bd | |||
08dc9f8538 | |||
6373d1e02c | |||
83d7d119be | |||
a1d959299a | |||
070637bf1d | |||
19a6475097 | |||
fd9a4902ae | |||
082f93ff9d | |||
25eaee31a4 | |||
d5c30f2029 | |||
cae28e0b54 | |||
54200dba52 | |||
846a9d42d4 | |||
69c19cf097 | |||
18cb885410 | |||
4ca60af5da | |||
4d7209df19 | |||
94c76b7f2e | |||
af8ea39200 | |||
6e9304b2cc | |||
5eab8f4907 | |||
807526acfa | |||
f750487b88 | |||
8b0032f6d2 | |||
bb65f81200 | |||
04db40e100 | |||
1e162255d7 | |||
f4149379c2 | |||
a479f3cb62 | |||
fbd6f9ece5 | |||
11c84208b7 | |||
4f02cef0f9 | |||
b8f1acaed8 | |||
80dffb7942 | |||
0a8c2cf56d | |||
9e5dd5e3d5 | |||
03fb4f4176 | |||
22afe7e555 | |||
69723b362f | |||
9e612c7171 | |||
7a9be0326a | |||
aaa1c55227 | |||
656f0da89a | |||
b961044e4d | |||
28e71e132c | |||
b30643299f | |||
b6b70af13f | |||
22dd7fc10b | |||
2498394127 | |||
924bc20193 | |||
af7582b9e2 | |||
856d27d803 | |||
19f56280e4 | |||
2fc65d462d | |||
d3800dbc85 | |||
9373dc4697 | |||
32eaa6c3f2 | |||
11ff8fc700 | |||
9c75cb1615 | |||
3f8b0e2eb7 | |||
69ce6857e8 | |||
cdf656fdad | |||
3efa31efe4 | |||
275b09d2ea | |||
3cb0e3ed8b | |||
823c5ea4c4 | |||
08750813ce | |||
75f4f66285 | |||
881bda739b | |||
d86f082e58 | |||
8eaf8751c2 | |||
b12c5071fe | |||
f75201b3dd | |||
6867920754 | |||
45ea7013f8 | |||
7d428f5eb5 | |||
22ba4288d9 | |||
9ff64e64ef | |||
4b23394569 | |||
81893c8841 | |||
7ab16cb3de | |||
7fca493d0d | |||
85e4ae8761 | |||
6ebd5ae410 | |||
f57b111555 | |||
6596414675 | |||
ed7fd3c15d | |||
8cf7148114 | |||
b98443479f | |||
f917244265 | |||
07d150309b | |||
3801e6e027 | |||
e89fc513cb | |||
e1af1c200d | |||
b238379a22 | |||
c081bc621d | |||
d2c8b18e3f | |||
bdc0ba4fd5 | |||
3af2584d6a | |||
62cf2b14bf | |||
cbd3d139a5 | |||
7cb792c6cf | |||
5ea28bbd75 | |||
180104ece8 |
@ -2,7 +2,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
"""Import django app"""
|
||||
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails
|
||||
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails, ForceUpdate
|
||||
# Register your models here.
|
||||
|
||||
@admin.register(UserDelete)
|
||||
@ -39,6 +39,19 @@ class UserEmailOtpAdmin(admin.ModelAdmin):
|
||||
"""Return object in email and otp format"""
|
||||
return self.email + '-' + self.otp
|
||||
|
||||
@admin.register(ForceUpdate)
|
||||
class ForceUpdateAdmin(admin.ModelAdmin):
|
||||
"""Force update"""
|
||||
list_display = ['version', 'device_type']
|
||||
readonly_fields = ('device_type',)
|
||||
|
||||
def has_add_permission(self, request):
|
||||
count = ForceUpdate.objects.all().count()
|
||||
if count < 2:
|
||||
return True
|
||||
return False
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
@admin.register(UserDeviceDetails)
|
||||
class UserDeviceDetailsAdmin(admin.ModelAdmin):
|
||||
"""User profile admin"""
|
||||
|
@ -5,9 +5,11 @@ from rest_framework.response import Response
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
"""App django"""
|
||||
from account.utils import custom_error_response
|
||||
from account.models import UserDeviceDetails
|
||||
from account.models import UserDeviceDetails, ForceUpdate
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
|
||||
from base.constants import NUMBER
|
||||
from junior.models import Junior
|
||||
from guardian.models import Guardian
|
||||
# Custom middleware
|
||||
# when user login with
|
||||
# multiple device simultaneously
|
||||
@ -15,6 +17,18 @@ from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
# multiple devices only
|
||||
# user can login in single
|
||||
# device at a time"""
|
||||
# force update
|
||||
# use 308 status code for force update
|
||||
|
||||
def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND):
|
||||
"""custom response"""
|
||||
response = Response(custom_error.data, status=response_status)
|
||||
# Set content type header to "application/json"
|
||||
response['Content-Type'] = 'application/json'
|
||||
# Render the response as JSON
|
||||
renderer = JSONRenderer()
|
||||
response.content = renderer.render(response.data)
|
||||
return response
|
||||
class CustomMiddleware(object):
|
||||
"""Custom middleware"""
|
||||
def __init__(self, get_response):
|
||||
@ -26,15 +40,34 @@ class CustomMiddleware(object):
|
||||
response = self.get_response(request)
|
||||
# Code to be executed after the view is called
|
||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||
user_type = request.META.get('HTTP_USER_TYPE')
|
||||
version = request.META.get('HTTP_VERSION')
|
||||
device_type = str(request.META.get('HTTP_TYPE'))
|
||||
|
||||
api_endpoint = request.path
|
||||
unrestricted_api = ('/api/v1/user/login/', '/api/v1/logout/', '/api/v1/generate-token/')
|
||||
if request.user.is_authenticated:
|
||||
"""device details"""
|
||||
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
|
||||
if device_id and not device_details:
|
||||
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
|
||||
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND)
|
||||
# Set content type header to "application/json"
|
||||
response['Content-Type'] = 'application/json'
|
||||
# Render the response as JSON
|
||||
renderer = JSONRenderer()
|
||||
response.content = renderer.render(response.data)
|
||||
# device details
|
||||
if device_id:
|
||||
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
|
||||
if not device_details and api_endpoint not in unrestricted_api:
|
||||
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
|
||||
response = custom_response(custom_error)
|
||||
if user_type and str(user_type) == str(NUMBER['one']):
|
||||
junior = Junior.objects.filter(auth=request.user, is_active=False).last()
|
||||
if junior:
|
||||
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
|
||||
response = custom_response(custom_error)
|
||||
elif user_type and str(user_type) == str(NUMBER['two']):
|
||||
guardian = Guardian.objects.filter(user=request.user, is_active=False).last()
|
||||
if guardian:
|
||||
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
|
||||
response = custom_response(custom_error)
|
||||
|
||||
if version and device_type:
|
||||
force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last()
|
||||
if not force_update:
|
||||
custom_error = custom_error_response(ERROR_CODE['2079'],
|
||||
response_status=status.HTTP_308_PERMANENT_REDIRECT)
|
||||
response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT)
|
||||
return response
|
||||
|
28
account/migrations/0010_forceupdate.py
Normal file
28
account/migrations/0010_forceupdate.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-22 07:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0009_alter_userdevicedetails_device_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ForceUpdate',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('version', models.CharField(blank=True, max_length=50, null=True)),
|
||||
('device_type', models.CharField(blank=True, choices=[('1', 'android'), ('2', 'ios')], default=None, max_length=15, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Force Update Version',
|
||||
'verbose_name_plural': 'Force Update Version',
|
||||
'db_table': 'force_update',
|
||||
},
|
||||
),
|
||||
]
|
@ -2,8 +2,9 @@
|
||||
"""Django import"""
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
"""App import"""
|
||||
from base.constants import USER_TYPE
|
||||
from base.constants import USER_TYPE, DEVICE_TYPE
|
||||
# Create your models here.
|
||||
|
||||
class UserProfile(models.Model):
|
||||
@ -165,3 +166,25 @@ class UserDeviceDetails(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return self.user.email
|
||||
|
||||
|
||||
|
||||
|
||||
class ForceUpdate(models.Model):
|
||||
"""
|
||||
Force update
|
||||
"""
|
||||
"""Version ID"""
|
||||
version = models.CharField(max_length=50, null=True, blank=True)
|
||||
device_type = models.CharField(max_length=15, choices=DEVICE_TYPE, null=True, blank=True, default=None)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(object):
|
||||
""" Meta information """
|
||||
db_table = 'force_update'
|
||||
verbose_name = 'Force Update Version'
|
||||
verbose_name_plural = 'Force Update Version'
|
||||
|
||||
def __str__(self):
|
||||
return self.version
|
||||
|
@ -18,7 +18,7 @@ import secrets
|
||||
|
||||
from guardian.models import Guardian
|
||||
from junior.models import Junior
|
||||
from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp
|
||||
from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp, ForceUpdate
|
||||
from base.constants import GUARDIAN, JUNIOR, SUPERUSER, NUMBER
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR
|
||||
from .utils import delete_user_account_condition_social, delete_user_account_condition
|
||||
@ -104,10 +104,12 @@ class ResetPasswordSerializer(serializers.Serializer):
|
||||
return user_opt_details
|
||||
return ''
|
||||
|
||||
|
||||
class ChangePasswordSerializer(serializers.Serializer):
|
||||
"""Update Password after verification"""
|
||||
current_password = serializers.CharField(max_length=100)
|
||||
current_password = serializers.CharField(max_length=100, required=True)
|
||||
new_password = serializers.CharField(required=True)
|
||||
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
model = User
|
||||
@ -118,25 +120,36 @@ class ChangePasswordSerializer(serializers.Serializer):
|
||||
if self.context.password not in ('', None) and user.check_password(value):
|
||||
return value
|
||||
raise serializers.ValidationError(ERROR_CODE['2015'])
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
change password
|
||||
"""
|
||||
new_password = validated_data.pop('new_password')
|
||||
current_password = validated_data.pop('current_password')
|
||||
"""Check new password is different from current password"""
|
||||
# Check new password is different from current password
|
||||
if new_password == current_password:
|
||||
raise serializers.ValidationError({"details": ERROR_CODE['2026']})
|
||||
user_details = User.objects.filter(email=self.context).last()
|
||||
if user_details:
|
||||
user_details.set_password(new_password)
|
||||
user_details.save()
|
||||
return {'password':new_password}
|
||||
return ''
|
||||
|
||||
user_details = self.context
|
||||
user_details.set_password(new_password)
|
||||
user_details.save()
|
||||
return {'password':new_password}
|
||||
|
||||
|
||||
class ForgotPasswordSerializer(serializers.Serializer):
|
||||
"""Forget password serializer"""
|
||||
email = serializers.EmailField()
|
||||
email = serializers.EmailField(required=True)
|
||||
|
||||
def validate_email(self, value):
|
||||
"""
|
||||
validate email exist ot not
|
||||
value: string
|
||||
return none
|
||||
"""
|
||||
if not User.objects.get(email=value):
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2004']})
|
||||
return value
|
||||
|
||||
class AdminLoginSerializer(serializers.ModelSerializer):
|
||||
"""admin login serializer"""
|
||||
@ -152,12 +165,12 @@ class AdminLoginSerializer(serializers.ModelSerializer):
|
||||
|
||||
def validate(self, attrs):
|
||||
user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True
|
||||
).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first()
|
||||
).only('id', 'first_name', 'last_name', 'email',
|
||||
'username', 'is_active', 'is_superuser').first()
|
||||
|
||||
if not user:
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
|
||||
elif not user.check_password(attrs['password']):
|
||||
if not user or not user.check_password(attrs['password']):
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
|
||||
|
||||
self.context.update({'user': user})
|
||||
return attrs
|
||||
|
||||
@ -216,10 +229,17 @@ class GuardianSerializer(serializers.ModelSerializer):
|
||||
|
||||
def get_user_type(self, obj):
|
||||
"""user type"""
|
||||
email_verified = UserEmailOtp.objects.filter(email=obj.user.username).last()
|
||||
if email_verified and email_verified.user_type is not None:
|
||||
return email_verified.user_type
|
||||
return str(NUMBER['two'])
|
||||
if self.context.get('user_type', ''):
|
||||
return self.context.get('user_type')
|
||||
# remove the below code once user_type can be passed
|
||||
# from everywhere from where this serializer is being called
|
||||
else:
|
||||
email_verified = UserEmailOtp.objects.filter(
|
||||
email=obj.user.username
|
||||
).last()
|
||||
if email_verified and email_verified.user_type is not None:
|
||||
return email_verified.user_type
|
||||
return str(NUMBER['two'])
|
||||
|
||||
def get_auth(self, obj):
|
||||
"""user email address"""
|
||||
@ -237,7 +257,7 @@ class GuardianSerializer(serializers.ModelSerializer):
|
||||
"""Meta info"""
|
||||
model = Guardian
|
||||
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
||||
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active',
|
||||
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active', 'is_deleted',
|
||||
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
|
||||
|
||||
|
||||
@ -280,14 +300,15 @@ class JuniorSerializer(serializers.ModelSerializer):
|
||||
model = Junior
|
||||
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
||||
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set',
|
||||
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited']
|
||||
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited',
|
||||
'is_deleted']
|
||||
|
||||
class EmailVerificationSerializer(serializers.ModelSerializer):
|
||||
"""Email verification serializer"""
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
model = UserEmailOtp
|
||||
fields = '__all__'
|
||||
fields = ('email',)
|
||||
|
||||
|
||||
|
||||
@ -369,3 +390,12 @@ class UserPhoneOtpSerializer(serializers.ModelSerializer):
|
||||
"""Meta info"""
|
||||
model = UserPhoneOtp
|
||||
fields = '__all__'
|
||||
|
||||
class ForceUpdateSerializer(serializers.ModelSerializer):
|
||||
""" ForceUpdate Serializer
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
""" meta info """
|
||||
model = ForceUpdate
|
||||
fields = ('id', 'version', 'device_type')
|
||||
|
@ -1,21 +1,21 @@
|
||||
{% extends "templated_email/email_base.email" %}
|
||||
|
||||
{% block subject %}
|
||||
{{subject}}
|
||||
Support Mail
|
||||
{% endblock %}
|
||||
|
||||
{% block plain %}
|
||||
<tr>
|
||||
<td style="padding: 0 27px 15px;">
|
||||
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
||||
Hi {{name}},
|
||||
Hi Support Team,
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 27px 22px;">
|
||||
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
||||
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br> {{ message }}
|
||||
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br><li> {{ message }}</li>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
22
account/templates/templated_email/user_deactivate.email
Normal file
22
account/templates/templated_email/user_deactivate.email
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends "templated_email/email_base.email" %}
|
||||
|
||||
{% block subject %}
|
||||
Account Deactivated
|
||||
{% endblock %}
|
||||
|
||||
{% block plain %}
|
||||
<tr>
|
||||
<td style="padding: 0 27px 15px;">
|
||||
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
||||
Hi User,
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 0 27px 22px;">
|
||||
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
||||
Your account has been deactivated by admin. Please reach out to the admin for assistance.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
@ -29,7 +29,7 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer
|
||||
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
|
||||
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
|
||||
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView,
|
||||
AdminLoginViewSet)
|
||||
AdminLoginViewSet, ForceUpdateViewSet)
|
||||
"""Router"""
|
||||
router = routers.SimpleRouter()
|
||||
|
||||
@ -39,8 +39,6 @@ router.register('user', UserLogin, basename='user')
|
||||
router.register('admin', AdminLoginViewSet, basename='admin')
|
||||
"""google login end point"""
|
||||
router.register('google-login', GoogleLoginViewSet, basename='admin')
|
||||
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
|
||||
router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification')
|
||||
"""email verification end point"""
|
||||
router.register('user-email-verification', UserEmailVerification, basename='user-email-verification')
|
||||
"""Resend email otp end point"""
|
||||
@ -57,6 +55,8 @@ router.register('delete', DeleteUserProfileAPIViewSet, basename='delete')
|
||||
router.register('user-notification', UserNotificationAPIViewSet, basename='user-notification')
|
||||
"""update user account notification"""
|
||||
router.register('update-user-notification', UpdateUserNotificationAPIViewSet, basename='update-user-notification')
|
||||
# Force update entry API
|
||||
router.register('force-update', ForceUpdateViewSet, basename='force-update')
|
||||
"""Define url pattern"""
|
||||
urlpatterns = [
|
||||
path('api/v1/', include(router.urls)),
|
||||
|
114
account/utils.py
114
account/utils.py
@ -1,4 +1,6 @@
|
||||
"""Account utils"""
|
||||
from celery import shared_task
|
||||
import random
|
||||
"""Import django"""
|
||||
from django.conf import settings
|
||||
from rest_framework import viewsets, status
|
||||
@ -20,10 +22,11 @@ from rest_framework import serializers
|
||||
# Import messages from base package"""
|
||||
from junior.models import Junior
|
||||
from guardian.models import Guardian
|
||||
from account.models import UserDelete
|
||||
from account.models import UserDelete, UserDeviceDetails
|
||||
from base.messages import ERROR_CODE
|
||||
from django.utils import timezone
|
||||
|
||||
from base.constants import NUMBER
|
||||
from junior.models import JuniorPoints
|
||||
# Define delete
|
||||
# user account condition,
|
||||
# Define delete
|
||||
@ -43,7 +46,7 @@ from django.utils import timezone
|
||||
# referral code,
|
||||
# Define function for generating
|
||||
# alphanumeric code
|
||||
|
||||
# otp expiry
|
||||
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
|
||||
"""delete user account"""
|
||||
if user_type == '1' and user_type_data == '1':
|
||||
@ -90,8 +93,11 @@ def junior_account_update(user_tb):
|
||||
# Update junior account
|
||||
junior_data.is_active = False
|
||||
junior_data.is_verified = False
|
||||
junior_data.guardian_code = '{}'
|
||||
junior_data.guardian_code = None
|
||||
junior_data.guardian_code_status = None
|
||||
junior_data.is_deleted = True
|
||||
junior_data.save()
|
||||
JuniorPoints.objects.filter(junior=junior_data).delete()
|
||||
|
||||
def guardian_account_update(user_tb):
|
||||
"""update guardian account after delete the user account"""
|
||||
@ -100,12 +106,14 @@ def guardian_account_update(user_tb):
|
||||
# Update guardian account
|
||||
guardian_data.is_active = False
|
||||
guardian_data.is_verified = False
|
||||
guardian_data.is_deleted = True
|
||||
guardian_data.save()
|
||||
jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
|
||||
"""Disassociate relation between guardian and junior"""
|
||||
for data in jun_data:
|
||||
data.guardian_code.remove(guardian_data.guardian_code)
|
||||
data.save()
|
||||
@shared_task()
|
||||
def send_otp_email(recipient_email, otp):
|
||||
"""Send otp on email with template"""
|
||||
from_email = settings.EMAIL_FROM_ADDRESS
|
||||
@ -121,7 +129,45 @@ def send_otp_email(recipient_email, otp):
|
||||
)
|
||||
return otp
|
||||
|
||||
def send_support_email(name, sender, subject, message):
|
||||
|
||||
@shared_task()
|
||||
def send_all_email(template_name, email, otp):
|
||||
"""
|
||||
Send all type of email by passing template name
|
||||
template_name: string
|
||||
email: string
|
||||
otp: string
|
||||
"""
|
||||
from_email = settings.EMAIL_FROM_ADDRESS
|
||||
recipient_list = [email]
|
||||
send_templated_mail(
|
||||
template_name=template_name,
|
||||
from_email=from_email,
|
||||
recipient_list=recipient_list,
|
||||
context={
|
||||
'verification_code': otp
|
||||
}
|
||||
)
|
||||
|
||||
return otp
|
||||
|
||||
@shared_task
|
||||
def user_device_details(user, device_id):
|
||||
"""
|
||||
Used to store the device id of the user
|
||||
user: user object
|
||||
device_id: string
|
||||
return
|
||||
"""
|
||||
device_details, created = UserDeviceDetails.objects.get_or_create(user__id=user)
|
||||
if device_details:
|
||||
device_details.device_id = device_id
|
||||
device_details.save()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def send_support_email(name, sender, message):
|
||||
"""Send otp on email with template"""
|
||||
to_email = [settings.EMAIL_FROM_ADDRESS]
|
||||
from_email = settings.DEFAULT_ADDRESS
|
||||
@ -133,7 +179,6 @@ def send_support_email(name, sender, subject, message):
|
||||
context={
|
||||
'name': name.title(),
|
||||
'sender': sender,
|
||||
'subject': subject,
|
||||
'message': message
|
||||
}
|
||||
)
|
||||
@ -145,7 +190,8 @@ def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count
|
||||
if not data:
|
||||
"""when data is none"""
|
||||
data = None
|
||||
return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count})
|
||||
return Response({"data": data, "message": detail, "status": "success",
|
||||
"code": response_status, "count": count})
|
||||
|
||||
|
||||
def custom_error_response(detail, response_status):
|
||||
@ -158,8 +204,12 @@ def custom_error_response(detail, response_status):
|
||||
if not detail:
|
||||
"""when details is empty"""
|
||||
detail = {}
|
||||
return Response({"error": detail, "status": "failed", "code": response_status})
|
||||
|
||||
if response_status == 406:
|
||||
return Response({"error": detail, "status": "failed", "code": response_status,},
|
||||
status=status.HTTP_308_PERMANENT_REDIRECT)
|
||||
else:
|
||||
return Response({"error": detail, "status": "failed", "code": response_status},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def get_user_data(attrs):
|
||||
"""
|
||||
@ -231,3 +281,49 @@ def generate_code(value, user_id):
|
||||
|
||||
|
||||
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
|
||||
|
||||
|
||||
def get_user_full_name(user_obj):
|
||||
"""
|
||||
to get user's full name
|
||||
"""
|
||||
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User"
|
||||
|
||||
|
||||
def make_special_password(length=10):
|
||||
"""
|
||||
to make secured password
|
||||
:param length:
|
||||
:return:
|
||||
"""
|
||||
# Define character sets
|
||||
lowercase_letters = string.ascii_lowercase
|
||||
uppercase_letters = string.ascii_uppercase
|
||||
digits = string.digits
|
||||
special_characters = '!@#$%^&*()_-+=<>?/[]{}|'
|
||||
|
||||
# Combine character sets
|
||||
all_characters = lowercase_letters + uppercase_letters + digits + special_characters
|
||||
|
||||
# Create a password with random characters
|
||||
password = (
|
||||
secrets.choice(lowercase_letters) +
|
||||
secrets.choice(uppercase_letters) +
|
||||
secrets.choice(digits) +
|
||||
secrets.choice(special_characters) +
|
||||
''.join(secrets.choice(all_characters) for _ in range(length - 4))
|
||||
)
|
||||
|
||||
# Shuffle the characters to make it more random
|
||||
password_list = list(password)
|
||||
random.shuffle(password_list)
|
||||
return ''.join(password_list)
|
||||
|
||||
def task_status_fun(status_value):
|
||||
"""task status"""
|
||||
task_status_value = ['1']
|
||||
if str(status_value) == '2':
|
||||
task_status_value = ['2', '4']
|
||||
elif str(status_value) == '3':
|
||||
task_status_value = ['3', '5', '6']
|
||||
return task_status_value
|
||||
|
387
account/views.py
387
account/views.py
@ -1,11 +1,13 @@
|
||||
"""Account view """
|
||||
import threading
|
||||
|
||||
from notifications.utils import remove_fcm_token
|
||||
|
||||
# django imports
|
||||
from rest_framework.viewsets import GenericViewSet, mixins
|
||||
from datetime import datetime, timedelta
|
||||
from rest_framework import viewsets, status, views
|
||||
from rest_framework.decorators import action
|
||||
import random
|
||||
import logging
|
||||
from django.utils import timezone
|
||||
import jwt
|
||||
@ -23,22 +25,23 @@ from django.conf import settings
|
||||
|
||||
# local imports
|
||||
from guardian.models import Guardian
|
||||
from junior.models import Junior
|
||||
from junior.models import Junior, JuniorPoints
|
||||
from guardian.utils import upload_image_to_alibaba
|
||||
from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
|
||||
from account.models import (UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification,
|
||||
ForceUpdate)
|
||||
from django.contrib.auth.models import User
|
||||
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
|
||||
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
|
||||
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
|
||||
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
|
||||
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer,
|
||||
AdminLoginSerializer)
|
||||
AdminLoginSerializer, ForceUpdateSerializer)
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
from base.constants import NUMBER, ZOD, JUN, GRD
|
||||
from base.constants import NUMBER, ZOD, JUN, GRD, USER_TYPE_FLAG
|
||||
from guardian.tasks import generate_otp
|
||||
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
|
||||
generate_code, OTP_EXPIRY)
|
||||
generate_code, OTP_EXPIRY, user_device_details, send_all_email)
|
||||
from junior.serializers import JuniorProfileSerializer
|
||||
from guardian.serializers import GuardianProfileSerializer
|
||||
|
||||
@ -48,7 +51,8 @@ class GoogleLoginMixin(object):
|
||||
def google_login(request):
|
||||
"""google login function"""
|
||||
access_token = request.data.get('access_token')
|
||||
user_type = request.data.get('user_type')
|
||||
user_type = request.META.get('HTTP_USER_TYPE')
|
||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||
if not access_token:
|
||||
return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@ -81,14 +85,29 @@ class GoogleLoginMixin(object):
|
||||
if user_data.exists():
|
||||
if str(user_type) == '1':
|
||||
junior_query = Junior.objects.filter(auth=user_data.last()).last()
|
||||
if not junior_query:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2071"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
serializer = JuniorSerializer(junior_query)
|
||||
if str(user_type) == '2':
|
||||
elif str(user_type) == '2':
|
||||
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
|
||||
if not guardian_query:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2070"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
serializer = GuardianSerializer(guardian_query)
|
||||
else:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2069"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
return custom_response(SUCCESS_CODE['3003'], serializer.data,
|
||||
response_status=status.HTTP_200_OK)
|
||||
|
||||
if not User.objects.filter(email__iexact=email).exists():
|
||||
else:
|
||||
user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name)
|
||||
if str(user_type) == '1':
|
||||
junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True,
|
||||
@ -97,13 +116,25 @@ class GoogleLoginMixin(object):
|
||||
referral_code=generate_code(ZOD, user_obj.id)
|
||||
)
|
||||
serializer = JuniorSerializer(junior_query)
|
||||
if str(user_type) == '2':
|
||||
position = Junior.objects.all().count()
|
||||
JuniorPoints.objects.create(junior=junior_query, position=position)
|
||||
elif str(user_type) == '2':
|
||||
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
|
||||
image=profile_picture,signup_method='2',
|
||||
guardian_code=generate_code(GRD, user_obj.id),
|
||||
referral_code=generate_code(ZOD, user_obj.id)
|
||||
)
|
||||
serializer = GuardianSerializer(guardian_query)
|
||||
else:
|
||||
user_obj.delete()
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2069"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_obj)
|
||||
if device_detail:
|
||||
device_detail.device_id = device_id
|
||||
device_detail.save()
|
||||
# Return a JSON response with the user's email and name
|
||||
return custom_response(SUCCESS_CODE['3003'], serializer.data,
|
||||
response_status=status.HTTP_200_OK)
|
||||
@ -114,16 +145,26 @@ class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet):
|
||||
serializer_class = GoogleLoginSerializer
|
||||
|
||||
def create(self, request):
|
||||
"""create method"""
|
||||
"""Payload
|
||||
{
|
||||
"access_token",
|
||||
"user_type": "1"
|
||||
}"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return self.google_login(request)
|
||||
|
||||
class SigninWithApple(views.APIView):
|
||||
"""This API is for sign in with Apple for app."""
|
||||
"""This API is for sign in with Apple for app.
|
||||
Payload
|
||||
{
|
||||
"access_token",
|
||||
"user_type": "1"
|
||||
}"""
|
||||
def post(self, request):
|
||||
token = request.data.get("access_token")
|
||||
user_type = request.data.get("user_type")
|
||||
user_type = request.META.get('HTTP_USER_TYPE')
|
||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||
try:
|
||||
decoded_data = jwt.decode(token, options={"verify_signature": False})
|
||||
user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True}
|
||||
@ -131,11 +172,26 @@ class SigninWithApple(views.APIView):
|
||||
try:
|
||||
user = User.objects.get(email=decoded_data.get("email"))
|
||||
if str(user_type) == '1':
|
||||
junior_query = Junior.objects.filter(auth=user).last()
|
||||
serializer = JuniorSerializer(junior_query)
|
||||
if str(user_type) == '2':
|
||||
guardian_query = Guardian.objects.filter(user=user).last()
|
||||
serializer = GuardianSerializer(guardian_query)
|
||||
junior_data = Junior.objects.filter(auth=user).last()
|
||||
if not junior_data:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2071"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
serializer = JuniorSerializer(junior_data)
|
||||
elif str(user_type) == '2':
|
||||
guardian_data = Guardian.objects.filter(user=user).last()
|
||||
if not guardian_data:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2070"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
serializer = GuardianSerializer(guardian_data)
|
||||
else:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2069"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
return custom_response(SUCCESS_CODE['3003'], serializer.data,
|
||||
response_status=status.HTTP_200_OK)
|
||||
|
||||
@ -147,12 +203,24 @@ class SigninWithApple(views.APIView):
|
||||
junior_code=generate_code(JUN, user.id),
|
||||
referral_code=generate_code(ZOD, user.id))
|
||||
serializer = JuniorSerializer(junior_query)
|
||||
if str(user_type) == '2':
|
||||
position = Junior.objects.all().count()
|
||||
JuniorPoints.objects.create(junior=junior_query, position=position)
|
||||
elif str(user_type) == '2':
|
||||
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
|
||||
signup_method='3',
|
||||
guardian_code=generate_code(GRD, user.id),
|
||||
referral_code=generate_code(ZOD, user.id))
|
||||
serializer = GuardianSerializer(guardian_query)
|
||||
else:
|
||||
user.delete()
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2069"],
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user)
|
||||
if device_detail:
|
||||
device_detail.device_id = device_id
|
||||
device_detail.save()
|
||||
return custom_response(SUCCESS_CODE['3003'], serializer.data,
|
||||
response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
@ -188,18 +256,42 @@ class UpdateProfileImage(views.APIView):
|
||||
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ChangePasswordAPIView(views.APIView):
|
||||
"""change password"""
|
||||
"""
|
||||
change password"
|
||||
"""
|
||||
serializer_class = ChangePasswordSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
serializer = ChangePasswordSerializer(context=request.user, data=request.data)
|
||||
"""
|
||||
POST request to change current login user password
|
||||
Payload
|
||||
{ "current_password":"Demo@123",
|
||||
"new_password":"Demo@123"
|
||||
}
|
||||
"""
|
||||
serializer = ChangePasswordSerializer(
|
||||
context=request.user,
|
||||
data=request.data
|
||||
)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return custom_response(SUCCESS_CODE['3007'], response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
return custom_response(
|
||||
SUCCESS_CODE['3007'],
|
||||
response_status=status.HTTP_200_OK
|
||||
)
|
||||
return custom_error_response(
|
||||
serializer.errors,
|
||||
response_status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
class ResetPasswordAPIView(views.APIView):
|
||||
"""Reset password"""
|
||||
"""Reset password
|
||||
Payload
|
||||
{
|
||||
"verification_code":"373770",
|
||||
"password":"Demo@1323"
|
||||
}"""
|
||||
def post(self, request):
|
||||
serializer = ResetPasswordSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
@ -208,44 +300,46 @@ class ResetPasswordAPIView(views.APIView):
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ForgotPasswordAPIView(views.APIView):
|
||||
"""Forgot password"""
|
||||
"""
|
||||
Forgot password
|
||||
"""
|
||||
serializer_class = ForgotPasswordSerializer
|
||||
|
||||
def post(self, request):
|
||||
serializer = ForgotPasswordSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
email = serializer.validated_data['email']
|
||||
try:
|
||||
User.objects.get(email=email)
|
||||
except User.DoesNotExist:
|
||||
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
|
||||
verification_code = generate_otp()
|
||||
# Send the verification code to the user's email
|
||||
from_email = settings.EMAIL_FROM_ADDRESS
|
||||
recipient_list = [email]
|
||||
send_templated_mail(
|
||||
template_name='email_reset_verification.email',
|
||||
from_email=from_email,
|
||||
recipient_list=recipient_list,
|
||||
context={
|
||||
'verification_code': verification_code
|
||||
}
|
||||
)
|
||||
expiry = OTP_EXPIRY
|
||||
user_data, created = UserEmailOtp.objects.get_or_create(email=email)
|
||||
if created:
|
||||
user_data.expired_at = expiry
|
||||
user_data.save()
|
||||
if user_data:
|
||||
user_data.otp = verification_code
|
||||
user_data.expired_at = expiry
|
||||
user_data.save()
|
||||
return custom_response(SUCCESS_CODE['3015'],
|
||||
response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
"""
|
||||
Payload
|
||||
{
|
||||
"email": "abc@yopmail.com"
|
||||
}
|
||||
"""
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
email = serializer.validated_data['email']
|
||||
# generate otp
|
||||
verification_code = generate_otp()
|
||||
# Send the verification code to the user's email
|
||||
send_all_email.delay(
|
||||
'email_reset_verification.email', email, verification_code
|
||||
)
|
||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||
user_data, created = UserEmailOtp.objects.get_or_create(
|
||||
email=email
|
||||
)
|
||||
if created:
|
||||
user_data.expired_at = expiry
|
||||
user_data.save()
|
||||
if user_data:
|
||||
user_data.otp = verification_code
|
||||
user_data.expired_at = expiry
|
||||
user_data.save()
|
||||
return custom_response(
|
||||
SUCCESS_CODE['3015'],
|
||||
response_status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
class SendPhoneOtp(viewsets.ModelViewSet):
|
||||
"""Send otp on phone"""
|
||||
queryset = UserPhoneOtp.objects.all()
|
||||
serializer_class = UserPhoneOtpSerializer
|
||||
def create(self, request, *args, **kwargs):
|
||||
otp = generate_otp()
|
||||
@ -262,7 +356,6 @@ class SendPhoneOtp(viewsets.ModelViewSet):
|
||||
|
||||
class UserPhoneVerification(viewsets.ModelViewSet):
|
||||
"""Send otp on phone"""
|
||||
queryset = UserPhoneOtp.objects.all()
|
||||
serializer_class = UserPhoneOtpSerializer
|
||||
def list(self, request, *args, **kwargs):
|
||||
try:
|
||||
@ -278,25 +371,50 @@ class UserPhoneVerification(viewsets.ModelViewSet):
|
||||
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
|
||||
class UserLogin(viewsets.ViewSet):
|
||||
"""User login"""
|
||||
@action(methods=['post'], detail=False)
|
||||
def login(self, request):
|
||||
username = request.data.get('username')
|
||||
password = request.data.get('password')
|
||||
user_type = request.META.get('HTTP_USER_TYPE')
|
||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||
user = authenticate(request, username=username, password=password)
|
||||
|
||||
try:
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
|
||||
if guardian_data:
|
||||
serializer = GuardianSerializer(guardian_data).data
|
||||
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
|
||||
if junior_data:
|
||||
serializer = JuniorSerializer(junior_data).data
|
||||
if str(user_type) == USER_TYPE_FLAG["TWO"]:
|
||||
guardian_data = Guardian.objects.filter(user__username=username).last()
|
||||
if guardian_data:
|
||||
if guardian_data.is_verified:
|
||||
serializer = GuardianSerializer(
|
||||
guardian_data, context={'user_type': user_type}
|
||||
).data
|
||||
else:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2070"],
|
||||
response_status=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
elif str(user_type) == USER_TYPE_FLAG["FIRST"]:
|
||||
junior_data = Junior.objects.filter(auth__username=username).last()
|
||||
if junior_data:
|
||||
if junior_data.is_verified:
|
||||
serializer = JuniorSerializer(
|
||||
junior_data, context={'user_type': user_type}
|
||||
).data
|
||||
else:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2071"],
|
||||
response_status=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
else:
|
||||
return custom_error_response(
|
||||
ERROR_CODE["2069"],
|
||||
response_status=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
# storing device id in using celery task so the time would be reduced
|
||||
# user_device_details.delay(user.id, device_id)
|
||||
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
|
||||
if device_details:
|
||||
device_details.device_id = device_id
|
||||
@ -310,9 +428,12 @@ class UserLogin(viewsets.ViewSet):
|
||||
refresh = RefreshToken.for_user(user)
|
||||
access_token = str(refresh.access_token)
|
||||
refresh_token = str(refresh)
|
||||
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False,
|
||||
"user_type": email_verified.user_type,
|
||||
}
|
||||
data = {
|
||||
"auth_token":access_token,
|
||||
"refresh_token":refresh_token,
|
||||
"is_profile_complete": False,
|
||||
"user_type": user_type,
|
||||
}
|
||||
is_verified = False
|
||||
if email_verified:
|
||||
is_verified = email_verified.is_verified
|
||||
@ -321,23 +442,30 @@ class UserLogin(viewsets.ViewSet):
|
||||
email_verified.otp = otp
|
||||
email_verified.save()
|
||||
data.update({"email_otp":otp})
|
||||
send_otp_email(username, otp)
|
||||
return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified},
|
||||
response_status=status.HTTP_200_OK)
|
||||
send_otp_email.delay(username, otp)
|
||||
return custom_response(
|
||||
ERROR_CODE['2024'],
|
||||
{"email_otp": otp, "is_email_verified": is_verified},
|
||||
response_status=status.HTTP_200_OK
|
||||
)
|
||||
data.update({"is_email_verified": is_verified})
|
||||
return custom_response(SUCCESS_CODE['3003'], data, response_status=status.HTTP_200_OK)
|
||||
return custom_response(
|
||||
SUCCESS_CODE['3003'],
|
||||
data,
|
||||
response_status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
@action(methods=['post'], detail=False)
|
||||
def admin_login(self, request):
|
||||
email = request.data.get('email')
|
||||
password = request.data.get('password')
|
||||
user = User.objects.filter(email__iexact=email, is_superuser=True
|
||||
).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first()
|
||||
).only('id', 'first_name', 'last_name', 'email',
|
||||
'username', 'is_active', 'is_superuser').first()
|
||||
|
||||
if not user:
|
||||
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
elif not user.check_password(password):
|
||||
if not user or not user.check_password(password):
|
||||
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
serializer = SuperUserSerializer(user)
|
||||
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
|
||||
|
||||
@ -367,15 +495,20 @@ class AdminLoginViewSet(viewsets.GenericViewSet):
|
||||
|
||||
|
||||
class UserEmailVerification(viewsets.ModelViewSet):
|
||||
"""User Email verification"""
|
||||
"""User Email verification
|
||||
Payload
|
||||
{
|
||||
"email":"ramu@yopmail.com",
|
||||
"otp":"361123"
|
||||
}"""
|
||||
serializer_class = EmailVerificationSerializer
|
||||
queryset = UserEmailOtp.objects.all()
|
||||
http_method_names = ('post',)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
def create(self, request, *args, **kwargs):
|
||||
try:
|
||||
user_obj = User.objects.filter(username=self.request.GET.get('email')).last()
|
||||
email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'),
|
||||
otp=self.request.GET.get('otp')).last()
|
||||
user_obj = User.objects.filter(username=self.request.data.get('email')).last()
|
||||
email_data = UserEmailOtp.objects.filter(email=self.request.data.get('email'),
|
||||
otp=self.request.data.get('otp')).last()
|
||||
if email_data:
|
||||
input_datetime_str = str(email_data.expired_at)
|
||||
input_format = "%Y-%m-%d %H:%M:%S.%f%z"
|
||||
@ -389,12 +522,12 @@ class UserEmailVerification(viewsets.ModelViewSet):
|
||||
email_data.is_verified = True
|
||||
email_data.save()
|
||||
if email_data.user_type == '1':
|
||||
junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last()
|
||||
junior_data = Junior.objects.filter(auth__email=self.request.data.get('email')).last()
|
||||
if junior_data:
|
||||
junior_data.is_verified = True
|
||||
junior_data.save()
|
||||
else:
|
||||
guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last()
|
||||
guardian_data = Guardian.objects.filter(user__email=self.request.data.get('email')).last()
|
||||
if guardian_data:
|
||||
guardian_data.is_verified = True
|
||||
guardian_data.save()
|
||||
@ -412,14 +545,15 @@ class UserEmailVerification(viewsets.ModelViewSet):
|
||||
|
||||
class ReSendEmailOtp(viewsets.ModelViewSet):
|
||||
"""Send otp on phone"""
|
||||
queryset = UserEmailOtp.objects.all()
|
||||
serializer_class = EmailVerificationSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
http_method_names = ('post',)
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Param
|
||||
{"email":"ashok@yopmail.com"}
|
||||
"""
|
||||
otp = generate_otp()
|
||||
if User.objects.filter(email=request.data['email']):
|
||||
expiry = OTP_EXPIRY
|
||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||
email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email'])
|
||||
if created:
|
||||
email_data.expired_at = expiry
|
||||
@ -428,34 +562,37 @@ class ReSendEmailOtp(viewsets.ModelViewSet):
|
||||
email_data.otp = otp
|
||||
email_data.expired_at = expiry
|
||||
email_data.save()
|
||||
send_otp_email(request.data['email'], otp)
|
||||
send_otp_email.delay(request.data['email'], otp)
|
||||
return custom_response(SUCCESS_CODE['3016'], response_status=status.HTTP_200_OK)
|
||||
else:
|
||||
return custom_error_response(ERROR_CODE["2023"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ProfileAPIViewSet(viewsets.ModelViewSet):
|
||||
"""Profile viewset"""
|
||||
queryset = User.objects.all()
|
||||
serializer_class = JuniorProfileSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""profile view"""
|
||||
if str(self.request.GET.get('user_type')) == '1':
|
||||
"""profile view
|
||||
Params
|
||||
user_type"""
|
||||
user_type = request.META.get('HTTP_USER_TYPE')
|
||||
if str(user_type) == '1':
|
||||
junior_data = Junior.objects.filter(auth=self.request.user).last()
|
||||
if junior_data:
|
||||
serializer = JuniorProfileSerializer(junior_data)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
elif str(self.request.GET.get('user_type')) == '2':
|
||||
elif str(user_type) == '2':
|
||||
guardian_data = Guardian.objects.filter(user=self.request.user).last()
|
||||
if guardian_data:
|
||||
serializer = GuardianProfileSerializer(guardian_data)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
|
||||
return custom_error_response(None, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
class UploadImageAPIViewSet(viewsets.ModelViewSet):
|
||||
"""upload task image"""
|
||||
queryset = DefaultTaskImages.objects.all()
|
||||
serializer_class = DefaultTaskImagesSerializer
|
||||
http_method_names = ('post',)
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""upload images"""
|
||||
image_data = request.data['image_url']
|
||||
@ -473,9 +610,9 @@ class UploadImageAPIViewSet(viewsets.ModelViewSet):
|
||||
|
||||
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
|
||||
"""Profile viewset"""
|
||||
queryset = DefaultTaskImages.objects.all()
|
||||
serializer_class = DefaultTaskImagesDetailsSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""profile view"""
|
||||
queryset = DefaultTaskImages.objects.all()
|
||||
@ -484,7 +621,13 @@ class DefaultImageAPIViewSet(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
|
||||
""" Delete user API view set """
|
||||
""" Delete user API view set
|
||||
{"user_type":1,
|
||||
"signup_method":"1",
|
||||
"password":"Demo@123"}
|
||||
signup_method 1 for manual
|
||||
2 for google login
|
||||
3 for apple login"""
|
||||
|
||||
@action(detail=False, methods=['POST'], url_path='user-account',serializer_class=UserDeleteSerializer,
|
||||
permission_classes=[IsAuthenticated])
|
||||
@ -504,24 +647,28 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
|
||||
|
||||
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
|
||||
"""notification viewset"""
|
||||
queryset = UserNotification.objects.all()
|
||||
serializer_class = UserNotificationSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""profile view"""
|
||||
queryset = self.queryset.filter(user=request.user)
|
||||
"""notification view"""
|
||||
queryset = UserNotification.objects.filter(user=request.user)
|
||||
serializer = UserNotificationSerializer(queryset, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
|
||||
"""Update notification viewset"""
|
||||
queryset = UserNotification.objects.all()
|
||||
serializer_class = UpdateUserNotificationSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""profile view"""
|
||||
"""Payload
|
||||
{"email_notification": false,
|
||||
"sms_notification": false,
|
||||
"push_notification": false}
|
||||
"""
|
||||
serializer = UpdateUserNotificationSerializer(data=request.data,
|
||||
context=request.user)
|
||||
if serializer.is_valid():
|
||||
@ -531,17 +678,21 @@ class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class SendSupportEmail(views.APIView):
|
||||
"""support email api"""
|
||||
"""support email api
|
||||
payload
|
||||
name
|
||||
email
|
||||
message
|
||||
"""
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
def post(self, request):
|
||||
name = request.data.get('name')
|
||||
sender = request.data.get('email')
|
||||
subject = request.data.get('subject')
|
||||
message = request.data.get('message')
|
||||
if name and sender and subject and message:
|
||||
if name and sender and message:
|
||||
try:
|
||||
send_support_email(name, sender, subject, message)
|
||||
send_support_email(name, sender, message)
|
||||
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
@ -575,3 +726,27 @@ class AccessTokenAPIView(views.APIView):
|
||||
data = {"auth_token": access_token}
|
||||
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class ForceUpdateViewSet(GenericViewSet, mixins.CreateModelMixin):
|
||||
"""FAQ view set"""
|
||||
|
||||
serializer_class = ForceUpdateSerializer
|
||||
http_method_names = ['post']
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
faq create api method
|
||||
:param request:
|
||||
:param args: version, device type
|
||||
:param kwargs:
|
||||
:return: success message
|
||||
"""
|
||||
if ForceUpdate.objects.all().count() >= 2:
|
||||
return custom_error_response(ERROR_CODE['2080'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
obj_data = [ForceUpdate(**item) for item in request.data]
|
||||
try:
|
||||
ForceUpdate.objects.bulk_create(obj_data)
|
||||
return custom_response(SUCCESS_CODE["3046"], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
@ -27,7 +27,7 @@ NUMBER = {
|
||||
'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600
|
||||
}
|
||||
|
||||
|
||||
none = "none"
|
||||
|
||||
# Super Admin string constant for 'role'
|
||||
SUPER_ADMIN = "Super Admin"
|
||||
@ -43,13 +43,24 @@ FILE_SIZE = 5 * 1024 * 1024
|
||||
|
||||
# String constant for configurable date for allocation lock period
|
||||
ALLOCATION_LOCK_DATE = 1
|
||||
|
||||
# guardian code status tuple
|
||||
guardian_code_tuple = ('1','3')
|
||||
"""user type"""
|
||||
USER_TYPE = (
|
||||
('1', 'junior'),
|
||||
('2', 'guardian'),
|
||||
('3', 'superuser')
|
||||
)
|
||||
DEVICE_TYPE = (
|
||||
('1', 'android'),
|
||||
('2', 'ios')
|
||||
)
|
||||
USER_TYPE_FLAG = {
|
||||
"FIRST" : "1",
|
||||
"TWO" : "2",
|
||||
"THREE": "3"
|
||||
}
|
||||
|
||||
"""gender"""
|
||||
GENDERS = (
|
||||
('1', 'Male'),
|
||||
@ -70,6 +81,18 @@ SIGNUP_METHODS = (
|
||||
('2', 'google'),
|
||||
('3', 'apple')
|
||||
)
|
||||
# guardian code status
|
||||
GUARDIAN_CODE_STATUS = (
|
||||
('1', 'no guardian code'),
|
||||
('2', 'exist guardian code'),
|
||||
('3', 'request for guardian code')
|
||||
)
|
||||
# article status
|
||||
ARTICLE_STATUS = (
|
||||
('1', 'read'),
|
||||
('2', 'in_progress'),
|
||||
('3', 'completed')
|
||||
)
|
||||
# relationship
|
||||
RELATIONSHIP = (
|
||||
('1', 'parent'),
|
||||
@ -106,7 +129,9 @@ MAX_ARTICLE_CARD = 6
|
||||
MIN_ARTICLE_SURVEY = 5
|
||||
MAX_ARTICLE_SURVEY = 10
|
||||
|
||||
# real time url
|
||||
time_url = "http://worldtimeapi.org/api/timezone/Asia/Riyadh"
|
||||
# already register
|
||||
Already_register_user = "duplicate key value violates unique constraint"
|
||||
|
||||
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'
|
||||
|
||||
DATE_FORMAT = '%Y-%m-%d'
|
||||
|
@ -94,7 +94,27 @@ ERROR_CODE = {
|
||||
"2065": "Passwords do not match. Please try again.",
|
||||
"2066": "Task does not exist or not in expired state",
|
||||
"2067": "Action not allowed. User type missing.",
|
||||
"2068": "No guardian associated with this junior"
|
||||
"2068": "No guardian associated with this junior",
|
||||
"2069": "Invalid user type",
|
||||
"2070": "You do not find as a guardian",
|
||||
"2071": "You do not find as a junior",
|
||||
"2072": "You can not approve or reject this task because junior does not exist in the system",
|
||||
"2073": "You can not approve or reject this junior because junior does not exist in the system",
|
||||
"2074": "You can not complete this task because you does not exist in the system",
|
||||
# deactivate account
|
||||
"2075": "Your account is deactivated. Please contact with admin",
|
||||
"2076": "This junior already associate with you",
|
||||
"2077": "You can not add guardian",
|
||||
"2078": "This junior is not associate with you",
|
||||
# force update
|
||||
"2079": "Please update your app version for enjoying uninterrupted services",
|
||||
"2080": "Can not add App version",
|
||||
"2081": "A junior can only be associated with a maximum of 3 guardian",
|
||||
# guardian code not exist
|
||||
"2082": "Guardian code does not exist",
|
||||
"2083": "You can not start this task because guardian is not associate with you",
|
||||
"2084": "You can not complete this task because guardian is not associate with you",
|
||||
"2085": "You can not take action on this task because junior is not associate with you"
|
||||
|
||||
}
|
||||
"""Success message code"""
|
||||
@ -105,11 +125,11 @@ SUCCESS_CODE = {
|
||||
# Success code for Thank you
|
||||
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
|
||||
# Success code for account activation
|
||||
"3003": "Log in successful",
|
||||
"3003": "Log in successful.",
|
||||
# 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
|
||||
"3005": "Your account is deleted successfully.",
|
||||
"3005": "Your account has been deleted successfully.",
|
||||
# Success code for password reset
|
||||
"3006": "Password reset successful. You can now log in with your new password.",
|
||||
# Success code for password update
|
||||
@ -117,11 +137,11 @@ SUCCESS_CODE = {
|
||||
# Success code for valid link
|
||||
"3008": "You have a valid link.",
|
||||
# Success code for logged out
|
||||
"3009": "You have successfully logged out!",
|
||||
"3009": "You have successfully logged out.",
|
||||
# Success code for check all fields
|
||||
"3010": "All fields are valid",
|
||||
"3011": "Email OTP Verified successfully",
|
||||
"3012": "Phone OTP Verified successfully",
|
||||
"3010": "All fields are valid.",
|
||||
"3011": "Email OTP has been verified successfully.",
|
||||
"3012": "Phone OTP has been verified successfully.",
|
||||
"3013": "Valid Guardian code",
|
||||
"3014": "Password has been updated successfully.",
|
||||
"3015": "Verification code has been sent on your email.",
|
||||
@ -130,26 +150,40 @@ SUCCESS_CODE = {
|
||||
"3018": "Task created successfully",
|
||||
"3019": "Support Email sent successfully",
|
||||
"3020": "Logged out successfully.",
|
||||
"3021": "Add junior successfully",
|
||||
"3022": "Remove junior successfully",
|
||||
"3023": "Junior is approved successfully",
|
||||
"3024": "Junior request is rejected successfully",
|
||||
"3025": "Task is approved successfully",
|
||||
"3026": "Task is rejected successfully",
|
||||
"3021": "Junior has been added successfully.",
|
||||
"3022": "Junior has been removed successfully.",
|
||||
"3023": "Junior has been approved successfully.",
|
||||
"3024": "Junior request is rejected successfully.",
|
||||
"3025": "Task is approved successfully.",
|
||||
"3026": "Task is rejected successfully.",
|
||||
"3027": "Article has been created successfully.",
|
||||
"3028": "Article has been updated successfully.",
|
||||
"3029": "Article has been deleted successfully.",
|
||||
"3030": "Article Card has been removed successfully.",
|
||||
"3031": "Article Survey has been removed successfully.",
|
||||
"3032": "Task request sent successfully",
|
||||
"3033": "Valid Referral code",
|
||||
"3034": "Invite guardian successfully",
|
||||
"3035": "Task started successfully",
|
||||
"3036": "Task reassign successfully",
|
||||
"3032": "Task request sent successfully.",
|
||||
"3033": "Valid Referral code.",
|
||||
"3034": "Invite guardian successfully.",
|
||||
"3035": "Task started successfully.",
|
||||
"3036": "Task reassign successfully.",
|
||||
"3037": "Profile has been updated successfully.",
|
||||
"3038": "Status has been changed successfully.",
|
||||
# notification read
|
||||
"3039": "Notification read successfully"
|
||||
"3039": "Notification read successfully.",
|
||||
# start article
|
||||
"3040": "Start article successfully.",
|
||||
# complete article
|
||||
"3041": "Article completed successfully.",
|
||||
# submit assessment successfully
|
||||
"3042": "Assessment completed successfully.",
|
||||
# read article
|
||||
"3043": "Read article card successfully.",
|
||||
# remove guardian code request
|
||||
"3044": "Remove guardian code request successfully.",
|
||||
# create faq
|
||||
"3045": "Create FAQ data.",
|
||||
"3046": "Add App version successfully."
|
||||
|
||||
}
|
||||
"""status code error"""
|
||||
STATUS_CODE_ERROR = {
|
||||
|
46
base/pagination.py
Normal file
46
base/pagination.py
Normal file
@ -0,0 +1,46 @@
|
||||
"""
|
||||
web_admin pagination file
|
||||
"""
|
||||
# third party imports
|
||||
from collections import OrderedDict
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
|
||||
from account.utils import custom_response
|
||||
from base.constants import NUMBER
|
||||
|
||||
|
||||
class CustomPageNumberPagination(PageNumberPagination):
|
||||
"""
|
||||
custom paginator class
|
||||
"""
|
||||
# Set the desired page size
|
||||
page_size = NUMBER['ten']
|
||||
page_size_query_param = 'page_size'
|
||||
# Set a maximum page size if needed
|
||||
max_page_size = NUMBER['hundred']
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
"""
|
||||
:param data: queryset to be paginated
|
||||
:return: return a OrderedDict
|
||||
"""
|
||||
return custom_response(None, OrderedDict([
|
||||
('count', self.page.paginator.count),
|
||||
('data', data),
|
||||
('current_page', self.page.number),
|
||||
('total_pages', self.page.paginator.num_pages),
|
||||
|
||||
|
||||
]))
|
||||
|
||||
def get_paginated_dict_response(self, data):
|
||||
"""
|
||||
:param data: queryset to be paginated
|
||||
:return: return a simple dict obj
|
||||
"""
|
||||
return {
|
||||
'count': self.page.paginator.count,
|
||||
'data': data,
|
||||
'current_page': self.page.number,
|
||||
'total_pages': self.page.paginator.num_pages,
|
||||
}
|
@ -1,29 +1,88 @@
|
||||
"""
|
||||
web_admin tasks file
|
||||
"""
|
||||
import datetime
|
||||
|
||||
# third party imports
|
||||
from celery import shared_task
|
||||
from templated_email import send_templated_mail
|
||||
|
||||
# django imports
|
||||
from django.conf import settings
|
||||
from django.db.models import F, Window
|
||||
from django.db.models.functions.window import Rank
|
||||
|
||||
# local imports
|
||||
from base.constants import PENDING, IN_PROGRESS, JUNIOR
|
||||
from guardian.models import JuniorTask
|
||||
from junior.models import JuniorPoints
|
||||
from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING, NOTIFICATION_DICT, TOP_JUNIOR
|
||||
from notifications.models import Notification
|
||||
from notifications.utils import send_notification, get_from_user_details, send_notification_multiple_user
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_email_otp(email, verification_code):
|
||||
def send_email(recipient_list, template, context: dict = None):
|
||||
"""
|
||||
used to send otp on email
|
||||
:param email: e-mail
|
||||
:param verification_code: otp
|
||||
:param context:
|
||||
:param recipient_list: e-mail list
|
||||
:param template: email template
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
from_email = settings.EMAIL_FROM_ADDRESS
|
||||
recipient_list = [email]
|
||||
send_templated_mail(
|
||||
template_name='email_reset_verification.email',
|
||||
template_name=template,
|
||||
from_email=from_email,
|
||||
recipient_list=recipient_list,
|
||||
context={
|
||||
'verification_code': verification_code
|
||||
}
|
||||
context=context
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@shared_task()
|
||||
def notify_task_expiry():
|
||||
"""
|
||||
task to send notification for those task which expiring soon
|
||||
:return:
|
||||
"""
|
||||
all_pending_tasks = JuniorTask.objects.filter(
|
||||
task_status__in=[PENDING, IN_PROGRESS],
|
||||
due_date__range=[datetime.datetime.now().date(),
|
||||
(datetime.datetime.now().date() + datetime.timedelta(days=1))])
|
||||
if pending_tasks := all_pending_tasks.filter(task_status=PENDING):
|
||||
for task in pending_tasks:
|
||||
send_notification(PENDING_TASK_EXPIRING, None, None, task.junior.auth.id,
|
||||
{'task_id': task.id})
|
||||
if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS):
|
||||
for task in in_progress_tasks:
|
||||
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth.id, JUNIOR, task.guardian.user.id,
|
||||
{'task_id': task.id})
|
||||
return True
|
||||
|
||||
|
||||
@shared_task()
|
||||
def notify_top_junior():
|
||||
"""
|
||||
task to send notification for top leaderboard junior to all junior's
|
||||
:return:
|
||||
"""
|
||||
junior_points_qs = JuniorPoints.objects.filter(
|
||||
junior__is_verified=True
|
||||
).select_related(
|
||||
'junior', 'junior__auth'
|
||||
).annotate(rank=Window(
|
||||
expression=Rank(),
|
||||
order_by=[F('total_points').desc(), 'junior__created_at'])
|
||||
).order_by('-total_points', 'junior__created_at')
|
||||
|
||||
prev_top_position = junior_points_qs.filter(position=1).first()
|
||||
new_top_position = junior_points_qs.filter(rank=1).first()
|
||||
if prev_top_position != new_top_position:
|
||||
send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth.id, JUNIOR,
|
||||
{'points': new_top_position.total_points})
|
||||
for junior_point in junior_points_qs:
|
||||
junior_point.position = junior_point.rank
|
||||
junior_point.save()
|
||||
return True
|
||||
|
Binary file not shown.
39
docker-compose-prod.yml
Normal file
39
docker-compose-prod.yml
Normal file
@ -0,0 +1,39 @@
|
||||
version: '3'
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
container_name: nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./nginx:/etc/nginx/conf.d
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- web
|
||||
web:
|
||||
build: .
|
||||
container_name: prod_django
|
||||
restart: always
|
||||
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
|
||||
broker:
|
||||
image: rabbitmq:3.7
|
||||
container_name: prod_rabbitmq
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
ports:
|
||||
- 5673:5673
|
||||
|
||||
worker:
|
||||
build: .
|
||||
image: celery
|
||||
container_name: prod_celery
|
||||
restart: "always"
|
||||
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- broker
|
39
docker-compose-qa.yml
Normal file
39
docker-compose-qa.yml
Normal file
@ -0,0 +1,39 @@
|
||||
version: '3'
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
container_name: nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./nginx:/etc/nginx/conf.d
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- web
|
||||
web:
|
||||
build: .
|
||||
container_name: qa_django
|
||||
restart: always
|
||||
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
|
||||
broker:
|
||||
image: rabbitmq:3.7
|
||||
container_name: qa_rabbitmq
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
ports:
|
||||
- 5673:5673
|
||||
|
||||
worker:
|
||||
build: .
|
||||
image: celery
|
||||
container_name: qa_celery
|
||||
restart: "always"
|
||||
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- broker
|
39
docker-compose-stage.yml
Normal file
39
docker-compose-stage.yml
Normal file
@ -0,0 +1,39 @@
|
||||
version: '3'
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
container_name: nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./nginx:/etc/nginx/conf.d
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- web
|
||||
web:
|
||||
build: .
|
||||
container_name: stage_django
|
||||
restart: always
|
||||
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
|
||||
broker:
|
||||
image: rabbitmq:3.7
|
||||
container_name: stage_rabbitmq
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
ports:
|
||||
- 5673:5673
|
||||
|
||||
worker:
|
||||
build: .
|
||||
image: celery
|
||||
container_name: stage_celery
|
||||
restart: "always"
|
||||
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
depends_on:
|
||||
- broker
|
18
guardian/migrations/0021_guardian_is_deleted.py
Normal file
18
guardian/migrations/0021_guardian_is_deleted.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-17 12:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('guardian', '0020_alter_juniortask_task_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='guardian',
|
||||
name='is_deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -57,6 +57,8 @@ class Guardian(models.Model):
|
||||
is_invited = models.BooleanField(default=False)
|
||||
# Profile activity"""
|
||||
is_password_set = models.BooleanField(default=True)
|
||||
# guardian profile deleted or not"""
|
||||
is_deleted = models.BooleanField(default=False)
|
||||
"""Profile activity"""
|
||||
is_active = models.BooleanField(default=True)
|
||||
"""guardian is verified or not"""
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Serializer of Guardian"""
|
||||
# third party imports
|
||||
import logging
|
||||
from django.contrib.auth import password_validation
|
||||
from rest_framework import serializers
|
||||
# Import Refresh token of jwt
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
@ -23,17 +24,19 @@ from account.models import UserProfile, UserEmailOtp, UserNotification
|
||||
from account.utils import generate_code
|
||||
from junior.serializers import JuniorDetailSerializer
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
from base.constants import NUMBER, JUN, ZOD, GRD
|
||||
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIAN
|
||||
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
|
||||
# notification's constant
|
||||
from notifications.constants import TASK_POINTS, TASK_REJECTED
|
||||
from notifications.constants import TASK_APPROVED, TASK_REJECTED, TASK_ASSIGNED
|
||||
# send notification function
|
||||
from notifications.utils import send_notification
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
# In this serializer file
|
||||
# define user serializer,
|
||||
# define password validation
|
||||
# create guardian serializer,
|
||||
# task serializer,
|
||||
# guardian serializer,
|
||||
@ -42,10 +45,51 @@ from notifications.utils import send_notification
|
||||
# guardian profile serializer,
|
||||
# approve junior serializer,
|
||||
# approve task serializer,
|
||||
from rest_framework import serializers
|
||||
|
||||
class PasswordValidator:
|
||||
"""Password validation"""
|
||||
def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True):
|
||||
self.min_length = min_length
|
||||
self.max_length = max_length
|
||||
self.require_uppercase = require_uppercase
|
||||
self.require_numbers = require_numbers
|
||||
|
||||
def __call__(self, value):
|
||||
self.enforce_password_policy(value)
|
||||
|
||||
def enforce_password_policy(self, password):
|
||||
# add validation for password
|
||||
special_characters = "!@#$%^&*()_-+=<>?/[]{}|"
|
||||
if len(password) < self.min_length:
|
||||
raise serializers.ValidationError(
|
||||
_("Password must be at least %(min_length)d characters long.") % {'min_length': self.min_length}
|
||||
)
|
||||
|
||||
if self.max_length is not None and len(password) > self.max_length:
|
||||
# must be 8 character
|
||||
raise serializers.ValidationError(
|
||||
_("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length}
|
||||
)
|
||||
|
||||
if self.require_uppercase and not any(char.isupper() for char in password):
|
||||
# must contain upper case letter
|
||||
raise serializers.ValidationError(_("Password must contain at least one uppercase letter."))
|
||||
|
||||
if self.require_numbers and not any(char.isdigit() for char in password):
|
||||
# must contain digit
|
||||
raise serializers.ValidationError(_("Password must contain at least one digit."))
|
||||
if self.require_numbers and not any(char in special_characters for char in password):
|
||||
# must contain special character
|
||||
raise serializers.ValidationError(_("Password must contain at least one special character."))
|
||||
|
||||
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
"""User serializer"""
|
||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
||||
password = serializers.CharField(write_only=True, validators=[PasswordValidator()])
|
||||
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
@ -68,8 +112,10 @@ class UserSerializer(serializers.ModelSerializer):
|
||||
UserNotification.objects.get_or_create(user=user)
|
||||
if user_type == str(NUMBER['one']):
|
||||
# create junior profile
|
||||
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
|
||||
referral_code=generate_code(ZOD, user.id))
|
||||
junior = Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
|
||||
referral_code=generate_code(ZOD, user.id))
|
||||
position = Junior.objects.all().count()
|
||||
JuniorPoints.objects.create(junior=junior, position=position)
|
||||
if user_type == str(NUMBER['two']):
|
||||
# create guardian profile
|
||||
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
|
||||
@ -82,7 +128,7 @@ class UserSerializer(serializers.ModelSerializer):
|
||||
otp_verified = False
|
||||
if otp and otp.is_verified:
|
||||
otp_verified = True
|
||||
raise serializers.ValidationError({"details":ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
|
||||
raise serializers.ValidationError({"details": ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
|
||||
"code": 400, "status":"failed",
|
||||
})
|
||||
|
||||
@ -172,7 +218,7 @@ class TaskSerializer(serializers.ModelSerializer):
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
model = JuniorTask
|
||||
fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image']
|
||||
fields = ['id', 'task_name','task_description','points', 'due_date','default_image']
|
||||
|
||||
def validate_due_date(self, value):
|
||||
"""validation on due date"""
|
||||
@ -183,11 +229,22 @@ class TaskSerializer(serializers.ModelSerializer):
|
||||
return value
|
||||
def create(self, validated_data):
|
||||
"""create default task image data"""
|
||||
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last()
|
||||
guardian = self.context['guardian']
|
||||
# update image of the task
|
||||
images = self.context['image']
|
||||
validated_data['default_image'] = images
|
||||
instance = JuniorTask.objects.create(**validated_data)
|
||||
junior_data = self.context['junior_data']
|
||||
tasks_created = []
|
||||
|
||||
for junior in junior_data:
|
||||
# create task
|
||||
task_data = validated_data.copy()
|
||||
task_data['guardian'] = guardian
|
||||
task_data['default_image'] = images
|
||||
task_data['junior'] = junior
|
||||
instance = JuniorTask.objects.create(**task_data)
|
||||
tasks_created.append(instance)
|
||||
send_notification.delay(TASK_ASSIGNED, guardian.user.id, GUARDIAN,
|
||||
junior.auth.id, {'task_id': instance.id})
|
||||
return instance
|
||||
|
||||
class GuardianDetailSerializer(serializers.ModelSerializer):
|
||||
@ -212,7 +269,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer):
|
||||
"""Meta info"""
|
||||
model = Guardian
|
||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
||||
'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
||||
'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'is_deleted',
|
||||
'updated_at']
|
||||
class TaskDetailsSerializer(serializers.ModelSerializer):
|
||||
"""Task detail serializer"""
|
||||
@ -277,10 +334,11 @@ class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
|
||||
'requested_on', 'rejected_on', 'completed_on',
|
||||
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
|
||||
|
||||
|
||||
class TopJuniorSerializer(serializers.ModelSerializer):
|
||||
"""Top junior serializer"""
|
||||
junior = JuniorDetailSerializer()
|
||||
position = serializers.IntegerField()
|
||||
position = serializers.SerializerMethodField()
|
||||
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
@ -290,9 +348,13 @@ class TopJuniorSerializer(serializers.ModelSerializer):
|
||||
def to_representation(self, instance):
|
||||
"""Convert instance to representation"""
|
||||
representation = super().to_representation(instance)
|
||||
representation['position'] = instance.position
|
||||
return representation
|
||||
|
||||
@staticmethod
|
||||
def get_position(obj):
|
||||
""" get position/rank """
|
||||
return obj.rank
|
||||
|
||||
|
||||
class GuardianProfileSerializer(serializers.ModelSerializer):
|
||||
"""junior serializer"""
|
||||
@ -338,7 +400,7 @@ class GuardianProfileSerializer(serializers.ModelSerializer):
|
||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob',
|
||||
'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code',
|
||||
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
|
||||
'updated_at', 'passcode']
|
||||
'updated_at', 'passcode','is_deleted']
|
||||
|
||||
|
||||
class ApproveJuniorSerializer(serializers.ModelSerializer):
|
||||
@ -351,8 +413,9 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
|
||||
def create(self, validated_data):
|
||||
"""update guardian code"""
|
||||
instance = self.context['junior']
|
||||
instance.guardian_code = [self.context['guardian_code']]
|
||||
instance.guardian_code_approved = True
|
||||
guardian_code = self.context['guardian_code']
|
||||
index = instance.guardian_code.index(guardian_code)
|
||||
instance.guardian_code_status[index] = str(NUMBER['two'])
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
@ -380,17 +443,17 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
|
||||
# update complete time of task
|
||||
# instance.completed_on = real_time()
|
||||
instance.completed_on = timezone.now().astimezone(pytz.utc)
|
||||
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {})
|
||||
send_notification.delay(TASK_APPROVED, instance.guardian.user.id, GUARDIAN,
|
||||
junior_details.auth.id, {'task_id': instance.id})
|
||||
else:
|
||||
# reject the task
|
||||
instance.task_status = str(NUMBER['three'])
|
||||
instance.is_approved = False
|
||||
# update total task point
|
||||
junior_data.total_points = junior_data.total_points - instance.points
|
||||
# update reject time of task
|
||||
# instance.rejected_on = real_time()
|
||||
instance.rejected_on = timezone.now().astimezone(pytz.utc)
|
||||
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {})
|
||||
send_notification.delay(TASK_REJECTED, instance.guardian.user.id, GUARDIAN,
|
||||
junior_details.auth.id, {'task_id': instance.id})
|
||||
instance.save()
|
||||
junior_data.save()
|
||||
return junior_details
|
||||
@ -403,11 +466,20 @@ class GuardianDetailListSerializer(serializers.ModelSerializer):
|
||||
email = serializers.SerializerMethodField('get_email')
|
||||
image = serializers.SerializerMethodField('get_image')
|
||||
guardian_id = serializers.SerializerMethodField('get_guardian_id')
|
||||
guardian_code = serializers.SerializerMethodField('get_guardian_code')
|
||||
gender = serializers.SerializerMethodField('get_gender')
|
||||
phone = serializers.SerializerMethodField('get_phone')
|
||||
country_name = serializers.SerializerMethodField('get_country_name')
|
||||
dob = serializers.SerializerMethodField('get_dob')
|
||||
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
|
||||
# code info
|
||||
|
||||
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
model = JuniorGuardianRelationship
|
||||
fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'created_at',
|
||||
fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'dob',
|
||||
'guardian_code', 'gender', 'phone', 'country_name', 'created_at', 'guardian_code_status',
|
||||
'updated_at']
|
||||
|
||||
def get_guardian_id(self,obj):
|
||||
@ -422,9 +494,36 @@ class GuardianDetailListSerializer(serializers.ModelSerializer):
|
||||
return obj.guardian.user.last_name
|
||||
|
||||
def get_email(self,obj):
|
||||
"""emailof guardian"""
|
||||
"""email of guardian"""
|
||||
return obj.guardian.user.email
|
||||
|
||||
def get_image(self,obj):
|
||||
"""first name of guardian"""
|
||||
"""guardian image"""
|
||||
return obj.guardian.image
|
||||
|
||||
def get_guardian_code(self,obj):
|
||||
""" guardian code"""
|
||||
return obj.guardian.guardian_code
|
||||
|
||||
def get_gender(self,obj):
|
||||
""" guardian gender"""
|
||||
return obj.guardian.gender
|
||||
|
||||
def get_phone(self,obj):
|
||||
"""guardian phone"""
|
||||
return obj.guardian.phone
|
||||
|
||||
def get_country_name(self,obj):
|
||||
""" guardian country name """
|
||||
return obj.guardian.country_name
|
||||
|
||||
def get_dob(self,obj):
|
||||
"""guardian dob """
|
||||
return obj.guardian.dob
|
||||
|
||||
def get_guardian_code_status(self,obj):
|
||||
"""guardian code status"""
|
||||
if obj.guardian.guardian_code in obj.junior.guardian_code:
|
||||
index = obj.junior.guardian_code.index(obj.guardian.guardian_code)
|
||||
data = obj.junior.guardian_code_status[index]
|
||||
return data
|
||||
|
@ -1,7 +1,12 @@
|
||||
"""task files"""
|
||||
"""Django import"""
|
||||
|
||||
# Django import
|
||||
import secrets
|
||||
|
||||
|
||||
def generate_otp():
|
||||
"""generate random otp"""
|
||||
"""
|
||||
generate random otp
|
||||
"""
|
||||
digits = "0123456789"
|
||||
return "".join(secrets.choice(digits) for _ in range(6))
|
||||
|
@ -1,7 +1,7 @@
|
||||
""" Urls files"""
|
||||
"""Django import"""
|
||||
from django.urls import path, include
|
||||
from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
|
||||
from .views import (SignupViewset, UpdateGuardianProfile, CreateTaskAPIView, TaskListAPIView,
|
||||
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
|
||||
GuardianListAPIView)
|
||||
"""Third party import"""
|
||||
@ -25,8 +25,6 @@ router.register('sign-up', SignupViewset, basename='sign-up')
|
||||
router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile')
|
||||
# Create Task API"""
|
||||
router.register('create-task', CreateTaskAPIView, basename='create-task')
|
||||
# All Task list API"""
|
||||
router.register('all-task-list', AllTaskListAPIView, basename='all-task-list')
|
||||
# Task list bases on the status API"""
|
||||
router.register('task-list', TaskListAPIView, basename='task-list')
|
||||
# Leaderboard API"""
|
||||
|
@ -11,7 +11,7 @@ import tempfile
|
||||
# Import date time module's function
|
||||
from datetime import datetime, time
|
||||
# import Number constant
|
||||
from base.constants import NUMBER, time_url
|
||||
from base.constants import NUMBER
|
||||
# Import Junior's model
|
||||
from junior.models import Junior, JuniorPoints
|
||||
# Import guardian's model
|
||||
@ -22,6 +22,8 @@ from zod_bank.celery import app
|
||||
from notifications.constants import REFERRAL_POINTS
|
||||
# send notification function
|
||||
from notifications.utils import send_notification
|
||||
|
||||
|
||||
# Define upload image on
|
||||
# ali baba cloud
|
||||
# firstly save image
|
||||
@ -41,18 +43,41 @@ def upload_image_to_alibaba(image, filename):
|
||||
# Save the image object to a temporary file
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||
"""write image in temporary file"""
|
||||
if type(image) == bytes:
|
||||
temp_file.write(image)
|
||||
else:
|
||||
temp_file.write(image.read())
|
||||
"""auth of bucket"""
|
||||
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
|
||||
"""fetch bucket details"""
|
||||
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
|
||||
# Upload the temporary file to Alibaba OSS
|
||||
bucket.put_object_from_file(filename, temp_file.name)
|
||||
"""create perfect url for image"""
|
||||
new_filename = filename.replace(' ', '%20')
|
||||
temp_file.write(image.read())
|
||||
return upload_file_to_alibaba(temp_file, filename)
|
||||
|
||||
|
||||
def upload_base64_image_to_alibaba(image, filename):
|
||||
"""
|
||||
upload image on oss alibaba bucket
|
||||
"""
|
||||
# Save the image object to a temporary file
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||
# write image in temporary file
|
||||
temp_file.write(image)
|
||||
return upload_file_to_alibaba(temp_file, filename)
|
||||
|
||||
|
||||
def upload_excel_file_to_alibaba(response, filename):
|
||||
"""
|
||||
upload excel file on oss alibaba bucket
|
||||
"""
|
||||
# Save the image object to a temporary file
|
||||
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||
# write image in temporary file
|
||||
temp_file.write(response.content)
|
||||
return upload_file_to_alibaba(temp_file, filename)
|
||||
|
||||
|
||||
def upload_file_to_alibaba(temp_file, filename):
|
||||
"""auth of bucket"""
|
||||
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
|
||||
"""fetch bucket details"""
|
||||
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
|
||||
# Upload the temporary file to Alibaba OSS
|
||||
bucket.put_object_from_file(filename, temp_file.name)
|
||||
"""create perfect url for image"""
|
||||
new_filename = filename.replace(' ', '%20')
|
||||
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
|
||||
|
||||
|
||||
@ -92,7 +117,7 @@ def update_referral_points(referral_code, referral_code_used):
|
||||
junior_query.total_points = junior_query.total_points + NUMBER['five']
|
||||
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
|
||||
junior_query.save()
|
||||
send_notification.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
|
||||
send_notification.delay(REFERRAL_POINTS, None, None, junior_queryset.auth.id, {})
|
||||
|
||||
|
||||
|
||||
@ -102,7 +127,7 @@ def update_expired_task_status(data=None):
|
||||
Update task of the status if due date is in past
|
||||
"""
|
||||
try:
|
||||
task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])]
|
||||
task_status = [str(NUMBER['one']), str(NUMBER['two'])]
|
||||
JuniorTask.objects.filter(due_date__lt=datetime.today().date(),
|
||||
task_status__in=task_status).update(task_status=str(NUMBER['six']))
|
||||
except ObjectDoesNotExist as e:
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Views of Guardian"""
|
||||
import math
|
||||
|
||||
# django imports
|
||||
# Import IsAuthenticated
|
||||
@ -6,14 +7,17 @@
|
||||
# Import PageNumberPagination
|
||||
# Import User
|
||||
# Import timezone
|
||||
from django.db.models import F, Window
|
||||
from django.db.models.functions.window import Rank
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from django.contrib.auth.models import User
|
||||
from base.constants import guardian_code_tuple
|
||||
from rest_framework.filters import SearchFilter
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from base.pagination import CustomPageNumberPagination
|
||||
# Import guardian's model,
|
||||
# Import junior's model,
|
||||
# Import account's model,
|
||||
@ -32,13 +36,13 @@ from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializ
|
||||
GuardianDetailListSerializer)
|
||||
from .models import Guardian, JuniorTask
|
||||
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||
from account.models import UserEmailOtp, UserNotification
|
||||
from account.models import UserEmailOtp, UserNotification, UserDeviceDetails
|
||||
from .tasks import generate_otp
|
||||
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email
|
||||
from account.utils import custom_response, custom_error_response, send_otp_email, task_status_fun
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
from base.constants import NUMBER
|
||||
from base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
|
||||
from .utils import upload_image_to_alibaba
|
||||
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING
|
||||
from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED
|
||||
from notifications.utils import send_notification
|
||||
|
||||
""" Define APIs """
|
||||
@ -57,37 +61,36 @@ class SignupViewset(viewsets.ModelViewSet):
|
||||
"""Signup view set"""
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
http_method_names = ('post',)
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Create user profile"""
|
||||
try:
|
||||
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
|
||||
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
|
||||
if serializer.is_valid():
|
||||
user = serializer.save()
|
||||
"""Generate otp"""
|
||||
otp = generate_otp()
|
||||
# expire otp after 1 day
|
||||
expiry = OTP_EXPIRY
|
||||
# create user email otp object
|
||||
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
|
||||
user_type=str(request.data['user_type']), expired_at=expiry)
|
||||
"""Send email to the register user"""
|
||||
send_otp_email(request.data['email'], otp)
|
||||
# send push notification for registration
|
||||
send_notification.delay(REGISTRATION, None, user.id, {})
|
||||
return custom_response(SUCCESS_CODE['3001'],
|
||||
response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
|
||||
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
|
||||
if serializer.is_valid():
|
||||
user = serializer.save()
|
||||
"""Generate otp"""
|
||||
otp = generate_otp()
|
||||
# expire otp after 1 day
|
||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||
# create user email otp object
|
||||
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
|
||||
user_type=str(request.data['user_type']), expired_at=expiry)
|
||||
"""Send email to the register user"""
|
||||
send_otp_email.delay(request.data['email'], otp)
|
||||
UserDeviceDetails.objects.create(user=user, device_id=device_id)
|
||||
return custom_response(SUCCESS_CODE['3001'],
|
||||
response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class UpdateGuardianProfile(viewsets.ViewSet):
|
||||
|
||||
class UpdateGuardianProfile(viewsets.ModelViewSet):
|
||||
"""Update guardian profile"""
|
||||
queryset = Guardian.objects.all()
|
||||
serializer_class = CreateGuardianSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Create guardian profile"""
|
||||
@ -119,7 +122,6 @@ class UpdateGuardianProfile(viewsets.ViewSet):
|
||||
class AllTaskListAPIView(viewsets.ModelViewSet):
|
||||
"""Update guardian profile"""
|
||||
serializer_class = TaskDetailsSerializer
|
||||
queryset = JuniorTask.objects.all()
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
@ -129,41 +131,14 @@ class AllTaskListAPIView(viewsets.ModelViewSet):
|
||||
serializer = TaskDetailsSerializer(queryset, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
|
||||
# class TaskListAPIView(viewsets.ModelViewSet):
|
||||
# """Update guardian profile"""
|
||||
# serializer_class = TaskDetailsSerializer
|
||||
# permission_classes = [IsAuthenticated]
|
||||
# pagination_class = PageNumberPagination
|
||||
# http_method_names = ('get',)
|
||||
#
|
||||
# def list(self, request, *args, **kwargs):
|
||||
# """Create guardian profile"""
|
||||
# try:
|
||||
# status_value = self.request.GET.get('status')
|
||||
# search = self.request.GET.get('search')
|
||||
# if search and str(status_value) == '0':
|
||||
# queryset = JuniorTask.objects.filter(guardian__user=request.user,
|
||||
# task_name__icontains=search).order_by('due_date', 'created_at')
|
||||
# elif search and str(status_value) != '0':
|
||||
# queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search,
|
||||
# task_status=status_value).order_by('due_date', 'created_at')
|
||||
# if search is None and str(status_value) == '0':
|
||||
# queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
|
||||
# elif search is None and str(status_value) != '0':
|
||||
# queryset = JuniorTask.objects.filter(guardian__user=request.user,
|
||||
# task_status=status_value).order_by('due_date','created_at')
|
||||
# paginator = self.pagination_class()
|
||||
# # use Pagination
|
||||
# paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
# # use TaskDetailsSerializer serializer
|
||||
# serializer = TaskDetailsSerializer(paginated_queryset, many=True)
|
||||
# return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
# except Exception as e:
|
||||
# return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class TaskListAPIView(viewsets.ModelViewSet):
|
||||
"""Update guardian profile"""
|
||||
"""Task list
|
||||
Params
|
||||
status
|
||||
search
|
||||
page
|
||||
junior"""
|
||||
serializer_class = TaskDetailsSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filter_backends = (SearchFilter,)
|
||||
@ -173,8 +148,8 @@ class TaskListAPIView(viewsets.ModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = JuniorTask.objects.filter(guardian__user=self.request.user
|
||||
).prefetch_related('junior', 'junior__auth'
|
||||
).order_by('due_date', 'created_at')
|
||||
).select_related('junior', 'junior__auth'
|
||||
).order_by('due_date', 'created_at')
|
||||
|
||||
queryset = self.filter_queryset(queryset)
|
||||
return queryset
|
||||
@ -182,15 +157,19 @@ class TaskListAPIView(viewsets.ModelViewSet):
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Create guardian profile"""
|
||||
status_value = self.request.GET.get('status')
|
||||
junior = self.request.GET.get('junior')
|
||||
queryset = self.get_queryset()
|
||||
if status_value and status_value != '0':
|
||||
queryset = queryset.filter(task_status=status_value)
|
||||
paginator = self.pagination_class()
|
||||
task_status = task_status_fun(status_value)
|
||||
if status_value:
|
||||
queryset = queryset.filter(task_status__in=task_status)
|
||||
if junior:
|
||||
queryset = queryset.filter(junior=int(junior))
|
||||
paginator = CustomPageNumberPagination()
|
||||
# use Pagination
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
# use TaskDetailsSerializer serializer
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
return paginator.get_paginated_response(serializer.data)
|
||||
|
||||
|
||||
class CreateTaskAPIView(viewsets.ModelViewSet):
|
||||
@ -200,42 +179,63 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
|
||||
http_method_names = ('post', )
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
image should be in form data
|
||||
"""
|
||||
try:
|
||||
image = request.data['default_image']
|
||||
junior = request.data['junior']
|
||||
junior_ids = request.data['junior'].split(',')
|
||||
|
||||
invalid_junior_ids = [junior_id for junior_id in junior_ids if not junior_id.isnumeric()]
|
||||
if invalid_junior_ids:
|
||||
# At least one junior value is not an integer
|
||||
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
allowed_extensions = ['.jpg', '.jpeg', '.png']
|
||||
if not any(extension in str(image) for extension in allowed_extensions):
|
||||
return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
if 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')
|
||||
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 junior_data:
|
||||
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)
|
||||
else:
|
||||
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# use TaskSerializer serializer
|
||||
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
|
||||
serializer = TaskSerializer(context={"guardian": guardian, "image": image_data,
|
||||
"junior_data": junior_data}, data=request.data)
|
||||
if serializer.is_valid():
|
||||
# save serializer
|
||||
serializer.save()
|
||||
junior_id = Junior.objects.filter(id=junior).last()
|
||||
send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {})
|
||||
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
|
||||
# removed send notification method and used in serializer
|
||||
return custom_response(SUCCESS_CODE['3018'], response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class SearchTaskListAPIView(viewsets.ModelViewSet):
|
||||
"""Update guardian profile"""
|
||||
"""Filter task"""
|
||||
serializer_class = TaskDetailsSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
pagination_class = PageNumberPagination
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get the queryset for the view"""
|
||||
@ -246,7 +246,7 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
|
||||
return junior_queryset
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Create guardian profile"""
|
||||
"""Filter task"""
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
paginator = self.pagination_class()
|
||||
@ -260,10 +260,11 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class TopJuniorListAPIView(viewsets.ModelViewSet):
|
||||
"""Top juniors list"""
|
||||
"""Top juniors list
|
||||
No Params"""
|
||||
serializer_class = TopJuniorSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = JuniorPoints.objects.all()
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_serializer_context(self):
|
||||
# context list
|
||||
@ -271,98 +272,118 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
|
||||
context.update({'view': self})
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = JuniorPoints.objects.filter(
|
||||
junior__is_verified=True
|
||||
).select_related(
|
||||
'junior', 'junior__auth'
|
||||
).annotate(rank=Window(
|
||||
expression=Rank(),
|
||||
order_by=[F('total_points').desc(), 'junior__created_at'])
|
||||
).order_by('-total_points', 'junior__created_at')
|
||||
return queryset
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Fetch junior list of those who complete their tasks"""
|
||||
try:
|
||||
junior_total_points = self.get_queryset().order_by('-total_points')
|
||||
|
||||
# Update the position field for each JuniorPoints object
|
||||
for index, junior in enumerate(junior_total_points):
|
||||
junior.position = index + 1
|
||||
send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
|
||||
junior.save()
|
||||
|
||||
junior_total_points = self.get_queryset()[:15]
|
||||
serializer = self.get_serializer(junior_total_points, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ApproveJuniorAPIView(viewsets.ViewSet):
|
||||
class ApproveJuniorAPIView(viewsets.ModelViewSet):
|
||||
"""approve junior by guardian"""
|
||||
serializer_class = ApproveJuniorSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get the queryset for the view"""
|
||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||
# fetch junior query
|
||||
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
|
||||
return guardian, junior_queryset
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
""" Use below param
|
||||
{"junior_id":"75",
|
||||
"action":"1"}
|
||||
"""
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||
# fetch junior query
|
||||
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
|
||||
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)
|
||||
# action 1 is use for approve and 2 for reject
|
||||
if request.data['action'] == '1':
|
||||
# use ApproveJuniorSerializer serializer
|
||||
serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code,
|
||||
"junior": queryset[1], "action": request.data['action']},
|
||||
serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code,
|
||||
"junior": junior_queryset,
|
||||
"action": request.data['action']},
|
||||
data=request.data)
|
||||
if serializer.is_valid():
|
||||
# save serializer
|
||||
serializer.save()
|
||||
send_notification.delay(ASSOCIATE_APPROVED, guardian.user.id, GUARDIAN,
|
||||
junior_queryset.auth.id, {})
|
||||
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
|
||||
else:
|
||||
queryset[1].guardian_code = None
|
||||
queryset[1].save()
|
||||
if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code):
|
||||
junior_queryset.guardian_code.remove('-')
|
||||
if junior_queryset.guardian_code_status and ('-' in junior_queryset.guardian_code_status):
|
||||
junior_queryset.guardian_code_status.remove('-')
|
||||
index = junior_queryset.guardian_code.index(guardian.guardian_code)
|
||||
junior_queryset.guardian_code.remove(guardian.guardian_code)
|
||||
junior_queryset.guardian_code_status.pop(index)
|
||||
junior_queryset.save()
|
||||
send_notification.delay(ASSOCIATE_REJECTED, guardian.user.id, GUARDIAN, junior_queryset.auth.id, {})
|
||||
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ApproveTaskAPIView(viewsets.ViewSet):
|
||||
class ApproveTaskAPIView(viewsets.ModelViewSet):
|
||||
"""approve junior by guardian"""
|
||||
serializer_class = ApproveTaskSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get the queryset for the view"""
|
||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||
# task query
|
||||
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
|
||||
guardian=guardian,
|
||||
junior=self.request.data.get('junior_id')).last()
|
||||
return guardian, task_queryset
|
||||
|
||||
http_method_names = ('post',)
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
""" Params
|
||||
{"junior_id":"82",
|
||||
"task_id":"43",
|
||||
"action":"1"}
|
||||
action 1 for approve
|
||||
2 for reject
|
||||
"""
|
||||
# action 1 is use for approve and 2 for reject
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||
# task query
|
||||
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
|
||||
guardian=guardian,
|
||||
junior=self.request.data.get('junior_id')).last()
|
||||
if task_queryset and guardian.guardian_code not in task_queryset.junior.guardian_code:
|
||||
return custom_error_response(ERROR_CODE['2084'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active):
|
||||
return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
# use ApproveJuniorSerializer serializer
|
||||
serializer = ApproveTaskSerializer(context={"guardian_code": queryset[0].guardian_code,
|
||||
"task_instance": queryset[1],
|
||||
serializer = ApproveTaskSerializer(context={"guardian_code": guardian.guardian_code,
|
||||
"task_instance": task_queryset,
|
||||
"action": str(request.data['action']),
|
||||
"junior": self.request.data['junior_id']},
|
||||
data=request.data)
|
||||
unexpected_task_status = [str(NUMBER['five']), str(NUMBER['six'])]
|
||||
if (str(request.data['action']) == str(NUMBER['one']) and serializer.is_valid()
|
||||
and queryset[1] and queryset[1].task_status not in unexpected_task_status):
|
||||
and task_queryset and task_queryset.task_status not in unexpected_task_status):
|
||||
# save serializer
|
||||
serializer.save()
|
||||
return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK)
|
||||
elif (str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid()
|
||||
and queryset[1] and queryset[1].task_status not in unexpected_task_status):
|
||||
and task_queryset and task_queryset.task_status not in unexpected_task_status):
|
||||
# save serializer
|
||||
serializer.save()
|
||||
return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK)
|
||||
else:
|
||||
return custom_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
return custom_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
#
|
||||
class GuardianListAPIView(viewsets.ModelViewSet):
|
||||
"""Guardian list of assosicated junior"""
|
||||
|
||||
@ -371,7 +392,8 @@ class GuardianListAPIView(viewsets.ModelViewSet):
|
||||
http_method_names = ('get',)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
""" Guardian list of assosicated junior
|
||||
No Params"""
|
||||
try:
|
||||
guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user)
|
||||
# fetch junior object
|
||||
@ -379,6 +401,6 @@ class GuardianListAPIView(viewsets.ModelViewSet):
|
||||
# use GuardianDetailListSerializer serializer
|
||||
serializer = GuardianDetailListSerializer(guardian_data, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(ERROR_CODE['2068'], response_status=status.HTTP_200_OK)
|
||||
return custom_response({"status": GUARDIAN_CODE_STATUS[1][0]}, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
@ -2,8 +2,27 @@
|
||||
"""Third party Django app"""
|
||||
from django.contrib import admin
|
||||
"""Import Django app"""
|
||||
from .models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||
from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
|
||||
JuniorArticleCard, FAQ)
|
||||
# Register your models here.
|
||||
admin.site.register(FAQ)
|
||||
@admin.register(JuniorArticle)
|
||||
class JuniorArticleAdmin(admin.ModelAdmin):
|
||||
"""Junior Admin"""
|
||||
list_display = ['junior', 'article', 'status', 'is_completed']
|
||||
|
||||
def __str__(self):
|
||||
"""Return email id"""
|
||||
return self.junior__auth__email
|
||||
|
||||
@admin.register(JuniorArticleCard)
|
||||
class JuniorArticleCardAdmin(admin.ModelAdmin):
|
||||
"""Junior Admin"""
|
||||
list_display = ['junior', 'article', 'article_card', 'is_read']
|
||||
|
||||
def __str__(self):
|
||||
"""Return email id"""
|
||||
return self.junior__auth__email
|
||||
@admin.register(Junior)
|
||||
class JuniorAdmin(admin.ModelAdmin):
|
||||
"""Junior Admin"""
|
||||
@ -27,3 +46,7 @@ class JuniorGuardianRelationshipAdmin(admin.ModelAdmin):
|
||||
"""Junior Admin"""
|
||||
list_display = ['guardian', 'junior', 'relationship']
|
||||
|
||||
@admin.register(JuniorArticlePoints)
|
||||
class JuniorArticlePointsAdmin(admin.ModelAdmin):
|
||||
"""Junior Admin"""
|
||||
list_display = ['junior', 'article', 'question', 'submitted_answer', 'is_answer_correct']
|
||||
|
30
junior/migrations/0019_juniorarticlepoints.py
Normal file
30
junior/migrations/0019_juniorarticlepoints.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-07 13:29
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web_admin', '0004_alter_surveyoption_survey'),
|
||||
('junior', '0018_remove_junior_relationship_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='JuniorArticlePoints',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('earn_points', models.IntegerField(blank=True, default=5, null=True)),
|
||||
('is_attempt', models.BooleanField(default=False)),
|
||||
('is_answer_correct', models.BooleanField(default=False)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles', to='web_admin.article')),
|
||||
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_details', to='junior.junior', verbose_name='Junior')),
|
||||
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='web_admin.articlesurvey')),
|
||||
('submitted_answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submitted_answer', to='web_admin.surveyoption')),
|
||||
],
|
||||
),
|
||||
]
|
18
junior/migrations/0020_junior_guardian_code_status.py
Normal file
18
junior/migrations/0020_junior_guardian_code_status.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-08 05:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0019_juniorarticlepoints'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='junior',
|
||||
name='guardian_code_status',
|
||||
field=models.CharField(blank=True, choices=[('1', 'no guardian code'), ('2', 'exist guardian code'), ('3', 'request for guardian code')], default='1', max_length=31, null=True),
|
||||
),
|
||||
]
|
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-08 09:45
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web_admin', '0004_alter_surveyoption_survey'),
|
||||
('junior', '0020_junior_guardian_code_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='juniorarticlepoints',
|
||||
name='submitted_answer',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submitted_answer', to='web_admin.surveyoption'),
|
||||
),
|
||||
]
|
27
junior/migrations/0022_juniorarticle.py
Normal file
27
junior/migrations/0022_juniorarticle.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-09 09:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web_admin', '0004_alter_surveyoption_survey'),
|
||||
('junior', '0021_alter_juniorarticlepoints_submitted_answer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='JuniorArticle',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_completed', models.BooleanField(default=False)),
|
||||
('status', models.CharField(blank=True, choices=[('1', 'read'), ('2', 'in_progress'), ('3', 'completed')], default='1', max_length=10, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_details', to='web_admin.article')),
|
||||
('junior', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior')),
|
||||
],
|
||||
),
|
||||
]
|
27
junior/migrations/0023_juniorarticlecard.py
Normal file
27
junior/migrations/0023_juniorarticlecard.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-09 10:47
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('web_admin', '0004_alter_surveyoption_survey'),
|
||||
('junior', '0022_juniorarticle'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='JuniorArticleCard',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_read', models.BooleanField(default=False)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_detail', to='web_admin.article')),
|
||||
('article_card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_article_card', to='web_admin.articlecard')),
|
||||
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article_card', to='junior.junior', verbose_name='Junior')),
|
||||
],
|
||||
),
|
||||
]
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-10 08:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0023_juniorarticlecard'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='juniorarticle',
|
||||
name='current_card_page',
|
||||
field=models.IntegerField(blank=True, default=0, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='juniorarticle',
|
||||
name='current_que_page',
|
||||
field=models.IntegerField(blank=True, default=0, null=True),
|
||||
),
|
||||
]
|
19
junior/migrations/0025_alter_juniorarticle_junior.py
Normal file
19
junior/migrations/0025_alter_juniorarticle_junior.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-10 14:46
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0024_juniorarticle_current_card_page_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='juniorarticle',
|
||||
name='junior',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior'),
|
||||
),
|
||||
]
|
@ -0,0 +1,39 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-17 09:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0025_alter_juniorarticle_junior'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FAQ',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('question', models.IntegerField(max_length=100)),
|
||||
('description', models.CharField(max_length=500)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'FAQ',
|
||||
'verbose_name_plural': 'FAQ',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='juniorarticle',
|
||||
options={'verbose_name': 'Junior Article', 'verbose_name_plural': 'Junior Article'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='juniorarticlecard',
|
||||
options={'verbose_name': 'Junior Article Card', 'verbose_name_plural': 'Junior Article Card'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='juniorarticlepoints',
|
||||
options={'verbose_name': 'Junior Article Points', 'verbose_name_plural': 'Junior Article Points'},
|
||||
),
|
||||
]
|
18
junior/migrations/0027_alter_faq_question.py
Normal file
18
junior/migrations/0027_alter_faq_question.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-17 09:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0026_faq_alter_juniorarticle_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='faq',
|
||||
name='question',
|
||||
field=models.CharField(max_length=100),
|
||||
),
|
||||
]
|
18
junior/migrations/0028_faq_status.py
Normal file
18
junior/migrations/0028_faq_status.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-17 10:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0027_alter_faq_question'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='faq',
|
||||
name='status',
|
||||
field=models.IntegerField(blank=True, default=1, null=True),
|
||||
),
|
||||
]
|
18
junior/migrations/0029_junior_is_deleted.py
Normal file
18
junior/migrations/0029_junior_is_deleted.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-17 12:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0028_faq_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='junior',
|
||||
name='is_deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
17
junior/migrations/0030_remove_junior_guardian_code_status.py
Normal file
17
junior/migrations/0030_remove_junior_guardian_code_status.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-26 08:59
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0029_junior_is_deleted'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='junior',
|
||||
name='guardian_code_status',
|
||||
),
|
||||
]
|
19
junior/migrations/0031_junior_guardian_code_status.py
Normal file
19
junior/migrations/0031_junior_guardian_code_status.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-26 08:59
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('junior', '0030_remove_junior_guardian_code_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='junior',
|
||||
name='guardian_code_status',
|
||||
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None),
|
||||
),
|
||||
]
|
109
junior/models.py
109
junior/models.py
@ -6,10 +6,11 @@ from django.contrib.auth import get_user_model
|
||||
"""Import ArrayField"""
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
"""Import django app"""
|
||||
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
|
||||
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP, GUARDIAN_CODE_STATUS, ARTICLE_STATUS
|
||||
# Import guardian's model
|
||||
from guardian.models import Guardian
|
||||
|
||||
# Import web admin's model
|
||||
from web_admin.models import SurveyOption, ArticleSurvey, Article, ArticleCard
|
||||
"""Define User model"""
|
||||
User = get_user_model()
|
||||
# Create your models here.
|
||||
@ -67,12 +68,17 @@ class Junior(models.Model):
|
||||
is_password_set = models.BooleanField(default=True)
|
||||
# junior profile is complete or not"""
|
||||
is_complete_profile = models.BooleanField(default=False)
|
||||
# junior profile deleted or not"""
|
||||
is_deleted = models.BooleanField(default=False)
|
||||
# passcode of the junior profile"""
|
||||
passcode = models.IntegerField(null=True, blank=True, default=None)
|
||||
# junior is verified or not"""
|
||||
is_verified = models.BooleanField(default=False)
|
||||
"""guardian code is approved or not"""
|
||||
guardian_code_approved = models.BooleanField(default=False)
|
||||
# # guardian code status"""
|
||||
guardian_code_status = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None), null=True,
|
||||
)
|
||||
# Profile created and updated time"""
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
@ -137,3 +143,102 @@ class JuniorGuardianRelationship(models.Model):
|
||||
return f'{self.guardian.user}'
|
||||
|
||||
|
||||
class JuniorArticlePoints(models.Model):
|
||||
"""
|
||||
Survey Options model
|
||||
"""
|
||||
# associated junior with the task
|
||||
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_details', verbose_name='Junior')
|
||||
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles')
|
||||
question = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='questions')
|
||||
submitted_answer = models.ForeignKey(SurveyOption, on_delete=models.SET_NULL, null=True,
|
||||
related_name='submitted_answer')
|
||||
# earn points"""
|
||||
earn_points = models.IntegerField(blank=True, null=True, default=5)
|
||||
is_attempt = models.BooleanField(default=False)
|
||||
is_answer_correct = models.BooleanField(default=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(object):
|
||||
""" Meta class """
|
||||
verbose_name = 'Junior Article Points'
|
||||
# another name of the model"""
|
||||
verbose_name_plural = 'Junior Article Points'
|
||||
|
||||
def __str__(self):
|
||||
"""Return title"""
|
||||
return f'{self.id} | {self.question}'
|
||||
|
||||
class JuniorArticle(models.Model):
|
||||
"""
|
||||
Survey Options model
|
||||
"""
|
||||
# associated junior with the task
|
||||
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article',
|
||||
verbose_name='Junior')
|
||||
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_details')
|
||||
# article completed"""
|
||||
is_completed = models.BooleanField(default=False)
|
||||
status = models.CharField(max_length=10, choices=ARTICLE_STATUS, null=True, blank=True, default='1')
|
||||
current_card_page = models.IntegerField(blank=True, null=True, default=0)
|
||||
current_que_page = models.IntegerField(blank=True, null=True, default=0)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(object):
|
||||
""" Meta class """
|
||||
verbose_name = 'Junior Article'
|
||||
# another name of the model"""
|
||||
verbose_name_plural = 'Junior Article'
|
||||
|
||||
def __str__(self):
|
||||
"""Return title"""
|
||||
return f'{self.id} | {self.article}'
|
||||
|
||||
class JuniorArticleCard(models.Model):
|
||||
"""
|
||||
Survey Options model
|
||||
"""
|
||||
# associated junior with the task
|
||||
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article_card',
|
||||
verbose_name='Junior')
|
||||
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_detail')
|
||||
article_card = models.ForeignKey(ArticleCard, on_delete=models.CASCADE, related_name='junior_article_card')
|
||||
|
||||
# article card read"""
|
||||
is_read = models.BooleanField(default=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(object):
|
||||
""" Meta class """
|
||||
verbose_name = 'Junior Article Card'
|
||||
# another name of the model"""
|
||||
verbose_name_plural = 'Junior Article Card'
|
||||
|
||||
def __str__(self):
|
||||
"""Return title"""
|
||||
return f'{self.id} | {self.article}'
|
||||
|
||||
|
||||
class FAQ(models.Model):
|
||||
"""FAQ model"""
|
||||
# questions"""
|
||||
question = models.CharField(max_length=100)
|
||||
# answer"""
|
||||
description = models.CharField(max_length=500)
|
||||
# status
|
||||
status = models.IntegerField(default=1, null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(object):
|
||||
""" Meta class """
|
||||
verbose_name = 'FAQ'
|
||||
# another name of the model"""
|
||||
verbose_name_plural = 'FAQ'
|
||||
|
||||
def __str__(self):
|
||||
"""Return email id"""
|
||||
return f'{self.question}'
|
||||
|
@ -11,19 +11,20 @@ from django.utils import timezone
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
# local imports
|
||||
from account.utils import send_otp_email, generate_code
|
||||
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||
from account.utils import send_otp_email, generate_code, make_special_password
|
||||
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ
|
||||
from guardian.tasks import generate_otp
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED
|
||||
from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED,
|
||||
GUARDIAN_CODE_STATUS, JUNIOR)
|
||||
from guardian.models import Guardian, JuniorTask
|
||||
from account.models import UserEmailOtp, UserNotification
|
||||
from junior.utils import junior_notification_email, junior_approval_mail
|
||||
from junior.utils import junior_notification_email, junior_approval_mail, get_junior_leaderboard_rank
|
||||
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
|
||||
from notifications.utils import send_notification
|
||||
from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION,
|
||||
TASK_SUBMITTED)
|
||||
|
||||
from notifications.constants import (ASSOCIATE_REQUEST, ASSOCIATE_JUNIOR, TASK_ACTION,
|
||||
)
|
||||
from web_admin.models import ArticleCard
|
||||
|
||||
class ListCharField(serializers.ListField):
|
||||
"""Serializer for Array field"""
|
||||
@ -87,14 +88,27 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
|
||||
if junior:
|
||||
"""update details according to the data get from request"""
|
||||
junior.gender = validated_data.get('gender',junior.gender)
|
||||
"""Update guardian code"""
|
||||
junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code)
|
||||
"""condition for guardian code"""
|
||||
# Update guardian code"""
|
||||
# condition for guardian code
|
||||
if guardian_code:
|
||||
junior.guardian_code = guardian_code
|
||||
if junior.guardian_code and guardian_code:
|
||||
if guardian_code[0] in junior.guardian_code:
|
||||
raise serializers.ValidationError({"error":ERROR_CODE['2076'],"code":"400", "status":"failed"})
|
||||
if not junior.guardian_code:
|
||||
junior.guardian_code = []
|
||||
junior.guardian_code_status = []
|
||||
junior.guardian_code.extend(guardian_code)
|
||||
junior.guardian_code_status.extend(str(NUMBER['three']))
|
||||
elif len(junior.guardian_code) < 3 and len(guardian_code) < 3:
|
||||
junior.guardian_code.extend(guardian_code)
|
||||
junior.guardian_code_status.extend(str(NUMBER['three']))
|
||||
else:
|
||||
raise serializers.ValidationError({"error":ERROR_CODE['2081'],"code":"400", "status":"failed"})
|
||||
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
|
||||
if guardian_data:
|
||||
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
|
||||
send_notification.delay(ASSOCIATE_REQUEST, junior.auth.id, JUNIOR, guardian_data.user.id, {})
|
||||
junior_approval_mail.delay(user.email, user.first_name)
|
||||
junior.dob = validated_data.get('dob', junior.dob)
|
||||
junior.passcode = validated_data.get('passcode', junior.passcode)
|
||||
junior.country_name = validated_data.get('country_name', junior.country_name)
|
||||
@ -142,8 +156,8 @@ class JuniorDetailSerializer(serializers.ModelSerializer):
|
||||
"""Meta info"""
|
||||
model = Junior
|
||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
||||
'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
|
||||
'image', 'updated_at']
|
||||
'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile',
|
||||
'created_at', 'image', 'is_deleted', 'updated_at']
|
||||
|
||||
class JuniorDetailListSerializer(serializers.ModelSerializer):
|
||||
"""junior serializer"""
|
||||
@ -159,6 +173,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
|
||||
rejected_task = serializers.SerializerMethodField('get_rejected_task')
|
||||
pending_task = serializers.SerializerMethodField('get_pending_task')
|
||||
position = serializers.SerializerMethodField('get_position')
|
||||
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
|
||||
|
||||
|
||||
def get_auth(self, obj):
|
||||
@ -175,13 +190,13 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
|
||||
return data
|
||||
|
||||
def get_position(self, obj):
|
||||
return get_junior_leaderboard_rank(obj)
|
||||
|
||||
def get_points(self, obj):
|
||||
data = JuniorPoints.objects.filter(junior=obj).last()
|
||||
if data:
|
||||
return data.position
|
||||
return 99999
|
||||
def get_points(self, obj):
|
||||
data = sum(JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).values_list('points', flat=True))
|
||||
return data
|
||||
return data.total_points
|
||||
return NUMBER['zero']
|
||||
|
||||
def get_in_progress_task(self, obj):
|
||||
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
|
||||
@ -203,13 +218,21 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
|
||||
def get_pending_task(self, obj):
|
||||
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
|
||||
return data
|
||||
|
||||
def get_guardian_code_status(self, obj):
|
||||
if self.context['guardian_code'] in obj.guardian_code:
|
||||
index = obj.guardian_code.index(self.context['guardian_code'])
|
||||
if obj.guardian_code_status:
|
||||
data = obj.guardian_code_status[index]
|
||||
return data
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
model = Junior
|
||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'country_name', 'phone', 'gender', 'dob',
|
||||
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
||||
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
|
||||
'requested_task', 'rejected_task', 'position', 'is_invited']
|
||||
'requested_task', 'rejected_task', 'position', 'is_invited', 'guardian_code_status',
|
||||
'is_deleted']
|
||||
|
||||
class JuniorProfileSerializer(serializers.ModelSerializer):
|
||||
"""junior serializer"""
|
||||
@ -252,15 +275,14 @@ class JuniorProfileSerializer(serializers.ModelSerializer):
|
||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
|
||||
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
||||
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
|
||||
'is_invited', 'passcode', 'guardian_code_approved']
|
||||
'is_invited', 'passcode', 'guardian_code_approved', 'is_deleted']
|
||||
|
||||
class AddJuniorSerializer(serializers.ModelSerializer):
|
||||
"""Add junior serializer"""
|
||||
|
||||
class Meta(object):
|
||||
"""Meta info"""
|
||||
model = Junior
|
||||
fields = ['id', 'gender','dob', 'is_invited']
|
||||
fields = ['id', 'gender', 'dob', 'is_invited']
|
||||
|
||||
|
||||
def create(self, validated_data):
|
||||
@ -269,23 +291,28 @@ class AddJuniorSerializer(serializers.ModelSerializer):
|
||||
email = self.context['email']
|
||||
guardian = self.context['user']
|
||||
relationship = self.context['relationship']
|
||||
profile_image = self.context['image']
|
||||
full_name = self.context['first_name'] + ' ' + self.context['last_name']
|
||||
guardian_data = Guardian.objects.filter(user__username=guardian).last()
|
||||
user_data = User.objects.create(username=email, email=email,
|
||||
first_name=self.context['first_name'],
|
||||
last_name=self.context['last_name'])
|
||||
password = User.objects.make_random_password()
|
||||
user_data.set_password(password)
|
||||
special_password = make_special_password()
|
||||
user_data.set_password(special_password)
|
||||
user_data.save()
|
||||
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
|
||||
image=profile_image,
|
||||
dob=validated_data.get('dob'), is_invited=True,
|
||||
guardian_code=[guardian_data.guardian_code],
|
||||
junior_code=generate_code(JUN, user_data.id),
|
||||
referral_code=generate_code(ZOD, user_data.id),
|
||||
referral_code_used=guardian_data.referral_code,
|
||||
is_password_set=False, is_verified=True)
|
||||
is_password_set=False, is_verified=True,
|
||||
guardian_code_status=[str(NUMBER['two'])])
|
||||
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
|
||||
relationship=relationship)
|
||||
total_junior = Junior.objects.all().count()
|
||||
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
|
||||
"""Generate otp"""
|
||||
otp_value = generate_otp()
|
||||
expiry_time = timezone.now() + timezone.timedelta(days=1)
|
||||
@ -294,9 +321,9 @@ class AddJuniorSerializer(serializers.ModelSerializer):
|
||||
# add push notification
|
||||
UserNotification.objects.get_or_create(user=user_data)
|
||||
"""Notification email"""
|
||||
junior_notification_email(email, full_name, email, password)
|
||||
junior_notification_email.delay(email, full_name, email, special_password)
|
||||
# push notification
|
||||
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
|
||||
send_notification.delay(ASSOCIATE_JUNIOR, None, None, junior_data.auth.id, {})
|
||||
return junior_data
|
||||
|
||||
|
||||
@ -309,8 +336,13 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
|
||||
fields = ('id', 'is_invited')
|
||||
def update(self, instance, validated_data):
|
||||
if instance:
|
||||
guardian_code = self.context['guardian_code']
|
||||
instance.is_invited = False
|
||||
instance.guardian_code = '{}'
|
||||
if instance.guardian_code and ('-' in instance.guardian_code):
|
||||
instance.guardian_code.remove('-')
|
||||
index = instance.guardian_code.index(guardian_code)
|
||||
instance.guardian_code.remove(guardian_code)
|
||||
instance.guardian_code_status.pop(index)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
@ -323,13 +355,12 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
|
||||
fields = ('id', 'image')
|
||||
def update(self, instance, validated_data):
|
||||
instance.image = validated_data.get('image', instance.image)
|
||||
# instance.requested_on = real_time()
|
||||
instance.requested_on = timezone.now().astimezone(pytz.utc)
|
||||
instance.task_status = str(NUMBER['four'])
|
||||
instance.is_approved = False
|
||||
instance.save()
|
||||
send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {})
|
||||
send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {})
|
||||
send_notification.delay(TASK_ACTION, instance.junior.auth.id, JUNIOR,
|
||||
instance.guardian.user.id, {'task_id': instance.id})
|
||||
return instance
|
||||
|
||||
class JuniorPointsSerializer(serializers.ModelSerializer):
|
||||
@ -349,10 +380,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
|
||||
return obj.junior.id
|
||||
|
||||
def get_position(self, obj):
|
||||
data = JuniorPoints.objects.filter(junior=obj.junior).last()
|
||||
if data:
|
||||
return data.position
|
||||
return 99999
|
||||
return get_junior_leaderboard_rank(obj.junior)
|
||||
def get_points(self, obj):
|
||||
"""total points"""
|
||||
points = JuniorPoints.objects.filter(junior=obj.junior).last()
|
||||
@ -384,7 +412,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
|
||||
"""Meta info"""
|
||||
model = Junior
|
||||
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
|
||||
'requested_task', 'rejected_task', 'expired_task']
|
||||
'requested_task', 'rejected_task', 'expired_task', 'is_deleted']
|
||||
|
||||
class AddGuardianSerializer(serializers.ModelSerializer):
|
||||
"""Add guardian serializer"""
|
||||
@ -403,13 +431,15 @@ class AddGuardianSerializer(serializers.ModelSerializer):
|
||||
relationship = self.context['relationship']
|
||||
full_name = self.context['first_name'] + ' ' + self.context['last_name']
|
||||
junior_data = Junior.objects.filter(auth__username=junior).last()
|
||||
junior_data.guardian_code_status = GUARDIAN_CODE_STATUS[2][0]
|
||||
junior_data.save()
|
||||
instance = User.objects.filter(username=email).last()
|
||||
if instance:
|
||||
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
|
||||
referral_code=generate_code(ZOD,
|
||||
referral_code=generate_code(ZOD,
|
||||
instance.id),
|
||||
referral_code_used=junior_data.referral_code,
|
||||
is_verified=True)
|
||||
is_verified=True)
|
||||
UserNotification.objects.get_or_create(user=instance)
|
||||
return guardian_data
|
||||
else:
|
||||
@ -436,9 +466,8 @@ class AddGuardianSerializer(serializers.ModelSerializer):
|
||||
|
||||
"""Notification email"""
|
||||
junior_notification_email(email, full_name, email, password)
|
||||
junior_approval_mail(email, full_name)
|
||||
send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {})
|
||||
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
|
||||
junior_approval_mail.delay(email, full_name)
|
||||
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth.id, JUNIOR, guardian_data.user.id, {})
|
||||
return guardian_data
|
||||
|
||||
class StartTaskSerializer(serializers.ModelSerializer):
|
||||
@ -481,3 +510,41 @@ class ReAssignTaskSerializer(serializers.ModelSerializer):
|
||||
instance.requested_on = None
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
|
||||
"""User task Serializer"""
|
||||
class Meta(object):
|
||||
"""Meta class"""
|
||||
model = Junior
|
||||
fields = ('id', )
|
||||
def update(self, instance, validated_data):
|
||||
guardian_code = self.context['guardian_code']
|
||||
if guardian_code in instance.guardian_code:
|
||||
if instance.guardian_code and ('-' in instance.guardian_code):
|
||||
instance.guardian_code.remove('-')
|
||||
if instance.guardian_code_status and ('-' in instance.guardian_code_status):
|
||||
instance.guardian_code_status.remove('-')
|
||||
index = instance.guardian_code.index(guardian_code)
|
||||
instance.guardian_code.remove(guardian_code)
|
||||
instance.guardian_code_status.pop(index)
|
||||
else:
|
||||
raise serializers.ValidationError({"error":ERROR_CODE['2082'],"code":"400", "status":"failed"})
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
class FAQSerializer(serializers.ModelSerializer):
|
||||
"""FAQ Serializer"""
|
||||
|
||||
class Meta(object):
|
||||
"""meta info"""
|
||||
model = FAQ
|
||||
fields = ('id', 'question', 'description')
|
||||
|
||||
class CreateArticleCardSerializer(serializers.ModelSerializer):
|
||||
"""Article card Serializer"""
|
||||
|
||||
class Meta(object):
|
||||
"""meta info"""
|
||||
model = ArticleCard
|
||||
fields = ('id', 'article')
|
||||
|
||||
|
@ -4,7 +4,9 @@ from django.urls import path, include
|
||||
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
|
||||
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
|
||||
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
|
||||
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView)
|
||||
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
|
||||
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
|
||||
CreateArticleCardAPIView, RemoveGuardianCodeAPIView, FAQViewSet)
|
||||
"""Third party import"""
|
||||
from rest_framework import routers
|
||||
|
||||
@ -41,6 +43,16 @@ router.register('junior-points', JuniorPointsListAPIView, basename='junior-point
|
||||
router.register('validate-referral-code', ValidateReferralCode, basename='validate-referral-code')
|
||||
# invite guardian API"""
|
||||
router.register('invite-guardian', InviteGuardianAPIView, basename='invite-guardian')
|
||||
# start article"""
|
||||
router.register('start-article', StartArticleAPIView, basename='start-article')
|
||||
# start assessment api"""
|
||||
router.register('start-assessment', StartAssessmentAPIView, basename='start-assessment')
|
||||
# check answer api"""
|
||||
router.register('check-answer', CheckAnswerAPIView, basename='check-answer')
|
||||
# start article"""
|
||||
router.register('create-article-card', CreateArticleCardAPIView, basename='create-article-card')
|
||||
# FAQ API
|
||||
router.register('faq', FAQViewSet, basename='faq')
|
||||
# Define url pattern"""
|
||||
urlpatterns = [
|
||||
path('api/v1/', include(router.urls)),
|
||||
@ -48,4 +60,7 @@ urlpatterns = [
|
||||
path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()),
|
||||
path('api/v1/start-task/', StartTaskAPIView.as_view()),
|
||||
path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()),
|
||||
path('api/v1/complete-article/', CompleteArticleAPIView.as_view()),
|
||||
path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()),
|
||||
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view())
|
||||
]
|
||||
|
@ -4,7 +4,9 @@ from django.conf import settings
|
||||
"""Third party Django app"""
|
||||
from templated_email import send_templated_mail
|
||||
from .models import JuniorPoints
|
||||
from django.db.models import F
|
||||
from base.constants import NUMBER
|
||||
from django.db.models import F, Window
|
||||
from django.db.models.functions.window import Rank
|
||||
# junior notification
|
||||
# email for sending email
|
||||
# when guardian create junior profile
|
||||
@ -13,6 +15,8 @@ from django.db.models import F
|
||||
# being part of the zod bank and access the platform
|
||||
# define junior notification email
|
||||
# junior approval email
|
||||
from celery import shared_task
|
||||
@shared_task()
|
||||
def junior_notification_email(recipient_email, full_name, email, password):
|
||||
"""Notification email"""
|
||||
from_email = settings.EMAIL_FROM_ADDRESS
|
||||
@ -31,7 +35,7 @@ def junior_notification_email(recipient_email, full_name, email, password):
|
||||
}
|
||||
)
|
||||
return full_name
|
||||
|
||||
@shared_task()
|
||||
def junior_approval_mail(guardian, full_name):
|
||||
"""junior approval mail"""
|
||||
from_email = settings.EMAIL_FROM_ADDRESS
|
||||
@ -50,11 +54,29 @@ def junior_approval_mail(guardian, full_name):
|
||||
def update_positions_based_on_points():
|
||||
"""Update position of the junior"""
|
||||
# First, retrieve all the JuniorPoints instances ordered by total_points in descending order.
|
||||
juniors_points = JuniorPoints.objects.order_by('-total_points')
|
||||
juniors_points = JuniorPoints.objects.order_by('-total_points', 'created_at')
|
||||
|
||||
# Now, iterate through the queryset and update the position field based on the order.
|
||||
position = 1
|
||||
position = NUMBER['one']
|
||||
for junior_point in juniors_points:
|
||||
junior_point.position = position
|
||||
junior_point.save()
|
||||
position += 1
|
||||
position += NUMBER['one']
|
||||
|
||||
|
||||
def get_junior_leaderboard_rank(junior_obj):
|
||||
"""
|
||||
to get junior's position/rank
|
||||
:param junior_obj:
|
||||
:return: junior's position/rank
|
||||
"""
|
||||
queryset = JuniorPoints.objects.filter(
|
||||
junior__is_verified=True
|
||||
).select_related('junior', 'junior__auth').annotate(rank=Window(
|
||||
expression=Rank(),
|
||||
order_by=[F('total_points').desc(), 'junior__created_at']
|
||||
)).order_by('-total_points', 'junior__created_at')
|
||||
|
||||
junior = next((query for query in queryset if query.junior == junior_obj), None)
|
||||
|
||||
return junior.rank if junior else None
|
||||
|
507
junior/views.py
507
junior/views.py
@ -6,11 +6,20 @@ from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.filters import SearchFilter
|
||||
from django.db.models import F
|
||||
|
||||
import datetime
|
||||
import requests
|
||||
"""Django app import"""
|
||||
|
||||
from rest_framework.viewsets import GenericViewSet, mixins
|
||||
import math
|
||||
|
||||
from base.pagination import CustomPageNumberPagination
|
||||
|
||||
"""Django app import"""
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
# Import guardian's model,
|
||||
# Import junior's model,
|
||||
# Import account's model,
|
||||
@ -27,20 +36,25 @@ import requests
|
||||
# Import upload_image_to_alibaba
|
||||
# Import custom_response, custom_error_response
|
||||
# Import constants
|
||||
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
|
||||
from django.db.models import Sum
|
||||
from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
|
||||
JuniorArticleCard, FAQ)
|
||||
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,
|
||||
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer,
|
||||
AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer)
|
||||
AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer,
|
||||
RemoveGuardianCodeSerializer, FAQSerializer, CreateArticleCardSerializer)
|
||||
from guardian.models import Guardian, JuniorTask
|
||||
from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
from base.constants import NUMBER
|
||||
from account.utils import custom_response, custom_error_response
|
||||
from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN
|
||||
from account.utils import custom_response, custom_error_response, task_status_fun
|
||||
from guardian.utils import upload_image_to_alibaba
|
||||
from .utils import update_positions_based_on_points
|
||||
from notifications.utils import send_notification
|
||||
from notifications.constants import REMOVE_JUNIOR
|
||||
|
||||
from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS, ASSOCIATE_EXISTING_JUNIOR
|
||||
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
|
||||
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
|
||||
StartAssessmentSerializer)
|
||||
""" Define APIs """
|
||||
# Define validate guardian code API,
|
||||
# update junior profile,
|
||||
@ -57,14 +71,14 @@ from notifications.constants import REMOVE_JUNIOR
|
||||
# Start task
|
||||
# by junior API
|
||||
# Create your views here.
|
||||
class UpdateJuniorProfile(viewsets.ViewSet):
|
||||
class UpdateJuniorProfile(viewsets.ModelViewSet):
|
||||
"""Update junior profile"""
|
||||
queryset = Junior.objects.all()
|
||||
serializer_class = CreateJuniorSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Use CreateJuniorSerializer"""
|
||||
"""Create Junior Profile"""
|
||||
try:
|
||||
request_data = request.data
|
||||
image = request.data.get('image')
|
||||
@ -89,15 +103,22 @@ class UpdateJuniorProfile(viewsets.ViewSet):
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
if e.detail:
|
||||
error_detail = e.detail.get('error', None)
|
||||
else:
|
||||
error_detail = str(e)
|
||||
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ValidateGuardianCode(viewsets.ViewSet):
|
||||
class ValidateGuardianCode(viewsets.ModelViewSet):
|
||||
"""Check guardian code exist or not"""
|
||||
queryset = Guardian.objects.all()
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""check guardian code"""
|
||||
"""check guardian code
|
||||
Params
|
||||
"guardian_code"
|
||||
"""
|
||||
try:
|
||||
guardian_code = self.request.GET.get('guardian_code').split(',')
|
||||
for code in guardian_code:
|
||||
@ -128,14 +149,15 @@ class JuniorListAPIView(viewsets.ModelViewSet):
|
||||
def list(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
try:
|
||||
update_positions_based_on_points()
|
||||
# update_positions_based_on_points, function removed
|
||||
guardian_data = Guardian.objects.filter(user__email=request.user).last()
|
||||
# fetch junior object
|
||||
if guardian_data:
|
||||
queryset = self.get_queryset()
|
||||
queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code))
|
||||
# use JuniorDetailListSerializer serializer
|
||||
serializer = JuniorDetailListSerializer(queryset, many=True)
|
||||
serializer = JuniorDetailListSerializer(queryset, context={"guardian_code":
|
||||
guardian_data.guardian_code}, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
@ -149,12 +171,36 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
""" add junior
|
||||
{ "gender":"1",
|
||||
"first_name":"abc",
|
||||
"last_name":"xyz",
|
||||
"dob":"2023-12-12",
|
||||
"relationship":"2",
|
||||
"email":"abc@yopmail.com"
|
||||
}"""
|
||||
try:
|
||||
info_data = {'user': request.user, 'relationship': str(request.data['relationship']), 'email': request.data['email'], 'first_name': request.data['first_name'],
|
||||
'last_name': request.data['last_name']}
|
||||
info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
|
||||
'email': request.data['email'], 'first_name': request.data['first_name'],
|
||||
'last_name': request.data['last_name'], 'image':None}
|
||||
profile_image = request.data.get('image')
|
||||
if profile_image:
|
||||
# check image size
|
||||
if profile_image.size == NUMBER['zero']:
|
||||
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
# convert into file
|
||||
filename = f"images/{profile_image.name}"
|
||||
# upload image on ali baba
|
||||
image_url = upload_image_to_alibaba(profile_image, filename)
|
||||
info_data.update({"image": image_url})
|
||||
if user := User.objects.filter(username=request.data['email']).first():
|
||||
self.associate_guardian(user)
|
||||
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
|
||||
serializer = AddJuniorSerializer(data=request.data, context=info_data)
|
||||
@ -162,25 +208,44 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
|
||||
# save serializer
|
||||
serializer.save()
|
||||
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def associate_guardian(self, user):
|
||||
junior = Junior.objects.filter(auth=user).first()
|
||||
junior = Junior.objects.filter(auth__email=self.request.data['email']).first()
|
||||
guardian = Guardian.objects.filter(user=self.request.user).first()
|
||||
junior.guardian_code = [guardian.guardian_code]
|
||||
if junior is None:
|
||||
return none
|
||||
if junior.guardian_code and (guardian.guardian_code in junior.guardian_code):
|
||||
return False
|
||||
if junior.guardian_code and ('-' in junior.guardian_code):
|
||||
junior.guardian_code.remove('-')
|
||||
if not junior.guardian_code:
|
||||
junior.guardian_code = [guardian.guardian_code]
|
||||
elif type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
|
||||
junior.guardian_code.append(guardian.guardian_code)
|
||||
else:
|
||||
return "Max"
|
||||
if junior.guardian_code_status and ('-' in junior.guardian_code_status):
|
||||
junior.guardian_code_status.remove('-')
|
||||
if not junior.guardian_code_status:
|
||||
junior.guardian_code_status = [str(NUMBER['two'])]
|
||||
else:
|
||||
junior.guardian_code_status.append(str(NUMBER['two']))
|
||||
junior.save()
|
||||
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior,
|
||||
relationship=str(self.request.data['relationship']))
|
||||
jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior)
|
||||
if jun_data:
|
||||
jun_data.relationship = str(self.request.data['relationship'])
|
||||
jun_data.save()
|
||||
send_notification.delay(ASSOCIATE_EXISTING_JUNIOR, self.request.user.id, GUARDIAN, junior.auth.id, {})
|
||||
return True
|
||||
|
||||
|
||||
class InvitedJuniorAPIView(viewsets.ModelViewSet):
|
||||
"""Junior list of assosicated guardian"""
|
||||
"""Invited Junior list of assosicated guardian"""
|
||||
|
||||
serializer_class = JuniorDetailListSerializer
|
||||
queryset = Junior.objects.all()
|
||||
permission_classes = [IsAuthenticated]
|
||||
pagination_class = PageNumberPagination
|
||||
http_method_names = ('get',)
|
||||
@ -192,7 +257,8 @@ class InvitedJuniorAPIView(viewsets.ModelViewSet):
|
||||
is_invited=True)
|
||||
return junior_queryset
|
||||
def list(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
""" Invited Junior list of assosicated guardian
|
||||
No Params"""
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
paginator = self.pagination_class()
|
||||
@ -206,13 +272,25 @@ class InvitedJuniorAPIView(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class FilterJuniorAPIView(viewsets.ModelViewSet):
|
||||
"""Update guardian profile"""
|
||||
"""filter junior profile"""
|
||||
serializer_class = JuniorDetailListSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
pagination_class = PageNumberPagination
|
||||
queryset = Junior.objects.all()
|
||||
http_method_names = ('get',)
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
# Example of a query parameter
|
||||
openapi.Parameter(
|
||||
'title',
|
||||
openapi.IN_QUERY,
|
||||
description='title of the name',
|
||||
type=openapi.TYPE_STRING,
|
||||
),
|
||||
# Add more parameters as needed
|
||||
]
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get the queryset for the view"""
|
||||
title = self.request.GET.get('title')
|
||||
@ -223,7 +301,7 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
|
||||
return queryset
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Create guardian profile"""
|
||||
"""Filter junior"""
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
paginator = self.pagination_class()
|
||||
@ -237,7 +315,9 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class RemoveJuniorAPIView(views.APIView):
|
||||
"""Remove junior API"""
|
||||
"""Remove junior API
|
||||
Params
|
||||
id=37"""
|
||||
serializer_class = RemoveJuniorSerializer
|
||||
model = Junior
|
||||
permission_classes = [IsAuthenticated]
|
||||
@ -247,14 +327,17 @@ class RemoveJuniorAPIView(views.APIView):
|
||||
junior_id = self.request.GET.get('id')
|
||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||
# fetch junior query
|
||||
junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code)).last()
|
||||
junior_queryset = Junior.objects.filter(id=junior_id,
|
||||
guardian_code__icontains=str(guardian.guardian_code)).last()
|
||||
if junior_queryset:
|
||||
# use RemoveJuniorSerializer serializer
|
||||
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
|
||||
serializer = RemoveJuniorSerializer(junior_queryset, context={"guardian_code":guardian.guardian_code},
|
||||
data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
# save serializer
|
||||
serializer.save()
|
||||
send_notification.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
|
||||
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
|
||||
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_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
@ -264,45 +347,47 @@ class RemoveJuniorAPIView(views.APIView):
|
||||
|
||||
|
||||
class JuniorTaskListAPIView(viewsets.ModelViewSet):
|
||||
"""Update guardian profile"""
|
||||
"""Junior task list"""
|
||||
serializer_class = TaskDetailsjuniorSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filter_backends = (SearchFilter,)
|
||||
search_fields = ['task_name', ]
|
||||
pagination_class = PageNumberPagination
|
||||
queryset = JuniorTask.objects.all()
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = JuniorTask.objects.filter(junior__auth=self.request.user
|
||||
).select_related('junior', 'junior__auth'
|
||||
).order_by('due_date', 'created_at')
|
||||
|
||||
queryset = self.filter_queryset(queryset)
|
||||
return queryset
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Create guardian profile"""
|
||||
"""Junior task list
|
||||
status=0
|
||||
search='title'
|
||||
page=1"""
|
||||
try:
|
||||
status_value = self.request.GET.get('status')
|
||||
search = self.request.GET.get('search')
|
||||
if search and str(status_value) == '0':
|
||||
# search with title and for all task list
|
||||
queryset = JuniorTask.objects.filter(junior__auth=request.user,
|
||||
task_name__icontains=search).order_by('due_date', 'created_at')
|
||||
elif search and str(status_value) != '0':
|
||||
# search with title and fetch task list with status wise
|
||||
queryset = JuniorTask.objects.filter(junior__auth=request.user, task_name__icontains=search,
|
||||
task_status=status_value).order_by('due_date', 'created_at')
|
||||
if search is None and str(status_value) == '0':
|
||||
# fetch all task list
|
||||
queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at')
|
||||
elif search is None and str(status_value) != '0':
|
||||
# fetch task list with status wise
|
||||
queryset = JuniorTask.objects.filter(junior__auth=request.user,
|
||||
task_status=status_value).order_by('due_date','created_at')
|
||||
paginator = self.pagination_class()
|
||||
queryset = self.get_queryset()
|
||||
task_status = task_status_fun(status_value)
|
||||
if status_value:
|
||||
queryset = queryset.filter(task_status__in=task_status)
|
||||
paginator = CustomPageNumberPagination()
|
||||
# use Pagination
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
# use TaskDetailsSerializer serializer
|
||||
serializer = TaskDetailsjuniorSerializer(paginated_queryset, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
# use TaskDetails juniorSerializer serializer
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
return paginator.get_paginated_response(serializer.data)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class CompleteJuniorTaskAPIView(views.APIView):
|
||||
"""Update junior task API"""
|
||||
"""Payload
|
||||
task_id
|
||||
image"""
|
||||
serializer_class = CompleteTaskSerializer
|
||||
model = JuniorTask
|
||||
permission_classes = [IsAuthenticated]
|
||||
@ -320,10 +405,15 @@ class CompleteJuniorTaskAPIView(views.APIView):
|
||||
|
||||
image_url = upload_image_to_alibaba(image, filename)
|
||||
# fetch junior query
|
||||
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
|
||||
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user
|
||||
).select_related('guardian', 'junior').last()
|
||||
if task_queryset:
|
||||
if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
|
||||
return custom_error_response(ERROR_CODE['2085'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
elif task_queryset.junior.is_deleted or not task_queryset.junior.is_active:
|
||||
return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
# use CompleteTaskSerializer serializer
|
||||
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
|
||||
elif task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
|
||||
"""Already request send """
|
||||
return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
|
||||
@ -343,23 +433,20 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet):
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_queryset(self):
|
||||
"""get queryset"""
|
||||
return JuniorTask.objects.filter(junior__auth__email=self.request.user).last()
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""profile view"""
|
||||
|
||||
"""Junior Points
|
||||
No Params"""
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
# update_positions_based_on_points, function removed
|
||||
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
|
||||
# update position of junior
|
||||
update_positions_based_on_points()
|
||||
serializer = JuniorPointsSerializer(queryset)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ValidateReferralCode(viewsets.ViewSet):
|
||||
class ValidateReferralCode(viewsets.ModelViewSet):
|
||||
"""Check guardian code exist or not"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
@ -394,7 +481,13 @@ class InviteGuardianAPIView(viewsets.ModelViewSet):
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('post',)
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" junior list"""
|
||||
""" add guardian
|
||||
{
|
||||
"first_name":"abc",
|
||||
"last_name":"xyz",
|
||||
"email":"abc@yopmail.com",
|
||||
"relationship":2
|
||||
}"""
|
||||
try:
|
||||
if request.data['email'] == '':
|
||||
return custom_error_response(ERROR_CODE['2062'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
@ -412,7 +505,11 @@ class InviteGuardianAPIView(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class StartTaskAPIView(views.APIView):
|
||||
"""Update junior task API"""
|
||||
"""Update junior task API
|
||||
Paylod
|
||||
{
|
||||
"task_id":28
|
||||
}"""
|
||||
serializer_class = StartTaskSerializer
|
||||
model = JuniorTask
|
||||
permission_classes = [IsAuthenticated]
|
||||
@ -421,7 +518,10 @@ class StartTaskAPIView(views.APIView):
|
||||
try:
|
||||
task_id = self.request.data.get('task_id')
|
||||
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
|
||||
print("task_queryset==>",task_queryset)
|
||||
if task_queryset and task_queryset.task_status == str(NUMBER['one']):
|
||||
if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
|
||||
return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
# use StartTaskSerializer serializer
|
||||
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
@ -436,7 +536,13 @@ class StartTaskAPIView(views.APIView):
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ReAssignJuniorTaskAPIView(views.APIView):
|
||||
"""Update junior task API"""
|
||||
"""Update junior task API
|
||||
Payload
|
||||
{
|
||||
"task_id":34,
|
||||
"due_date":"2023-08-22"
|
||||
}
|
||||
"""
|
||||
serializer_class = ReAssignTaskSerializer
|
||||
model = JuniorTask
|
||||
permission_classes = [IsAuthenticated]
|
||||
@ -458,3 +564,258 @@ class ReAssignJuniorTaskAPIView(views.APIView):
|
||||
return custom_error_response(ERROR_CODE['2066'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class StartArticleAPIView(viewsets.ModelViewSet):
|
||||
"""Start article"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" Payload
|
||||
{
|
||||
"article_id":"2"
|
||||
}"""
|
||||
try:
|
||||
junior_instance = Junior.objects.filter(auth=self.request.user).last()
|
||||
article_id = request.data.get('article_id')
|
||||
article_data = Article.objects.filter(id=article_id).last()
|
||||
if not JuniorArticle.objects.filter(junior=junior_instance, article=article_data).last():
|
||||
JuniorArticle.objects.create(junior=junior_instance, article=article_data, status=str(NUMBER['two']),
|
||||
current_card_page=NUMBER['zero'], current_que_page=NUMBER['zero'])
|
||||
if article_data:
|
||||
question_query = ArticleSurvey.objects.filter(article=article_id)
|
||||
for question in question_query:
|
||||
if not JuniorArticlePoints.objects.filter(junior=junior_instance,
|
||||
article=article_data,
|
||||
question=question):
|
||||
JuniorArticlePoints.objects.create(junior=junior_instance,
|
||||
article=article_data,
|
||||
question=question)
|
||||
return custom_response(SUCCESS_CODE['3040'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class StartAssessmentAPIView(viewsets.ModelViewSet):
|
||||
"""Question answer viewset"""
|
||||
serializer_class = StartAssessmentSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_queryset(self):
|
||||
article_id = self.request.GET.get('article_id')
|
||||
# if referral_code:
|
||||
article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related(
|
||||
'article_survey'
|
||||
)
|
||||
return article
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Params
|
||||
article_id
|
||||
"""
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
paginator = self.pagination_class()
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
serializer = self.serializer_class(paginated_queryset, context={"user":request.user}, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class CheckAnswerAPIView(viewsets.ModelViewSet):
|
||||
"""Params
|
||||
question_id=1
|
||||
answer_id=1"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_queryset(self):
|
||||
question_id = self.request.GET.get('question_id')
|
||||
article = ArticleSurvey.objects.filter(id=question_id).last()
|
||||
return article
|
||||
def list(self, request, *args, **kwargs):
|
||||
""" Params
|
||||
question_id=1
|
||||
answer_id=1
|
||||
"""
|
||||
|
||||
try:
|
||||
answer_id = self.request.GET.get('answer_id')
|
||||
current_page = self.request.GET.get('current_page')
|
||||
queryset = self.get_queryset()
|
||||
submit_ans = SurveyOption.objects.filter(id=answer_id).last()
|
||||
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
|
||||
question=queryset)
|
||||
if submit_ans:
|
||||
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=
|
||||
F('total_points') + queryset.points)
|
||||
else:
|
||||
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, earn_points=0,
|
||||
is_answer_correct=False)
|
||||
JuniorArticle.objects.filter(junior__auth=self.request.user,
|
||||
article=queryset.article).update(
|
||||
current_que_page=int(current_page) + NUMBER['one'])
|
||||
return custom_response(None, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class CompleteArticleAPIView(views.APIView):
|
||||
"""Params
|
||||
article_id
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('put', 'get',)
|
||||
def put(self, request, format=None):
|
||||
try:
|
||||
article_id = self.request.data.get('article_id')
|
||||
JuniorArticle.objects.filter(junior__auth=request.user, article__id=article_id).update(
|
||||
is_completed=True, status=str(NUMBER['three'])
|
||||
)
|
||||
return custom_response(SUCCESS_CODE['3041'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
""" Params
|
||||
article_id=1"""
|
||||
try:
|
||||
article_id = self.request.GET.get('article_id')
|
||||
total_earn_points = JuniorArticlePoints.objects.filter(junior__auth=request.user,
|
||||
article__id=article_id,
|
||||
is_answer_correct=True).aggregate(
|
||||
total_earn_points=Sum('earn_points'))['total_earn_points']
|
||||
data = {"total_earn_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)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ReadArticleCardAPIView(views.APIView):
|
||||
"""Read article card API"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('put',)
|
||||
|
||||
def put(self, request, *args, **kwargs):
|
||||
""" Read article card
|
||||
Payload
|
||||
{"article_id":"1",
|
||||
"article_card":"2",
|
||||
"current_page":"2"}"""
|
||||
try:
|
||||
junior_instance = Junior.objects.filter(auth=self.request.user).last()
|
||||
article = self.request.data.get('article_id')
|
||||
article_card = self.request.data.get('article_card')
|
||||
current_page = self.request.data.get('current_page')
|
||||
JuniorArticleCard.objects.filter(junior=junior_instance,
|
||||
article__id=article,
|
||||
article_card__id=article_card).update(is_read=True)
|
||||
JuniorArticle.objects.filter(junior=junior_instance,
|
||||
article__id=article).update(current_card_page=int(current_page)+NUMBER['one'])
|
||||
return custom_response(SUCCESS_CODE['3043'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class CreateArticleCardAPIView(viewsets.ModelViewSet):
|
||||
"""Start article"""
|
||||
serializer_class = CreateArticleCardSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('post',)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" create article card
|
||||
Params
|
||||
{"article_id":1}"""
|
||||
try:
|
||||
junior_instance = Junior.objects.filter(auth=self.request.user).last()
|
||||
article_id = request.data.get('article_id')
|
||||
article_data = Article.objects.filter(id=article_id).last()
|
||||
if article_data:
|
||||
article_cards = ArticleCard.objects.filter(article=article_id)
|
||||
for article_card in article_cards:
|
||||
if not JuniorArticleCard.objects.filter(junior=junior_instance,
|
||||
article=article_data,
|
||||
article_card=article_card):
|
||||
JuniorArticleCard.objects.create(junior=junior_instance,
|
||||
article=article_data,
|
||||
article_card=article_card)
|
||||
return custom_response(None, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class RemoveGuardianCodeAPIView(views.APIView):
|
||||
"""Remove guardian code request API
|
||||
Payload
|
||||
{"guardian_code"
|
||||
:"GRD037"
|
||||
}"""
|
||||
serializer_class = RemoveGuardianCodeSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def put(self, request, format=None):
|
||||
try:
|
||||
guardian_code = self.request.data.get("guardian_code")
|
||||
guardian_data = Guardian.objects.filter(guardian_code=guardian_code).last()
|
||||
junior_queryset = Junior.objects.filter(auth=self.request.user).last()
|
||||
if junior_queryset:
|
||||
# use RemoveGuardianCodeSerializer serializer
|
||||
serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code},
|
||||
data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
# save serializer
|
||||
serializer.save()
|
||||
JuniorGuardianRelationship.objects.filter(guardian=guardian_data, junior=junior_queryset).delete()
|
||||
return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK)
|
||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
# task in another state
|
||||
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||
except Exception as e:
|
||||
error_detail = e.detail.get('error', None)
|
||||
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class FAQViewSet(GenericViewSet, mixins.CreateModelMixin,
|
||||
mixins.ListModelMixin):
|
||||
"""FAQ view set"""
|
||||
|
||||
serializer_class = FAQSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ['get', 'post']
|
||||
|
||||
def get_queryset(self):
|
||||
return FAQ.objects.all()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
faq create api method
|
||||
:param request:
|
||||
:param args: question, description
|
||||
:param kwargs:
|
||||
:return: success message
|
||||
"""
|
||||
obj_data = [FAQ(**item) for item in request.data]
|
||||
try:
|
||||
FAQ.objects.bulk_create(obj_data)
|
||||
return custom_response(SUCCESS_CODE["3045"], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""
|
||||
article list api method
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return: list of article
|
||||
"""
|
||||
queryset = self.get_queryset()
|
||||
paginator = self.pagination_class()
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
return custom_response(None, data=serializer.data, response_status=status.HTTP_200_OK)
|
||||
|
||||
|
@ -10,3 +10,4 @@ from notifications.models import Notification
|
||||
class NotificationAdmin(admin.ModelAdmin):
|
||||
"""Notification Admin"""
|
||||
list_display = ['id', 'notification_type', 'notification_to', 'data', 'is_read']
|
||||
list_filter = ['notification_type']
|
||||
|
@ -1,72 +1,129 @@
|
||||
"""
|
||||
notification constants file
|
||||
"""
|
||||
from base.constants import NUMBER
|
||||
REGISTRATION = NUMBER['one']
|
||||
TASK_CREATED = NUMBER['two']
|
||||
INVITED_GUARDIAN = NUMBER['three']
|
||||
APPROVED_JUNIOR = NUMBER['four']
|
||||
REFERRAL_POINTS = NUMBER['five']
|
||||
TASK_POINTS = NUMBER['six']
|
||||
TASK_REJECTED = NUMBER['seven']
|
||||
SKIPPED_PROFILE_SETUP = NUMBER['eight']
|
||||
TASK_SUBMITTED = NUMBER['nine']
|
||||
TASK_ACTION = NUMBER['ten']
|
||||
LEADERBOARD_RANKING = NUMBER['eleven']
|
||||
REMOVE_JUNIOR = NUMBER['twelve']
|
||||
TEST_NOTIFICATION = 99
|
||||
REGISTRATION = 1
|
||||
ASSOCIATE_REQUEST = 3
|
||||
ASSOCIATE_REJECTED = 4
|
||||
ASSOCIATE_APPROVED = 5
|
||||
REFERRAL_POINTS = 6
|
||||
ASSOCIATE_JUNIOR = 7
|
||||
ASSOCIATE_EXISTING_JUNIOR = 8
|
||||
|
||||
TASK_ASSIGNED = 9
|
||||
TASK_ACTION = 10
|
||||
TASK_REJECTED = 11
|
||||
TASK_APPROVED = 12
|
||||
PENDING_TASK_EXPIRING = 13
|
||||
IN_PROGRESS_TASK_EXPIRING = 14
|
||||
TOP_JUNIOR = 15
|
||||
|
||||
NEW_ARTICLE_PUBLISHED = 16
|
||||
ARTICLE_REWARD_POINTS = 17
|
||||
REMOVE_JUNIOR = 18
|
||||
|
||||
TEST_NOTIFICATION = 99
|
||||
# notification dictionary
|
||||
NOTIFICATION_DICT = {
|
||||
REGISTRATION: {
|
||||
"title": "Successfully registered!",
|
||||
"body": "You have registered successfully. Now login and complete your profile."
|
||||
},
|
||||
TASK_CREATED: {
|
||||
"title": "Task created!",
|
||||
"body": "Task created successfully."
|
||||
# user will receive notification as soon junior
|
||||
# sign up application using their guardian code for association
|
||||
ASSOCIATE_REQUEST: {
|
||||
"title": "Associate request!",
|
||||
"body": "You have request from {from_user} to associate with you."
|
||||
},
|
||||
INVITED_GUARDIAN: {
|
||||
"title": "Invite guardian",
|
||||
"body": "Invite guardian successfully"
|
||||
# Juniors will receive notification when
|
||||
# custodians reject their request for associate
|
||||
ASSOCIATE_REJECTED: {
|
||||
"title": "Associate request rejected!",
|
||||
"body": "Your request to associate has been rejected by {from_user}."
|
||||
},
|
||||
APPROVED_JUNIOR: {
|
||||
"title": "Approve junior",
|
||||
"body": "You have request from junior to associate with you"
|
||||
# Juniors will receive notification when
|
||||
# custodians approve their request for associate
|
||||
ASSOCIATE_APPROVED: {
|
||||
"title": "Associate request approved!",
|
||||
"body": "Your request to associate has been approved by {from_user}."
|
||||
},
|
||||
# Juniors will receive Notifications
|
||||
# for every Points earned by referrals
|
||||
REFERRAL_POINTS: {
|
||||
"title": "Earn Referral points",
|
||||
"title": "Earn Referral points!",
|
||||
"body": "You earn 5 points for referral."
|
||||
},
|
||||
TASK_POINTS: {
|
||||
"title": "Earn Task points!",
|
||||
"body": "You earn 5 points for task."
|
||||
# Juniors will receive notification
|
||||
# once any custodians add them in their account
|
||||
ASSOCIATE_JUNIOR: {
|
||||
"title": "Profile already setup!",
|
||||
"body": "Your guardian has already setup your profile."
|
||||
},
|
||||
TASK_REJECTED: {
|
||||
"title": "Task rejected!",
|
||||
"body": "Your task has been rejected."
|
||||
ASSOCIATE_EXISTING_JUNIOR: {
|
||||
"title": "Associated to guardian",
|
||||
"body": "Your are associated to your guardian {from_user}."
|
||||
},
|
||||
SKIPPED_PROFILE_SETUP: {
|
||||
"title": "Skipped profile setup!",
|
||||
"body": "Your guardian has been setup your profile."
|
||||
},
|
||||
TASK_SUBMITTED: {
|
||||
"title": "Task submitted!",
|
||||
"body": "Your task has been submitted successfully."
|
||||
# Juniors will receive Notification
|
||||
# for every Task Assign by Custodians
|
||||
TASK_ASSIGNED: {
|
||||
"title": "New task assigned!",
|
||||
"body": "{from_user} has assigned you a new task."
|
||||
},
|
||||
# Guardian will receive notification as soon
|
||||
# as junior send task for approval
|
||||
TASK_ACTION: {
|
||||
"title": "Task approval!",
|
||||
"body": "You have request for task approval."
|
||||
"title": "Task completion approval!",
|
||||
"body": "{from_user} completed their task {task_name}."
|
||||
},
|
||||
LEADERBOARD_RANKING: {
|
||||
"title": "Leader board rank!",
|
||||
"body": "Your rank is ."
|
||||
# Juniors will receive notification as soon
|
||||
# as their task is rejected by custodians
|
||||
TASK_REJECTED: {
|
||||
"title": "Task completion rejected!",
|
||||
"body": "Your task completion request has been rejected by {from_user}."
|
||||
},
|
||||
# Juniors will receive notification as soon as their task is approved by custodians
|
||||
# and for every Points earned by Task completion
|
||||
TASK_APPROVED: {
|
||||
"title": "Task completion approved!",
|
||||
"body": "Your task completion request has been approved by {from_user}. "
|
||||
"Also you earned 5 points for successful completion."
|
||||
},
|
||||
# Juniors will receive notification when their task end date about to end
|
||||
PENDING_TASK_EXPIRING: {
|
||||
"title": "Task expiring soon!",
|
||||
"body": "Your task {task_name} is expiring soon. Please complete it."
|
||||
},
|
||||
# User will receive notification when their assigned task is about to end
|
||||
# and juniors have not performed any action
|
||||
IN_PROGRESS_TASK_EXPIRING: {
|
||||
"title": "Task expiring soon!",
|
||||
"body": "{from_user} didn't take any action on assigned task {task_name} and it's expiring soon. "
|
||||
"Please assist to complete it."
|
||||
},
|
||||
# Juniors will receive Notification
|
||||
# related to Leaderboard progress
|
||||
TOP_JUNIOR: {
|
||||
"title": "Leaderboard topper!",
|
||||
"body": "{from_user} is on top in leaderboard with {points} points."
|
||||
},
|
||||
# Juniors will receive notification
|
||||
# when admin add any new financial learnings
|
||||
NEW_ARTICLE_PUBLISHED: {
|
||||
"title": "Time to read!",
|
||||
"body": "A new article has been published."
|
||||
},
|
||||
# Juniors will receive notification when they earn points by reading financial Learning
|
||||
ARTICLE_REWARD_POINTS: {
|
||||
"title": "Article reward points!",
|
||||
"body": "You are rewarded with {points} points for reading article and answering questions. "
|
||||
},
|
||||
# Juniors will receive notification as soon as their custodians remove them from account
|
||||
REMOVE_JUNIOR: {
|
||||
"title": "Disassociate by guardian!",
|
||||
"body": "Your guardian disassociate you ."
|
||||
"body": "Your guardian has disassociated you."
|
||||
},
|
||||
# Test notification
|
||||
TEST_NOTIFICATION: {
|
||||
"title": "Test Notification",
|
||||
"body": "This notification is for testing purpose"
|
||||
"body": "This notification is for testing purpose from {from_user}."
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,22 @@ class RegisterDevice(serializers.Serializer):
|
||||
|
||||
class NotificationListSerializer(serializers.ModelSerializer):
|
||||
"""List of notification"""
|
||||
badge = serializers.SerializerMethodField()
|
||||
|
||||
class Meta(object):
|
||||
"""meta info"""
|
||||
model = Notification
|
||||
fields = ['id', 'data', 'is_read']
|
||||
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):
|
||||
"""User task Serializer"""
|
||||
id = serializers.ListSerializer(child=serializers.IntegerField())
|
||||
|
||||
class Meta(object):
|
||||
"""Meta class"""
|
||||
model = Notification
|
||||
|
@ -6,7 +6,7 @@ from django.urls import path, include
|
||||
from rest_framework import routers
|
||||
|
||||
# local imports
|
||||
from notifications.views import NotificationViewSet, ReadNotification
|
||||
from notifications.views import NotificationViewSet
|
||||
|
||||
# initiate router
|
||||
router = routers.SimpleRouter()
|
||||
@ -15,5 +15,4 @@ router.register('notifications', NotificationViewSet, basename='notifications')
|
||||
|
||||
urlpatterns = [
|
||||
path('api/v1/', include(router.urls)),
|
||||
path('api/v1/read-notification/', ReadNotification.as_view()),
|
||||
]
|
||||
|
@ -8,21 +8,24 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati
|
||||
|
||||
# django imports
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from account.models import UserNotification
|
||||
from notifications.constants import NOTIFICATION_DICT
|
||||
from notifications.models import Notification
|
||||
from django.db.models import Q
|
||||
|
||||
# local imports
|
||||
|
||||
from account.models import UserNotification
|
||||
from account.utils import get_user_full_name
|
||||
from base.constants import GUARDIAN, JUNIOR
|
||||
from guardian.models import Guardian, JuniorTask
|
||||
from junior.models import Junior
|
||||
from notifications.constants import NOTIFICATION_DICT
|
||||
from notifications.models import Notification
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
def register_fcm_token(user_id, registration_id, device_id, device_type):
|
||||
""" used to register the fcm device token"""
|
||||
device, _ = FCMDevice.objects.update_or_create(device_id=device_id,
|
||||
defaults={'user_id': user_id, 'type': device_type,
|
||||
device, _ = FCMDevice.objects.update_or_create(user_id=user_id,
|
||||
defaults={'device_id': device_id, 'type': device_type,
|
||||
'active': True,
|
||||
'registration_id': registration_id})
|
||||
return device
|
||||
@ -39,30 +42,140 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
|
||||
print(e)
|
||||
|
||||
|
||||
def get_basic_detail(notification_type, from_user_id, to_user_id):
|
||||
""" used to get the basic details """
|
||||
notification_data = NOTIFICATION_DICT[notification_type]
|
||||
from_user = User.objects.get(id=from_user_id) if from_user_id else None
|
||||
to_user = User.objects.get(id=to_user_id)
|
||||
return notification_data, from_user, to_user
|
||||
def get_from_user_details(from_user_id, from_user_type):
|
||||
"""
|
||||
used to get from user details
|
||||
"""
|
||||
from_user = None
|
||||
from_user_name = None
|
||||
from_user_image = None
|
||||
if from_user_id:
|
||||
if from_user_type == GUARDIAN:
|
||||
guardian = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
|
||||
from_user = guardian.user
|
||||
from_user_name = get_user_full_name(from_user)
|
||||
from_user_image = guardian.image
|
||||
elif from_user_type == JUNIOR:
|
||||
junior = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
|
||||
from_user = junior.auth
|
||||
from_user_name = get_user_full_name(from_user)
|
||||
from_user_image = junior.image
|
||||
return from_user_name, from_user_image, from_user
|
||||
|
||||
|
||||
def get_notification_data(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
|
||||
"""
|
||||
get notification and push data
|
||||
:param from_user_type: GUARDIAN or JUNIOR
|
||||
:param notification_type: notification_type
|
||||
:param from_user_id: from_user obj
|
||||
:param to_user_id: to_user obj
|
||||
:param extra_data: any extra data provided
|
||||
:return: notification and push data
|
||||
"""
|
||||
push_data = NOTIFICATION_DICT[notification_type].copy()
|
||||
notification_data = push_data.copy()
|
||||
task_name = None
|
||||
points = extra_data.get('points', None)
|
||||
if 'task_id' in extra_data:
|
||||
task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first()
|
||||
task_name = task.task_name
|
||||
extra_data['task_name'] = task_name
|
||||
extra_data['task_image'] = task.image if task.image else task.default_image
|
||||
|
||||
from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type)
|
||||
|
||||
push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name, points=points)
|
||||
notification_data['body'] = notification_data['body'].format(from_user=from_user_name,
|
||||
task_name=task_name, points=points)
|
||||
notification_data['from_user'] = from_user_name
|
||||
notification_data['from_user_image'] = from_user_image
|
||||
|
||||
notification_data.update(extra_data)
|
||||
to_user = User.objects.filter(id=to_user_id).first()
|
||||
return notification_data, push_data, from_user, to_user
|
||||
|
||||
|
||||
@shared_task()
|
||||
def send_notification(notification_type, from_user_id, to_user_id, extra_data):
|
||||
""" used to send the push for the given notification type """
|
||||
(notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id)
|
||||
def send_notification(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
|
||||
"""
|
||||
used to send the push for the given notification type
|
||||
"""
|
||||
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
|
||||
from_user_type, to_user_id, extra_data)
|
||||
user_notification_type = UserNotification.objects.filter(user=to_user).first()
|
||||
data = notification_data
|
||||
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_to=to_user, data=data)
|
||||
if user_notification_type.push_notification:
|
||||
data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
|
||||
send_push(to_user, data)
|
||||
notification_to=to_user, data=notification_data)
|
||||
if user_notification_type and user_notification_type.push_notification:
|
||||
send_push(to_user, push_data)
|
||||
|
||||
|
||||
def send_push(user, data):
|
||||
""" used to send push notification to specific user """
|
||||
notification_data = data.pop('data', None)
|
||||
user.fcmdevice_set.filter(active=True).send_message(
|
||||
Message(notification=FirebaseNotification(data['title'], data['body']), data=notification_data)
|
||||
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
|
||||
)
|
||||
|
||||
|
||||
def send_multiple_push(queryset, data):
|
||||
""" used to send same notification to multiple users """
|
||||
FCMDevice.objects.filter(user__in=queryset, active=True).send_message(
|
||||
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
|
||||
)
|
||||
|
||||
|
||||
@shared_task()
|
||||
def send_notification_multiple_user(notification_type, from_user_id, from_user_type,
|
||||
extra_data: dict = {}):
|
||||
"""
|
||||
used to send notification to multiple user for the given notification type
|
||||
"""
|
||||
to_user_list = User.objects.filter(junior_profile__is_verified=True, is_superuser=False
|
||||
).exclude(junior_profile__isnull=True, guardian_profile__isnull=True)
|
||||
|
||||
notification_data, push_data, from_user, _ = get_notification_data(notification_type, from_user_id,
|
||||
from_user_type, None, extra_data)
|
||||
|
||||
notification_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_to=user,
|
||||
notification_from=from_user,
|
||||
data=notification_copy_data))
|
||||
Notification.objects.bulk_create(notification_list)
|
||||
to_user_list = to_user_list.filter(user_notification__push_notification=True)
|
||||
send_multiple_push(to_user_list, push_data)
|
||||
|
||||
|
||||
@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)
|
||||
|
@ -4,12 +4,15 @@ notifications views file
|
||||
# django imports
|
||||
from django.db.models import Q
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import viewsets, status, views
|
||||
|
||||
# local imports
|
||||
from account.utils import custom_response, custom_error_response
|
||||
from base.messages import SUCCESS_CODE, ERROR_CODE
|
||||
from base.pagination import CustomPageNumberPagination
|
||||
from base.tasks import notify_task_expiry, notify_top_junior
|
||||
from notifications.constants import TEST_NOTIFICATION
|
||||
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
|
||||
from notifications.utils import send_notification
|
||||
@ -17,25 +20,24 @@ from notifications.models import Notification
|
||||
|
||||
|
||||
class NotificationViewSet(viewsets.GenericViewSet):
|
||||
""" used to do the notification actions """
|
||||
"""
|
||||
used to do the notification actions
|
||||
"""
|
||||
serializer_class = NotificationListSerializer
|
||||
permission_classes = [IsAuthenticated, ]
|
||||
|
||||
def list(self, request, *args, **kwargs) -> Response:
|
||||
""" list the notifications """
|
||||
"""
|
||||
to list user's notifications
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
|
||||
).select_related('notification_to').order_by('-id')
|
||||
paginator = self.pagination_class()
|
||||
paginator = CustomPageNumberPagination()
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
self.mark_notifications_as_read(serializer.data)
|
||||
return custom_response(None, serializer.data)
|
||||
|
||||
@staticmethod
|
||||
def mark_notifications_as_read(data):
|
||||
""" used to mark notification queryset as read """
|
||||
ids = [obj['id'] for obj in data]
|
||||
Notification.objects.filter(id__in=ids).update(is_read=True)
|
||||
return paginator.get_paginated_response(serializer.data)
|
||||
|
||||
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
|
||||
def fcm_registration(self, request):
|
||||
@ -51,38 +53,20 @@ class NotificationViewSet(viewsets.GenericViewSet):
|
||||
@action(methods=['get'], detail=False, url_path='test', url_name='test')
|
||||
def send_test_notification(self, request):
|
||||
"""
|
||||
to send test notification
|
||||
to test send notification, task expiry, top junior
|
||||
:return:
|
||||
"""
|
||||
send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {})
|
||||
notify_task_expiry()
|
||||
notify_top_junior()
|
||||
send_notification(TEST_NOTIFICATION, None, None, request.auth.payload['user_id'],
|
||||
{})
|
||||
return custom_response(SUCCESS_CODE["3000"])
|
||||
|
||||
@action(methods=['get'], detail=False, url_path='list', url_name='list',
|
||||
serializer_class=NotificationListSerializer)
|
||||
def notification_list(self, request):
|
||||
@action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False,
|
||||
serializer_class=ReadNotificationSerializer)
|
||||
def mark_as_read(self, request, *args, **kwargs):
|
||||
"""
|
||||
notification list
|
||||
"""
|
||||
try:
|
||||
queryset = Notification.objects.filter(notification_to=request.user)
|
||||
serializer = NotificationListSerializer(queryset, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ReadNotification(views.APIView):
|
||||
"""Update notification API"""
|
||||
serializer_class = ReadNotificationSerializer
|
||||
model = Notification
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def put(self, request, format=None):
|
||||
try:
|
||||
notification_id = self.request.data.get('notification_id')
|
||||
notification_queryset = Notification.objects.filter(id__in=notification_id,
|
||||
notification_to=self.request.user).update(is_read=True)
|
||||
if notification_queryset:
|
||||
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
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)
|
||||
|
@ -99,3 +99,6 @@ uritemplate==4.1.1
|
||||
urllib3==1.26.16
|
||||
vine==5.0.0
|
||||
wcwidth==0.2.6
|
||||
|
||||
pandas==2.0.3
|
||||
XlsxWriter==3.1.2
|
@ -49,7 +49,7 @@ class ArticleSurvey(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
"""Return title"""
|
||||
return f'{self.id} | {self.article}'
|
||||
return f'{self.id} | {self.question}'
|
||||
|
||||
|
||||
class SurveyOption(models.Model):
|
||||
@ -64,7 +64,7 @@ class SurveyOption(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
"""Return title"""
|
||||
return f'{self.id} | {self.survey}'
|
||||
return f'{self.id} | {self.option}'
|
||||
|
||||
|
||||
class DefaultArticleCardImage(models.Model):
|
||||
|
150
web_admin/serializers/analytics_serializer.py
Normal file
150
web_admin/serializers/analytics_serializer.py
Normal file
@ -0,0 +1,150 @@
|
||||
"""
|
||||
web_admin analytics serializer file
|
||||
"""
|
||||
# third party imports
|
||||
from rest_framework import serializers
|
||||
|
||||
# django imports
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from account.utils import get_user_full_name
|
||||
# local imports
|
||||
from base.constants import USER_TYPE, JUNIOR
|
||||
|
||||
from junior.models import JuniorPoints, Junior
|
||||
|
||||
USER = get_user_model()
|
||||
|
||||
|
||||
class JuniorLeaderboardSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
junior leaderboard serializer
|
||||
"""
|
||||
name = serializers.SerializerMethodField()
|
||||
first_name = serializers.SerializerMethodField()
|
||||
last_name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = Junior
|
||||
fields = ('id', 'name', 'first_name', 'last_name', 'is_active', 'image', 'is_deleted')
|
||||
|
||||
@staticmethod
|
||||
def get_name(obj):
|
||||
"""
|
||||
:param obj: junior object
|
||||
:return: full name
|
||||
"""
|
||||
return get_user_full_name(obj.auth)
|
||||
|
||||
@staticmethod
|
||||
def get_first_name(obj):
|
||||
"""
|
||||
:param obj: junior object
|
||||
:return: first name
|
||||
"""
|
||||
return obj.auth.first_name
|
||||
|
||||
@staticmethod
|
||||
def get_last_name(obj):
|
||||
"""
|
||||
:param obj: junior object
|
||||
:return: last name
|
||||
"""
|
||||
return obj.auth.last_name
|
||||
|
||||
|
||||
class LeaderboardSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
leaderboard serializer
|
||||
"""
|
||||
user_id = serializers.SerializerMethodField()
|
||||
user_type = serializers.SerializerMethodField()
|
||||
junior = JuniorLeaderboardSerializer()
|
||||
rank = serializers.IntegerField()
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = JuniorPoints
|
||||
fields = ('user_id', 'user_type', 'total_points', 'rank', 'junior')
|
||||
|
||||
@staticmethod
|
||||
def get_user_id(obj):
|
||||
return obj.junior.auth.id
|
||||
|
||||
@staticmethod
|
||||
def get_user_type(obj):
|
||||
return JUNIOR
|
||||
|
||||
|
||||
class UserCSVReportSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
user csv/xls report serializer
|
||||
"""
|
||||
name = serializers.SerializerMethodField()
|
||||
phone_number = serializers.SerializerMethodField()
|
||||
user_type = serializers.SerializerMethodField()
|
||||
is_active = serializers.SerializerMethodField()
|
||||
date_joined = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = USER
|
||||
fields = ('name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
|
||||
|
||||
@staticmethod
|
||||
def get_name(obj):
|
||||
"""
|
||||
:param obj: user object
|
||||
:return: full name
|
||||
"""
|
||||
return get_user_full_name(obj)
|
||||
|
||||
@staticmethod
|
||||
def get_phone_number(obj):
|
||||
"""
|
||||
:param obj: user object
|
||||
:return: user phone number
|
||||
"""
|
||||
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
|
||||
return f"+{profile.country_code}{profile.phone}" \
|
||||
if profile.country_code and profile.phone else profile.phone
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_user_type(obj):
|
||||
"""
|
||||
:param obj: user object
|
||||
:return: user type
|
||||
"""
|
||||
if obj.guardian_profile.all().first():
|
||||
return dict(USER_TYPE).get('2').capitalize()
|
||||
elif obj.junior_profile.all().first():
|
||||
return dict(USER_TYPE).get('1').capitalize()
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_is_active(obj):
|
||||
"""
|
||||
:param obj: user object
|
||||
:return: user type
|
||||
"""
|
||||
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
|
||||
return "Active" if profile.is_active else "Inactive"
|
||||
|
||||
@staticmethod
|
||||
def get_date_joined(obj):
|
||||
"""
|
||||
:param obj: user obj
|
||||
:return: formatted date
|
||||
"""
|
||||
date = obj.date_joined.strftime("%d %b %Y")
|
||||
return date
|
@ -10,8 +10,11 @@ from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE
|
||||
# local imports
|
||||
from base.messages import ERROR_CODE
|
||||
from guardian.utils import upload_image_to_alibaba
|
||||
from notifications.constants import NEW_ARTICLE_PUBLISHED
|
||||
from notifications.utils import send_notification_multiple_user
|
||||
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
|
||||
from web_admin.utils import pop_id, get_image_url
|
||||
from junior.models import JuniorArticlePoints, JuniorArticle
|
||||
|
||||
USER = get_user_model()
|
||||
|
||||
@ -79,7 +82,7 @@ class ArticleSerializer(serializers.ModelSerializer):
|
||||
meta class
|
||||
"""
|
||||
model = Article
|
||||
fields = ('id', 'title', 'description', 'article_cards', 'article_survey')
|
||||
fields = ('id', 'title', 'description', 'is_published', 'article_cards', 'article_survey')
|
||||
|
||||
def validate(self, attrs):
|
||||
"""
|
||||
@ -88,10 +91,9 @@ class ArticleSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
article_cards = attrs.get('article_cards', 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']})
|
||||
if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int(
|
||||
MAX_ARTICLE_SURVEY) < len(article_survey):
|
||||
if not int(MIN_ARTICLE_SURVEY) <= len(article_survey) <= int(MAX_ARTICLE_SURVEY):
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
|
||||
return attrs
|
||||
|
||||
@ -119,11 +121,15 @@ class ArticleSerializer(serializers.ModelSerializer):
|
||||
option = pop_id(option)
|
||||
SurveyOption.objects.create(survey=survey_obj, **option)
|
||||
|
||||
# All juniors will receive notification when admin add any new financial learnings/article
|
||||
send_notification_multiple_user.delay(NEW_ARTICLE_PUBLISHED, None, None, {})
|
||||
|
||||
return article
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
to update article and related table
|
||||
:param validated_data:
|
||||
:param instance: article object,
|
||||
:return: article object
|
||||
"""
|
||||
@ -179,6 +185,28 @@ class ArticleSerializer(serializers.ModelSerializer):
|
||||
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):
|
||||
"""
|
||||
Article Card serializer
|
||||
@ -215,21 +243,127 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
|
||||
card_image = DefaultArticleCardImage.objects.create(**validated_data)
|
||||
return card_image
|
||||
|
||||
|
||||
class ArticleListSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
serializer for article API
|
||||
"""
|
||||
article_cards = ArticleCardSerializer(many=True)
|
||||
image = serializers.SerializerMethodField('get_image')
|
||||
total_points = serializers.SerializerMethodField('get_total_points')
|
||||
is_completed = serializers.SerializerMethodField('get_is_completed')
|
||||
|
||||
class Meta:
|
||||
class Meta(object):
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = Article
|
||||
fields = ('id', 'title', 'description', 'article_cards', 'total_points')
|
||||
fields = ('id', 'title', 'description', 'image', 'total_points', 'is_completed')
|
||||
|
||||
def get_image(self, obj):
|
||||
"""article image"""
|
||||
if obj.article_cards.first():
|
||||
return obj.article_cards.first().image_url
|
||||
return None
|
||||
|
||||
def get_total_points(self, obj):
|
||||
"""total points of article"""
|
||||
total_question = ArticleSurvey.objects.filter(article=obj).count()
|
||||
return total_question * 5
|
||||
return obj.article_survey.all().count() * NUMBER['five']
|
||||
|
||||
def get_is_completed(self, obj):
|
||||
"""complete all question"""
|
||||
context_data = self.context.get('user')
|
||||
junior_article = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
|
||||
if junior_article:
|
||||
return junior_article.is_completed
|
||||
return False
|
||||
|
||||
|
||||
class ArticleQuestionSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
article survey serializer
|
||||
"""
|
||||
id = serializers.IntegerField(required=False)
|
||||
options = SurveyOptionSerializer(many=True)
|
||||
is_attempt = serializers.SerializerMethodField('get_is_attempt')
|
||||
correct_answer = serializers.SerializerMethodField('get_correct_answer')
|
||||
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
|
||||
|
||||
def get_is_attempt(self, obj):
|
||||
"""attempt question or not"""
|
||||
context_data = self.context.get('user')
|
||||
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, question=obj).last()
|
||||
if junior_article_obj:
|
||||
return junior_article_obj.is_attempt
|
||||
return False
|
||||
|
||||
def get_correct_answer(self, obj):
|
||||
"""attempt question or not"""
|
||||
ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last()
|
||||
if ans_obj:
|
||||
return ans_obj.id
|
||||
return None
|
||||
|
||||
def get_attempted_answer(self, obj):
|
||||
"""attempt question or not"""
|
||||
context_data = self.context.get('user')
|
||||
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data,
|
||||
question=obj).last()
|
||||
if junior_article_obj and junior_article_obj.submitted_answer:
|
||||
return junior_article_obj.submitted_answer.id
|
||||
return None
|
||||
|
||||
class Meta(object):
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = ArticleSurvey
|
||||
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
|
||||
|
||||
|
||||
class StartAssessmentSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
serializer for article API
|
||||
"""
|
||||
article_survey = ArticleQuestionSerializer(many=True)
|
||||
current_page = serializers.SerializerMethodField('get_current_page')
|
||||
|
||||
def get_current_page(self, obj):
|
||||
"""current page"""
|
||||
context_data = self.context.get('user')
|
||||
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
|
||||
total_count = obj.article_survey.all().count()
|
||||
if data:
|
||||
return data.current_que_page if data.current_que_page < total_count else data.current_que_page - 1
|
||||
return NUMBER['zero']
|
||||
|
||||
class Meta(object):
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = Article
|
||||
fields = ('article_survey', 'current_page')
|
||||
|
||||
|
||||
class ArticleCardlistSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Article Card serializer
|
||||
"""
|
||||
id = serializers.IntegerField(required=False)
|
||||
image_name = serializers.CharField(required=False)
|
||||
image_url = serializers.CharField(required=False)
|
||||
current_page = serializers.SerializerMethodField('get_current_page')
|
||||
|
||||
def get_current_page(self, obj):
|
||||
"""current page"""
|
||||
context_data = self.context.get('user')
|
||||
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj.article).last()
|
||||
if data:
|
||||
return data.current_card_page
|
||||
return NUMBER['zero']
|
||||
|
||||
class Meta(object):
|
||||
"""
|
||||
meta class
|
||||
"""
|
||||
model = ArticleCard
|
||||
fields = ('id', 'title', 'description', 'image_name', 'image_url', 'current_page')
|
||||
|
@ -14,7 +14,7 @@ from account.models import UserEmailOtp
|
||||
from base.constants import USER_TYPE
|
||||
from base.messages import ERROR_CODE
|
||||
from guardian.tasks import generate_otp
|
||||
from base.tasks import send_email_otp
|
||||
from base.tasks import send_email
|
||||
|
||||
USER = get_user_model()
|
||||
|
||||
@ -48,11 +48,13 @@ class AdminOTPSerializer(serializers.ModelSerializer):
|
||||
:return: user_data
|
||||
"""
|
||||
email = validated_data['email']
|
||||
|
||||
verification_code = generate_otp()
|
||||
|
||||
template = 'email_reset_verification.email'
|
||||
# Send the verification code to the user's email
|
||||
send_email_otp.delay(email, verification_code)
|
||||
data = {
|
||||
"verification_code": verification_code
|
||||
}
|
||||
send_email.delay([email], template, data)
|
||||
|
||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||
user_data, created = UserEmailOtp.objects.update_or_create(email=email,
|
||||
@ -87,9 +89,9 @@ class AdminVerifyOTPSerializer(serializers.Serializer):
|
||||
# fetch email otp object of the user
|
||||
user_otp_details = UserEmailOtp.objects.filter(email=email, otp=otp).last()
|
||||
if not user_otp_details:
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2064']})
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2008']})
|
||||
if user_otp_details.user_type != dict(USER_TYPE).get('3'):
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2063']})
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2008']})
|
||||
if user_otp_details.expired_at.replace(tzinfo=None) < datetime.utcnow():
|
||||
raise serializers.ValidationError({'details': ERROR_CODE['2029']})
|
||||
user_otp_details.is_verified = True
|
||||
|
@ -5,7 +5,8 @@ web_admin user_management serializers file
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from base.constants import USER_TYPE
|
||||
from account.utils import get_user_full_name
|
||||
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
|
||||
# local imports
|
||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||
from guardian.models import Guardian
|
||||
@ -37,7 +38,7 @@ class UserManagementListSerializer(serializers.ModelSerializer):
|
||||
:param obj: user object
|
||||
:return: full name
|
||||
"""
|
||||
return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name
|
||||
return get_user_full_name(obj)
|
||||
|
||||
@staticmethod
|
||||
def get_country_code(obj):
|
||||
@ -108,7 +109,7 @@ class GuardianSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
model = Guardian
|
||||
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
|
||||
'is_active', 'country_name', 'image', 'email')
|
||||
'is_active', 'country_name', 'image', 'email', 'is_deleted')
|
||||
|
||||
def validate(self, attrs):
|
||||
"""
|
||||
@ -144,7 +145,7 @@ class GuardianSerializer(serializers.ModelSerializer):
|
||||
:param obj: guardian object
|
||||
:return: full name
|
||||
"""
|
||||
return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name
|
||||
return get_user_full_name(obj.user)
|
||||
|
||||
@staticmethod
|
||||
def get_first_name(obj):
|
||||
@ -187,7 +188,7 @@ class JuniorSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
model = Junior
|
||||
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
|
||||
'is_active', 'country_name', 'image', 'email')
|
||||
'is_active', 'country_name', 'image', 'email', 'is_deleted')
|
||||
|
||||
def validate(self, attrs):
|
||||
"""
|
||||
@ -210,10 +211,10 @@ class JuniorSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
instance.auth.email = self.validated_data.get('email', instance.auth.email)
|
||||
instance.auth.username = self.validated_data.get('email', instance.auth.username)
|
||||
instance.auth.save()
|
||||
instance.auth.save(update_fields=['email', 'username'])
|
||||
instance.country_code = validated_data.get('country_code', instance.country_code)
|
||||
instance.phone = validated_data.get('phone', instance.phone)
|
||||
instance.save()
|
||||
instance.save(update_fields=['country_code', 'phone'])
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
@ -222,7 +223,7 @@ class JuniorSerializer(serializers.ModelSerializer):
|
||||
:param obj: junior object
|
||||
:return: full name
|
||||
"""
|
||||
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
|
||||
return get_user_full_name(obj.auth)
|
||||
|
||||
@staticmethod
|
||||
def get_first_name(obj):
|
||||
@ -265,33 +266,30 @@ class UserManagementDetailSerializer(serializers.ModelSerializer):
|
||||
model = USER
|
||||
fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users')
|
||||
|
||||
@staticmethod
|
||||
def get_user_type(obj):
|
||||
def get_user_type(self, obj):
|
||||
"""
|
||||
:param obj: user object
|
||||
:return: user type
|
||||
"""
|
||||
if obj.guardian_profile.all().first():
|
||||
return dict(USER_TYPE).get('2')
|
||||
elif obj.junior_profile.all().first():
|
||||
return dict(USER_TYPE).get('1')
|
||||
else:
|
||||
return None
|
||||
return GUARDIAN if self.context['user_type'] == GUARDIAN else JUNIOR
|
||||
|
||||
@staticmethod
|
||||
def get_associated_users(obj):
|
||||
def get_associated_users(self, obj):
|
||||
"""
|
||||
:param obj: user object
|
||||
:return: associated user
|
||||
"""
|
||||
if profile := obj.guardian_profile.all().first():
|
||||
if self.context['user_type'] == GUARDIAN:
|
||||
profile = obj.guardian_profile.all().only('user_id', 'guardian_code').first()
|
||||
if profile.guardian_code:
|
||||
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True)
|
||||
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code],
|
||||
is_verified=True).select_related('auth')
|
||||
serializer = JuniorSerializer(junior, many=True)
|
||||
return serializer.data
|
||||
elif profile := obj.junior_profile.all().first():
|
||||
elif self.context['user_type'] == JUNIOR:
|
||||
profile = obj.junior_profile.all().only('auth_id', 'guardian_code').first()
|
||||
if profile.guardian_code:
|
||||
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True)
|
||||
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code,
|
||||
is_verified=True).select_related('user')
|
||||
serializer = GuardianSerializer(guardian, many=True)
|
||||
return serializer.data
|
||||
else:
|
||||
|
@ -6,7 +6,9 @@ from django.urls import path, include
|
||||
from rest_framework import routers
|
||||
|
||||
# local imports
|
||||
from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet
|
||||
from web_admin.views.analytics import AnalyticsViewSet
|
||||
from web_admin.views.article import (ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet,
|
||||
ArticleCardListViewSet)
|
||||
from web_admin.views.auth import ForgotAndResetPasswordViewSet
|
||||
from web_admin.views.user_management import UserManagementViewSet
|
||||
|
||||
@ -16,7 +18,11 @@ router = routers.SimpleRouter()
|
||||
router.register('article', ArticleViewSet, basename='article')
|
||||
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
|
||||
router.register('user-management', UserManagementViewSet, basename='user')
|
||||
router.register('analytics', AnalyticsViewSet, basename='user-analytics')
|
||||
|
||||
router.register('article-list', ArticleListViewSet, basename='article-list')
|
||||
router.register('article-card-list', ArticleCardListViewSet, basename='article-card-list')
|
||||
|
||||
# forgot and reset password api for admin
|
||||
router.register('admin', ForgotAndResetPasswordViewSet, basename='admin')
|
||||
|
||||
|
@ -2,9 +2,10 @@
|
||||
web_utils file
|
||||
"""
|
||||
import base64
|
||||
import datetime
|
||||
|
||||
from base.constants import ARTICLE_CARD_IMAGE_FOLDER
|
||||
from guardian.utils import upload_image_to_alibaba
|
||||
from base.constants import ARTICLE_CARD_IMAGE_FOLDER, DATE_FORMAT
|
||||
from guardian.utils import upload_image_to_alibaba, upload_base64_image_to_alibaba
|
||||
|
||||
|
||||
def pop_id(data):
|
||||
@ -29,10 +30,10 @@ def get_image_url(data):
|
||||
return data['image_url']
|
||||
elif 'image_url' in data and type(data['image_url']) == str and data['image_url'].startswith('data:image'):
|
||||
base64_image = base64.b64decode(data.get('image_url').split(',')[1])
|
||||
image_name = f"{data['title']} | {data.pop('image_name')}" if 'image_name' in data else data['title']
|
||||
image_name = data.pop('image_name') if 'image_name' in data else f"{data['title']}.jpg"
|
||||
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}"
|
||||
# upload image on ali baba
|
||||
image_url = upload_image_to_alibaba(base64_image, filename)
|
||||
image_url = upload_base64_image_to_alibaba(base64_image, filename)
|
||||
return image_url
|
||||
elif 'image' in data and data['image'] is not None:
|
||||
image = data.pop('image')
|
||||
@ -40,3 +41,21 @@ def get_image_url(data):
|
||||
# upload image on ali baba
|
||||
image_url = upload_image_to_alibaba(image, filename)
|
||||
return image_url
|
||||
|
||||
|
||||
def get_dates(start_date, end_date):
|
||||
"""
|
||||
to get start and end date
|
||||
:param start_date: format (yyyy-mm-dd)
|
||||
:param end_date: format (yyyy-mm-dd)
|
||||
:return: start and end date
|
||||
"""
|
||||
|
||||
if start_date and end_date:
|
||||
start_date = datetime.datetime.strptime(start_date, DATE_FORMAT).date()
|
||||
end_date = datetime.datetime.strptime(end_date, DATE_FORMAT).date()
|
||||
else:
|
||||
end_date = datetime.date.today()
|
||||
start_date = end_date - datetime.timedelta(days=6)
|
||||
|
||||
return start_date, end_date
|
||||
|
247
web_admin/views/analytics.py
Normal file
247
web_admin/views/analytics.py
Normal file
@ -0,0 +1,247 @@
|
||||
"""
|
||||
web_admin analytics view file
|
||||
"""
|
||||
# python imports
|
||||
import datetime
|
||||
import io
|
||||
import pandas as pd
|
||||
import xlsxwriter
|
||||
|
||||
# third party imports
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
# django imports
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db.models import Q
|
||||
from django.db.models import Count
|
||||
from django.db.models.functions import TruncDate
|
||||
from django.db.models import F, Window
|
||||
from django.db.models.functions.window import Rank
|
||||
from django.http import HttpResponse
|
||||
|
||||
# local imports
|
||||
from account.utils import custom_response, get_user_full_name
|
||||
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, TASK_STATUS
|
||||
from guardian.models import JuniorTask
|
||||
from guardian.utils import upload_excel_file_to_alibaba
|
||||
from junior.models import JuniorPoints
|
||||
from base.pagination import CustomPageNumberPagination
|
||||
from web_admin.permission import AdminPermission
|
||||
from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer
|
||||
from web_admin.utils import get_dates
|
||||
|
||||
USER = get_user_model()
|
||||
|
||||
|
||||
class AnalyticsViewSet(GenericViewSet):
|
||||
"""
|
||||
analytics api view
|
||||
to get user report (active users, guardians and juniors counts)
|
||||
to get new user sign up report
|
||||
to get task report (completed, in-progress, requested and rejected tasks count)
|
||||
to get junior leaderboard and ranking
|
||||
"""
|
||||
serializer_class = None
|
||||
permission_classes = [IsAuthenticated, AdminPermission]
|
||||
|
||||
def get_queryset(self):
|
||||
user_qs = USER.objects.filter(
|
||||
(Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)),
|
||||
is_superuser=False
|
||||
).prefetch_related('guardian_profile',
|
||||
'junior_profile'
|
||||
).exclude(junior_profile__isnull=True,
|
||||
guardian_profile__isnull=True).order_by('-date_joined')
|
||||
return user_qs
|
||||
|
||||
@action(methods=['get'], url_name='users-count', url_path='users-count', detail=False)
|
||||
def total_users_count(self, request, *args, **kwargs):
|
||||
"""
|
||||
api method to get total users, guardians and juniors
|
||||
:param request: start_date: date format (yyyy-mm-dd)
|
||||
:param request: end_date: date format (yyyy-mm-dd)
|
||||
:return:
|
||||
"""
|
||||
start_date, end_date = get_dates(request.query_params.get('start_date'),
|
||||
request.query_params.get('end_date'))
|
||||
|
||||
user_qs = self.get_queryset()
|
||||
queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1))))
|
||||
|
||||
data = {'total_users': queryset.count(),
|
||||
'total_guardians': queryset.filter(junior_profile__isnull=True).count(),
|
||||
'total_juniors': queryset.filter(guardian_profile__isnull=True).count()}
|
||||
|
||||
return custom_response(None, data)
|
||||
|
||||
@action(methods=['get'], url_name='new-signups', url_path='new-signups', detail=False)
|
||||
def new_signups(self, request, *args, **kwargs):
|
||||
"""
|
||||
api method to get new signups
|
||||
:param request: start_date: date format (yyyy-mm-dd)
|
||||
:param request: end_date: date format (yyyy-mm-dd)
|
||||
:return:
|
||||
"""
|
||||
start_date, end_date = get_dates(request.query_params.get('start_date'),
|
||||
request.query_params.get('end_date'))
|
||||
|
||||
user_qs = self.get_queryset()
|
||||
signup_data = user_qs.filter(date_joined__range=[start_date, (end_date + datetime.timedelta(days=1))]
|
||||
).annotate(date=TruncDate('date_joined')
|
||||
).values('date').annotate(signups=Count('id')).order_by('date')
|
||||
|
||||
return custom_response(None, signup_data)
|
||||
|
||||
@action(methods=['get'], url_name='assign-tasks', url_path='assign-tasks', detail=False)
|
||||
def assign_tasks_report(self, request, *args, **kwargs):
|
||||
"""
|
||||
api method to get assign tasks count for (completed, in-progress, requested and rejected) task
|
||||
:param request: start_date: date format (yyyy-mm-dd)
|
||||
:param request: end_date: date format (yyyy-mm-dd)
|
||||
:return:
|
||||
"""
|
||||
start_date, end_date = get_dates(request.query_params.get('start_date'),
|
||||
request.query_params.get('end_date'))
|
||||
|
||||
assign_tasks = JuniorTask.objects.filter(
|
||||
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
|
||||
)
|
||||
|
||||
data = {
|
||||
'task_completed': assign_tasks.filter(task_status=COMPLETED).count(),
|
||||
'task_pending': assign_tasks.filter(task_status=PENDING).count(),
|
||||
'task_in_progress': assign_tasks.filter(task_status=IN_PROGRESS).count(),
|
||||
'task_requested': assign_tasks.filter(task_status=REQUESTED).count(),
|
||||
'task_rejected': assign_tasks.filter(task_status=REJECTED).count(),
|
||||
'task_expired': assign_tasks.filter(task_status=EXPIRED).count(),
|
||||
}
|
||||
|
||||
return custom_response(None, data)
|
||||
|
||||
@action(methods=['get'], url_name='junior-leaderboard', url_path='junior-leaderboard', detail=False,
|
||||
serializer_class=LeaderboardSerializer)
|
||||
def junior_leaderboard(self, request):
|
||||
"""
|
||||
to get junior leaderboard and rank
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
queryset = JuniorPoints.objects.filter(
|
||||
junior__is_verified=True
|
||||
).select_related('junior', 'junior__auth').annotate(rank=Window(
|
||||
expression=Rank(),
|
||||
order_by=[F('total_points').desc(), 'junior__created_at']
|
||||
)).order_by('-total_points', 'junior__created_at')
|
||||
paginator = CustomPageNumberPagination()
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
return custom_response(None, serializer.data)
|
||||
|
||||
@action(methods=['get'], url_name='export-excel', url_path='export-excel', detail=False)
|
||||
def export_excel(self, request):
|
||||
"""
|
||||
to export users count, task details and top juniors in csv/excel file
|
||||
:param request: start_date: date format (yyyy-mm-dd)
|
||||
:param request: end_date: date format (yyyy-mm-dd)
|
||||
:return:
|
||||
"""
|
||||
|
||||
response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||
response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.xlsx"'
|
||||
|
||||
start_date, end_date = get_dates(request.query_params.get('start_date'),
|
||||
request.query_params.get('end_date'))
|
||||
|
||||
# Use BytesIO for binary data
|
||||
buffer = io.BytesIO()
|
||||
|
||||
# Create an XlsxWriter Workbook object
|
||||
workbook = xlsxwriter.Workbook(buffer)
|
||||
|
||||
# Add sheets
|
||||
sheets = ['Users', 'Assign Tasks', 'Juniors Leaderboard']
|
||||
|
||||
for sheet_name in sheets:
|
||||
worksheet = workbook.add_worksheet(name=sheet_name)
|
||||
|
||||
# sheet 1 for Total Users
|
||||
if sheet_name == 'Users':
|
||||
user_qs = self.get_queryset()
|
||||
queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1))))
|
||||
serializer = UserCSVReportSerializer(queryset, many=True)
|
||||
|
||||
df_users = pd.DataFrame([
|
||||
{'Name': user['name'], 'Email': user['email'],
|
||||
'Phone Number': user['phone_number'], 'User Type': user['user_type'],
|
||||
'Status': user['is_active'], 'Date Joined': user['date_joined']}
|
||||
for user in serializer.data
|
||||
])
|
||||
write_excel_worksheet(worksheet, df_users)
|
||||
|
||||
# sheet 2 for Assign Task
|
||||
elif sheet_name == 'Assign Tasks':
|
||||
assign_tasks = JuniorTask.objects.filter(
|
||||
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
|
||||
).select_related('junior__auth', 'guardian__user').only('task_name', 'task_status',
|
||||
'junior__auth__first_name',
|
||||
'junior__auth__last_name',
|
||||
'guardian__user__first_name',
|
||||
'guardian__user__last_name',)
|
||||
|
||||
df_tasks = pd.DataFrame([
|
||||
{'Task Name': task.task_name, 'Assign To': get_user_full_name(task.junior.auth),
|
||||
'Assign By': get_user_full_name(task.guardian.user),
|
||||
'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()}
|
||||
for task in assign_tasks
|
||||
])
|
||||
|
||||
write_excel_worksheet(worksheet, df_tasks)
|
||||
|
||||
# sheet 3 for Juniors Leaderboard and rank
|
||||
elif sheet_name == 'Juniors Leaderboard':
|
||||
queryset = JuniorPoints.objects.filter(
|
||||
junior__is_verified=True
|
||||
).select_related('junior', 'junior__auth').annotate(rank=Window(
|
||||
expression=Rank(),
|
||||
order_by=[F('total_points').desc(), 'junior__created_at']
|
||||
)).order_by('-total_points', 'junior__created_at')[:15]
|
||||
df_leaderboard = pd.DataFrame([
|
||||
{
|
||||
'Name': get_user_full_name(junior.junior.auth),
|
||||
'Points': junior.total_points,
|
||||
'Rank': junior.rank
|
||||
}
|
||||
for junior in queryset
|
||||
])
|
||||
|
||||
write_excel_worksheet(worksheet, df_leaderboard)
|
||||
|
||||
# Close the workbook to save the content
|
||||
workbook.close()
|
||||
|
||||
# Reset the buffer position and write the content to the response
|
||||
buffer.seek(0)
|
||||
response.write(buffer.getvalue())
|
||||
buffer.close()
|
||||
|
||||
filename = f"{'analytics'}/{'ZOD_Bank_Analytics.xlsx'}"
|
||||
file_link = upload_excel_file_to_alibaba(response, filename)
|
||||
return custom_response(None, file_link)
|
||||
|
||||
|
||||
def write_excel_worksheet(worksheet, dataframe):
|
||||
"""
|
||||
to perform write action on worksheets
|
||||
:param worksheet:
|
||||
:param dataframe:
|
||||
:return: worksheet
|
||||
"""
|
||||
for idx, col in enumerate(dataframe.columns):
|
||||
# Write header
|
||||
worksheet.write(0, idx, col)
|
||||
for row_num, row in enumerate(dataframe.values, start=1):
|
||||
for col_num, value in enumerate(row):
|
||||
worksheet.write(row_num, col_num, value)
|
||||
return worksheet
|
@ -4,7 +4,7 @@ web_admin views file
|
||||
# django imports
|
||||
from rest_framework.viewsets import GenericViewSet, mixins
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework import status
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||
from django.contrib.auth import get_user_model
|
||||
@ -16,7 +16,8 @@ from base.messages import SUCCESS_CODE, ERROR_CODE
|
||||
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
|
||||
from web_admin.permission import AdminPermission
|
||||
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
|
||||
DefaultArticleCardImageSerializer, ArticleListSerializer)
|
||||
DefaultArticleCardImageSerializer, ArticleListSerializer,
|
||||
ArticleCardlistSerializer, ArticleStatusChangeSerializer)
|
||||
|
||||
USER = get_user_model()
|
||||
|
||||
@ -31,7 +32,6 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
queryset = Article
|
||||
filter_backends = (SearchFilter,)
|
||||
search_fields = ['title']
|
||||
http_method_names = ['get', 'post', 'put', 'delete']
|
||||
|
||||
def get_queryset(self):
|
||||
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
|
||||
@ -43,9 +43,20 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
article create api method
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:param request: { "title": "string", "description": "string",
|
||||
"article_cards": [
|
||||
{ "title": "string",
|
||||
"description": "string",
|
||||
"image_name": "string",
|
||||
"image_url": "string"
|
||||
} ],
|
||||
"article_survey": [
|
||||
{ "question": "string",
|
||||
"options": [
|
||||
{ "option": "string",
|
||||
"is_answer": true }
|
||||
] }
|
||||
] }
|
||||
:return: success message
|
||||
"""
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
@ -56,9 +67,24 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
def update(self, request, *args, **kwargs):
|
||||
"""
|
||||
article update api method
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:param request: article_id,
|
||||
{ "title": "string", "description": "string",
|
||||
"article_cards": [
|
||||
{ "id": 0,
|
||||
"title": "string",
|
||||
"description": "string",
|
||||
"image_name": "string",
|
||||
"image_url": "string"
|
||||
} ],
|
||||
"article_survey": [
|
||||
{ "id": 0,
|
||||
"question": "string",
|
||||
"options": [
|
||||
{ "id": 0,
|
||||
"option": "string",
|
||||
"is_answer": true
|
||||
} ]
|
||||
} ] }
|
||||
:return: success message
|
||||
"""
|
||||
article = self.get_object()
|
||||
@ -71,8 +97,6 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
"""
|
||||
article list api method
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return: list of article
|
||||
"""
|
||||
queryset = self.get_queryset()
|
||||
@ -85,9 +109,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
"""
|
||||
article detail api method
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:param request: article_id
|
||||
:return: article detail data
|
||||
"""
|
||||
queryset = self.get_object()
|
||||
@ -97,9 +119,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
"""
|
||||
article delete (soft delete) api method
|
||||
:param request:
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:param request: article_id
|
||||
:return: success message
|
||||
"""
|
||||
article = self.get_object()
|
||||
@ -109,6 +129,23 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
|
||||
return custom_response(SUCCESS_CODE["3029"])
|
||||
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@action(methods=['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',
|
||||
detail=True)
|
||||
def remove_article_card(self, request, *args, **kwargs):
|
||||
@ -176,7 +213,10 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
api method to upload default article card images
|
||||
:param request:
|
||||
:param request: {
|
||||
"image_name": "string",
|
||||
"image": "image_file"
|
||||
}
|
||||
:return: success message
|
||||
"""
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
@ -190,7 +230,7 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
|
||||
:param request:
|
||||
:return: default article card images
|
||||
"""
|
||||
queryset = self.queryset
|
||||
queryset = self.get_queryset()
|
||||
serializer = self.serializer_class(queryset, many=True)
|
||||
return custom_response(None, data=serializer.data)
|
||||
|
||||
@ -205,10 +245,7 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
|
||||
http_method_names = ['get',]
|
||||
|
||||
def get_queryset(self):
|
||||
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
|
||||
'article_cards', 'article_survey', 'article_survey__options'
|
||||
).order_by('-created_at')
|
||||
queryset = self.filter_queryset(article)
|
||||
queryset = self.queryset.objects.filter(is_deleted=False, is_published=True).order_by('-created_at')
|
||||
return queryset
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
@ -220,8 +257,29 @@ class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
|
||||
:return: list of article
|
||||
"""
|
||||
queryset = self.get_queryset()
|
||||
count = queryset.count()
|
||||
paginator = self.pagination_class()
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
return custom_response(None, data=serializer.data, count=count)
|
||||
serializer = self.serializer_class(queryset, context={"user": request.user}, many=True)
|
||||
return custom_response(None, data=serializer.data)
|
||||
|
||||
class ArticleCardListViewSet(viewsets.ModelViewSet):
|
||||
"""Article card list
|
||||
use below query param
|
||||
article_id"""
|
||||
serializer_class = ArticleCardlistSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
http_method_names = ('get',)
|
||||
|
||||
def get_queryset(self):
|
||||
"""get queryset"""
|
||||
return ArticleCard.objects.filter(article=self.request.GET.get('article_id'))
|
||||
def list(self, request, *args, **kwargs):
|
||||
"""Article card list
|
||||
use below query param
|
||||
article_id"""
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
# article card list
|
||||
serializer = ArticleCardlistSerializer(queryset, context={"user": self.request.user}, many=True)
|
||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||
except Exception as e:
|
||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||
|
@ -27,6 +27,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet):
|
||||
def admin_otp(self, request):
|
||||
"""
|
||||
api method to send otp
|
||||
:param request: {"email": "string"}
|
||||
:return: success message
|
||||
"""
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
@ -40,6 +41,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet):
|
||||
def admin_verify_otp(self, request):
|
||||
"""
|
||||
api method to verify otp
|
||||
:param request: {"email": "string", "otp": "otp"}
|
||||
:return: success message
|
||||
"""
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
@ -52,6 +54,7 @@ class ForgotAndResetPasswordViewSet(GenericViewSet):
|
||||
def admin_create_password(self, request):
|
||||
"""
|
||||
api method to create new password
|
||||
:param request: {"email": "string", "new_password": "string", "confirm_password": "string"}
|
||||
:return: success message
|
||||
"""
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
|
@ -12,8 +12,11 @@ from django.db.models import Q
|
||||
|
||||
# local imports
|
||||
from account.utils import custom_response, custom_error_response
|
||||
from base.constants import USER_TYPE
|
||||
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
|
||||
from base.messages import SUCCESS_CODE, ERROR_CODE
|
||||
from base.tasks import send_email
|
||||
from guardian.models import Guardian
|
||||
from junior.models import Junior
|
||||
from web_admin.permission import AdminPermission
|
||||
from web_admin.serializers.user_management_serializer import (UserManagementListSerializer,
|
||||
UserManagementDetailSerializer, GuardianSerializer,
|
||||
@ -29,11 +32,12 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
|
||||
"""
|
||||
serializer_class = UserManagementListSerializer
|
||||
permission_classes = [IsAuthenticated, AdminPermission]
|
||||
queryset = USER.objects.filter(is_superuser=False
|
||||
).prefetch_related('guardian_profile',
|
||||
'junior_profile'
|
||||
).exclude(junior_profile__isnull=True,
|
||||
guardian_profile__isnull=True).order_by('date_joined')
|
||||
queryset = USER.objects.filter(
|
||||
(Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)),
|
||||
is_superuser=False).prefetch_related('guardian_profile',
|
||||
'junior_profile'
|
||||
).exclude(junior_profile__isnull=True,
|
||||
guardian_profile__isnull=True).order_by('-date_joined')
|
||||
filter_backends = (SearchFilter,)
|
||||
search_fields = ['first_name', 'last_name']
|
||||
http_method_names = ['get', 'post', 'patch']
|
||||
@ -55,8 +59,6 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
|
||||
:return:
|
||||
"""
|
||||
queryset = self.get_queryset()
|
||||
queryset = queryset.filter(
|
||||
(Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)))
|
||||
paginator = self.pagination_class()
|
||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||
@ -71,12 +73,14 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
|
||||
"""
|
||||
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
|
||||
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
queryset = self.queryset
|
||||
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
|
||||
queryset = queryset.filter(guardian_profile__user__id=kwargs['pk'])
|
||||
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
|
||||
queryset = queryset.filter(junior_profile__auth__id=kwargs['pk'])
|
||||
serializer = UserManagementDetailSerializer(queryset, many=True)
|
||||
queryset = queryset.filter(id=kwargs['pk'])
|
||||
|
||||
serializer = UserManagementDetailSerializer(
|
||||
queryset, many=True,
|
||||
context={'user_type': self.request.query_params.get('user_type')}
|
||||
)
|
||||
return custom_response(None, data=serializer.data)
|
||||
|
||||
def partial_update(self, request, *args, **kwargs):
|
||||
@ -88,15 +92,16 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
|
||||
"""
|
||||
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
|
||||
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
|
||||
queryset = self.queryset
|
||||
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
|
||||
user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first()
|
||||
serializer = GuardianSerializer(user_obj.guardian_profile.all().first(),
|
||||
guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True
|
||||
).select_related('user').first()
|
||||
serializer = GuardianSerializer(guardian,
|
||||
request.data, context={'user_id': kwargs['pk']})
|
||||
|
||||
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
|
||||
user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first()
|
||||
serializer = JuniorSerializer(user_obj.junior_profile.all().first(),
|
||||
junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True
|
||||
).select_related('auth').first()
|
||||
serializer = JuniorSerializer(junior,
|
||||
request.data, context={'user_id': kwargs['pk']})
|
||||
|
||||
serializer.is_valid(raise_exception=True)
|
||||
@ -111,17 +116,21 @@ class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
|
||||
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
|
||||
:return: success message
|
||||
"""
|
||||
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
|
||||
user_type = self.request.query_params.get('user_type')
|
||||
if user_type not in [GUARDIAN, JUNIOR]:
|
||||
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
|
||||
queryset = self.queryset
|
||||
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
|
||||
user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first()
|
||||
obj = user_obj.guardian_profile.all().first()
|
||||
obj.is_active = False if obj.is_active else True
|
||||
obj.save()
|
||||
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
|
||||
user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first()
|
||||
obj = user_obj.junior_profile.all().first()
|
||||
obj.is_active = False if obj.is_active else True
|
||||
obj.save()
|
||||
|
||||
email_template = 'user_deactivate.email'
|
||||
|
||||
if user_type == GUARDIAN:
|
||||
obj = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).select_related('user').first()
|
||||
elif user_type == JUNIOR:
|
||||
obj = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first()
|
||||
|
||||
if obj.is_active:
|
||||
obj.is_active = False
|
||||
send_email([obj.user.email if user_type == GUARDIAN else obj.auth.email], email_template)
|
||||
else:
|
||||
obj.is_active = True
|
||||
obj.save()
|
||||
return custom_response(SUCCESS_CODE['3038'])
|
||||
|
@ -27,19 +27,24 @@ app.config_from_object('django.conf:settings')
|
||||
# Load task modules from all registered Django apps.
|
||||
app.autodiscover_tasks()
|
||||
|
||||
# scheduled task
|
||||
app.conf.beat_schedule = {
|
||||
"expired_task": {
|
||||
"task": "guardian.utils.update_expired_task_status",
|
||||
"schedule": crontab(minute=0, hour=0),
|
||||
},
|
||||
'notify_task_expiry': {
|
||||
'task': 'base.tasks.notify_task_expiry',
|
||||
'schedule': crontab(minute='0', hour='18'),
|
||||
},
|
||||
'notify_top_junior': {
|
||||
'task': 'base.tasks.notify_top_junior',
|
||||
'schedule': crontab(minute='0', hour='*/2'),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.task(bind=True)
|
||||
def debug_task(self):
|
||||
""" celery debug task """
|
||||
print(f'Request: {self.request!r}')
|
||||
|
||||
|
||||
"""cron task"""
|
||||
|
||||
|
||||
app.conf.beat_schedule = {
|
||||
"expired_task": {
|
||||
"task": "guardian.utils.update_expired_task_status",
|
||||
"schedule": crontab(minute=0, hour=0),
|
||||
},
|
||||
}
|
||||
|
@ -35,9 +35,28 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SECRET_KEY = os.getenv('SECRET_KEY')
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = os.getenv('DEBUG')
|
||||
ENV = os.getenv('ENV')
|
||||
|
||||
# cors allow setting
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
CORS_ORIGIN_ALLOW_ALL = False
|
||||
|
||||
# Allow specific origins
|
||||
if ENV in ['dev', 'qa', 'stage']:
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
# backend base url
|
||||
"https://dev-api.zodqaapp.com",
|
||||
"https://qa-api.zodqaapp.com",
|
||||
"https://stage-api.zodqaapp.com",
|
||||
|
||||
# frontend url
|
||||
"http://localhost:3000",
|
||||
"https://zod-dev.zodqaapp.com",
|
||||
"https://zod-qa.zodqaapp.com",
|
||||
"https://zod-stage.zodqaapp.com",
|
||||
# Add more trusted origins as needed
|
||||
]
|
||||
if ENV == "prod":
|
||||
CORS_ALLOWED_ORIGINS = []
|
||||
|
||||
# allow all host
|
||||
ALLOWED_HOSTS = ['*']
|
||||
@ -53,7 +72,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# Add Django rest frame work apps here
|
||||
# Add Django rest framework apps here
|
||||
'django_extensions',
|
||||
'storages',
|
||||
'drf_yasg',
|
||||
@ -125,6 +144,7 @@ SIMPLE_JWT = {
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
DATABASES = {
|
||||
# default db setting
|
||||
'default': {
|
||||
'ENGINE': 'django.contrib.gis.db.backends.postgis',
|
||||
'NAME':os.getenv('DB_NAME'),
|
||||
@ -177,6 +197,35 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
# database query logs settings
|
||||
# Allows us to check db hits
|
||||
# useful to optimize db query and hit
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"filters": {
|
||||
"require_debug_true": {
|
||||
"()": "django.utils.log.RequireDebugTrue"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"level": "DEBUG",
|
||||
"filters": [
|
||||
"require_debug_true"
|
||||
],
|
||||
"class": "logging.StreamHandler"
|
||||
}
|
||||
},
|
||||
# database logger
|
||||
"loggers": {
|
||||
"django.db.backends": {
|
||||
"level": "DEBUG",
|
||||
"handlers": [
|
||||
"console"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
||||
@ -217,6 +266,7 @@ CORS_ALLOW_HEADERS = (
|
||||
'x-requested-with',
|
||||
)
|
||||
|
||||
# CORS header settings
|
||||
CORS_EXPOSE_HEADERS = (
|
||||
'Access-Control-Allow-Origin: *',
|
||||
)
|
||||
@ -272,5 +322,7 @@ STATIC_URL = 'static/'
|
||||
# define static root
|
||||
STATIC_ROOT = 'static'
|
||||
|
||||
# media url
|
||||
MEDIA_URL = "/media/"
|
||||
# media path
|
||||
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media')
|
||||
|
@ -20,12 +20,11 @@ from django.urls import path, include
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
from django.urls import path
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
schema_view = get_schema_view(openapi.Info(title="Zod Bank API", default_version='v1'), public=True, )
|
||||
|
||||
urlpatterns = [
|
||||
path('apidoc/', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'),
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include(('account.urls', 'account'), namespace='account')),
|
||||
path('', include('guardian.urls')),
|
||||
@ -33,3 +32,6 @@ urlpatterns = [
|
||||
path('', include(('notifications.urls', 'notifications'), namespace='notifications')),
|
||||
path('', include(('web_admin.urls', 'web_admin'), namespace='web_admin')),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += [(path('apidoc/', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'))]
|
||||
|
Reference in New Issue
Block a user