diff --git a/account/serializers.py b/account/serializers.py
index 11944e6..5810f1d 100644
--- a/account/serializers.py
+++ b/account/serializers.py
@@ -225,7 +225,7 @@ class JuniorSerializer(serializers.ModelSerializer):
return access_token
def get_refresh_token(self, obj):
- refresh = RefreshToken.for_user(obj.user)
+ refresh = RefreshToken.for_user(obj.auth)
refresh_token = str(refresh)
return refresh_token
@@ -248,7 +248,7 @@ class JuniorSerializer(serializers.ModelSerializer):
"""Meta info"""
model = Junior
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']
class EmailVerificationSerializer(serializers.ModelSerializer):
diff --git a/account/templates/templated_email/junior_notification_email.email b/account/templates/templated_email/junior_notification_email.email
index ea9c214..9f489ce 100644
--- a/account/templates/templated_email/junior_notification_email.email
+++ b/account/templates/templated_email/junior_notification_email.email
@@ -15,7 +15,7 @@
- You are receiving this email for join the ZOD bank platform. Please use {{ url }} link to join the platform.
+ You are receiving this email for joining the ZOD bank platform. Please use {{ url }} link to join the platform.
Your credentials are:- username = {{email}} and password {{password}} Below are the steps to complete the account and how to use this platform.
diff --git a/base/constants.py b/base/constants.py
index d8ca8a8..29da8db 100644
--- a/base/constants.py
+++ b/base/constants.py
@@ -24,7 +24,7 @@ NUMBER = {
'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,
'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
}
diff --git a/guardian/migrations/0016_juniortask_completed_on_juniortask_rejected_on_and_more.py b/guardian/migrations/0016_juniortask_completed_on_juniortask_rejected_on_and_more.py
new file mode 100644
index 0000000..2223e0e
--- /dev/null
+++ b/guardian/migrations/0016_juniortask_completed_on_juniortask_rejected_on_and_more.py
@@ -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),
+ ),
+ ]
diff --git a/guardian/models.py b/guardian/models.py
index 1746c44..b4a31dd 100644
--- a/guardian/models.py
+++ b/guardian/models.py
@@ -108,6 +108,12 @@ class JuniorTask(models.Model):
is_active = models.BooleanField(default=True)
"""Task is approved or not"""
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"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
diff --git a/guardian/serializers.py b/guardian/serializers.py
index ae96969..9459334 100644
--- a/guardian/serializers.py
+++ b/guardian/serializers.py
@@ -6,6 +6,7 @@ from rest_framework import serializers
from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction
from django.contrib.auth.models import User
+from datetime import datetime, time
"""Import Django app"""
# Import guardian'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.constants import NUMBER, JUN, ZOD, GRD
from junior.models import Junior, JuniorPoints
+from .utils import real_time, convert_timedelta_into_datetime
+
# In this serializer file
@@ -201,11 +204,27 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
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):
"""Meta info"""
model = JuniorTask
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):
@@ -303,12 +322,14 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['five'])
instance.is_approved = True
junior_data.total_task_points = junior_data.total_task_points + instance.points
+ instance.completed_on = datetime.today()
junior_data.save()
else:
# reject the task
instance.task_status = str(NUMBER['three'])
instance.is_approved = False
junior_data.total_task_points = junior_data.total_task_points - instance.points
+ instance.rejected_on = datetime.today()
junior_data.save()
instance.save()
return instance
diff --git a/guardian/utils.py b/guardian/utils.py
index c3f9f39..8619b66 100644
--- a/guardian/utils.py
+++ b/guardian/utils.py
@@ -5,6 +5,12 @@ import oss2
from django.conf import settings
"""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
# ali baba cloud
@@ -13,6 +19,8 @@ import tempfile
# then check bucket name
# then upload on ali baba
# 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):
"""upload image on oss alibaba bucket"""
@@ -30,3 +38,22 @@ def upload_image_to_alibaba(image, filename):
new_filename = filename.replace(' ', '%20')
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
diff --git a/guardian/views.py b/guardian/views.py
index 3450be7..ec14ef7 100644
--- a/guardian/views.py
+++ b/guardian/views.py
@@ -119,9 +119,16 @@ class TaskListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
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')
- else:
+ 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()
diff --git a/junior/migrations/0014_junior_is_password_set.py b/junior/migrations/0014_junior_is_password_set.py
new file mode 100644
index 0000000..a71e6c0
--- /dev/null
+++ b/junior/migrations/0014_junior_is_password_set.py
@@ -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),
+ ),
+ ]
diff --git a/junior/models.py b/junior/models.py
index 41dddc8..f4ef488 100644
--- a/junior/models.py
+++ b/junior/models.py
@@ -63,6 +63,8 @@ class Junior(models.Model):
is_invited = models.BooleanField(default=False)
# Profile activity"""
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"""
is_complete_profile = models.BooleanField(default=False)
# passcode of the junior profile"""
diff --git a/junior/serializers.py b/junior/serializers.py
index 013cd58..011cf97 100644
--- a/junior/serializers.py
+++ b/junior/serializers.py
@@ -3,7 +3,7 @@
from rest_framework import serializers
from django.contrib.auth.models import User
from django.db import transaction
-import random
+from datetime import datetime
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
@@ -277,12 +277,13 @@ class AddJuniorSerializer(serializers.ModelSerializer):
relationship=validated_data.get('relationship'),
junior_code=generate_code(JUN, 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"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
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_otp_email(email, otp_value)
"""Notification email"""
@@ -305,3 +306,17 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
instance.save()
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
diff --git a/junior/urls.py b/junior/urls.py
index ecedb1a..013b497 100644
--- a/junior/urls.py
+++ b/junior/urls.py
@@ -2,7 +2,8 @@
"""Django import"""
from django.urls import path, include
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
- InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView)
+ InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
+ CompleteJuniorTaskAPIView)
"""Third party import"""
from rest_framework import routers
@@ -17,7 +18,8 @@ router = routers.SimpleRouter()
# junior list,
# add junior list, invited junior,
# filter-junior,
-# remove junior"""
+# remove junior,
+# junior task list
"""API End points with router"""
router.register('create-junior-profile', UpdateJuniorProfile, basename='profile-update')
# validate guardian code API"""
@@ -30,8 +32,11 @@ router.register('add-junior', AddJuniorAPIView, basename='add-junior')
router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior')
# Filter junior list API"""
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"""
urlpatterns = [
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())
]
diff --git a/junior/views.py b/junior/views.py
index d77727b..6e4c494 100644
--- a/junior/views.py
+++ b/junior/views.py
@@ -17,8 +17,9 @@ from rest_framework.pagination import PageNumberPagination
# Import account's task
from junior.models import Junior
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
- RemoveJuniorSerializer)
-from guardian.models import Guardian
+ RemoveJuniorSerializer, CompleteTaskSerializer)
+from guardian.models import Guardian, JuniorTask
+from guardian.serializers import TaskDetailsSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
from account.utils import custom_response, custom_error_response
@@ -163,7 +164,7 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
class RemoveJuniorAPIView(views.APIView):
- """Eater Update API"""
+ """Remove junior API"""
serializer_class = RemoveJuniorSerializer
model = Junior
permission_classes = [IsAuthenticated]
@@ -184,3 +185,60 @@ class RemoveJuniorAPIView(views.APIView):
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)
+
+
+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)
diff --git a/zod_bank/settings.py b/zod_bank/settings.py
index aea5896..39ddbd2 100644
--- a/zod_bank/settings.py
+++ b/zod_bank/settings.py
@@ -105,11 +105,12 @@ REST_FRAMEWORK = {
'rest_framework.authentication.BasicAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
- 'PAGE_SIZE': 5,
+ 'PAGE_SIZE': 10,
}
# define jwt token
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),
}
# Database
|