Merge pull request #77 from KiwiTechLLC/sprint3

Sprint3
This commit is contained in:
dilipshrivastwa-kiwi
2023-07-18 16:31:20 +05:30
committed by GitHub
14 changed files with 206 additions and 18 deletions

View File

@ -225,7 +225,7 @@ class JuniorSerializer(serializers.ModelSerializer):
return access_token return access_token
def get_refresh_token(self, obj): def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj.user) refresh = RefreshToken.for_user(obj.auth)
refresh_token = str(refresh) refresh_token = str(refresh)
return refresh_token return refresh_token
@ -248,7 +248,7 @@ class JuniorSerializer(serializers.ModelSerializer):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', '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']
class EmailVerificationSerializer(serializers.ModelSerializer): class EmailVerificationSerializer(serializers.ModelSerializer):

View File

@ -15,7 +15,7 @@
<tr> <tr>
<td style="padding: 0 27px 22px;"> <td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;"> <p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for join the ZOD bank platform. Please use <b>{{ url }} </b> link to join the platform. You are receiving this email for joining the ZOD bank platform. Please use <b>{{ url }} </b> link to join the platform.
<br> Your credentials are:- username = <b>{{email}}</b> and password <b>{{password}}</b><br> <br>Below are the steps to complete the account and how to use this platform. <br> Your credentials are:- username = <b>{{email}}</b> and password <b>{{password}}</b><br> <br>Below are the steps to complete the account and how to use this platform.
</p> </p>

View File

@ -24,7 +24,7 @@ NUMBER = {
'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19, 'twenty': 20, 'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19, 'twenty': 20,
'twenty_one': 21, 'twenty_two': 22,'twenty_three': 23, 'twenty_four': 24, 'twenty_five': 25, 'twenty_one': 21, 'twenty_two': 22,'twenty_three': 23, 'twenty_four': 24, 'twenty_five': 25,
'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 'eighty': 80, 'ninty': 90, 'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 'eighty': 80, 'ninty': 90,
'ninety_nine': 99, 'hundred': 100, 'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600
} }

View File

@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-07-18 07:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0015_alter_guardian_options_alter_juniortask_options'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='completed_on',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='juniortask',
name='rejected_on',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='juniortask',
name='requested_on',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@ -108,6 +108,12 @@ class JuniorTask(models.Model):
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
"""Task is approved or not""" """Task is approved or not"""
is_approved = models.BooleanField(default=False) is_approved = models.BooleanField(default=False)
"""request task on particular date"""
requested_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""reject task on particular date"""
rejected_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""complete task on particular date"""
completed_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""Profile created and updated time""" """Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)

View File

