Merge pull request #251 from KiwiTechLLC/dev

Dev
This commit is contained in:
Ankitajain-kiwi
2023-08-23 12:57:22 +05:30
committed by GitHub
15 changed files with 119 additions and 103 deletions

View File

@ -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"""

View File

@ -18,9 +18,9 @@ from guardian.models import Guardian
# user can login in single
# device at a time"""
def custom_response(custom_error):
def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND):
"""custom response"""
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND)
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
@ -41,6 +41,7 @@ class CustomMiddleware(object):
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
if request.user.is_authenticated:
# device details
@ -59,5 +60,9 @@ class CustomMiddleware(object):
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error)
force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last()
api_endpoint_checks = not any(endpoint in api_endpoint for endpoint in ['/admin/', '/api/v1/admin/'])
if not force_update and version and device_type:
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

View File

@ -2,6 +2,7 @@
"""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, DEVICE_TYPE
# Create your models here.

View File

@ -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
@ -390,3 +390,11 @@ 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')

View File

@ -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()
@ -55,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)),

View File

@ -204,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}, status=status.HTTP_400_BAD_REQUEST)
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):
"""

View File

@ -4,6 +4,7 @@ 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
@ -26,14 +27,15 @@ from django.conf import settings
from guardian.models import Guardian
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, USER_TYPE_FLAG
@ -524,17 +526,18 @@ class ProfileAPIViewSet(viewsets.ModelViewSet):
"""profile view
Params
user_type"""
if str(self.request.GET.get('user_type')) == '1':
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"""
serializer_class = DefaultTaskImagesSerializer
@ -673,3 +676,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)

View File

@ -105,6 +105,8 @@ ERROR_CODE = {
"2076": "This junior already associate with you",
"2077": "You can not add guardian",
"2078": "This junior is not associate with you",
"2079": "Please update your app version for enjoying uninterrupted services",
"2080": "Can not add App version"
}
"""Success message code"""
@ -171,7 +173,8 @@ SUCCESS_CODE = {
# remove guardian code request
"3044": "Remove guardian code request successfully",
# create faq
"3045": "Create FAQ data"
"3045": "Create FAQ data",
"3046": "Add App version successfully"
}
"""status code error"""

View File

@ -28,7 +28,7 @@ from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
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
# send notification function
from notifications.utils import send_notification, send_notification_to_junior
from django.core.exceptions import ValidationError
@ -420,7 +420,7 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update complete time of task
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_POINTS, None, junior_details.auth.id,
send_notification_to_junior.delay(TASK_APPROVED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
else:
# reject the task
@ -429,7 +429,7 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update reject time of task
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_REJECTED, None, junior_details.auth.id,
send_notification_to_junior.delay(TASK_REJECTED, instance.guardian.user.id, junior_details.auth.id,
{'task_id': instance.id})
instance.save()
junior_data.save()

View File

@ -38,7 +38,7 @@ from account.utils import custom_response, custom_error_response, OTP_EXPIRY, se
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, GUARDIAN_CODE_STATUS
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_ASSIGNED, LEADERBOARD_RANKING
from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED
from notifications.utils import send_notification_to_junior
""" Define APIs """
@ -178,7 +178,7 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
junior = request.data['junior']
junior_id = Junior.objects.filter(id=junior).last()
guardian_data = Guardian.objects.filter(user=request.user).last()
if guardian_data.guardian_code in junior_id.guardian_code:
if guardian_data.guardian_code not in junior_id.guardian_code:
return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST)
allowed_extensions = ['.jpg', '.jpeg', '.png']
if not any(extension in str(image) for extension in allowed_extensions):
@ -259,7 +259,6 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
# send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save()
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
@ -293,11 +292,13 @@ class ApproveJuniorAPIView(viewsets.ModelViewSet):
if serializer.is_valid():
# save serializer
serializer.save()
send_notification_to_junior.delay(ASSOCIATE_APPROVED, guardian.user.id, junior_queryset.auth.id)
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
junior_queryset.guardian_code = None
junior_queryset.guardian_code_status = str(NUMBER['one'])
junior_queryset.save()
send_notification_to_junior.delay(ASSOCIATE_REJECTED, guardian.user.id, 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)

View File

@ -22,8 +22,8 @@ from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian
from notifications.constants import (INVITATION, ASSOCIATE_REQUEST, SKIPPED_PROFILE_SETUP, TASK_ACTION,
TASK_SUBMITTED)
from notifications.constants import (ASSOCIATE_REQUEST, JUNIOR_ADDED, TASK_ACTION,
)
from web_admin.models import ArticleCard
class ListCharField(serializers.ListField):
@ -293,7 +293,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True,
guardian_code_status=GUARDIAN_CODE_STATUS[1][0])
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
total_junior = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
@ -307,7 +307,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
"""Notification email"""
junior_notification_email.delay(email, full_name, email, password)
# push notification
send_notification_to_junior.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
send_notification_to_junior.delay(JUNIOR_ADDED, None, junior_data.auth.id, {})
return junior_data
@ -339,8 +339,6 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification_to_junior.delay(TASK_SUBMITTED, instance.guardian.user.id,
instance.junior.auth.id, {'task_id': instance.id})
send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id,
instance.guardian.user.id, {'task_id': instance.id})
return instance
@ -446,13 +444,12 @@ class AddGuardianSerializer(serializers.ModelSerializer):
user_type=str(NUMBER['two']), expired_at=expiry_time,
is_verified=True)
UserNotification.objects.get_or_create(user=user)
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail.delay(email, full_name)
send_notification_to_junior.delay(INVITATION, guardian_data.user.id, junior_data.auth.id, {})
send_notification_to_guardian.delay(ASSOCIATE_REQUEST, junior_data.auth.id, guardian_data.user.id, {})
return guardian_data

View File

@ -206,7 +206,7 @@ class AddJuniorAPIView(viewsets.ModelViewSet):
guardian = Guardian.objects.filter(user=self.request.user).first()
if not junior:
return none
if guardian.guardian_code in junior.guardian_code:
if junior.guardian_code and (guardian.guardian_code in junior.guardian_code):
return False
if type(junior.guardian_code) is list:
junior.guardian_code.append(guardian.guardian_code)
@ -781,6 +781,3 @@ class FAQViewSet(GenericViewSet, mixins.CreateModelMixin,
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, data=serializer.data, response_status=status.HTTP_200_OK)

View File

@ -2,19 +2,17 @@
notification constants file
"""
REGISTRATION = 1
INVITATION = 2
ASSOCIATE_REQUEST = 3
REFERRAL_POINTS = 4
SKIPPED_PROFILE_SETUP = 5
ASSOCIATE_REJECTED = 4
ASSOCIATE_APPROVED = 5
REFERRAL_POINTS = 6
JUNIOR_ADDED = 7
TASK_ASSIGNED = 6
TASK_SUBMITTED = 7
TASK_ACTION = 8
TASK_REJECTED = 9
TASK_APPROVED = 10
TASK_POINTS = 11
TASK_ASSIGNED = 8
TASK_ACTION = 9
TASK_REJECTED = 10
TASK_APPROVED = 11
LEADERBOARD_RANKING = 12
REMOVE_JUNIOR = 13
TEST_NOTIFICATION = 99
@ -24,21 +22,27 @@ NOTIFICATION_DICT = {
"title": "Successfully registered!",
"body": "You have registered successfully. Now login and complete your profile."
},
INVITATION: {
"title": "Invitation sent!",
"body": "Invitation has been successfully sent."
},
ASSOCIATE_REQUEST: {
"title": "Associate request!",
"body": "You have request from {from_user} to associate with you."
},
ASSOCIATE_REJECTED: {
"title": "Associate request rejected!",
"body": "Your request to associate has been rejected by {from_user}."
},
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!",
"body": "You earn 5 points for referral."
},
# Juniors will receive notification once any custodians add them in their account
SKIPPED_PROFILE_SETUP: {
JUNIOR_ADDED: {
"title": "Profile already setup!",
"body": "Your guardian has already setup your profile."
},
@ -47,34 +51,22 @@ NOTIFICATION_DICT = {
"title": "New task assigned!",
"body": "{from_user} has assigned you a new task."
},
TASK_SUBMITTED: {
"title": "Task submitted!",
"body": "Your task has been submitted for approval."
},
# Guardian will receive notification as soon as junior send task for approval
TASK_ACTION: {
"title": "Task completion approval!",
"body": "You have request from {from_user} for task completion."
},
# Juniors will receive notification as soon as their task is approved or reject by custodians
# 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}."
},
# Juniors will receive Notifications for every Points earned either by Task completion
# Juniors will receive notification as soon as their task is approved or reject by custodians
TASK_POINTS: {
"title": "Earned Task points!",
"body": "You earn 5 points for task."
},
# Juniors will receive Notification related to Leaderboard progress
LEADERBOARD_RANKING: {
"title": "Leader board rank!",
"body": "Your rank is ."
"body": "Your task completion request has been approved by {from_user}. "
"Also you earned 5 points for successful completion."
},
# Juniors will receive notification as soon as their custodians remove them from account
REMOVE_JUNIOR: {

View File

@ -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()),
]

View File

@ -30,15 +30,8 @@ class NotificationViewSet(viewsets.GenericViewSet):
paginator = self.pagination_class()
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)
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request):
"""
@ -62,36 +55,10 @@ class NotificationViewSet(viewsets.GenericViewSet):
{'task_id': None})
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=['get'], url_path='mark-as-read', url_name='mark-as-read', detail=True, )
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
Payload
{
"notification_id": []
}"""
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=kwargs['pk']).update(is_read=True)
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)