@ -6,6 +6,7 @@ from rest_framework import serializers
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction from django.db import transaction
from django.contrib.auth.models import User from django.contrib.auth.models import User
from datetime import datetime, time
"""Import Django app""" """Import Django app"""
# Import guardian's model, # Import guardian's model,
# Import junior's model, # Import junior's model,
@ -23,6 +24,8 @@ from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD from base.constants import NUMBER, JUN, ZOD, GRD
from junior.models import Junior, JuniorPoints from junior.models import Junior, JuniorPoints
from .utils import real_time, convert_timedelta_into_datetime
# In this serializer file # In this serializer file
@ -201,11 +204,27 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
"""Task detail serializer""" """Task detail serializer"""
junior = JuniorDetailSerializer() junior = JuniorDetailSerializer()
remaining_time = serializers.SerializerMethodField('get_remaining_time')
def get_remaining_time(self, obj):
""" remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
current_datetime = real_time()
# Perform the subtraction
if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = JuniorTask model = JuniorTask
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image', fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
'junior', 'task_status', 'is_active', 'created_at','updated_at'] 'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer): class TopJuniorSerializer(serializers.ModelSerializer):
@ -303,12 +322,14 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['five']) instance.task_status = str(NUMBER['five'])
instance.is_approved = True instance.is_approved = True
junior_data.total_task_points = junior_data.total_task_points + instance.points junior_data.total_task_points = junior_data.total_task_points + instance.points
instance.completed_on = datetime.today()
junior_data.save() junior_data.save()
else: else:
# reject the task # reject the task
instance.task_status = str(NUMBER['three']) instance.task_status = str(NUMBER['three'])
instance.is_approved = False instance.is_approved = False
junior_data.total_task_points = junior_data.total_task_points - instance.points junior_data.total_task_points = junior_data.total_task_points - instance.points
instance.rejected_on = datetime.today()
junior_data.save() junior_data.save()
instance.save() instance.save()
return instance return instance

View File

@ -5,6 +5,12 @@ import oss2
from django.conf import settings from django.conf import settings
"""Import tempfile""" """Import tempfile"""
import tempfile import tempfile
# Import date time module's function
from datetime import datetime, time
# Import real time client module
import ntplib
# import Number constant
from base.constants import NUMBER
# Define upload image on # Define upload image on
# ali baba cloud # ali baba cloud
@ -13,6 +19,8 @@ import tempfile
# then check bucket name # then check bucket name
# then upload on ali baba # then upload on ali baba
# bucket and reform the image url""" # bucket and reform the image url"""
# fetch real time data without depend on system time
# convert time delta into date time object
def upload_image_to_alibaba(image, filename): def upload_image_to_alibaba(image, filename):
"""upload image on oss alibaba bucket""" """upload image on oss alibaba bucket"""
@ -30,3 +38,22 @@ def upload_image_to_alibaba(image, filename):
new_filename = filename.replace(' ', '%20') new_filename = filename.replace(' ', '%20')
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}" return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
def real_time():
# Fetch real time.
# time is not depend on system time
# Get the current datetime
ntp_client = ntplib.NTPClient()
ntp_server = 'pool.ntp.org'
response = ntp_client.request(ntp_server)
current_datetime = datetime.fromtimestamp(response.tx_time)
return current_datetime
def convert_timedelta_into_datetime(time_difference):
# convert timedelta into datetime format
hours = time_difference.seconds // NUMBER['thirty_six_hundred']
minutes = (time_difference.seconds // NUMBER['sixty']) % NUMBER['sixty']
seconds = time_difference.seconds % NUMBER['sixty']
microseconds = time_difference.microseconds
# Create a new time object with the extracted time components
time_only = time(hours, minutes, seconds, microseconds)
return time_only

View File

@ -119,9 +119,16 @@ class TaskListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""Create guardian profile""" """Create guardian profile"""
status_value = self.request.GET.get('status') status_value = self.request.GET.get('status')
if str(status_value) == '0': 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') queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
else: elif search is None and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user, queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_status=status_value).order_by('due_date','created_at') task_status=status_value).order_by('due_date','created_at')
paginator = self.pagination_class() paginator = self.pagination_class()

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-18 09:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0013_alter_junior_options_alter_juniorpoints_options'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_password_set',
field=models.BooleanField(default=True),
),
]

View File

@ -63,6 +63,8 @@ class Junior(models.Model):
is_invited = models.BooleanField(default=False) is_invited = models.BooleanField(default=False)
# Profile activity""" # Profile activity"""
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
# check password is set or not
is_password_set = models.BooleanField(default=True)
# junior profile is complete or not""" # junior profile is complete or not"""
is_complete_profile = models.BooleanField(default=False) is_complete_profile = models.BooleanField(default=False)
# passcode of the junior profile""" # passcode of the junior profile"""

View File

@ -3,7 +3,7 @@
from rest_framework import serializers from rest_framework import serializers
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import transaction from django.db import transaction
import random from datetime import datetime
from django.utils import timezone from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
@ -277,12 +277,13 @@ class AddJuniorSerializer(serializers.ModelSerializer):
relationship=validated_data.get('relationship'), relationship=validated_data.get('relationship'),
junior_code=generate_code(JUN, user_data.id), junior_code=generate_code(JUN, user_data.id),
referral_code=generate_code(ZOD, user_data.id), referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code) referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True)
"""Generate otp""" """Generate otp"""
otp_value = generate_otp() otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1) expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value, UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type='1', expired_at=expiry_time) user_type='1', expired_at=expiry_time, is_verified=True)
"""Send email to the register user""" """Send email to the register user"""
send_otp_email(email, otp_value) send_otp_email(email, otp_value)
"""Notification email""" """Notification email"""
@ -305,3 +306,17 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
instance.save() instance.save()
return instance return instance
class CompleteTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'image')
def update(self, instance, validated_data):
instance.image = validated_data.get('image', instance.image)
instance.requested_on = datetime.today()
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
return instance

View File

@ -2,7 +2,8 @@
"""Django import""" """Django import"""
from django.urls import path, include from django.urls import path, include
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView, from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView) InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
CompleteJuniorTaskAPIView)
"""Third party import""" """Third party import"""
from rest_framework import routers from rest_framework import routers
@ -17,7 +18,8 @@ router = routers.SimpleRouter()
# junior list, # junior list,
# add junior list, invited junior, # add junior list, invited junior,
# filter-junior, # filter-junior,
# remove junior""" # remove junior,
# junior task list
"""API End points with router""" """API End points with router"""
router.register('create-junior-profile', UpdateJuniorProfile, basename='profile-update') router.register('create-junior-profile', UpdateJuniorProfile, basename='profile-update')
# validate guardian code API""" # validate guardian code API"""
@ -30,8 +32,11 @@ router.register('add-junior', AddJuniorAPIView, basename='add-junior')
router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior') router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior')
# Filter junior list API""" # Filter junior list API"""
router.register('filter-junior', FilterJuniorAPIView, basename='filter-junior') router.register('filter-junior', FilterJuniorAPIView, basename='filter-junior')
# junior's task list API"""
router.register('junior-task-list', JuniorTaskListAPIView, basename='junior-task-list')
# Define url pattern""" # Define url pattern"""
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()) path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()),
path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view())
] ]

View File

@ -17,8 +17,9 @@ from rest_framework.pagination import PageNumberPagination
# Import account's task # Import account's task
from junior.models import Junior from junior.models import Junior
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\ from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
RemoveJuniorSerializer) RemoveJuniorSerializer, CompleteTaskSerializer)
from guardian.models import Guardian from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER from base.constants import NUMBER
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response
@ -163,7 +164,7 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
class RemoveJuniorAPIView(views.APIView): class RemoveJuniorAPIView(views.APIView):
"""Eater Update API""" """Remove junior API"""
serializer_class = RemoveJuniorSerializer serializer_class = RemoveJuniorSerializer
model = Junior model = Junior
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@ -184,3 +185,60 @@ class RemoveJuniorAPIView(views.APIView):
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else: else:
return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST)
class JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
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':
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':
queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
queryset = JuniorTask.objects.filter(junior__auth=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)
class CompleteJuniorTaskAPIView(views.APIView):
"""Update junior task API"""
serializer_class = CompleteTaskSerializer
model = JuniorTask
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
task_id = self.request.data.get('task_id')
image = request.data['image']
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image.name}"
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()
if task_queryset:
# use RemoveJuniorSerializer serializer
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
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:
return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST)

View File

@ -105,11 +105,12 @@ REST_FRAMEWORK = {
'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.BasicAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',], 'rest_framework_simplejwt.authentication.JWTAuthentication',],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5, 'PAGE_SIZE': 10,
} }
# define jwt token # define jwt token
SIMPLE_JWT = { SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=2, minutes=59, seconds=59, microseconds=999999), # 'ACCESS_TOKEN_LIFETIME': timedelta(hours=2, minutes=59, seconds=59, microseconds=999999),
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(hours=71, minutes=59, seconds=59, microseconds=999999), 'REFRESH_TOKEN_LIFETIME': timedelta(hours=71, minutes=59, seconds=59, microseconds=999999),
} }
# Database # Database